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