qemu/hw/misc/imx7_ccm.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2018, Impinj, Inc.
   3 *
   4 * i.MX7 CCM, PMU and ANALOG IP blocks emulation code
   5 *
   6 * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
   7 *
   8 * This work is licensed under the terms of the GNU GPL, version 2 or later.
   9 * See the COPYING file in the top-level directory.
  10 */
  11
  12#include "qemu/osdep.h"
  13#include "qemu/log.h"
  14
  15#include "hw/misc/imx7_ccm.h"
  16
  17static void imx7_analog_reset(DeviceState *dev)
  18{
  19    IMX7AnalogState *s = IMX7_ANALOG(dev);
  20
  21    memset(s->pmu, 0, sizeof(s->pmu));
  22    memset(s->analog, 0, sizeof(s->analog));
  23
  24    s->analog[ANALOG_PLL_ARM]         = 0x00002042;
  25    s->analog[ANALOG_PLL_DDR]         = 0x0060302c;
  26    s->analog[ANALOG_PLL_DDR_SS]      = 0x00000000;
  27    s->analog[ANALOG_PLL_DDR_NUM]     = 0x06aaac4d;
  28    s->analog[ANALOG_PLL_DDR_DENOM]   = 0x100003ec;
  29    s->analog[ANALOG_PLL_480]         = 0x00002000;
  30    s->analog[ANALOG_PLL_480A]        = 0x52605a56;
  31    s->analog[ANALOG_PLL_480B]        = 0x52525216;
  32    s->analog[ANALOG_PLL_ENET]        = 0x00001fc0;
  33    s->analog[ANALOG_PLL_AUDIO]       = 0x0001301b;
  34    s->analog[ANALOG_PLL_AUDIO_SS]    = 0x00000000;
  35    s->analog[ANALOG_PLL_AUDIO_NUM]   = 0x05f5e100;
  36    s->analog[ANALOG_PLL_AUDIO_DENOM] = 0x2964619c;
  37    s->analog[ANALOG_PLL_VIDEO]       = 0x0008201b;
  38    s->analog[ANALOG_PLL_VIDEO_SS]    = 0x00000000;
  39    s->analog[ANALOG_PLL_VIDEO_NUM]   = 0x0000f699;
  40    s->analog[ANALOG_PLL_VIDEO_DENOM] = 0x000f4240;
  41    s->analog[ANALOG_PLL_MISC0]       = 0x00000000;
  42
  43    /* all PLLs need to be locked */
  44    s->analog[ANALOG_PLL_ARM]   |= ANALOG_PLL_LOCK;
  45    s->analog[ANALOG_PLL_DDR]   |= ANALOG_PLL_LOCK;
  46    s->analog[ANALOG_PLL_480]   |= ANALOG_PLL_LOCK;
  47    s->analog[ANALOG_PLL_480A]  |= ANALOG_PLL_LOCK;
  48    s->analog[ANALOG_PLL_480B]  |= ANALOG_PLL_LOCK;
  49    s->analog[ANALOG_PLL_ENET]  |= ANALOG_PLL_LOCK;
  50    s->analog[ANALOG_PLL_AUDIO] |= ANALOG_PLL_LOCK;
  51    s->analog[ANALOG_PLL_VIDEO] |= ANALOG_PLL_LOCK;
  52    s->analog[ANALOG_PLL_MISC0] |= ANALOG_PLL_LOCK;
  53
  54    /*
  55     * Since I couldn't find any info about this in the reference
  56     * manual the value of this register is based strictly on matching
  57     * what Linux kernel expects it to be.
  58     */
  59    s->analog[ANALOG_DIGPROG]  = 0x720000;
  60    /*
  61     * Set revision to be 1.0 (Arbitrary choice, no particular
  62     * reason).
  63     */
  64    s->analog[ANALOG_DIGPROG] |= 0x000010;
  65}
  66
  67static void imx7_ccm_reset(DeviceState *dev)
  68{
  69    IMX7CCMState *s = IMX7_CCM(dev);
  70
  71    memset(s->ccm, 0, sizeof(s->ccm));
  72}
  73
  74#define CCM_INDEX(offset)   (((offset) & ~(hwaddr)0xF) / sizeof(uint32_t))
  75#define CCM_BITOP(offset)   ((offset) & (hwaddr)0xF)
  76
  77enum {
  78    CCM_BITOP_NONE = 0x00,
  79    CCM_BITOP_SET  = 0x04,
  80    CCM_BITOP_CLR  = 0x08,
  81    CCM_BITOP_TOG  = 0x0C,
  82};
  83
  84static uint64_t imx7_set_clr_tog_read(void *opaque, hwaddr offset,
  85                                      unsigned size)
  86{
  87    const uint32_t *mmio = opaque;
  88
  89    return mmio[CCM_INDEX(offset)];
  90}
  91
  92static void imx7_set_clr_tog_write(void *opaque, hwaddr offset,
  93                                   uint64_t value, unsigned size)
  94{
  95    const uint8_t  bitop = CCM_BITOP(offset);
  96    const uint32_t index = CCM_INDEX(offset);
  97    uint32_t *mmio = opaque;
  98
  99    switch (bitop) {
 100    case CCM_BITOP_NONE:
 101        mmio[index]  = value;
 102        break;
 103    case CCM_BITOP_SET:
 104        mmio[index] |= value;
 105        break;
 106    case CCM_BITOP_CLR:
 107        mmio[index] &= ~value;
 108        break;
 109    case CCM_BITOP_TOG:
 110        mmio[index] ^= value;
 111        break;
 112    };
 113}
 114
 115static const struct MemoryRegionOps imx7_set_clr_tog_ops = {
 116    .read = imx7_set_clr_tog_read,
 117    .write = imx7_set_clr_tog_write,
 118    .endianness = DEVICE_NATIVE_ENDIAN,
 119    .impl = {
 120        /*
 121         * Our device would not work correctly if the guest was doing
 122         * unaligned access. This might not be a limitation on the real
 123         * device but in practice there is no reason for a guest to access
 124         * this device unaligned.
 125         */
 126        .min_access_size = 4,
 127        .max_access_size = 4,
 128        .unaligned = false,
 129    },
 130};
 131
 132static const struct MemoryRegionOps imx7_digprog_ops = {
 133    .read = imx7_set_clr_tog_read,
 134    .endianness = DEVICE_NATIVE_ENDIAN,
 135    .impl = {
 136        .min_access_size = 4,
 137        .max_access_size = 4,
 138        .unaligned = false,
 139    },
 140};
 141
 142static void imx7_ccm_init(Object *obj)
 143{
 144    SysBusDevice *sd = SYS_BUS_DEVICE(obj);
 145    IMX7CCMState *s = IMX7_CCM(obj);
 146
 147    memory_region_init_io(&s->iomem,
 148                          obj,
 149                          &imx7_set_clr_tog_ops,
 150                          s->ccm,
 151                          TYPE_IMX7_CCM ".ccm",
 152                          sizeof(s->ccm));
 153
 154    sysbus_init_mmio(sd, &s->iomem);
 155}
 156
 157static void imx7_analog_init(Object *obj)
 158{
 159    SysBusDevice *sd = SYS_BUS_DEVICE(obj);
 160    IMX7AnalogState *s = IMX7_ANALOG(obj);
 161
 162    memory_region_init(&s->mmio.container, obj, TYPE_IMX7_ANALOG,
 163                       0x10000);
 164
 165    memory_region_init_io(&s->mmio.analog,
 166                          obj,
 167                          &imx7_set_clr_tog_ops,
 168                          s->analog,
 169                          TYPE_IMX7_ANALOG,
 170                          sizeof(s->analog));
 171
 172    memory_region_add_subregion(&s->mmio.container,
 173                                0x60, &s->mmio.analog);
 174
 175    memory_region_init_io(&s->mmio.pmu,
 176                          obj,
 177                          &imx7_set_clr_tog_ops,
 178                          s->pmu,
 179                          TYPE_IMX7_ANALOG ".pmu",
 180                          sizeof(s->pmu));
 181
 182    memory_region_add_subregion(&s->mmio.container,
 183                                0x200, &s->mmio.pmu);
 184
 185    memory_region_init_io(&s->mmio.digprog,
 186                          obj,
 187                          &imx7_digprog_ops,
 188                          &s->analog[ANALOG_DIGPROG],
 189                          TYPE_IMX7_ANALOG ".digprog",
 190                          sizeof(uint32_t));
 191
 192    memory_region_add_subregion_overlap(&s->mmio.container,
 193                                        0x800, &s->mmio.digprog, 10);
 194
 195
 196    sysbus_init_mmio(sd, &s->mmio.container);
 197}
 198
 199static const VMStateDescription vmstate_imx7_ccm = {
 200    .name = TYPE_IMX7_CCM,
 201    .version_id = 1,
 202    .minimum_version_id = 1,
 203    .fields = (VMStateField[]) {
 204        VMSTATE_UINT32_ARRAY(ccm, IMX7CCMState, CCM_MAX),
 205        VMSTATE_END_OF_LIST()
 206    },
 207};
 208
 209static uint32_t imx7_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
 210{
 211    /*
 212     * This function is "consumed" by GPT emulation code, however on
 213     * i.MX7 each GPT block can have their own clock root. This means
 214     * that this functions needs somehow to know requester's identity
 215     * and the way to pass it: be it via additional IMXClk constants
 216     * or by adding another argument to this method needs to be
 217     * figured out
 218     */
 219    qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Not implemented\n",
 220                  TYPE_IMX7_CCM, __func__);
 221    return 0;
 222}
 223
 224static void imx7_ccm_class_init(ObjectClass *klass, void *data)
 225{
 226    DeviceClass *dc = DEVICE_CLASS(klass);
 227    IMXCCMClass *ccm = IMX_CCM_CLASS(klass);
 228
 229    dc->reset = imx7_ccm_reset;
 230    dc->vmsd  = &vmstate_imx7_ccm;
 231    dc->desc  = "i.MX7 Clock Control Module";
 232
 233    ccm->get_clock_frequency = imx7_ccm_get_clock_frequency;
 234}
 235
 236static const TypeInfo imx7_ccm_info = {
 237    .name          = TYPE_IMX7_CCM,
 238    .parent        = TYPE_IMX_CCM,
 239    .instance_size = sizeof(IMX7CCMState),
 240    .instance_init = imx7_ccm_init,
 241    .class_init    = imx7_ccm_class_init,
 242};
 243
 244static const VMStateDescription vmstate_imx7_analog = {
 245    .name = TYPE_IMX7_ANALOG,
 246    .version_id = 1,
 247    .minimum_version_id = 1,
 248    .fields = (VMStateField[]) {
 249        VMSTATE_UINT32_ARRAY(analog, IMX7AnalogState, ANALOG_MAX),
 250        VMSTATE_UINT32_ARRAY(pmu,    IMX7AnalogState, PMU_MAX),
 251        VMSTATE_END_OF_LIST()
 252    },
 253};
 254
 255static void imx7_analog_class_init(ObjectClass *klass, void *data)
 256{
 257    DeviceClass *dc = DEVICE_CLASS(klass);
 258
 259    dc->reset = imx7_analog_reset;
 260    dc->vmsd  = &vmstate_imx7_analog;
 261    dc->desc  = "i.MX7 Analog Module";
 262}
 263
 264static const TypeInfo imx7_analog_info = {
 265    .name          = TYPE_IMX7_ANALOG,
 266    .parent        = TYPE_SYS_BUS_DEVICE,
 267    .instance_size = sizeof(IMX7AnalogState),
 268    .instance_init = imx7_analog_init,
 269    .class_init    = imx7_analog_class_init,
 270};
 271
 272static void imx7_ccm_register_type(void)
 273{
 274    type_register_static(&imx7_ccm_info);
 275    type_register_static(&imx7_analog_info);
 276}
 277type_init(imx7_ccm_register_type)
 278