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