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