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