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