]>
Commit | Line | Data |
---|---|---|
781182e1 JCD |
1 | /* |
2 | * IMX6UL Clock Control Module | |
3 | * | |
4 | * Copyright (c) 2018 Jean-Christophe Dubois <[email protected]> | |
5 | * | |
6 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
7 | * See the COPYING file in the top-level directory. | |
8 | * | |
9 | * To get the timer frequencies right, we need to emulate at least part of | |
10 | * the CCM. | |
11 | */ | |
12 | ||
13 | #include "qemu/osdep.h" | |
14 | #include "hw/registerfields.h" | |
15 | #include "hw/misc/imx6ul_ccm.h" | |
16 | #include "qemu/log.h" | |
17 | ||
18 | #include "trace.h" | |
19 | ||
20 | static const char *imx6ul_ccm_reg_name(uint32_t reg) | |
21 | { | |
22 | static char unknown[20]; | |
23 | ||
24 | switch (reg) { | |
25 | case CCM_CCR: | |
26 | return "CCR"; | |
27 | case CCM_CCDR: | |
28 | return "CCDR"; | |
29 | case CCM_CSR: | |
30 | return "CSR"; | |
31 | case CCM_CCSR: | |
32 | return "CCSR"; | |
33 | case CCM_CACRR: | |
34 | return "CACRR"; | |
35 | case CCM_CBCDR: | |
36 | return "CBCDR"; | |
37 | case CCM_CBCMR: | |
38 | return "CBCMR"; | |
39 | case CCM_CSCMR1: | |
40 | return "CSCMR1"; | |
41 | case CCM_CSCMR2: | |
42 | return "CSCMR2"; | |
43 | case CCM_CSCDR1: | |
44 | return "CSCDR1"; | |
45 | case CCM_CS1CDR: | |
46 | return "CS1CDR"; | |
47 | case CCM_CS2CDR: | |
48 | return "CS2CDR"; | |
49 | case CCM_CDCDR: | |
50 | return "CDCDR"; | |
51 | case CCM_CHSCCDR: | |
52 | return "CHSCCDR"; | |
53 | case CCM_CSCDR2: | |
54 | return "CSCDR2"; | |
55 | case CCM_CSCDR3: | |
56 | return "CSCDR3"; | |
57 | case CCM_CDHIPR: | |
58 | return "CDHIPR"; | |
59 | case CCM_CTOR: | |
60 | return "CTOR"; | |
61 | case CCM_CLPCR: | |
62 | return "CLPCR"; | |
63 | case CCM_CISR: | |
64 | return "CISR"; | |
65 | case CCM_CIMR: | |
66 | return "CIMR"; | |
67 | case CCM_CCOSR: | |
68 | return "CCOSR"; | |
69 | case CCM_CGPR: | |
70 | return "CGPR"; | |
71 | case CCM_CCGR0: | |
72 | return "CCGR0"; | |
73 | case CCM_CCGR1: | |
74 | return "CCGR1"; | |
75 | case CCM_CCGR2: | |
76 | return "CCGR2"; | |
77 | case CCM_CCGR3: | |
78 | return "CCGR3"; | |
79 | case CCM_CCGR4: | |
80 | return "CCGR4"; | |
81 | case CCM_CCGR5: | |
82 | return "CCGR5"; | |
83 | case CCM_CCGR6: | |
84 | return "CCGR6"; | |
85 | case CCM_CMEOR: | |
86 | return "CMEOR"; | |
87 | default: | |
88 | sprintf(unknown, "%d ?", reg); | |
89 | return unknown; | |
90 | } | |
91 | } | |
92 | ||
93 | static const char *imx6ul_analog_reg_name(uint32_t reg) | |
94 | { | |
95 | static char unknown[20]; | |
96 | ||
97 | switch (reg) { | |
98 | case CCM_ANALOG_PLL_ARM: | |
99 | return "PLL_ARM"; | |
100 | case CCM_ANALOG_PLL_ARM_SET: | |
101 | return "PLL_ARM_SET"; | |
102 | case CCM_ANALOG_PLL_ARM_CLR: | |
103 | return "PLL_ARM_CLR"; | |
104 | case CCM_ANALOG_PLL_ARM_TOG: | |
105 | return "PLL_ARM_TOG"; | |
106 | case CCM_ANALOG_PLL_USB1: | |
107 | return "PLL_USB1"; | |
108 | case CCM_ANALOG_PLL_USB1_SET: | |
109 | return "PLL_USB1_SET"; | |
110 | case CCM_ANALOG_PLL_USB1_CLR: | |
111 | return "PLL_USB1_CLR"; | |
112 | case CCM_ANALOG_PLL_USB1_TOG: | |
113 | return "PLL_USB1_TOG"; | |
114 | case CCM_ANALOG_PLL_USB2: | |
115 | return "PLL_USB2"; | |
116 | case CCM_ANALOG_PLL_USB2_SET: | |
117 | return "PLL_USB2_SET"; | |
118 | case CCM_ANALOG_PLL_USB2_CLR: | |
119 | return "PLL_USB2_CLR"; | |
120 | case CCM_ANALOG_PLL_USB2_TOG: | |
121 | return "PLL_USB2_TOG"; | |
122 | case CCM_ANALOG_PLL_SYS: | |
123 | return "PLL_SYS"; | |
124 | case CCM_ANALOG_PLL_SYS_SET: | |
125 | return "PLL_SYS_SET"; | |
126 | case CCM_ANALOG_PLL_SYS_CLR: | |
127 | return "PLL_SYS_CLR"; | |
128 | case CCM_ANALOG_PLL_SYS_TOG: | |
129 | return "PLL_SYS_TOG"; | |
130 | case CCM_ANALOG_PLL_SYS_SS: | |
131 | return "PLL_SYS_SS"; | |
132 | case CCM_ANALOG_PLL_SYS_NUM: | |
133 | return "PLL_SYS_NUM"; | |
134 | case CCM_ANALOG_PLL_SYS_DENOM: | |
135 | return "PLL_SYS_DENOM"; | |
136 | case CCM_ANALOG_PLL_AUDIO: | |
137 | return "PLL_AUDIO"; | |
138 | case CCM_ANALOG_PLL_AUDIO_SET: | |
139 | return "PLL_AUDIO_SET"; | |
140 | case CCM_ANALOG_PLL_AUDIO_CLR: | |
141 | return "PLL_AUDIO_CLR"; | |
142 | case CCM_ANALOG_PLL_AUDIO_TOG: | |
143 | return "PLL_AUDIO_TOG"; | |
144 | case CCM_ANALOG_PLL_AUDIO_NUM: | |
145 | return "PLL_AUDIO_NUM"; | |
146 | case CCM_ANALOG_PLL_AUDIO_DENOM: | |
147 | return "PLL_AUDIO_DENOM"; | |
148 | case CCM_ANALOG_PLL_VIDEO: | |
149 | return "PLL_VIDEO"; | |
150 | case CCM_ANALOG_PLL_VIDEO_SET: | |
151 | return "PLL_VIDEO_SET"; | |
152 | case CCM_ANALOG_PLL_VIDEO_CLR: | |
153 | return "PLL_VIDEO_CLR"; | |
154 | case CCM_ANALOG_PLL_VIDEO_TOG: | |
155 | return "PLL_VIDEO_TOG"; | |
156 | case CCM_ANALOG_PLL_VIDEO_NUM: | |
157 | return "PLL_VIDEO_NUM"; | |
158 | case CCM_ANALOG_PLL_VIDEO_DENOM: | |
159 | return "PLL_VIDEO_DENOM"; | |
160 | case CCM_ANALOG_PLL_ENET: | |
161 | return "PLL_ENET"; | |
162 | case CCM_ANALOG_PLL_ENET_SET: | |
163 | return "PLL_ENET_SET"; | |
164 | case CCM_ANALOG_PLL_ENET_CLR: | |
165 | return "PLL_ENET_CLR"; | |
166 | case CCM_ANALOG_PLL_ENET_TOG: | |
167 | return "PLL_ENET_TOG"; | |
168 | case CCM_ANALOG_PFD_480: | |
169 | return "PFD_480"; | |
170 | case CCM_ANALOG_PFD_480_SET: | |
171 | return "PFD_480_SET"; | |
172 | case CCM_ANALOG_PFD_480_CLR: | |
173 | return "PFD_480_CLR"; | |
174 | case CCM_ANALOG_PFD_480_TOG: | |
175 | return "PFD_480_TOG"; | |
176 | case CCM_ANALOG_PFD_528: | |
177 | return "PFD_528"; | |
178 | case CCM_ANALOG_PFD_528_SET: | |
179 | return "PFD_528_SET"; | |
180 | case CCM_ANALOG_PFD_528_CLR: | |
181 | return "PFD_528_CLR"; | |
182 | case CCM_ANALOG_PFD_528_TOG: | |
183 | return "PFD_528_TOG"; | |
184 | case CCM_ANALOG_MISC0: | |
185 | return "MISC0"; | |
186 | case CCM_ANALOG_MISC0_SET: | |
187 | return "MISC0_SET"; | |
188 | case CCM_ANALOG_MISC0_CLR: | |
189 | return "MISC0_CLR"; | |
190 | case CCM_ANALOG_MISC0_TOG: | |
191 | return "MISC0_TOG"; | |
192 | case CCM_ANALOG_MISC2: | |
193 | return "MISC2"; | |
194 | case CCM_ANALOG_MISC2_SET: | |
195 | return "MISC2_SET"; | |
196 | case CCM_ANALOG_MISC2_CLR: | |
197 | return "MISC2_CLR"; | |
198 | case CCM_ANALOG_MISC2_TOG: | |
199 | return "MISC2_TOG"; | |
200 | case PMU_REG_1P1: | |
201 | return "PMU_REG_1P1"; | |
202 | case PMU_REG_3P0: | |
203 | return "PMU_REG_3P0"; | |
204 | case PMU_REG_2P5: | |
205 | return "PMU_REG_2P5"; | |
206 | case PMU_REG_CORE: | |
207 | return "PMU_REG_CORE"; | |
208 | case PMU_MISC1: | |
209 | return "PMU_MISC1"; | |
210 | case PMU_MISC1_SET: | |
211 | return "PMU_MISC1_SET"; | |
212 | case PMU_MISC1_CLR: | |
213 | return "PMU_MISC1_CLR"; | |
214 | case PMU_MISC1_TOG: | |
215 | return "PMU_MISC1_TOG"; | |
216 | case USB_ANALOG_DIGPROG: | |
217 | return "USB_ANALOG_DIGPROG"; | |
218 | default: | |
219 | sprintf(unknown, "%d ?", reg); | |
220 | return unknown; | |
221 | } | |
222 | } | |
223 | ||
224 | #define CKIH_FREQ 24000000 /* 24MHz crystal input */ | |
225 | ||
226 | static const VMStateDescription vmstate_imx6ul_ccm = { | |
227 | .name = TYPE_IMX6UL_CCM, | |
228 | .version_id = 1, | |
229 | .minimum_version_id = 1, | |
230 | .fields = (VMStateField[]) { | |
231 | VMSTATE_UINT32_ARRAY(ccm, IMX6ULCCMState, CCM_MAX), | |
232 | VMSTATE_UINT32_ARRAY(analog, IMX6ULCCMState, CCM_ANALOG_MAX), | |
233 | VMSTATE_END_OF_LIST() | |
234 | }, | |
235 | }; | |
236 | ||
237 | static uint64_t imx6ul_analog_get_osc_clk(IMX6ULCCMState *dev) | |
238 | { | |
239 | uint64_t freq = CKIH_FREQ; | |
240 | ||
241 | trace_ccm_freq((uint32_t)freq); | |
242 | ||
243 | return freq; | |
244 | } | |
245 | ||
246 | static uint64_t imx6ul_analog_get_pll2_clk(IMX6ULCCMState *dev) | |
247 | { | |
248 | uint64_t freq = imx6ul_analog_get_osc_clk(dev); | |
249 | ||
250 | if (FIELD_EX32(dev->analog[CCM_ANALOG_PLL_SYS], | |
251 | ANALOG_PLL_SYS, DIV_SELECT)) { | |
252 | freq *= 22; | |
253 | } else { | |
254 | freq *= 20; | |
255 | } | |
256 | ||
257 | trace_ccm_freq((uint32_t)freq); | |
258 | ||
259 | return freq; | |
260 | } | |
261 | ||
262 | static uint64_t imx6ul_analog_get_pll3_clk(IMX6ULCCMState *dev) | |
263 | { | |
264 | uint64_t freq = imx6ul_analog_get_osc_clk(dev) * 20; | |
265 | ||
266 | trace_ccm_freq((uint32_t)freq); | |
267 | ||
268 | return freq; | |
269 | } | |
270 | ||
271 | static uint64_t imx6ul_analog_get_pll2_pfd0_clk(IMX6ULCCMState *dev) | |
272 | { | |
273 | uint64_t freq = 0; | |
274 | ||
275 | freq = imx6ul_analog_get_pll2_clk(dev) * 18 | |
276 | / FIELD_EX32(dev->analog[CCM_ANALOG_PFD_528], | |
277 | ANALOG_PFD_528, PFD0_FRAC); | |
278 | ||
279 | trace_ccm_freq((uint32_t)freq); | |
280 | ||
281 | return freq; | |
282 | } | |
283 | ||
284 | static uint64_t imx6ul_analog_get_pll2_pfd2_clk(IMX6ULCCMState *dev) | |
285 | { | |
286 | uint64_t freq = 0; | |
287 | ||
288 | freq = imx6ul_analog_get_pll2_clk(dev) * 18 | |
289 | / FIELD_EX32(dev->analog[CCM_ANALOG_PFD_528], | |
290 | ANALOG_PFD_528, PFD2_FRAC); | |
291 | ||
292 | trace_ccm_freq((uint32_t)freq); | |
293 | ||
294 | return freq; | |
295 | } | |
296 | ||
297 | static uint64_t imx6ul_analog_pll2_bypass_clk(IMX6ULCCMState *dev) | |
298 | { | |
299 | uint64_t freq = 0; | |
300 | ||
301 | trace_ccm_freq((uint32_t)freq); | |
302 | ||
303 | return freq; | |
304 | } | |
305 | ||
306 | static uint64_t imx6ul_ccm_get_periph_clk2_sel_clk(IMX6ULCCMState *dev) | |
307 | { | |
308 | uint64_t freq = 0; | |
309 | ||
310 | switch (FIELD_EX32(dev->ccm[CCM_CBCMR], CBCMR, PERIPH_CLK2_SEL)) { | |
311 | case 0: | |
312 | freq = imx6ul_analog_get_pll3_clk(dev); | |
313 | break; | |
314 | case 1: | |
315 | freq = imx6ul_analog_get_osc_clk(dev); | |
316 | break; | |
317 | case 2: | |
318 | freq = imx6ul_analog_pll2_bypass_clk(dev); | |
319 | break; | |
320 | case 3: | |
321 | /* We should never get there as 3 is a reserved value */ | |
322 | qemu_log_mask(LOG_GUEST_ERROR, | |
323 | "[%s]%s: unsupported PERIPH_CLK2_SEL value 3\n", | |
324 | TYPE_IMX6UL_CCM, __func__); | |
325 | /* freq is set to 0 as we don't know what it should be */ | |
326 | break; | |
327 | default: | |
328 | g_assert_not_reached(); | |
329 | } | |
330 | ||
331 | trace_ccm_freq((uint32_t)freq); | |
332 | ||
333 | return freq; | |
334 | } | |
335 | ||
336 | static uint64_t imx6ul_ccm_get_periph_clk_sel_clk(IMX6ULCCMState *dev) | |
337 | { | |
338 | uint64_t freq = 0; | |
339 | ||
340 | switch (FIELD_EX32(dev->ccm[CCM_CBCMR], CBCMR, PRE_PERIPH_CLK_SEL)) { | |
341 | case 0: | |
342 | freq = imx6ul_analog_get_pll2_clk(dev); | |
343 | break; | |
344 | case 1: | |
345 | freq = imx6ul_analog_get_pll2_pfd2_clk(dev); | |
346 | break; | |
347 | case 2: | |
348 | freq = imx6ul_analog_get_pll2_pfd0_clk(dev); | |
349 | break; | |
350 | case 3: | |
351 | freq = imx6ul_analog_get_pll2_pfd2_clk(dev) / 2; | |
352 | break; | |
353 | default: | |
354 | g_assert_not_reached(); | |
355 | } | |
356 | ||
357 | trace_ccm_freq((uint32_t)freq); | |
358 | ||
359 | return freq; | |
360 | } | |
361 | ||
362 | static uint64_t imx6ul_ccm_get_periph_clk2_clk(IMX6ULCCMState *dev) | |
363 | { | |
364 | uint64_t freq = 0; | |
365 | ||
366 | freq = imx6ul_ccm_get_periph_clk2_sel_clk(dev) | |
367 | / (1 + FIELD_EX32(dev->ccm[CCM_CBCDR], CBCDR, PERIPH_CLK2_PODF)); | |
368 | ||
369 | trace_ccm_freq((uint32_t)freq); | |
370 | ||
371 | return freq; | |
372 | } | |
373 | ||
374 | static uint64_t imx6ul_ccm_get_periph_sel_clk(IMX6ULCCMState *dev) | |
375 | { | |
376 | uint64_t freq = 0; | |
377 | ||
378 | switch (FIELD_EX32(dev->ccm[CCM_CBCDR], CBCDR, PERIPH_CLK_SEL)) { | |
379 | case 0: | |
380 | freq = imx6ul_ccm_get_periph_clk_sel_clk(dev); | |
381 | break; | |
382 | case 1: | |
383 | freq = imx6ul_ccm_get_periph_clk2_clk(dev); | |
384 | break; | |
385 | default: | |
386 | g_assert_not_reached(); | |
387 | } | |
388 | ||
389 | trace_ccm_freq((uint32_t)freq); | |
390 | ||
391 | return freq; | |
392 | } | |
393 | ||
394 | static uint64_t imx6ul_ccm_get_ahb_clk(IMX6ULCCMState *dev) | |
395 | { | |
396 | uint64_t freq = 0; | |
397 | ||
398 | freq = imx6ul_ccm_get_periph_sel_clk(dev) | |
399 | / (1 + FIELD_EX32(dev->ccm[CCM_CBCDR], CBCDR, AHB_PODF)); | |
400 | ||
401 | trace_ccm_freq((uint32_t)freq); | |
402 | ||
403 | return freq; | |
404 | } | |
405 | ||
406 | static uint64_t imx6ul_ccm_get_ipg_clk(IMX6ULCCMState *dev) | |
407 | { | |
408 | uint64_t freq = 0; | |
409 | ||
410 | freq = imx6ul_ccm_get_ahb_clk(dev) | |
411 | / (1 + FIELD_EX32(dev->ccm[CCM_CBCDR], CBCDR, IPG_PODF)); | |
412 | ||
413 | trace_ccm_freq((uint32_t)freq); | |
414 | ||
415 | return freq; | |
416 | } | |
417 | ||
418 | static uint64_t imx6ul_ccm_get_per_sel_clk(IMX6ULCCMState *dev) | |
419 | { | |
420 | uint64_t freq = 0; | |
421 | ||
422 | switch (FIELD_EX32(dev->ccm[CCM_CSCMR1], CSCMR1, PERCLK_CLK_SEL)) { | |
423 | case 0: | |
424 | freq = imx6ul_ccm_get_ipg_clk(dev); | |
425 | break; | |
426 | case 1: | |
427 | freq = imx6ul_analog_get_osc_clk(dev); | |
428 | break; | |
429 | default: | |
430 | g_assert_not_reached(); | |
431 | } | |
432 | ||
433 | trace_ccm_freq((uint32_t)freq); | |
434 | ||
435 | return freq; | |
436 | } | |
437 | ||
438 | static uint64_t imx6ul_ccm_get_per_clk(IMX6ULCCMState *dev) | |
439 | { | |
440 | uint64_t freq = 0; | |
441 | ||
442 | freq = imx6ul_ccm_get_per_sel_clk(dev) | |
443 | / (1 + FIELD_EX32(dev->ccm[CCM_CSCMR1], CSCMR1, PERCLK_PODF)); | |
444 | ||
445 | trace_ccm_freq((uint32_t)freq); | |
446 | ||
447 | return freq; | |
448 | } | |
449 | ||
450 | static uint32_t imx6ul_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) | |
451 | { | |
452 | uint32_t freq = 0; | |
453 | IMX6ULCCMState *s = IMX6UL_CCM(dev); | |
454 | ||
455 | switch (clock) { | |
456 | case CLK_NONE: | |
457 | break; | |
458 | case CLK_IPG: | |
459 | freq = imx6ul_ccm_get_ipg_clk(s); | |
460 | break; | |
461 | case CLK_IPG_HIGH: | |
462 | freq = imx6ul_ccm_get_per_clk(s); | |
463 | break; | |
464 | case CLK_32k: | |
465 | freq = CKIL_FREQ; | |
466 | break; | |
467 | case CLK_HIGH: | |
468 | freq = CKIH_FREQ; | |
469 | break; | |
470 | case CLK_HIGH_DIV: | |
471 | freq = CKIH_FREQ / 8; | |
472 | break; | |
473 | default: | |
474 | qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n", | |
475 | TYPE_IMX6UL_CCM, __func__, clock); | |
476 | break; | |
477 | } | |
478 | ||
479 | trace_ccm_clock_freq(clock, freq); | |
480 | ||
481 | return freq; | |
482 | } | |
483 | ||
484 | static void imx6ul_ccm_reset(DeviceState *dev) | |
485 | { | |
486 | IMX6ULCCMState *s = IMX6UL_CCM(dev); | |
487 | ||
488 | trace_ccm_entry(); | |
489 | ||
490 | s->ccm[CCM_CCR] = 0x0401167F; | |
491 | s->ccm[CCM_CCDR] = 0x00000000; | |
492 | s->ccm[CCM_CSR] = 0x00000010; | |
493 | s->ccm[CCM_CCSR] = 0x00000100; | |
494 | s->ccm[CCM_CACRR] = 0x00000000; | |
495 | s->ccm[CCM_CBCDR] = 0x00018D00; | |
496 | s->ccm[CCM_CBCMR] = 0x24860324; | |
497 | s->ccm[CCM_CSCMR1] = 0x04900080; | |
498 | s->ccm[CCM_CSCMR2] = 0x03192F06; | |
499 | s->ccm[CCM_CSCDR1] = 0x00490B00; | |
500 | s->ccm[CCM_CS1CDR] = 0x0EC102C1; | |
501 | s->ccm[CCM_CS2CDR] = 0x000336C1; | |
502 | s->ccm[CCM_CDCDR] = 0x33F71F92; | |
503 | s->ccm[CCM_CHSCCDR] = 0x000248A4; | |
504 | s->ccm[CCM_CSCDR2] = 0x00029B48; | |
505 | s->ccm[CCM_CSCDR3] = 0x00014841; | |
506 | s->ccm[CCM_CDHIPR] = 0x00000000; | |
507 | s->ccm[CCM_CTOR] = 0x00000000; | |
508 | s->ccm[CCM_CLPCR] = 0x00000079; | |
509 | s->ccm[CCM_CISR] = 0x00000000; | |
510 | s->ccm[CCM_CIMR] = 0xFFFFFFFF; | |
511 | s->ccm[CCM_CCOSR] = 0x000A0001; | |
512 | s->ccm[CCM_CGPR] = 0x0000FE62; | |
513 | s->ccm[CCM_CCGR0] = 0xFFFFFFFF; | |
514 | s->ccm[CCM_CCGR1] = 0xFFFFFFFF; | |
515 | s->ccm[CCM_CCGR2] = 0xFC3FFFFF; | |
516 | s->ccm[CCM_CCGR3] = 0xFFFFFFFF; | |
517 | s->ccm[CCM_CCGR4] = 0xFFFFFFFF; | |
518 | s->ccm[CCM_CCGR5] = 0xFFFFFFFF; | |
519 | s->ccm[CCM_CCGR6] = 0xFFFFFFFF; | |
520 | s->ccm[CCM_CMEOR] = 0xFFFFFFFF; | |
521 | ||
522 | s->analog[CCM_ANALOG_PLL_ARM] = 0x00013063; | |
523 | s->analog[CCM_ANALOG_PLL_USB1] = 0x00012000; | |
524 | s->analog[CCM_ANALOG_PLL_USB2] = 0x00012000; | |
525 | s->analog[CCM_ANALOG_PLL_SYS] = 0x00013001; | |
526 | s->analog[CCM_ANALOG_PLL_SYS_SS] = 0x00000000; | |
527 | s->analog[CCM_ANALOG_PLL_SYS_NUM] = 0x00000000; | |
528 | s->analog[CCM_ANALOG_PLL_SYS_DENOM] = 0x00000012; | |
529 | s->analog[CCM_ANALOG_PLL_AUDIO] = 0x00011006; | |
530 | s->analog[CCM_ANALOG_PLL_AUDIO_NUM] = 0x05F5E100; | |
531 | s->analog[CCM_ANALOG_PLL_AUDIO_DENOM] = 0x2964619C; | |
532 | s->analog[CCM_ANALOG_PLL_VIDEO] = 0x0001100C; | |
533 | s->analog[CCM_ANALOG_PLL_VIDEO_NUM] = 0x05F5E100; | |
534 | s->analog[CCM_ANALOG_PLL_VIDEO_DENOM] = 0x10A24447; | |
535 | s->analog[CCM_ANALOG_PLL_ENET] = 0x00011001; | |
536 | s->analog[CCM_ANALOG_PFD_480] = 0x1311100C; | |
537 | s->analog[CCM_ANALOG_PFD_528] = 0x1018101B; | |
538 | ||
539 | s->analog[PMU_REG_1P1] = 0x00001073; | |
540 | s->analog[PMU_REG_3P0] = 0x00000F74; | |
541 | s->analog[PMU_REG_2P5] = 0x00001073; | |
542 | s->analog[PMU_REG_CORE] = 0x00482012; | |
543 | s->analog[PMU_MISC0] = 0x04000000; | |
544 | s->analog[PMU_MISC1] = 0x00000000; | |
545 | s->analog[PMU_MISC2] = 0x00272727; | |
546 | s->analog[PMU_LOWPWR_CTRL] = 0x00004009; | |
547 | ||
548 | s->analog[USB_ANALOG_USB1_VBUS_DETECT] = 0x01000004; | |
549 | s->analog[USB_ANALOG_USB1_CHRG_DETECT] = 0x00000000; | |
550 | s->analog[USB_ANALOG_USB1_VBUS_DETECT_STAT] = 0x00000000; | |
551 | s->analog[USB_ANALOG_USB1_CHRG_DETECT_STAT] = 0x00000000; | |
552 | s->analog[USB_ANALOG_USB1_MISC] = 0x00000002; | |
553 | s->analog[USB_ANALOG_USB2_VBUS_DETECT] = 0x01000004; | |
554 | s->analog[USB_ANALOG_USB2_CHRG_DETECT] = 0x00000000; | |
555 | s->analog[USB_ANALOG_USB2_MISC] = 0x00000002; | |
556 | s->analog[USB_ANALOG_DIGPROG] = 0x00640000; | |
557 | ||
558 | /* all PLLs need to be locked */ | |
559 | s->analog[CCM_ANALOG_PLL_ARM] |= CCM_ANALOG_PLL_LOCK; | |
560 | s->analog[CCM_ANALOG_PLL_USB1] |= CCM_ANALOG_PLL_LOCK; | |
561 | s->analog[CCM_ANALOG_PLL_USB2] |= CCM_ANALOG_PLL_LOCK; | |
562 | s->analog[CCM_ANALOG_PLL_SYS] |= CCM_ANALOG_PLL_LOCK; | |
563 | s->analog[CCM_ANALOG_PLL_AUDIO] |= CCM_ANALOG_PLL_LOCK; | |
564 | s->analog[CCM_ANALOG_PLL_VIDEO] |= CCM_ANALOG_PLL_LOCK; | |
565 | s->analog[CCM_ANALOG_PLL_ENET] |= CCM_ANALOG_PLL_LOCK; | |
566 | ||
567 | s->analog[TEMPMON_TEMPSENSE0] = 0x00000001; | |
568 | s->analog[TEMPMON_TEMPSENSE1] = 0x00000001; | |
569 | s->analog[TEMPMON_TEMPSENSE2] = 0x00000000; | |
570 | } | |
571 | ||
572 | static uint64_t imx6ul_ccm_read(void *opaque, hwaddr offset, unsigned size) | |
573 | { | |
574 | uint32_t value = 0; | |
575 | uint32_t index = offset >> 2; | |
576 | IMX6ULCCMState *s = (IMX6ULCCMState *)opaque; | |
577 | ||
578 | assert(index < CCM_MAX); | |
579 | ||
580 | value = s->ccm[index]; | |
581 | ||
582 | trace_ccm_read_reg(imx6ul_ccm_reg_name(index), (uint32_t)value); | |
583 | ||
584 | return (uint64_t)value; | |
585 | } | |
586 | ||
587 | static void imx6ul_ccm_write(void *opaque, hwaddr offset, uint64_t value, | |
588 | unsigned size) | |
589 | { | |
590 | uint32_t index = offset >> 2; | |
591 | IMX6ULCCMState *s = (IMX6ULCCMState *)opaque; | |
592 | ||
593 | assert(index < CCM_MAX); | |
594 | ||
595 | trace_ccm_write_reg(imx6ul_ccm_reg_name(index), (uint32_t)value); | |
596 | ||
597 | /* | |
598 | * We will do a better implementation later. In particular some bits | |
599 | * cannot be written to. | |
600 | */ | |
601 | s->ccm[index] = (uint32_t)value; | |
602 | } | |
603 | ||
604 | static uint64_t imx6ul_analog_read(void *opaque, hwaddr offset, unsigned size) | |
605 | { | |
606 | uint32_t value; | |
607 | uint32_t index = offset >> 2; | |
608 | IMX6ULCCMState *s = (IMX6ULCCMState *)opaque; | |
609 | ||
610 | assert(index < CCM_ANALOG_MAX); | |
611 | ||
612 | switch (index) { | |
613 | case CCM_ANALOG_PLL_ARM_SET: | |
614 | case CCM_ANALOG_PLL_USB1_SET: | |
615 | case CCM_ANALOG_PLL_USB2_SET: | |
616 | case CCM_ANALOG_PLL_SYS_SET: | |
617 | case CCM_ANALOG_PLL_AUDIO_SET: | |
618 | case CCM_ANALOG_PLL_VIDEO_SET: | |
619 | case CCM_ANALOG_PLL_ENET_SET: | |
620 | case CCM_ANALOG_PFD_480_SET: | |
621 | case CCM_ANALOG_PFD_528_SET: | |
622 | case CCM_ANALOG_MISC0_SET: | |
623 | case PMU_MISC1_SET: | |
624 | case CCM_ANALOG_MISC2_SET: | |
625 | case USB_ANALOG_USB1_VBUS_DETECT_SET: | |
626 | case USB_ANALOG_USB1_CHRG_DETECT_SET: | |
627 | case USB_ANALOG_USB1_MISC_SET: | |
628 | case USB_ANALOG_USB2_VBUS_DETECT_SET: | |
629 | case USB_ANALOG_USB2_CHRG_DETECT_SET: | |
630 | case USB_ANALOG_USB2_MISC_SET: | |
631 | case TEMPMON_TEMPSENSE0_SET: | |
632 | case TEMPMON_TEMPSENSE1_SET: | |
633 | case TEMPMON_TEMPSENSE2_SET: | |
634 | /* | |
635 | * All REG_NAME_SET register access are in fact targeting | |
636 | * the REG_NAME register. | |
637 | */ | |
638 | value = s->analog[index - 1]; | |
639 | break; | |
640 | case CCM_ANALOG_PLL_ARM_CLR: | |
641 | case CCM_ANALOG_PLL_USB1_CLR: | |
642 | case CCM_ANALOG_PLL_USB2_CLR: | |
643 | case CCM_ANALOG_PLL_SYS_CLR: | |
644 | case CCM_ANALOG_PLL_AUDIO_CLR: | |
645 | case CCM_ANALOG_PLL_VIDEO_CLR: | |
646 | case CCM_ANALOG_PLL_ENET_CLR: | |
647 | case CCM_ANALOG_PFD_480_CLR: | |
648 | case CCM_ANALOG_PFD_528_CLR: | |
649 | case CCM_ANALOG_MISC0_CLR: | |
650 | case PMU_MISC1_CLR: | |
651 | case CCM_ANALOG_MISC2_CLR: | |
652 | case USB_ANALOG_USB1_VBUS_DETECT_CLR: | |
653 | case USB_ANALOG_USB1_CHRG_DETECT_CLR: | |
654 | case USB_ANALOG_USB1_MISC_CLR: | |
655 | case USB_ANALOG_USB2_VBUS_DETECT_CLR: | |
656 | case USB_ANALOG_USB2_CHRG_DETECT_CLR: | |
657 | case USB_ANALOG_USB2_MISC_CLR: | |
658 | case TEMPMON_TEMPSENSE0_CLR: | |
659 | case TEMPMON_TEMPSENSE1_CLR: | |
660 | case TEMPMON_TEMPSENSE2_CLR: | |
661 | /* | |
662 | * All REG_NAME_CLR register access are in fact targeting | |
663 | * the REG_NAME register. | |
664 | */ | |
665 | value = s->analog[index - 2]; | |
666 | break; | |
667 | case CCM_ANALOG_PLL_ARM_TOG: | |
668 | case CCM_ANALOG_PLL_USB1_TOG: | |
669 | case CCM_ANALOG_PLL_USB2_TOG: | |
670 | case CCM_ANALOG_PLL_SYS_TOG: | |
671 | case CCM_ANALOG_PLL_AUDIO_TOG: | |
672 | case CCM_ANALOG_PLL_VIDEO_TOG: | |
673 | case CCM_ANALOG_PLL_ENET_TOG: | |
674 | case CCM_ANALOG_PFD_480_TOG: | |
675 | case CCM_ANALOG_PFD_528_TOG: | |
676 | case CCM_ANALOG_MISC0_TOG: | |
677 | case PMU_MISC1_TOG: | |
678 | case CCM_ANALOG_MISC2_TOG: | |
679 | case USB_ANALOG_USB1_VBUS_DETECT_TOG: | |
680 | case USB_ANALOG_USB1_CHRG_DETECT_TOG: | |
681 | case USB_ANALOG_USB1_MISC_TOG: | |
682 | case USB_ANALOG_USB2_VBUS_DETECT_TOG: | |
683 | case USB_ANALOG_USB2_CHRG_DETECT_TOG: | |
684 | case USB_ANALOG_USB2_MISC_TOG: | |
685 | case TEMPMON_TEMPSENSE0_TOG: | |
686 | case TEMPMON_TEMPSENSE1_TOG: | |
687 | case TEMPMON_TEMPSENSE2_TOG: | |
688 | /* | |
689 | * All REG_NAME_TOG register access are in fact targeting | |
690 | * the REG_NAME register. | |
691 | */ | |
692 | value = s->analog[index - 3]; | |
693 | break; | |
694 | default: | |
695 | value = s->analog[index]; | |
696 | break; | |
697 | } | |
698 | ||
699 | trace_ccm_read_reg(imx6ul_analog_reg_name(index), (uint32_t)value); | |
700 | ||
701 | return (uint64_t)value; | |
702 | } | |
703 | ||
704 | static void imx6ul_analog_write(void *opaque, hwaddr offset, uint64_t value, | |
705 | unsigned size) | |
706 | { | |
707 | uint32_t index = offset >> 2; | |
708 | IMX6ULCCMState *s = (IMX6ULCCMState *)opaque; | |
709 | ||
710 | assert(index < CCM_ANALOG_MAX); | |
711 | ||
712 | trace_ccm_write_reg(imx6ul_analog_reg_name(index), (uint32_t)value); | |
713 | ||
714 | switch (index) { | |
715 | case CCM_ANALOG_PLL_ARM_SET: | |
716 | case CCM_ANALOG_PLL_USB1_SET: | |
717 | case CCM_ANALOG_PLL_USB2_SET: | |
718 | case CCM_ANALOG_PLL_SYS_SET: | |
719 | case CCM_ANALOG_PLL_AUDIO_SET: | |
720 | case CCM_ANALOG_PLL_VIDEO_SET: | |
721 | case CCM_ANALOG_PLL_ENET_SET: | |
722 | case CCM_ANALOG_PFD_480_SET: | |
723 | case CCM_ANALOG_PFD_528_SET: | |
724 | case CCM_ANALOG_MISC0_SET: | |
725 | case PMU_MISC1_SET: | |
726 | case CCM_ANALOG_MISC2_SET: | |
727 | case USB_ANALOG_USB1_VBUS_DETECT_SET: | |
728 | case USB_ANALOG_USB1_CHRG_DETECT_SET: | |
729 | case USB_ANALOG_USB1_MISC_SET: | |
730 | case USB_ANALOG_USB2_VBUS_DETECT_SET: | |
731 | case USB_ANALOG_USB2_CHRG_DETECT_SET: | |
732 | case USB_ANALOG_USB2_MISC_SET: | |
733 | /* | |
734 | * All REG_NAME_SET register access are in fact targeting | |
735 | * the REG_NAME register. So we change the value of the | |
736 | * REG_NAME register, setting bits passed in the value. | |
737 | */ | |
738 | s->analog[index - 1] |= value; | |
739 | break; | |
740 | case CCM_ANALOG_PLL_ARM_CLR: | |
741 | case CCM_ANALOG_PLL_USB1_CLR: | |
742 | case CCM_ANALOG_PLL_USB2_CLR: | |
743 | case CCM_ANALOG_PLL_SYS_CLR: | |
744 | case CCM_ANALOG_PLL_AUDIO_CLR: | |
745 | case CCM_ANALOG_PLL_VIDEO_CLR: | |
746 | case CCM_ANALOG_PLL_ENET_CLR: | |
747 | case CCM_ANALOG_PFD_480_CLR: | |
748 | case CCM_ANALOG_PFD_528_CLR: | |
749 | case CCM_ANALOG_MISC0_CLR: | |
750 | case PMU_MISC1_CLR: | |
751 | case CCM_ANALOG_MISC2_CLR: | |
752 | case USB_ANALOG_USB1_VBUS_DETECT_CLR: | |
753 | case USB_ANALOG_USB1_CHRG_DETECT_CLR: | |
754 | case USB_ANALOG_USB1_MISC_CLR: | |
755 | case USB_ANALOG_USB2_VBUS_DETECT_CLR: | |
756 | case USB_ANALOG_USB2_CHRG_DETECT_CLR: | |
757 | case USB_ANALOG_USB2_MISC_CLR: | |
758 | /* | |
759 | * All REG_NAME_CLR register access are in fact targeting | |
760 | * the REG_NAME register. So we change the value of the | |
761 | * REG_NAME register, unsetting bits passed in the value. | |
762 | */ | |
763 | s->analog[index - 2] &= ~value; | |
764 | break; | |
765 | case CCM_ANALOG_PLL_ARM_TOG: | |
766 | case CCM_ANALOG_PLL_USB1_TOG: | |
767 | case CCM_ANALOG_PLL_USB2_TOG: | |
768 | case CCM_ANALOG_PLL_SYS_TOG: | |
769 | case CCM_ANALOG_PLL_AUDIO_TOG: | |
770 | case CCM_ANALOG_PLL_VIDEO_TOG: | |
771 | case CCM_ANALOG_PLL_ENET_TOG: | |
772 | case CCM_ANALOG_PFD_480_TOG: | |
773 | case CCM_ANALOG_PFD_528_TOG: | |
774 | case CCM_ANALOG_MISC0_TOG: | |
775 | case PMU_MISC1_TOG: | |
776 | case CCM_ANALOG_MISC2_TOG: | |
777 | case USB_ANALOG_USB1_VBUS_DETECT_TOG: | |
778 | case USB_ANALOG_USB1_CHRG_DETECT_TOG: | |
779 | case USB_ANALOG_USB1_MISC_TOG: | |
780 | case USB_ANALOG_USB2_VBUS_DETECT_TOG: | |
781 | case USB_ANALOG_USB2_CHRG_DETECT_TOG: | |
782 | case USB_ANALOG_USB2_MISC_TOG: | |
783 | /* | |
784 | * All REG_NAME_TOG register access are in fact targeting | |
785 | * the REG_NAME register. So we change the value of the | |
786 | * REG_NAME register, toggling bits passed in the value. | |
787 | */ | |
788 | s->analog[index - 3] ^= value; | |
789 | break; | |
790 | default: | |
791 | /* | |
792 | * We will do a better implementation later. In particular some bits | |
793 | * cannot be written to. | |
794 | */ | |
795 | s->analog[index] = value; | |
796 | break; | |
797 | } | |
798 | } | |
799 | ||
800 | static const struct MemoryRegionOps imx6ul_ccm_ops = { | |
801 | .read = imx6ul_ccm_read, | |
802 | .write = imx6ul_ccm_write, | |
803 | .endianness = DEVICE_NATIVE_ENDIAN, | |
804 | .valid = { | |
805 | /* | |
806 | * Our device would not work correctly if the guest was doing | |
807 | * unaligned access. This might not be a limitation on the real | |
808 | * device but in practice there is no reason for a guest to access | |
809 | * this device unaligned. | |
810 | */ | |
811 | .min_access_size = 4, | |
812 | .max_access_size = 4, | |
813 | .unaligned = false, | |
814 | }, | |
815 | }; | |
816 | ||
817 | static const struct MemoryRegionOps imx6ul_analog_ops = { | |
818 | .read = imx6ul_analog_read, | |
819 | .write = imx6ul_analog_write, | |
820 | .endianness = DEVICE_NATIVE_ENDIAN, | |
821 | .valid = { | |
822 | /* | |
823 | * Our device would not work correctly if the guest was doing | |
824 | * unaligned access. This might not be a limitation on the real | |
825 | * device but in practice there is no reason for a guest to access | |
826 | * this device unaligned. | |
827 | */ | |
828 | .min_access_size = 4, | |
829 | .max_access_size = 4, | |
830 | .unaligned = false, | |
831 | }, | |
832 | }; | |
833 | ||
834 | static void imx6ul_ccm_init(Object *obj) | |
835 | { | |
836 | DeviceState *dev = DEVICE(obj); | |
837 | SysBusDevice *sd = SYS_BUS_DEVICE(obj); | |
838 | IMX6ULCCMState *s = IMX6UL_CCM(obj); | |
839 | ||
840 | /* initialize a container for the all memory range */ | |
841 | memory_region_init(&s->container, OBJECT(dev), TYPE_IMX6UL_CCM, 0x8000); | |
842 | ||
843 | /* We initialize an IO memory region for the CCM part */ | |
844 | memory_region_init_io(&s->ioccm, OBJECT(dev), &imx6ul_ccm_ops, s, | |
845 | TYPE_IMX6UL_CCM ".ccm", CCM_MAX * sizeof(uint32_t)); | |
846 | ||
847 | /* Add the CCM as a subregion at offset 0 */ | |
848 | memory_region_add_subregion(&s->container, 0, &s->ioccm); | |
849 | ||
850 | /* We initialize an IO memory region for the ANALOG part */ | |
851 | memory_region_init_io(&s->ioanalog, OBJECT(dev), &imx6ul_analog_ops, s, | |
852 | TYPE_IMX6UL_CCM ".analog", | |
853 | CCM_ANALOG_MAX * sizeof(uint32_t)); | |
854 | ||
855 | /* Add the ANALOG as a subregion at offset 0x4000 */ | |
856 | memory_region_add_subregion(&s->container, 0x4000, &s->ioanalog); | |
857 | ||
858 | sysbus_init_mmio(sd, &s->container); | |
859 | } | |
860 | ||
861 | static void imx6ul_ccm_class_init(ObjectClass *klass, void *data) | |
862 | { | |
863 | DeviceClass *dc = DEVICE_CLASS(klass); | |
864 | IMXCCMClass *ccm = IMX_CCM_CLASS(klass); | |
865 | ||
866 | dc->reset = imx6ul_ccm_reset; | |
867 | dc->vmsd = &vmstate_imx6ul_ccm; | |
868 | dc->desc = "i.MX6UL Clock Control Module"; | |
869 | ||
870 | ccm->get_clock_frequency = imx6ul_ccm_get_clock_frequency; | |
871 | } | |
872 | ||
873 | static const TypeInfo imx6ul_ccm_info = { | |
874 | .name = TYPE_IMX6UL_CCM, | |
875 | .parent = TYPE_IMX_CCM, | |
876 | .instance_size = sizeof(IMX6ULCCMState), | |
877 | .instance_init = imx6ul_ccm_init, | |
878 | .class_init = imx6ul_ccm_class_init, | |
879 | }; | |
880 | ||
881 | static void imx6ul_ccm_register_types(void) | |
882 | { | |
883 | type_register_static(&imx6ul_ccm_info); | |
884 | } | |
885 | ||
886 | type_init(imx6ul_ccm_register_types) |