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