master
c 178 lines 4.73 KB
Raw
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_CAN:
133 freq = 40000000; /* 40Mhz, taken from device tree */
134 break;
135 case CLK_IPG:
136 case CLK_IPG_HIGH:
137 /*
138 * For now we don't have a way to figure out the device this
139 * function is called for. Until then the IPG derived clocks
140 * are left unimplemented.
141 */
142 qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Clock %d Not implemented\n",
143 TYPE_IMX8MP_CCM, __func__, clock);
144 break;
145 default:
146 qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n",
147 TYPE_IMX8MP_CCM, __func__, clock);
148 break;
149 }
150
151 trace_ccm_clock_freq(clock, freq);
152
153 return freq;
154 }
155
156 static void imx8mp_ccm_class_init(ObjectClass *klass, const void *data)
157 {
158 DeviceClass *dc = DEVICE_CLASS(klass);
159 IMXCCMClass *ccm = IMX_CCM_CLASS(klass);
160
161 device_class_set_legacy_reset(dc, imx8mp_ccm_reset);
162 dc->vmsd = &imx8mp_ccm_vmstate;
163 dc->desc = "i.MX 8M Plus Clock Control Module";
164
165 ccm->get_clock_frequency = imx8mp_ccm_get_clock_frequency;
166 }
167
168 static const TypeInfo imx8mp_ccm_types[] = {
169 {
170 .name = TYPE_IMX8MP_CCM,
171 .parent = TYPE_IMX_CCM,
172 .instance_size = sizeof(IMX8MPCCMState),
173 .instance_init = imx8mp_ccm_init,
174 .class_init = imx8mp_ccm_class_init,
175 },
176 };
177
178 DEFINE_TYPES(imx8mp_ccm_types);