2 * Copyright (c) 2018, Impinj, Inc.
4 * i.MX7 CCM, PMU and ANALOG IP blocks emulation code
6 * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
8 * This work is licensed under the terms of the GNU GPL, version 2 or later.
9 * See the COPYING file in the top-level directory.
12 #include "qemu/osdep.h"
14 #include "qemu/module.h"
16 #include "hw/misc/imx7_ccm.h"
17 #include "migration/vmstate.h"
21 #define CKIH_FREQ 24000000 /* 24MHz crystal input */
23 static void imx7_analog_reset(DeviceState
*dev
)
25 IMX7AnalogState
*s
= IMX7_ANALOG(dev
);
27 memset(s
->pmu
, 0, sizeof(s
->pmu
));
28 memset(s
->analog
, 0, sizeof(s
->analog
));
30 s
->analog
[ANALOG_PLL_ARM
] = 0x00002042;
31 s
->analog
[ANALOG_PLL_DDR
] = 0x0060302c;
32 s
->analog
[ANALOG_PLL_DDR_SS
] = 0x00000000;
33 s
->analog
[ANALOG_PLL_DDR_NUM
] = 0x06aaac4d;
34 s
->analog
[ANALOG_PLL_DDR_DENOM
] = 0x100003ec;
35 s
->analog
[ANALOG_PLL_480
] = 0x00002000;
36 s
->analog
[ANALOG_PLL_480A
] = 0x52605a56;
37 s
->analog
[ANALOG_PLL_480B
] = 0x52525216;
38 s
->analog
[ANALOG_PLL_ENET
] = 0x00001fc0;
39 s
->analog
[ANALOG_PLL_AUDIO
] = 0x0001301b;
40 s
->analog
[ANALOG_PLL_AUDIO_SS
] = 0x00000000;
41 s
->analog
[ANALOG_PLL_AUDIO_NUM
] = 0x05f5e100;
42 s
->analog
[ANALOG_PLL_AUDIO_DENOM
] = 0x2964619c;
43 s
->analog
[ANALOG_PLL_VIDEO
] = 0x0008201b;
44 s
->analog
[ANALOG_PLL_VIDEO_SS
] = 0x00000000;
45 s
->analog
[ANALOG_PLL_VIDEO_NUM
] = 0x0000f699;
46 s
->analog
[ANALOG_PLL_VIDEO_DENOM
] = 0x000f4240;
47 s
->analog
[ANALOG_PLL_MISC0
] = 0x00000000;
49 /* all PLLs need to be locked */
50 s
->analog
[ANALOG_PLL_ARM
] |= ANALOG_PLL_LOCK
;
51 s
->analog
[ANALOG_PLL_DDR
] |= ANALOG_PLL_LOCK
;
52 s
->analog
[ANALOG_PLL_480
] |= ANALOG_PLL_LOCK
;
53 s
->analog
[ANALOG_PLL_480A
] |= ANALOG_PLL_LOCK
;
54 s
->analog
[ANALOG_PLL_480B
] |= ANALOG_PLL_LOCK
;
55 s
->analog
[ANALOG_PLL_ENET
] |= ANALOG_PLL_LOCK
;
56 s
->analog
[ANALOG_PLL_AUDIO
] |= ANALOG_PLL_LOCK
;
57 s
->analog
[ANALOG_PLL_VIDEO
] |= ANALOG_PLL_LOCK
;
58 s
->analog
[ANALOG_PLL_MISC0
] |= ANALOG_PLL_LOCK
;
61 * Since I couldn't find any info about this in the reference
62 * manual the value of this register is based strictly on matching
63 * what Linux kernel expects it to be.
65 s
->analog
[ANALOG_DIGPROG
] = 0x720000;
67 * Set revision to be 1.0 (Arbitrary choice, no particular
70 s
->analog
[ANALOG_DIGPROG
] |= 0x000010;
73 static void imx7_ccm_reset(DeviceState
*dev
)
75 IMX7CCMState
*s
= IMX7_CCM(dev
);
77 memset(s
->ccm
, 0, sizeof(s
->ccm
));
80 #define CCM_INDEX(offset) (((offset) & ~(hwaddr)0xF) / sizeof(uint32_t))
81 #define CCM_BITOP(offset) ((offset) & (hwaddr)0xF)
84 CCM_BITOP_NONE
= 0x00,
90 static uint64_t imx7_set_clr_tog_read(void *opaque
, hwaddr offset
,
93 const uint32_t *mmio
= opaque
;
95 return mmio
[CCM_INDEX(offset
)];
98 static void imx7_set_clr_tog_write(void *opaque
, hwaddr offset
,
99 uint64_t value
, unsigned size
)
101 const uint8_t bitop
= CCM_BITOP(offset
);
102 const uint32_t index
= CCM_INDEX(offset
);
103 uint32_t *mmio
= opaque
;
110 mmio
[index
] |= value
;
113 mmio
[index
] &= ~value
;
116 mmio
[index
] ^= value
;
121 static const struct MemoryRegionOps imx7_set_clr_tog_ops
= {
122 .read
= imx7_set_clr_tog_read
,
123 .write
= imx7_set_clr_tog_write
,
124 .endianness
= DEVICE_NATIVE_ENDIAN
,
127 * Our device would not work correctly if the guest was doing
128 * unaligned access. This might not be a limitation on the real
129 * device but in practice there is no reason for a guest to access
130 * this device unaligned.
132 .min_access_size
= 4,
133 .max_access_size
= 4,
138 static void imx7_digprog_write(void *opaque
, hwaddr addr
,
139 uint64_t data
, unsigned size
)
141 qemu_log_mask(LOG_GUEST_ERROR
,
142 "Guest write to read-only ANALOG_DIGPROG register\n");
145 static const struct MemoryRegionOps imx7_digprog_ops
= {
146 .read
= imx7_set_clr_tog_read
,
147 .write
= imx7_digprog_write
,
148 .endianness
= DEVICE_NATIVE_ENDIAN
,
150 .min_access_size
= 4,
151 .max_access_size
= 4,
156 static void imx7_ccm_init(Object
*obj
)
158 SysBusDevice
*sd
= SYS_BUS_DEVICE(obj
);
159 IMX7CCMState
*s
= IMX7_CCM(obj
);
161 memory_region_init_io(&s
->iomem
,
163 &imx7_set_clr_tog_ops
,
165 TYPE_IMX7_CCM
".ccm",
168 sysbus_init_mmio(sd
, &s
->iomem
);
171 static void imx7_analog_init(Object
*obj
)
173 SysBusDevice
*sd
= SYS_BUS_DEVICE(obj
);
174 IMX7AnalogState
*s
= IMX7_ANALOG(obj
);
176 memory_region_init(&s
->mmio
.container
, obj
, TYPE_IMX7_ANALOG
,
179 memory_region_init_io(&s
->mmio
.analog
,
181 &imx7_set_clr_tog_ops
,
186 memory_region_add_subregion(&s
->mmio
.container
,
187 0x60, &s
->mmio
.analog
);
189 memory_region_init_io(&s
->mmio
.pmu
,
191 &imx7_set_clr_tog_ops
,
193 TYPE_IMX7_ANALOG
".pmu",
196 memory_region_add_subregion(&s
->mmio
.container
,
197 0x200, &s
->mmio
.pmu
);
199 memory_region_init_io(&s
->mmio
.digprog
,
202 &s
->analog
[ANALOG_DIGPROG
],
203 TYPE_IMX7_ANALOG
".digprog",
206 memory_region_add_subregion_overlap(&s
->mmio
.container
,
207 0x800, &s
->mmio
.digprog
, 10);
210 sysbus_init_mmio(sd
, &s
->mmio
.container
);
213 static const VMStateDescription vmstate_imx7_ccm
= {
214 .name
= TYPE_IMX7_CCM
,
216 .minimum_version_id
= 1,
217 .fields
= (const VMStateField
[]) {
218 VMSTATE_UINT32_ARRAY(ccm
, IMX7CCMState
, CCM_MAX
),
219 VMSTATE_END_OF_LIST()
223 static uint32_t imx7_ccm_get_clock_frequency(IMXCCMState
*dev
, IMXClk clock
)
226 * This function is "consumed" by GPT emulation code. Some clocks
227 * have fixed frequencies and we can provide requested frequency
228 * easily. However for CCM provided clocks (like IPG) each GPT
229 * timer can have its own clock root.
230 * This means we need additional information when calling this
231 * function to know the requester's identity.
247 * For now we don't have a way to figure out the device this
248 * function is called for. Until then the IPG derived clocks
249 * are left unimplemented.
251 qemu_log_mask(LOG_GUEST_ERROR
, "[%s]%s: Clock %d Not implemented\n",
252 TYPE_IMX7_CCM
, __func__
, clock
);
255 qemu_log_mask(LOG_GUEST_ERROR
, "[%s]%s: unsupported clock %d\n",
256 TYPE_IMX7_CCM
, __func__
, clock
);
260 trace_ccm_clock_freq(clock
, freq
);
265 static void imx7_ccm_class_init(ObjectClass
*klass
, void *data
)
267 DeviceClass
*dc
= DEVICE_CLASS(klass
);
268 IMXCCMClass
*ccm
= IMX_CCM_CLASS(klass
);
270 device_class_set_legacy_reset(dc
, imx7_ccm_reset
);
271 dc
->vmsd
= &vmstate_imx7_ccm
;
272 dc
->desc
= "i.MX7 Clock Control Module";
274 ccm
->get_clock_frequency
= imx7_ccm_get_clock_frequency
;
277 static const TypeInfo imx7_ccm_info
= {
278 .name
= TYPE_IMX7_CCM
,
279 .parent
= TYPE_IMX_CCM
,
280 .instance_size
= sizeof(IMX7CCMState
),
281 .instance_init
= imx7_ccm_init
,
282 .class_init
= imx7_ccm_class_init
,
285 static const VMStateDescription vmstate_imx7_analog
= {
286 .name
= TYPE_IMX7_ANALOG
,
288 .minimum_version_id
= 1,
289 .fields
= (const VMStateField
[]) {
290 VMSTATE_UINT32_ARRAY(analog
, IMX7AnalogState
, ANALOG_MAX
),
291 VMSTATE_UINT32_ARRAY(pmu
, IMX7AnalogState
, PMU_MAX
),
292 VMSTATE_END_OF_LIST()
296 static void imx7_analog_class_init(ObjectClass
*klass
, void *data
)
298 DeviceClass
*dc
= DEVICE_CLASS(klass
);
300 device_class_set_legacy_reset(dc
, imx7_analog_reset
);
301 dc
->vmsd
= &vmstate_imx7_analog
;
302 dc
->desc
= "i.MX7 Analog Module";
305 static const TypeInfo imx7_analog_info
= {
306 .name
= TYPE_IMX7_ANALOG
,
307 .parent
= TYPE_SYS_BUS_DEVICE
,
308 .instance_size
= sizeof(IMX7AnalogState
),
309 .instance_init
= imx7_analog_init
,
310 .class_init
= imx7_analog_class_init
,
313 static void imx7_ccm_register_type(void)
315 type_register_static(&imx7_ccm_info
);
316 type_register_static(&imx7_analog_info
);
318 type_init(imx7_ccm_register_type
)