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