1 /* 2 * Copyright (c) 2025 Bernhard Beschow <shentey@gmail.com> 3 * 4 * i.MX 8M Plus CCM IP block emulation code 5 * 6 * Based on hw/misc/imx7_ccm.c 7 * 8 * SPDX-License-Identifier: GPL-2.0-or-later 9 */ 10 11 #include "qemu/osdep.h" 12 #include "qemu/log.h" 13 14 #include "hw/misc/imx8mp_ccm.h" 15 #include "migration/vmstate.h" 16 17 #include "trace.h" 18 19 #define CKIH_FREQ 16000000 /* 16MHz crystal input */ 20 21 static void imx8mp_ccm_reset(DeviceState *dev) 22 { 23 IMX8MPCCMState *s = IMX8MP_CCM(dev); 24 25 memset(s->ccm, 0, sizeof(s->ccm)); 26 } 27 28 #define CCM_INDEX(offset) (((offset) & ~(hwaddr)0xF) / sizeof(uint32_t)) 29 #define CCM_BITOP(offset) ((offset) & (hwaddr)0xF) 30 31 enum { 32 CCM_BITOP_NONE = 0x00, 33 CCM_BITOP_SET = 0x04, 34 CCM_BITOP_CLR = 0x08, 35 CCM_BITOP_TOG = 0x0C, 36 }; 37 38 static uint64_t imx8mp_set_clr_tog_read(void *opaque, hwaddr offset, 39 unsigned size) 40 { 41 const uint32_t *mmio = opaque; 42 43 return mmio[CCM_INDEX(offset)]; 44 } 45 46 static void imx8mp_set_clr_tog_write(void *opaque, hwaddr offset, 47 uint64_t value, unsigned size) 48 { 49 const uint8_t bitop = CCM_BITOP(offset); 50 const uint32_t index = CCM_INDEX(offset); 51 uint32_t *mmio = opaque; 52 53 switch (bitop) { 54 case CCM_BITOP_NONE: 55 mmio[index] = value; 56 break; 57 case CCM_BITOP_SET: 58 mmio[index] |= value; 59 break; 60 case CCM_BITOP_CLR: 61 mmio[index] &= ~value; 62 break; 63 case CCM_BITOP_TOG: 64 mmio[index] ^= value; 65 break; 66 }; 67 } 68 69 static const struct MemoryRegionOps imx8mp_set_clr_tog_ops = { 70 .read = imx8mp_set_clr_tog_read, 71 .write = imx8mp_set_clr_tog_write, 72 .endianness = DEVICE_NATIVE_ENDIAN, 73 .impl = { 74 /* 75 * Our device would not work correctly if the guest was doing 76 * unaligned access. This might not be a limitation on the real 77 * device but in practice there is no reason for a guest to access 78 * this device unaligned. 79 */ 80 .min_access_size = 4, 81 .max_access_size = 4, 82 .unaligned = false, 83 }, 84 }; 85 86 static void imx8mp_ccm_init(Object *obj) 87 { 88 SysBusDevice *sd = SYS_BUS_DEVICE(obj); 89 IMX8MPCCMState *s = IMX8MP_CCM(obj); 90 91 memory_region_init_io(&s->iomem, 92 obj, 93 &imx8mp_set_clr_tog_ops, 94 s->ccm, 95 TYPE_IMX8MP_CCM ".ccm", 96 sizeof(s->ccm)); 97 98 sysbus_init_mmio(sd, &s->iomem); 99 } 100 101 static const VMStateDescription imx8mp_ccm_vmstate = { 102 .name = TYPE_IMX8MP_CCM, 103 .version_id = 1, 104 .minimum_version_id = 1, 105 .fields = (const VMStateField[]) { 106 VMSTATE_UINT32_ARRAY(ccm, IMX8MPCCMState, CCM_MAX), 107 VMSTATE_END_OF_LIST() 108 }, 109 }; 110 111 static uint32_t imx8mp_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) 112 { 113 /* 114 * This function is "consumed" by GPT emulation code. Some clocks 115 * have fixed frequencies and we can provide requested frequency 116 * easily. However for CCM provided clocks (like IPG) each GPT 117 * timer can have its own clock root. 118 * This means we need additional information when calling this 119 * function to know the requester's identity. 120 */ 121 uint32_t freq = 0; 122 123 switch (clock) { 124 case CLK_NONE: 125 break; 126 case CLK_32k: 127 freq = CKIL_FREQ; 128 break; 129 case CLK_HIGH: 130 freq = CKIH_FREQ; 131 break; 132 case CLK_IPG: 133 case CLK_IPG_HIGH: 134 /* 135 * For now we don't have a way to figure out the device this 136 * function is called for. Until then the IPG derived clocks 137 * are left unimplemented. 138 */ 139 qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Clock %d Not implemented\n", 140 TYPE_IMX8MP_CCM, __func__, clock); 141 break; 142 default: 143 qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n", 144 TYPE_IMX8MP_CCM, __func__, clock); 145 break; 146 } 147 148 trace_ccm_clock_freq(clock, freq); 149 150 return freq; 151 } 152 153 static void imx8mp_ccm_class_init(ObjectClass *klass, void *data) 154 { 155 DeviceClass *dc = DEVICE_CLASS(klass); 156 IMXCCMClass *ccm = IMX_CCM_CLASS(klass); 157 158 device_class_set_legacy_reset(dc, imx8mp_ccm_reset); 159 dc->vmsd = &imx8mp_ccm_vmstate; 160 dc->desc = "i.MX 8M Plus Clock Control Module"; 161 162 ccm->get_clock_frequency = imx8mp_ccm_get_clock_frequency; 163 } 164 165 static const TypeInfo imx8mp_ccm_types[] = { 166 { 167 .name = TYPE_IMX8MP_CCM, 168 .parent = TYPE_IMX_CCM, 169 .instance_size = sizeof(IMX8MPCCMState), 170 .instance_init = imx8mp_ccm_init, 171 .class_init = imx8mp_ccm_class_init, 172 }, 173 }; 174 175 DEFINE_TYPES(imx8mp_ccm_types); 176