imx7_ccm.c (8851B)
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 19static 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 69static 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 79enum { 80 CCM_BITOP_NONE = 0x00, 81 CCM_BITOP_SET = 0x04, 82 CCM_BITOP_CLR = 0x08, 83 CCM_BITOP_TOG = 0x0C, 84}; 85 86static 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 94static 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 117static 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 134static void imx7_digprog_write(void *opaque, hwaddr addr, 135 uint64_t data, unsigned size) 136{ 137 qemu_log_mask(LOG_GUEST_ERROR, 138 "Guest write to read-only ANALOG_DIGPROG register\n"); 139} 140 141static const struct MemoryRegionOps imx7_digprog_ops = { 142 .read = imx7_set_clr_tog_read, 143 .write = imx7_digprog_write, 144 .endianness = DEVICE_NATIVE_ENDIAN, 145 .impl = { 146 .min_access_size = 4, 147 .max_access_size = 4, 148 .unaligned = false, 149 }, 150}; 151 152static void imx7_ccm_init(Object *obj) 153{ 154 SysBusDevice *sd = SYS_BUS_DEVICE(obj); 155 IMX7CCMState *s = IMX7_CCM(obj); 156 157 memory_region_init_io(&s->iomem, 158 obj, 159 &imx7_set_clr_tog_ops, 160 s->ccm, 161 TYPE_IMX7_CCM ".ccm", 162 sizeof(s->ccm)); 163 164 sysbus_init_mmio(sd, &s->iomem); 165} 166 167static void imx7_analog_init(Object *obj) 168{ 169 SysBusDevice *sd = SYS_BUS_DEVICE(obj); 170 IMX7AnalogState *s = IMX7_ANALOG(obj); 171 172 memory_region_init(&s->mmio.container, obj, TYPE_IMX7_ANALOG, 173 0x10000); 174 175 memory_region_init_io(&s->mmio.analog, 176 obj, 177 &imx7_set_clr_tog_ops, 178 s->analog, 179 TYPE_IMX7_ANALOG, 180 sizeof(s->analog)); 181 182 memory_region_add_subregion(&s->mmio.container, 183 0x60, &s->mmio.analog); 184 185 memory_region_init_io(&s->mmio.pmu, 186 obj, 187 &imx7_set_clr_tog_ops, 188 s->pmu, 189 TYPE_IMX7_ANALOG ".pmu", 190 sizeof(s->pmu)); 191 192 memory_region_add_subregion(&s->mmio.container, 193 0x200, &s->mmio.pmu); 194 195 memory_region_init_io(&s->mmio.digprog, 196 obj, 197 &imx7_digprog_ops, 198 &s->analog[ANALOG_DIGPROG], 199 TYPE_IMX7_ANALOG ".digprog", 200 sizeof(uint32_t)); 201 202 memory_region_add_subregion_overlap(&s->mmio.container, 203 0x800, &s->mmio.digprog, 10); 204 205 206 sysbus_init_mmio(sd, &s->mmio.container); 207} 208 209static const VMStateDescription vmstate_imx7_ccm = { 210 .name = TYPE_IMX7_CCM, 211 .version_id = 1, 212 .minimum_version_id = 1, 213 .fields = (VMStateField[]) { 214 VMSTATE_UINT32_ARRAY(ccm, IMX7CCMState, CCM_MAX), 215 VMSTATE_END_OF_LIST() 216 }, 217}; 218 219static uint32_t imx7_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) 220{ 221 /* 222 * This function is "consumed" by GPT emulation code, however on 223 * i.MX7 each GPT block can have their own clock root. This means 224 * that this functions needs somehow to know requester's identity 225 * and the way to pass it: be it via additional IMXClk constants 226 * or by adding another argument to this method needs to be 227 * figured out 228 */ 229 qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Not implemented\n", 230 TYPE_IMX7_CCM, __func__); 231 return 0; 232} 233 234static void imx7_ccm_class_init(ObjectClass *klass, void *data) 235{ 236 DeviceClass *dc = DEVICE_CLASS(klass); 237 IMXCCMClass *ccm = IMX_CCM_CLASS(klass); 238 239 dc->reset = imx7_ccm_reset; 240 dc->vmsd = &vmstate_imx7_ccm; 241 dc->desc = "i.MX7 Clock Control Module"; 242 243 ccm->get_clock_frequency = imx7_ccm_get_clock_frequency; 244} 245 246static const TypeInfo imx7_ccm_info = { 247 .name = TYPE_IMX7_CCM, 248 .parent = TYPE_IMX_CCM, 249 .instance_size = sizeof(IMX7CCMState), 250 .instance_init = imx7_ccm_init, 251 .class_init = imx7_ccm_class_init, 252}; 253 254static const VMStateDescription vmstate_imx7_analog = { 255 .name = TYPE_IMX7_ANALOG, 256 .version_id = 1, 257 .minimum_version_id = 1, 258 .fields = (VMStateField[]) { 259 VMSTATE_UINT32_ARRAY(analog, IMX7AnalogState, ANALOG_MAX), 260 VMSTATE_UINT32_ARRAY(pmu, IMX7AnalogState, PMU_MAX), 261 VMSTATE_END_OF_LIST() 262 }, 263}; 264 265static void imx7_analog_class_init(ObjectClass *klass, void *data) 266{ 267 DeviceClass *dc = DEVICE_CLASS(klass); 268 269 dc->reset = imx7_analog_reset; 270 dc->vmsd = &vmstate_imx7_analog; 271 dc->desc = "i.MX7 Analog Module"; 272} 273 274static const TypeInfo imx7_analog_info = { 275 .name = TYPE_IMX7_ANALOG, 276 .parent = TYPE_SYS_BUS_DEVICE, 277 .instance_size = sizeof(IMX7AnalogState), 278 .instance_init = imx7_analog_init, 279 .class_init = imx7_analog_class_init, 280}; 281 282static void imx7_ccm_register_type(void) 283{ 284 type_register_static(&imx7_ccm_info); 285 type_register_static(&imx7_analog_info); 286} 287type_init(imx7_ccm_register_type)