Revisión | 41ef8d44465c3a953d8580f5895c2e1ba6bf2d08 (tree) |
---|---|
Tiempo | 2021-05-27 14:10:38 |
Autor | Yoshinori Sato <ysato@user...> |
Commiter | Yoshinori Sato |
hw/rx: Add RX62N Clock generator
This module generated core and peripheral clock.
Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
@@ -1,5 +1,5 @@ | ||
1 | 1 | rx_ss = ss.source_set() |
2 | 2 | rx_ss.add(when: 'CONFIG_RX_GDBSIM', if_true: files('rx-gdbsim.c')) |
3 | -rx_ss.add(when: 'CONFIG_RX62N_MCU', if_true: files('rx62n.c')) | |
3 | +rx_ss.add(when: 'CONFIG_RX62N_MCU', if_true: files('rx62n.c', 'rx62n-cpg.c')) | |
4 | 4 | |
5 | 5 | hw_arch += {'rx': rx_ss} |
@@ -0,0 +1,344 @@ | ||
1 | +/* | |
2 | + * RX62N Clock Generation Circuit | |
3 | + * | |
4 | + * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware | |
5 | + * (Rev.1.40 R01UH0033EJ0140) | |
6 | + * | |
7 | + * Copyright (c) 2020 Yoshinori Sato | |
8 | + * | |
9 | + * This program is free software; you can redistribute it and/or modify it | |
10 | + * under the terms and conditions of the GNU General Public License, | |
11 | + * version 2 or later, as published by the Free Software Foundation. | |
12 | + * | |
13 | + * This program is distributed in the hope it will be useful, but WITHOUT | |
14 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
16 | + * more details. | |
17 | + * | |
18 | + * You should have received a copy of the GNU General Public License along with | |
19 | + * this program. If not, see <http://www.gnu.org/licenses/>. | |
20 | + */ | |
21 | + | |
22 | +#include "qemu/osdep.h" | |
23 | +#include "qapi/error.h" | |
24 | +#include "qemu/log.h" | |
25 | +#include "hw/hw.h" | |
26 | +#include "hw/rx/rx62n-cpg.h" | |
27 | +#include "hw/sysbus.h" | |
28 | +#include "hw/qdev-properties.h" | |
29 | +#include "hw/registerfields.h" | |
30 | +#include "hw/qdev-properties.h" | |
31 | +#include "hw/clock.h" | |
32 | +#include "migration/vmstate.h" | |
33 | + | |
34 | +#define RX62N_XTAL_MIN_HZ (8 * 1000 * 1000) | |
35 | +#define RX62N_XTAL_MAX_HZ (14 * 1000 * 1000) | |
36 | + | |
37 | +REG32(MSTPCRA, 0) | |
38 | +REG32(MSTPCRB, 4) | |
39 | +REG32(MSTPCRC, 8) | |
40 | +REG32(SCKCR, 16) | |
41 | + FIELD(SCKCR, PCK, 8, 3) | |
42 | + FIELD(SCKCR, BCK, 16, 3) | |
43 | + FIELD(SCKCR, PSTOP, 22, 2) | |
44 | + FIELD(SCKCR, ICK, 24, 3) | |
45 | +REG8(BCKCR, 32) | |
46 | + FIELD(BCKCR, BCLKDIV, 0, 1) | |
47 | +REG16(OSTDCR, 48) | |
48 | + FIELD(OSTDCR, OSTDF, 6, 1) | |
49 | + FIELD(OSTDCR, OSTDE, 7, 1) | |
50 | + | |
51 | +static const int access_size[] = {4, 4, 1, 2}; | |
52 | + | |
53 | +typedef struct { | |
54 | + const char *name; | |
55 | + int devnum; | |
56 | + int reg; | |
57 | + int offset; | |
58 | + int parentck; | |
59 | +} dev_clock_t; | |
60 | + | |
61 | +enum { | |
62 | + parent_ick, parent_bck, parent_pck, | |
63 | +}; | |
64 | + | |
65 | +static const dev_clock_t dev_clock_list[] = { | |
66 | + { .name = "pck_tmr8-1", | |
67 | + .devnum = CK_TMR8_1, .reg = 0, .offset = 4, .parentck = parent_pck, }, | |
68 | + { .name = "pck_tmr8-0", | |
69 | + .devnum = CK_TMR8_0, .reg = 0, .offset = 5, .parentck = parent_pck, }, | |
70 | + { .name = "pck_mtu-1", | |
71 | + .devnum = CK_MTU_1, .reg = 0, .offset = 8, .parentck = parent_pck, }, | |
72 | + { .name = "pck_mtu-0", | |
73 | + .devnum = CK_MTU_0, .reg = 0, .offset = 9, .parentck = parent_pck, }, | |
74 | + { .name = "pck_cmt-1", | |
75 | + .devnum = CK_CMT_1, .reg = 0, .offset = 14, .parentck = parent_pck, }, | |
76 | + { .name = "pck_cmt-0", | |
77 | + .devnum = CK_CMT_0, .reg = 0, .offset = 15, .parentck = parent_pck, }, | |
78 | + { .name = "ick_edmac", | |
79 | + .devnum = CK_EDMAC, .reg = 1, .offset = 15, .parentck = parent_ick, }, | |
80 | + { .name = "pck_sci-6", | |
81 | + .devnum = CK_SCI6, .reg = 1, .offset = 25, .parentck = parent_pck, }, | |
82 | + { .name = "pck_sci-5", | |
83 | + .devnum = CK_SCI5, .reg = 1, .offset = 26, .parentck = parent_pck, }, | |
84 | + { .name = "pck_sci-3", | |
85 | + .devnum = CK_SCI3, .reg = 1, .offset = 28, .parentck = parent_pck, }, | |
86 | + { .name = "pck_sci-2", | |
87 | + .devnum = CK_SCI2, .reg = 1, .offset = 29, .parentck = parent_pck, }, | |
88 | + { .name = "pck_sci-1", | |
89 | + .devnum = CK_SCI1, .reg = 1, .offset = 30, .parentck = parent_pck, }, | |
90 | + { .name = "pck_sci-0", | |
91 | + .devnum = CK_SCI0, .reg = 1, .offset = 31, .parentck = parent_pck, }, | |
92 | + { }, | |
93 | +}; | |
94 | + | |
95 | +static void set_clock_in(RX62NCPGState *cpg, const dev_clock_t *ck) | |
96 | +{ | |
97 | + Clock *out; | |
98 | + uint64_t period; | |
99 | + | |
100 | + out = qdev_get_clock_out(DEVICE(cpg), ck->name); | |
101 | + g_assert(out); | |
102 | + period = 0; | |
103 | + if (extract32(cpg->mstpcr[ck->reg], ck->offset, 1) == 0) { | |
104 | + switch (ck->parentck) { | |
105 | + case parent_ick: | |
106 | + period = clock_get(cpg->clk_ick); | |
107 | + break; | |
108 | + case parent_pck: | |
109 | + period = clock_get(cpg->clk_pck); | |
110 | + break; | |
111 | + } | |
112 | + } | |
113 | + if (clock_get(out) != period) { | |
114 | + clock_update(out, period); | |
115 | + } | |
116 | +} | |
117 | + | |
118 | +#define update_ck(ckname) \ | |
119 | + if (cpg->ckname != ckname) { \ | |
120 | + cpg->ckname = ckname; \ | |
121 | + ckname = 8 / (1 << ckname); \ | |
122 | + clock_update_hz(cpg->clk_ ## ckname, \ | |
123 | + cpg->xtal_freq_hz * ckname); \ | |
124 | + } | |
125 | + | |
126 | +#define validate_setting(ckname) \ | |
127 | + if (ick > ckname) { \ | |
128 | + qemu_log_mask(LOG_GUEST_ERROR, \ | |
129 | + "rx62n-cpg: Invalid " #ckname " setting." \ | |
130 | + " (ick=%d " #ckname "=%d)\n", ick, ckname); \ | |
131 | + cpg->ckname = ckname = ick; \ | |
132 | + } | |
133 | + | |
134 | +static void update_divrate(RX62NCPGState *cpg) | |
135 | +{ | |
136 | + int ick = FIELD_EX32(cpg->sckcr, SCKCR, ICK); | |
137 | + int bck = FIELD_EX32(cpg->sckcr, SCKCR, BCK); | |
138 | + int pck = FIELD_EX32(cpg->sckcr, SCKCR, PCK); | |
139 | + const dev_clock_t *p = dev_clock_list; | |
140 | + validate_setting(pck); | |
141 | + validate_setting(bck); | |
142 | + update_ck(ick); | |
143 | + update_ck(bck); | |
144 | + update_ck(pck); | |
145 | + while (p->name) { | |
146 | + set_clock_in(cpg, p); | |
147 | + p++; | |
148 | + } | |
149 | +} | |
150 | + | |
151 | +static const dev_clock_t *find_clock_list(int crno, int bit) | |
152 | +{ | |
153 | + const dev_clock_t *ret = dev_clock_list; | |
154 | + while (ret->name) { | |
155 | + if (ret->reg == crno && ret->offset == bit) { | |
156 | + return ret; | |
157 | + } | |
158 | + ret++; | |
159 | + } | |
160 | + return NULL; | |
161 | +} | |
162 | + | |
163 | +static void update_mstpcr(RX62NCPGState *cpg, int crno, uint32_t diff) | |
164 | +{ | |
165 | + int bit = 0; | |
166 | + const dev_clock_t *p; | |
167 | + | |
168 | + while (diff) { | |
169 | + if (diff & 1) { | |
170 | + p = find_clock_list(crno, bit); | |
171 | + if (p) { | |
172 | + set_clock_in(cpg, p); | |
173 | + } else { | |
174 | + qemu_log_mask(LOG_UNIMP, "rx62n-cpg: MSTPCR%c " | |
175 | + " bit %d is not implement.\n", 'A' + crno, bit); | |
176 | + } | |
177 | + } | |
178 | + bit++; | |
179 | + diff >>= 1; | |
180 | + } | |
181 | +} | |
182 | + | |
183 | +static uint64_t cpg_read(void *opaque, hwaddr addr, unsigned size) | |
184 | +{ | |
185 | + RX62NCPGState *cpg = RX62NCPG(opaque); | |
186 | + | |
187 | + if (access_size[addr >> 4] != size) { | |
188 | + qemu_log_mask(LOG_GUEST_ERROR, "rx62n-cpg: Register 0x%" | |
189 | + HWADDR_PRIX " Invalid access size.\n", addr); | |
190 | + return UINT64_MAX; | |
191 | + } | |
192 | + switch (addr) { | |
193 | + case A_MSTPCRA: | |
194 | + return cpg->mstpcr[0] | 0x473530cf; | |
195 | + case A_MSTPCRB: | |
196 | + return cpg->mstpcr[1] | 0x09407ffe; | |
197 | + case A_MSTPCRC: | |
198 | + return (cpg->mstpcr[2] | 0xffff0000) & 0xffff0003; | |
199 | + case A_SCKCR: | |
200 | + return cpg->sckcr & 0x0fcf0f00; | |
201 | + case A_BCKCR: | |
202 | + return cpg->bckcr & 0x01; | |
203 | + case A_OSTDCR: | |
204 | + /* Main OSC always good */ | |
205 | + return cpg->ostdcr & 0x0080; | |
206 | + default: | |
207 | + qemu_log_mask(LOG_GUEST_ERROR, "rx62n-cpg: Register 0x%" | |
208 | + HWADDR_PRIX " Invalid address.\n", addr); | |
209 | + return UINT64_MAX; | |
210 | + } | |
211 | +} | |
212 | + | |
213 | +static void cpg_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) | |
214 | +{ | |
215 | + RX62NCPGState *cpg = RX62NCPG(opaque); | |
216 | + uint32_t old_mstpcr; | |
217 | + int cr_no; | |
218 | + if (access_size[addr >> 4] != size) { | |
219 | + qemu_log_mask(LOG_GUEST_ERROR, "rx62n-cpg: Register 0x%" | |
220 | + HWADDR_PRIX " Invalid access size.\n", addr); | |
221 | + return; | |
222 | + } | |
223 | + switch (addr) { | |
224 | + case A_MSTPCRA: | |
225 | + case A_MSTPCRB: | |
226 | + case A_MSTPCRC: | |
227 | + cr_no = (addr & 0x0f) >> 2; | |
228 | + old_mstpcr = cpg->mstpcr[cr_no]; | |
229 | + old_mstpcr ^= val; | |
230 | + cpg->mstpcr[cr_no] = val; | |
231 | + update_mstpcr(cpg, cr_no, old_mstpcr); | |
232 | + break; | |
233 | + case A_SCKCR: | |
234 | + cpg->sckcr = val; | |
235 | + update_divrate(cpg); | |
236 | + break; | |
237 | + case A_BCKCR: | |
238 | + cpg->bckcr = val; | |
239 | + break; | |
240 | + case A_OSTDCR: | |
241 | + if (extract16(val, 8, 8) == OSTDCR_KEY) { | |
242 | + cpg->ostdcr = val; | |
243 | + } else { | |
244 | + qemu_log_mask(LOG_GUEST_ERROR, "rx62n-cpg: Register 0x%" | |
245 | + HWADDR_PRIX " Invalid key value.\n", addr); | |
246 | + } | |
247 | + break; | |
248 | + default: | |
249 | + qemu_log_mask(LOG_GUEST_ERROR, "rx62n-cpg: Register 0x%" | |
250 | + HWADDR_PRIX " Invalid address.\n", addr); | |
251 | + } | |
252 | +} | |
253 | + | |
254 | +static const MemoryRegionOps cpg_ops = { | |
255 | + .write = cpg_write, | |
256 | + .read = cpg_read, | |
257 | + .endianness = DEVICE_NATIVE_ENDIAN, | |
258 | + .impl = { | |
259 | + .min_access_size = 1, | |
260 | + .max_access_size = 4, | |
261 | + }, | |
262 | +}; | |
263 | + | |
264 | +static const ClockPortInitArray rx62n_cpg_clocks = { | |
265 | + QDEV_CLOCK_OUT(RX62NCPGState, clk_ick), | |
266 | + QDEV_CLOCK_OUT(RX62NCPGState, clk_bck), | |
267 | + QDEV_CLOCK_OUT(RX62NCPGState, clk_pck), | |
268 | + QDEV_CLOCK_END | |
269 | +}; | |
270 | + | |
271 | +static void cpg_realize(DeviceState *dev, Error **errp) | |
272 | +{ | |
273 | + RX62NCPGState *cpg = RX62NCPG(dev); | |
274 | + const dev_clock_t *p = dev_clock_list; | |
275 | + | |
276 | + if (cpg->xtal_freq_hz == 0) { | |
277 | + error_setg(errp, "\"xtal-frequency-hz\" property must be provided."); | |
278 | + return; | |
279 | + } | |
280 | + /* XTAL range: 8-14 MHz */ | |
281 | + if (cpg->xtal_freq_hz < RX62N_XTAL_MIN_HZ || | |
282 | + cpg->xtal_freq_hz > RX62N_XTAL_MAX_HZ) { | |
283 | + error_setg(errp, "\"xtal-frequency-hz\" property in incorrect range."); | |
284 | + return; | |
285 | + } | |
286 | + | |
287 | + cpg->sckcr = FIELD_DP32(cpg->sckcr, SCKCR, ICK, 2); | |
288 | + cpg->sckcr = FIELD_DP32(cpg->sckcr, SCKCR, BCK, 2); | |
289 | + cpg->sckcr = FIELD_DP32(cpg->sckcr, SCKCR, PCK, 2); | |
290 | + cpg->ostdcr = FIELD_DP8(cpg->ostdcr, OSTDCR, OSTDE, 1); | |
291 | + cpg->mstpcr[0] = 0x47ffffff; | |
292 | + cpg->mstpcr[1] = 0xffffffff; | |
293 | + cpg->mstpcr[2] = 0xffff0000; | |
294 | + | |
295 | + /* set initial state */ | |
296 | + while (p->name) { | |
297 | + set_clock_in(cpg, p); | |
298 | + p++; | |
299 | + } | |
300 | + update_divrate(cpg); | |
301 | +} | |
302 | + | |
303 | +static void rx62n_cpg_init(Object *obj) | |
304 | +{ | |
305 | + RX62NCPGState *cpg = RX62NCPG(obj); | |
306 | + const dev_clock_t *p = dev_clock_list; | |
307 | + qdev_init_clocks(DEVICE(obj), rx62n_cpg_clocks); | |
308 | + /* connect parent clock */ | |
309 | + while (p->name) { | |
310 | + cpg->dev_clocks[p->devnum] = qdev_init_clock_out(DEVICE(obj), | |
311 | + p->name); | |
312 | + p++; | |
313 | + } | |
314 | + | |
315 | + memory_region_init_io(&cpg->memory, OBJECT(cpg), &cpg_ops, | |
316 | + cpg, "rx62n-cpg", 0x40); | |
317 | + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &cpg->memory); | |
318 | +} | |
319 | + | |
320 | +static Property rx62n_cpg_properties[] = { | |
321 | + DEFINE_PROP_UINT32("xtal-frequency-hz", RX62NCPGState, xtal_freq_hz, 0), | |
322 | + DEFINE_PROP_END_OF_LIST(), | |
323 | +}; | |
324 | + | |
325 | +static void rx62n_cpg_class_init(ObjectClass *klass, void *data) | |
326 | +{ | |
327 | + DeviceClass *dc = DEVICE_CLASS(klass); | |
328 | + | |
329 | + dc->realize = cpg_realize; | |
330 | + device_class_set_props(dc, rx62n_cpg_properties); | |
331 | +} | |
332 | + | |
333 | +static const TypeInfo rx62n_cpg_info[] = { | |
334 | + { | |
335 | + .name = TYPE_RX62N_CPG, | |
336 | + .parent = TYPE_SYS_BUS_DEVICE, | |
337 | + .instance_size = sizeof(RX62NCPGState), | |
338 | + .instance_init = rx62n_cpg_init, | |
339 | + .class_init = rx62n_cpg_class_init, | |
340 | + .class_size = sizeof(RX62NCPGClass), | |
341 | + }, | |
342 | +}; | |
343 | + | |
344 | +DEFINE_TYPES(rx62n_cpg_info) |
@@ -45,6 +45,7 @@ | ||
45 | 45 | #define RX62N_TMR_BASE 0x00088200 |
46 | 46 | #define RX62N_CMT_BASE 0x00088000 |
47 | 47 | #define RX62N_SCI_BASE 0x00088240 |
48 | +#define RX62N_CPG_BASE 0x00080010 | |
48 | 49 | |
49 | 50 | /* |
50 | 51 | * RX62N Peripheral IRQ |
@@ -56,7 +57,6 @@ | ||
56 | 57 | |
57 | 58 | #define RX62N_XTAL_MIN_HZ (8 * 1000 * 1000) |
58 | 59 | #define RX62N_XTAL_MAX_HZ (14 * 1000 * 1000) |
59 | -#define RX62N_PCLK_MAX_HZ (50 * 1000 * 1000) | |
60 | 60 | |
61 | 61 | struct RX62NClass { |
62 | 62 | /*< private >*/ |
@@ -161,36 +161,45 @@ static void register_tmr(RX62NState *s, int unit) | ||
161 | 161 | { |
162 | 162 | SysBusDevice *tmr; |
163 | 163 | int i, irqbase; |
164 | + char ckname[16]; | |
164 | 165 | |
165 | 166 | object_initialize_child(OBJECT(s), "tmr[*]", |
166 | 167 | &s->tmr[unit], TYPE_RENESAS_TMR); |
167 | 168 | tmr = SYS_BUS_DEVICE(&s->tmr[unit]); |
168 | - qdev_prop_set_uint64(DEVICE(tmr), "input-freq", s->pclk_freq_hz); | |
169 | - sysbus_realize(tmr, &error_abort); | |
170 | 169 | |
171 | 170 | irqbase = RX62N_TMR_IRQ + TMR_NR_IRQ * unit; |
172 | 171 | for (i = 0; i < TMR_NR_IRQ; i++) { |
173 | 172 | sysbus_connect_irq(tmr, i, s->irq[irqbase + i]); |
174 | 173 | } |
175 | 174 | sysbus_mmio_map(tmr, 0, RX62N_TMR_BASE + unit * 0x10); |
175 | + | |
176 | + qdev_prop_set_uint32(DEVICE(tmr), "unit", unit); | |
177 | + sysbus_realize(tmr, &error_abort); | |
178 | + snprintf(ckname, sizeof(ckname), "pck_tmr8-%d", unit); | |
179 | + qdev_connect_clock_in(DEVICE(tmr), "pck", | |
180 | + qdev_get_clock_out(DEVICE(&s->cpg), ckname)); | |
176 | 181 | } |
177 | 182 | |
178 | 183 | static void register_cmt(RX62NState *s, int unit) |
179 | 184 | { |
180 | 185 | SysBusDevice *cmt; |
181 | 186 | int i, irqbase; |
187 | + char ckname[16]; | |
182 | 188 | |
183 | 189 | object_initialize_child(OBJECT(s), "cmt[*]", |
184 | 190 | &s->cmt[unit], TYPE_RENESAS_CMT); |
185 | 191 | cmt = SYS_BUS_DEVICE(&s->cmt[unit]); |
186 | - qdev_prop_set_uint64(DEVICE(cmt), "input-freq", s->pclk_freq_hz); | |
187 | - sysbus_realize(cmt, &error_abort); | |
192 | + qdev_prop_set_uint32(DEVICE(cmt), "unit", unit); | |
188 | 193 | |
189 | 194 | irqbase = RX62N_CMT_IRQ + CMT_NR_IRQ * unit; |
190 | 195 | for (i = 0; i < CMT_NR_IRQ; i++) { |
191 | 196 | sysbus_connect_irq(cmt, i, s->irq[irqbase + i]); |
192 | 197 | } |
193 | 198 | sysbus_mmio_map(cmt, 0, RX62N_CMT_BASE + unit * 0x10); |
199 | + sysbus_realize(cmt, &error_abort); | |
200 | + snprintf(ckname, sizeof(ckname), "pck_cmt-%d", unit); | |
201 | + qdev_connect_clock_in(DEVICE(cmt), "pck", | |
202 | + qdev_get_clock_out(DEVICE(&s->cpg), ckname)); | |
194 | 203 | } |
195 | 204 | |
196 | 205 | static void register_sci(RX62NState *s, int unit) |
@@ -202,7 +211,6 @@ static void register_sci(RX62NState *s, int unit) | ||
202 | 211 | &s->sci[unit], TYPE_RENESAS_SCI); |
203 | 212 | sci = SYS_BUS_DEVICE(&s->sci[unit]); |
204 | 213 | qdev_prop_set_chr(DEVICE(sci), "chardev", serial_hd(unit)); |
205 | - qdev_prop_set_uint64(DEVICE(sci), "input-freq", s->pclk_freq_hz); | |
206 | 214 | sysbus_realize(sci, &error_abort); |
207 | 215 | |
208 | 216 | irqbase = RX62N_SCI_IRQ + SCI_NR_IRQ * unit; |
@@ -212,6 +220,18 @@ static void register_sci(RX62NState *s, int unit) | ||
212 | 220 | sysbus_mmio_map(sci, 0, RX62N_SCI_BASE + unit * 0x08); |
213 | 221 | } |
214 | 222 | |
223 | +static void register_cpg(RX62NState *s) | |
224 | +{ | |
225 | + SysBusDevice *cpg; | |
226 | + | |
227 | + object_initialize_child(OBJECT(s), "rx62n-cpg", &s->cpg, | |
228 | + TYPE_RX62N_CPG); | |
229 | + cpg = SYS_BUS_DEVICE(&s->cpg); | |
230 | + qdev_prop_set_uint64(DEVICE(cpg), "xtal-frequency-hz", s->xtal_freq_hz); | |
231 | + | |
232 | + sysbus_mmio_map(cpg, 0, RX62N_CPG_BASE); | |
233 | +} | |
234 | + | |
215 | 235 | static void rx62n_realize(DeviceState *dev, Error **errp) |
216 | 236 | { |
217 | 237 | RX62NState *s = RX62N_MCU(dev); |
@@ -227,11 +247,6 @@ static void rx62n_realize(DeviceState *dev, Error **errp) | ||
227 | 247 | error_setg(errp, "\"xtal-frequency-hz\" property in incorrect range."); |
228 | 248 | return; |
229 | 249 | } |
230 | - /* Use a 4x fixed multiplier */ | |
231 | - s->pclk_freq_hz = 4 * s->xtal_freq_hz; | |
232 | - /* PCLK range: 8-50 MHz */ | |
233 | - assert(s->pclk_freq_hz <= RX62N_PCLK_MAX_HZ); | |
234 | - | |
235 | 250 | memory_region_init_ram(&s->iram, OBJECT(dev), "iram", |
236 | 251 | rxc->ram_size, &error_abort); |
237 | 252 | memory_region_add_subregion(s->sysmem, RX62N_IRAM_BASE, &s->iram); |
@@ -248,11 +263,13 @@ static void rx62n_realize(DeviceState *dev, Error **errp) | ||
248 | 263 | |
249 | 264 | register_icu(s); |
250 | 265 | s->cpu.env.ack = qdev_get_gpio_in_named(DEVICE(&s->icu), "ack", 0); |
266 | + register_cpg(s); | |
251 | 267 | register_tmr(s, 0); |
252 | 268 | register_tmr(s, 1); |
253 | 269 | register_cmt(s, 0); |
254 | 270 | register_cmt(s, 1); |
255 | 271 | register_sci(s, 0); |
272 | + sysbus_realize(SYS_BUS_DEVICE(&s->cpg), &error_abort); | |
256 | 273 | } |
257 | 274 | |
258 | 275 | static Property rx62n_properties[] = { |
@@ -0,0 +1,72 @@ | ||
1 | +/* | |
2 | + * RX62N Clock generator circuit | |
3 | + * | |
4 | + * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware | |
5 | + * (Rev.1.40 R01UH0033EJ0140) | |
6 | + * | |
7 | + * Copyright (c) 2020 Yoshinori Sato | |
8 | + * | |
9 | + * This program is free software; you can redistribute it and/or modify it | |
10 | + * under the terms and conditions of the GNU General Public License, | |
11 | + * version 2 or later, as published by the Free Software Foundation. | |
12 | + * | |
13 | + * This program is distributed in the hope it will be useful, but WITHOUT | |
14 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
16 | + * more details. | |
17 | + * | |
18 | + * You should have received a copy of the GNU General Public License along with | |
19 | + * this program. If not, see <http://www.gnu.org/licenses/>. | |
20 | + */ | |
21 | + | |
22 | +#ifndef HW_RX_RX62N_CPG_H | |
23 | +#define HW_RX_RX62N_CPG_H | |
24 | + | |
25 | +#include "hw/sysbus.h" | |
26 | +#include "hw/qdev-clock.h" | |
27 | + | |
28 | +#define TYPE_RX62N_CPG "rx62n-cpg" | |
29 | +#define RX62NCPG(obj) OBJECT_CHECK(RX62NCPGState, (obj), TYPE_RX62N_CPG) | |
30 | + | |
31 | +enum { | |
32 | + CK_TMR8_1, | |
33 | + CK_TMR8_0, | |
34 | + CK_MTU_1, | |
35 | + CK_MTU_0, | |
36 | + CK_CMT_1, | |
37 | + CK_CMT_0, | |
38 | + CK_EDMAC, | |
39 | + CK_SCI6, | |
40 | + CK_SCI5, | |
41 | + CK_SCI3, | |
42 | + CK_SCI2, | |
43 | + CK_SCI1, | |
44 | + CK_SCI0, | |
45 | + NUM_SUBCLOCK, | |
46 | +}; | |
47 | + | |
48 | +typedef struct RX62NCPGState { | |
49 | + SysBusDevice parent_obj; | |
50 | + uint32_t mstpcr[3]; | |
51 | + uint32_t sckcr; | |
52 | + uint8_t bckcr; | |
53 | + uint8_t ostdcr; | |
54 | + | |
55 | + int ick; | |
56 | + Clock *clk_ick; | |
57 | + int bck; | |
58 | + Clock *clk_bck; | |
59 | + int pck; | |
60 | + Clock *clk_pck; | |
61 | + Clock *dev_clocks[NUM_SUBCLOCK]; | |
62 | + uint32_t xtal_freq_hz; | |
63 | + MemoryRegion memory; | |
64 | +} RX62NCPGState; | |
65 | + | |
66 | +typedef struct RX62NCPGClass { | |
67 | + SysBusDeviceClass parent; | |
68 | +} RX62NCPGClass; | |
69 | + | |
70 | +#define OSTDCR_KEY 0xac | |
71 | + | |
72 | +#endif |
@@ -29,6 +29,7 @@ | ||
29 | 29 | #include "hw/timer/renesas_tmr.h" |
30 | 30 | #include "hw/timer/renesas_cmt.h" |
31 | 31 | #include "hw/char/renesas_sci.h" |
32 | +#include "hw/rx/rx62n-cpg.h" | |
32 | 33 | #include "qemu/units.h" |
33 | 34 | #include "qom/object.h" |
34 | 35 |
@@ -58,9 +59,9 @@ struct RX62NState { | ||
58 | 59 | RTMRState tmr[RX62N_NR_TMR]; |
59 | 60 | RCMTState cmt[RX62N_NR_CMT]; |
60 | 61 | RSCIState sci[RX62N_NR_SCI]; |
62 | + RX62NCPGState cpg; | |
61 | 63 | |
62 | 64 | MemoryRegion *sysmem; |
63 | - bool kernel; | |
64 | 65 | |
65 | 66 | MemoryRegion iram; |
66 | 67 | MemoryRegion iomem1; |
@@ -72,8 +73,7 @@ struct RX62NState { | ||
72 | 73 | |
73 | 74 | /* Input Clock (XTAL) frequency */ |
74 | 75 | uint32_t xtal_freq_hz; |
75 | - /* Peripheral Module Clock frequency */ | |
76 | - uint32_t pclk_freq_hz; | |
77 | -}; | |
76 | +} RX62NState; | |
77 | + | |
78 | 78 | |
79 | 79 | #endif |