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