]>
Commit | Line | Data |
---|---|---|
bcc181b0 PC |
1 | /* |
2 | * IMX31 Clock Control Module | |
3 | * | |
4 | * Copyright (C) 2012 NICTA | |
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 "hw.h" | |
14 | #include "sysbus.h" | |
15 | #include "sysemu.h" | |
16 | #include "imx.h" | |
17 | ||
18 | #define CKIH_FREQ 26000000 /* 26MHz crystal input */ | |
19 | #define CKIL_FREQ 32768 /* nominal 32khz clock */ | |
20 | ||
21 | ||
22 | //#define DEBUG_CCM 1 | |
23 | #ifdef DEBUG_CCM | |
24 | #define DPRINTF(fmt, args...) \ | |
25 | do { printf("imx_ccm: " fmt , ##args); } while (0) | |
26 | #else | |
27 | #define DPRINTF(fmt, args...) do {} while (0) | |
28 | #endif | |
29 | ||
30 | static int imx_ccm_post_load(void *opaque, int version_id); | |
31 | ||
32 | typedef struct { | |
33 | SysBusDevice busdev; | |
34 | MemoryRegion iomem; | |
35 | ||
36 | uint32_t ccmr; | |
37 | uint32_t pdr0; | |
38 | uint32_t pdr1; | |
39 | uint32_t mpctl; | |
40 | uint32_t spctl; | |
41 | uint32_t cgr[3]; | |
42 | uint32_t pmcr0; | |
43 | uint32_t pmcr1; | |
44 | ||
45 | /* Frequencies precalculated on register changes */ | |
46 | uint32_t pll_refclk_freq; | |
47 | uint32_t mcu_clk_freq; | |
48 | uint32_t hsp_clk_freq; | |
49 | uint32_t ipg_clk_freq; | |
50 | } IMXCCMState; | |
51 | ||
52 | static const VMStateDescription vmstate_imx_ccm = { | |
53 | .name = "imx-ccm", | |
54 | .version_id = 1, | |
55 | .minimum_version_id = 1, | |
56 | .minimum_version_id_old = 1, | |
57 | .fields = (VMStateField[]) { | |
58 | VMSTATE_UINT32(ccmr, IMXCCMState), | |
59 | VMSTATE_UINT32(pdr0, IMXCCMState), | |
60 | VMSTATE_UINT32(pdr1, IMXCCMState), | |
61 | VMSTATE_UINT32(mpctl, IMXCCMState), | |
62 | VMSTATE_UINT32(spctl, IMXCCMState), | |
63 | VMSTATE_UINT32_ARRAY(cgr, IMXCCMState, 3), | |
64 | VMSTATE_UINT32(pmcr0, IMXCCMState), | |
65 | VMSTATE_UINT32(pmcr1, IMXCCMState), | |
66 | VMSTATE_UINT32(pll_refclk_freq, IMXCCMState), | |
67 | }, | |
68 | .post_load = imx_ccm_post_load, | |
69 | }; | |
70 | ||
71 | /* CCMR */ | |
72 | #define CCMR_FPME (1<<0) | |
73 | #define CCMR_MPE (1<<3) | |
74 | #define CCMR_MDS (1<<7) | |
75 | #define CCMR_FPMF (1<<26) | |
76 | #define CCMR_PRCS (3<<1) | |
77 | ||
78 | /* PDR0 */ | |
79 | #define PDR0_MCU_PODF_SHIFT (0) | |
80 | #define PDR0_MCU_PODF_MASK (0x7) | |
81 | #define PDR0_MAX_PODF_SHIFT (3) | |
82 | #define PDR0_MAX_PODF_MASK (0x7) | |
83 | #define PDR0_IPG_PODF_SHIFT (6) | |
84 | #define PDR0_IPG_PODF_MASK (0x3) | |
85 | #define PDR0_NFC_PODF_SHIFT (8) | |
86 | #define PDR0_NFC_PODF_MASK (0x7) | |
87 | #define PDR0_HSP_PODF_SHIFT (11) | |
88 | #define PDR0_HSP_PODF_MASK (0x7) | |
89 | #define PDR0_PER_PODF_SHIFT (16) | |
90 | #define PDR0_PER_PODF_MASK (0x1f) | |
91 | #define PDR0_CSI_PODF_SHIFT (23) | |
92 | #define PDR0_CSI_PODF_MASK (0x1ff) | |
93 | ||
94 | #define EXTRACT(value, name) (((value) >> PDR0_##name##_PODF_SHIFT) \ | |
95 | & PDR0_##name##_PODF_MASK) | |
96 | #define INSERT(value, name) (((value) & PDR0_##name##_PODF_MASK) << \ | |
97 | PDR0_##name##_PODF_SHIFT) | |
98 | /* PLL control registers */ | |
99 | #define PD(v) (((v) >> 26) & 0xf) | |
100 | #define MFD(v) (((v) >> 16) & 0x3ff) | |
101 | #define MFI(v) (((v) >> 10) & 0xf); | |
102 | #define MFN(v) ((v) & 0x3ff) | |
103 | ||
104 | #define PLL_PD(x) (((x) & 0xf) << 26) | |
105 | #define PLL_MFD(x) (((x) & 0x3ff) << 16) | |
106 | #define PLL_MFI(x) (((x) & 0xf) << 10) | |
107 | #define PLL_MFN(x) (((x) & 0x3ff) << 0) | |
108 | ||
109 | uint32_t imx_clock_frequency(DeviceState *dev, IMXClk clock) | |
110 | { | |
111 | IMXCCMState *s = container_of(dev, IMXCCMState, busdev.qdev); | |
112 | ||
113 | switch (clock) { | |
114 | case NOCLK: | |
115 | return 0; | |
116 | case MCU: | |
117 | return s->mcu_clk_freq; | |
118 | case HSP: | |
119 | return s->hsp_clk_freq; | |
120 | case IPG: | |
121 | return s->ipg_clk_freq; | |
122 | case CLK_32k: | |
123 | return CKIL_FREQ; | |
124 | } | |
125 | return 0; | |
126 | } | |
127 | ||
128 | /* | |
129 | * Calculate PLL output frequency | |
130 | */ | |
131 | static uint32_t calc_pll(uint32_t pllreg, uint32_t base_freq) | |
132 | { | |
133 | int32_t mfn = MFN(pllreg); /* Numerator */ | |
134 | uint32_t mfi = MFI(pllreg); /* Integer part */ | |
135 | uint32_t mfd = 1 + MFD(pllreg); /* Denominator */ | |
136 | uint32_t pd = 1 + PD(pllreg); /* Pre-divider */ | |
137 | ||
138 | if (mfi < 5) { | |
139 | mfi = 5; | |
140 | } | |
141 | /* mfn is 10-bit signed twos-complement */ | |
142 | mfn <<= 32 - 10; | |
143 | mfn >>= 32 - 10; | |
144 | ||
145 | return ((2 * (base_freq >> 10) * (mfi * mfd + mfn)) / | |
146 | (mfd * pd)) << 10; | |
147 | } | |
148 | ||
149 | static void update_clocks(IMXCCMState *s) | |
150 | { | |
151 | /* | |
152 | * If we ever emulate more clocks, this should switch to a data-driven | |
153 | * approach | |
154 | */ | |
155 | ||
156 | if ((s->ccmr & CCMR_PRCS) == 1) { | |
157 | s->pll_refclk_freq = CKIL_FREQ * 1024; | |
158 | } else { | |
159 | s->pll_refclk_freq = CKIH_FREQ; | |
160 | } | |
161 | ||
162 | /* ipg_clk_arm aka MCU clock */ | |
163 | if ((s->ccmr & CCMR_MDS) || !(s->ccmr & CCMR_MPE)) { | |
164 | s->mcu_clk_freq = s->pll_refclk_freq; | |
165 | } else { | |
166 | s->mcu_clk_freq = calc_pll(s->mpctl, s->pll_refclk_freq); | |
167 | } | |
168 | ||
169 | /* High-speed clock */ | |
170 | s->hsp_clk_freq = s->mcu_clk_freq / (1 + EXTRACT(s->pdr0, HSP)); | |
171 | s->ipg_clk_freq = s->hsp_clk_freq / (1 + EXTRACT(s->pdr0, IPG)); | |
172 | ||
173 | DPRINTF("Clocks: mcu %uMHz, HSP %uMHz, IPG %uHz\n", | |
174 | s->mcu_clk_freq / 1000000, | |
175 | s->hsp_clk_freq / 1000000, | |
176 | s->ipg_clk_freq); | |
177 | } | |
178 | ||
179 | static void imx_ccm_reset(DeviceState *dev) | |
180 | { | |
181 | IMXCCMState *s = container_of(dev, IMXCCMState, busdev.qdev); | |
182 | ||
183 | s->ccmr = 0x074b0b7b; | |
184 | s->pdr0 = 0xff870b48; | |
185 | s->pdr1 = 0x49fcfe7f; | |
186 | s->mpctl = PLL_PD(1) | PLL_MFD(0) | PLL_MFI(6) | PLL_MFN(0); | |
187 | s->cgr[0] = s->cgr[1] = s->cgr[2] = 0xffffffff; | |
188 | s->spctl = PLL_PD(1) | PLL_MFD(4) | PLL_MFI(0xc) | PLL_MFN(1); | |
189 | s->pmcr0 = 0x80209828; | |
190 | ||
191 | update_clocks(s); | |
192 | } | |
193 | ||
194 | static uint64_t imx_ccm_read(void *opaque, target_phys_addr_t offset, | |
195 | unsigned size) | |
196 | { | |
197 | IMXCCMState *s = (IMXCCMState *)opaque; | |
198 | ||
199 | DPRINTF("read(offset=%x)", offset >> 2); | |
200 | switch (offset >> 2) { | |
201 | case 0: /* CCMR */ | |
202 | DPRINTF(" ccmr = 0x%x\n", s->ccmr); | |
203 | return s->ccmr; | |
204 | case 1: | |
205 | DPRINTF(" pdr0 = 0x%x\n", s->pdr0); | |
206 | return s->pdr0; | |
207 | case 2: | |
208 | DPRINTF(" pdr1 = 0x%x\n", s->pdr1); | |
209 | return s->pdr1; | |
210 | case 4: | |
211 | DPRINTF(" mpctl = 0x%x\n", s->mpctl); | |
212 | return s->mpctl; | |
213 | case 6: | |
214 | DPRINTF(" spctl = 0x%x\n", s->spctl); | |
215 | return s->spctl; | |
216 | case 8: | |
217 | DPRINTF(" cgr0 = 0x%x\n", s->cgr[0]); | |
218 | return s->cgr[0]; | |
219 | case 9: | |
220 | DPRINTF(" cgr1 = 0x%x\n", s->cgr[1]); | |
221 | return s->cgr[1]; | |
222 | case 10: | |
223 | DPRINTF(" cgr2 = 0x%x\n", s->cgr[2]); | |
224 | return s->cgr[2]; | |
225 | case 18: /* LTR1 */ | |
226 | return 0x00004040; | |
227 | case 23: | |
228 | DPRINTF(" pcmr0 = 0x%x\n", s->pmcr0); | |
229 | return s->pmcr0; | |
230 | } | |
231 | DPRINTF(" return 0\n"); | |
232 | return 0; | |
233 | } | |
234 | ||
235 | static void imx_ccm_write(void *opaque, target_phys_addr_t offset, | |
236 | uint64_t value, unsigned size) | |
237 | { | |
238 | IMXCCMState *s = (IMXCCMState *)opaque; | |
239 | ||
240 | DPRINTF("write(offset=%x, value = %x)\n", | |
241 | offset >> 2, (unsigned int)value); | |
242 | switch (offset >> 2) { | |
243 | case 0: | |
244 | s->ccmr = CCMR_FPMF | (value & 0x3b6fdfff); | |
245 | break; | |
246 | case 1: | |
247 | s->pdr0 = value & 0xff9f3fff; | |
248 | break; | |
249 | case 2: | |
250 | s->pdr1 = value; | |
251 | break; | |
252 | case 4: | |
253 | s->mpctl = value & 0xbfff3fff; | |
254 | break; | |
255 | case 6: | |
256 | s->spctl = value & 0xbfff3fff; | |
257 | break; | |
258 | case 8: | |
259 | s->cgr[0] = value; | |
260 | return; | |
261 | case 9: | |
262 | s->cgr[1] = value; | |
263 | return; | |
264 | case 10: | |
265 | s->cgr[2] = value; | |
266 | return; | |
267 | ||
268 | default: | |
269 | return; | |
270 | } | |
271 | update_clocks(s); | |
272 | } | |
273 | ||
274 | static const struct MemoryRegionOps imx_ccm_ops = { | |
275 | .read = imx_ccm_read, | |
276 | .write = imx_ccm_write, | |
277 | .endianness = DEVICE_NATIVE_ENDIAN, | |
278 | }; | |
279 | ||
280 | static int imx_ccm_init(SysBusDevice *dev) | |
281 | { | |
282 | IMXCCMState *s = FROM_SYSBUS(typeof(*s), dev); | |
283 | ||
284 | memory_region_init_io(&s->iomem, &imx_ccm_ops, s, "imx_ccm", 0x1000); | |
285 | sysbus_init_mmio(dev, &s->iomem); | |
286 | ||
287 | return 0; | |
288 | } | |
289 | ||
290 | static int imx_ccm_post_load(void *opaque, int version_id) | |
291 | { | |
292 | IMXCCMState *s = (IMXCCMState *)opaque; | |
293 | ||
294 | update_clocks(s); | |
295 | return 0; | |
296 | } | |
297 | ||
298 | static void imx_ccm_class_init(ObjectClass *klass, void *data) | |
299 | { | |
300 | DeviceClass *dc = DEVICE_CLASS(klass); | |
301 | SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); | |
302 | ||
303 | sbc->init = imx_ccm_init; | |
304 | dc->reset = imx_ccm_reset; | |
305 | dc->vmsd = &vmstate_imx_ccm; | |
306 | dc->desc = "i.MX Clock Control Module"; | |
307 | } | |
308 | ||
309 | static TypeInfo imx_ccm_info = { | |
310 | .name = "imx_ccm", | |
311 | .parent = TYPE_SYS_BUS_DEVICE, | |
312 | .instance_size = sizeof(IMXCCMState), | |
313 | .class_init = imx_ccm_class_init, | |
314 | }; | |
315 | ||
316 | static void imx_ccm_register_types(void) | |
317 | { | |
318 | type_register_static(&imx_ccm_info); | |
319 | } | |
320 | ||
321 | type_init(imx_ccm_register_types) |