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