1 | /* |
2 | * IMX25 Clock Control Module |
3 | * |
4 | * Copyright (C) 2012 NICTA |
5 | * Updated by Jean-Christophe Dubois <jcd@tribudubois.net> |
6 | * |
7 | * This work is licensed under the terms of the GNU GPL, version 2 or later. |
8 | * See the COPYING file in the top-level directory. |
9 | * |
10 | * To get the timer frequencies right, we need to emulate at least part of |
11 | * the CCM. |
12 | */ |
13 | |
14 | #include "qemu/osdep.h" |
15 | #include "hw/misc/imx25_ccm.h" |
16 | #include "migration/vmstate.h" |
17 | #include "qemu/log.h" |
18 | #include "qemu/module.h" |
19 | |
20 | #ifndef DEBUG_IMX25_CCM |
21 | #define DEBUG_IMX25_CCM 0 |
22 | #endif |
23 | |
24 | #define DPRINTF(fmt, args...) \ |
25 | do { \ |
26 | if (DEBUG_IMX25_CCM) { \ |
27 | fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX25_CCM, \ |
28 | __func__, ##args); \ |
29 | } \ |
30 | } while (0) |
31 | |
32 | static const char *imx25_ccm_reg_name(uint32_t reg) |
33 | { |
34 | static char unknown[20]; |
35 | |
36 | switch (reg) { |
37 | case IMX25_CCM_MPCTL_REG: |
38 | return "mpctl" ; |
39 | case IMX25_CCM_UPCTL_REG: |
40 | return "upctl" ; |
41 | case IMX25_CCM_CCTL_REG: |
42 | return "cctl" ; |
43 | case IMX25_CCM_CGCR0_REG: |
44 | return "cgcr0" ; |
45 | case IMX25_CCM_CGCR1_REG: |
46 | return "cgcr1" ; |
47 | case IMX25_CCM_CGCR2_REG: |
48 | return "cgcr2" ; |
49 | case IMX25_CCM_PCDR0_REG: |
50 | return "pcdr0" ; |
51 | case IMX25_CCM_PCDR1_REG: |
52 | return "pcdr1" ; |
53 | case IMX25_CCM_PCDR2_REG: |
54 | return "pcdr2" ; |
55 | case IMX25_CCM_PCDR3_REG: |
56 | return "pcdr3" ; |
57 | case IMX25_CCM_RCSR_REG: |
58 | return "rcsr" ; |
59 | case IMX25_CCM_CRDR_REG: |
60 | return "crdr" ; |
61 | case IMX25_CCM_DCVR0_REG: |
62 | return "dcvr0" ; |
63 | case IMX25_CCM_DCVR1_REG: |
64 | return "dcvr1" ; |
65 | case IMX25_CCM_DCVR2_REG: |
66 | return "dcvr2" ; |
67 | case IMX25_CCM_DCVR3_REG: |
68 | return "dcvr3" ; |
69 | case IMX25_CCM_LTR0_REG: |
70 | return "ltr0" ; |
71 | case IMX25_CCM_LTR1_REG: |
72 | return "ltr1" ; |
73 | case IMX25_CCM_LTR2_REG: |
74 | return "ltr2" ; |
75 | case IMX25_CCM_LTR3_REG: |
76 | return "ltr3" ; |
77 | case IMX25_CCM_LTBR0_REG: |
78 | return "ltbr0" ; |
79 | case IMX25_CCM_LTBR1_REG: |
80 | return "ltbr1" ; |
81 | case IMX25_CCM_PMCR0_REG: |
82 | return "pmcr0" ; |
83 | case IMX25_CCM_PMCR1_REG: |
84 | return "pmcr1" ; |
85 | case IMX25_CCM_PMCR2_REG: |
86 | return "pmcr2" ; |
87 | case IMX25_CCM_MCR_REG: |
88 | return "mcr" ; |
89 | case IMX25_CCM_LPIMR0_REG: |
90 | return "lpimr0" ; |
91 | case IMX25_CCM_LPIMR1_REG: |
92 | return "lpimr1" ; |
93 | default: |
94 | sprintf(unknown, "[%d ?]" , reg); |
95 | return unknown; |
96 | } |
97 | } |
98 | #define CKIH_FREQ 24000000 /* 24MHz crystal input */ |
99 | |
100 | static const VMStateDescription vmstate_imx25_ccm = { |
101 | .name = TYPE_IMX25_CCM, |
102 | .version_id = 1, |
103 | .minimum_version_id = 1, |
104 | .fields = (VMStateField[]) { |
105 | VMSTATE_UINT32_ARRAY(reg, IMX25CCMState, IMX25_CCM_MAX_REG), |
106 | VMSTATE_END_OF_LIST() |
107 | }, |
108 | }; |
109 | |
110 | static uint32_t imx25_ccm_get_mpll_clk(IMXCCMState *dev) |
111 | { |
112 | uint32_t freq; |
113 | IMX25CCMState *s = IMX25_CCM(dev); |
114 | |
115 | if (EXTRACT(s->reg[IMX25_CCM_CCTL_REG], MPLL_BYPASS)) { |
116 | freq = CKIH_FREQ; |
117 | } else { |
118 | freq = imx_ccm_calc_pll(s->reg[IMX25_CCM_MPCTL_REG], CKIH_FREQ); |
119 | } |
120 | |
121 | DPRINTF("freq = %d\n" , freq); |
122 | |
123 | return freq; |
124 | } |
125 | |
126 | static uint32_t imx25_ccm_get_mcu_clk(IMXCCMState *dev) |
127 | { |
128 | uint32_t freq; |
129 | IMX25CCMState *s = IMX25_CCM(dev); |
130 | |
131 | freq = imx25_ccm_get_mpll_clk(dev); |
132 | |
133 | if (EXTRACT(s->reg[IMX25_CCM_CCTL_REG], ARM_SRC)) { |
134 | freq = (freq * 3 / 4); |
135 | } |
136 | |
137 | freq = freq / (1 + EXTRACT(s->reg[IMX25_CCM_CCTL_REG], ARM_CLK_DIV)); |
138 | |
139 | DPRINTF("freq = %d\n" , freq); |
140 | |
141 | return freq; |
142 | } |
143 | |
144 | static uint32_t imx25_ccm_get_ahb_clk(IMXCCMState *dev) |
145 | { |
146 | uint32_t freq; |
147 | IMX25CCMState *s = IMX25_CCM(dev); |
148 | |
149 | freq = imx25_ccm_get_mcu_clk(dev) |
150 | / (1 + EXTRACT(s->reg[IMX25_CCM_CCTL_REG], AHB_CLK_DIV)); |
151 | |
152 | DPRINTF("freq = %d\n" , freq); |
153 | |
154 | return freq; |
155 | } |
156 | |
157 | static uint32_t imx25_ccm_get_ipg_clk(IMXCCMState *dev) |
158 | { |
159 | uint32_t freq; |
160 | |
161 | freq = imx25_ccm_get_ahb_clk(dev) / 2; |
162 | |
163 | DPRINTF("freq = %d\n" , freq); |
164 | |
165 | return freq; |
166 | } |
167 | |
168 | static uint32_t imx25_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) |
169 | { |
170 | uint32_t freq = 0; |
171 | DPRINTF("Clock = %d)\n" , clock); |
172 | |
173 | switch (clock) { |
174 | case CLK_NONE: |
175 | break; |
176 | case CLK_IPG: |
177 | case CLK_IPG_HIGH: |
178 | freq = imx25_ccm_get_ipg_clk(dev); |
179 | break; |
180 | case CLK_32k: |
181 | freq = CKIL_FREQ; |
182 | break; |
183 | default: |
184 | qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n" , |
185 | TYPE_IMX25_CCM, __func__, clock); |
186 | break; |
187 | } |
188 | |
189 | DPRINTF("Clock = %d) = %d\n" , clock, freq); |
190 | |
191 | return freq; |
192 | } |
193 | |
194 | static void imx25_ccm_reset(DeviceState *dev) |
195 | { |
196 | IMX25CCMState *s = IMX25_CCM(dev); |
197 | |
198 | DPRINTF("\n" ); |
199 | |
200 | memset(s->reg, 0, IMX25_CCM_MAX_REG * sizeof(uint32_t)); |
201 | s->reg[IMX25_CCM_MPCTL_REG] = 0x800b2c01; |
202 | s->reg[IMX25_CCM_UPCTL_REG] = 0x84042800; |
203 | /* |
204 | * The value below gives: |
205 | * CPU = 133 MHz, AHB = 66,5 MHz, IPG = 33 MHz. |
206 | */ |
207 | s->reg[IMX25_CCM_CCTL_REG] = 0xd0030000; |
208 | s->reg[IMX25_CCM_CGCR0_REG] = 0x028A0100; |
209 | s->reg[IMX25_CCM_CGCR1_REG] = 0x04008100; |
210 | s->reg[IMX25_CCM_CGCR2_REG] = 0x00000438; |
211 | s->reg[IMX25_CCM_PCDR0_REG] = 0x01010101; |
212 | s->reg[IMX25_CCM_PCDR1_REG] = 0x01010101; |
213 | s->reg[IMX25_CCM_PCDR2_REG] = 0x01010101; |
214 | s->reg[IMX25_CCM_PCDR3_REG] = 0x01010101; |
215 | s->reg[IMX25_CCM_PMCR0_REG] = 0x00A00000; |
216 | s->reg[IMX25_CCM_PMCR1_REG] = 0x0000A030; |
217 | s->reg[IMX25_CCM_PMCR2_REG] = 0x0000A030; |
218 | s->reg[IMX25_CCM_MCR_REG] = 0x43000000; |
219 | |
220 | /* |
221 | * default boot will change the reset values to allow: |
222 | * CPU = 399 MHz, AHB = 133 MHz, IPG = 66,5 MHz. |
223 | * For some reason, this doesn't work. With the value below, linux |
224 | * detects a 88 MHz IPG CLK instead of 66,5 MHz. |
225 | s->reg[IMX25_CCM_CCTL_REG] = 0x20032000; |
226 | */ |
227 | } |
228 | |
229 | static uint64_t imx25_ccm_read(void *opaque, hwaddr offset, unsigned size) |
230 | { |
231 | uint32_t value = 0; |
232 | IMX25CCMState *s = (IMX25CCMState *)opaque; |
233 | |
234 | if (offset < 0x70) { |
235 | value = s->reg[offset >> 2]; |
236 | } else { |
237 | qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" |
238 | HWADDR_PRIx "\n" , TYPE_IMX25_CCM, __func__, offset); |
239 | } |
240 | |
241 | DPRINTF("reg[%s] => 0x%" PRIx32 "\n" , imx25_ccm_reg_name(offset >> 2), |
242 | value); |
243 | |
244 | return value; |
245 | } |
246 | |
247 | static void imx25_ccm_write(void *opaque, hwaddr offset, uint64_t value, |
248 | unsigned size) |
249 | { |
250 | IMX25CCMState *s = (IMX25CCMState *)opaque; |
251 | |
252 | DPRINTF("reg[%s] <= 0x%" PRIx32 "\n" , imx25_ccm_reg_name(offset >> 2), |
253 | (uint32_t)value); |
254 | |
255 | if (offset < 0x70) { |
256 | /* |
257 | * We will do a better implementation later. In particular some bits |
258 | * cannot be written to. |
259 | */ |
260 | s->reg[offset >> 2] = value; |
261 | } else { |
262 | qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" |
263 | HWADDR_PRIx "\n" , TYPE_IMX25_CCM, __func__, offset); |
264 | } |
265 | } |
266 | |
267 | static const struct MemoryRegionOps imx25_ccm_ops = { |
268 | .read = imx25_ccm_read, |
269 | .write = imx25_ccm_write, |
270 | .endianness = DEVICE_NATIVE_ENDIAN, |
271 | .valid = { |
272 | /* |
273 | * Our device would not work correctly if the guest was doing |
274 | * unaligned access. This might not be a limitation on the real |
275 | * device but in practice there is no reason for a guest to access |
276 | * this device unaligned. |
277 | */ |
278 | .min_access_size = 4, |
279 | .max_access_size = 4, |
280 | .unaligned = false, |
281 | }, |
282 | }; |
283 | |
284 | static void imx25_ccm_init(Object *obj) |
285 | { |
286 | DeviceState *dev = DEVICE(obj); |
287 | SysBusDevice *sd = SYS_BUS_DEVICE(obj); |
288 | IMX25CCMState *s = IMX25_CCM(obj); |
289 | |
290 | memory_region_init_io(&s->iomem, OBJECT(dev), &imx25_ccm_ops, s, |
291 | TYPE_IMX25_CCM, 0x1000); |
292 | sysbus_init_mmio(sd, &s->iomem); |
293 | } |
294 | |
295 | static void imx25_ccm_class_init(ObjectClass *klass, void *data) |
296 | { |
297 | DeviceClass *dc = DEVICE_CLASS(klass); |
298 | IMXCCMClass *ccm = IMX_CCM_CLASS(klass); |
299 | |
300 | dc->reset = imx25_ccm_reset; |
301 | dc->vmsd = &vmstate_imx25_ccm; |
302 | dc->desc = "i.MX25 Clock Control Module" ; |
303 | |
304 | ccm->get_clock_frequency = imx25_ccm_get_clock_frequency; |
305 | } |
306 | |
307 | static const TypeInfo imx25_ccm_info = { |
308 | .name = TYPE_IMX25_CCM, |
309 | .parent = TYPE_IMX_CCM, |
310 | .instance_size = sizeof(IMX25CCMState), |
311 | .instance_init = imx25_ccm_init, |
312 | .class_init = imx25_ccm_class_init, |
313 | }; |
314 | |
315 | static void imx25_ccm_register_types(void) |
316 | { |
317 | type_register_static(&imx25_ccm_info); |
318 | } |
319 | |
320 | type_init(imx25_ccm_register_types) |
321 | |