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 17 static 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 67 static 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 77 enum { 78 CCM_BITOP_NONE = 0x00, 79 CCM_BITOP_SET = 0x04, 80 CCM_BITOP_CLR = 0x08, 81 CCM_BITOP_TOG = 0x0C, 82 }; 83 84 static 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 92 static 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 115 static 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 132 static 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 142 static 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 157 static 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 199 static 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 209 static 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 224 static 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 236 static 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 244 static 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 255 static 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 264 static 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 272 static void imx7_ccm_register_type(void) 273 { 274 type_register_static(&imx7_ccm_info); 275 type_register_static(&imx7_analog_info); 276 } 277 type_init(imx7_ccm_register_type) 278