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