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 18 static 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 68 static 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 78 enum { 79 CCM_BITOP_NONE = 0x00, 80 CCM_BITOP_SET = 0x04, 81 CCM_BITOP_CLR = 0x08, 82 CCM_BITOP_TOG = 0x0C, 83 }; 84 85 static 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 93 static 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 116 static 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 133 static 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 143 static 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 158 static 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 200 static 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 210 static 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 225 static 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 237 static 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 245 static 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 256 static 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 265 static 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 273 static void imx7_ccm_register_type(void) 274 { 275 type_register_static(&imx7_ccm_info); 276 type_register_static(&imx7_analog_info); 277 } 278 type_init(imx7_ccm_register_type) 279