Revisión | 47faad96c3078255dc6db20bb4fc3d574433f068 (tree) |
---|---|
Tiempo | 2021-05-27 14:10:44 |
Autor | Yoshinori Sato <ysato@user...> |
Commiter | Yoshinori Sato |
hw/sh4: sh7750 Add CPG.
CPG required new hw modules.
Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
@@ -2,6 +2,7 @@ sh4_ss = ss.source_set() | ||
2 | 2 | sh4_ss.add(files( |
3 | 3 | 'sh7750.c', |
4 | 4 | 'sh7750_regnames.c', |
5 | + 'sh7751-cpg.c', | |
5 | 6 | )) |
6 | 7 | sh4_ss.add(when: 'CONFIG_R2D', if_true: files('r2d.c')) |
7 | 8 | sh4_ss.add(when: 'CONFIG_SHIX', if_true: files('shix.c')) |
@@ -24,6 +24,7 @@ | ||
24 | 24 | */ |
25 | 25 | |
26 | 26 | #include "qemu/osdep.h" |
27 | +#include "qapi/error.h" | |
27 | 28 | #include "hw/irq.h" |
28 | 29 | #include "hw/sh4/sh.h" |
29 | 30 | #include "sysemu/sysemu.h" |
@@ -32,6 +33,8 @@ | ||
32 | 33 | #include "hw/sh4/sh_intc.h" |
33 | 34 | #include "hw/timer/tmu012.h" |
34 | 35 | #include "exec/exec-all.h" |
36 | +#include "hw/sh4/sh7751-cpg.h" | |
37 | +#include "hw/qdev-properties.h" | |
35 | 38 | |
36 | 39 | #define NB_DEVICES 4 |
37 | 40 |
@@ -752,9 +755,29 @@ static const MemoryRegionOps sh7750_mmct_ops = { | ||
752 | 755 | .endianness = DEVICE_NATIVE_ENDIAN, |
753 | 756 | }; |
754 | 757 | |
758 | +static SH7751CPGBaseState *sh_cpg_init(MemoryRegion *sysmem, | |
759 | + int cputype) | |
760 | +{ | |
761 | + const char *cpgtype; | |
762 | + SH7751CPGBaseState *cpg; | |
763 | + if (cputype & (SH_CPU_SH7750R | SH_CPU_SH7751R)) { | |
764 | + cpgtype = TYPE_SH7751R_CPG; | |
765 | + } else { | |
766 | + cpgtype = TYPE_SH7751_CPG; | |
767 | + } | |
768 | + cpg = SH7751CPGBase(qdev_new(cpgtype)); | |
769 | + qdev_prop_set_uint32(DEVICE(cpg), "xtal-frequency-hz", 20 * 1000 * 1000); | |
770 | + qdev_prop_set_uint32(DEVICE(cpg), "clock-mode", 5); | |
771 | + sysbus_mmio_map(SYS_BUS_DEVICE(cpg), 0, 0x1fc00000); | |
772 | + sysbus_mmio_map(SYS_BUS_DEVICE(cpg), 1, P4ADDR(0x1fc00000)); | |
773 | + sysbus_mmio_map(SYS_BUS_DEVICE(cpg), 2, A7ADDR(0x1fc00000)); | |
774 | + return cpg; | |
775 | +} | |
776 | + | |
755 | 777 | SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem) |
756 | 778 | { |
757 | 779 | SH7750State *s; |
780 | + SH7751CPGBaseState *cpg; | |
758 | 781 | |
759 | 782 | s = g_malloc0(sizeof(SH7750State)); |
760 | 783 | s->cpu = cpu; |
@@ -800,6 +823,7 @@ SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem) | ||
800 | 823 | |
801 | 824 | cpu->env.intc_handle = &s->intc; |
802 | 825 | |
826 | + cpg = sh_cpg_init(sysmem, cpu->env.id); | |
803 | 827 | sh_serial_init(sysmem, 0x1fe00000, |
804 | 828 | 0, s->periph_freq, serial_hd(0), |
805 | 829 | s->intc.irqs[SCI1_ERI], |
@@ -824,6 +848,7 @@ SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem) | ||
824 | 848 | s->intc.irqs[TMU2_TUNI], |
825 | 849 | s->intc.irqs[TMU2_TICPI]); |
826 | 850 | |
851 | + sysbus_realize(SYS_BUS_DEVICE(cpg), &error_abort); | |
827 | 852 | if (cpu->env.id & (SH_CPU_SH7750 | SH_CPU_SH7750S | SH_CPU_SH7751)) { |
828 | 853 | sh_intc_register_sources(&s->intc, |
829 | 854 | _INTC_ARRAY(vectors_dma4), |
@@ -0,0 +1,457 @@ | ||
1 | +/* | |
2 | + * SH7750 / SH7751 Clock Generation Circuit | |
3 | + * | |
4 | + * Datasheet: SH7751 Group, SH7751R Group User's Manual: Hardware | |
5 | + * (Rev.4.01 R01UH0457EJ0401) | |
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/sh4/sh7751-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 SH7751_XTAL_MIN_HZ (1 * 1000 * 1000) | |
35 | +#define SH7751_XTAL_MAX_HZ (34 * 1000 * 1000) | |
36 | + | |
37 | +REG16(FREQCR, 0) | |
38 | + FIELD(FREQCR, PFC, 0, 3) | |
39 | + FIELD(FREQCR, BFC, 3, 3) | |
40 | + FIELD(FREQCR, IFC, 6, 3) | |
41 | + FIELD(FREQCR, PLL2EN, 9, 1) | |
42 | + FIELD(FREQCR, PLL1EN, 10, 1) | |
43 | + FIELD(FREQCR, CKOEN, 11, 1) | |
44 | +REG8(STBCR, 4) | |
45 | +REG8(STBCR2, 16) | |
46 | + | |
47 | +REG32(CLKSTP00, 0) | |
48 | +REG32(CLKSTPCLR00, 8) | |
49 | + | |
50 | +typedef struct { | |
51 | + const char *name; | |
52 | + int devnum; | |
53 | + int reg; | |
54 | + int offset; | |
55 | +} dev_clock_t; | |
56 | + | |
57 | +static const dev_clock_t dev_clock_list[] = { | |
58 | + { .name = "pck_sci", .devnum = CK_SCI, .reg = 0, .offset = 0}, | |
59 | + { .name = "pck_rtc", .devnum = CK_RTC, .reg = 0, .offset = 1}, | |
60 | + { .name = "pck_tmu-0", .devnum = CK_TMU_0, .reg = 0, .offset = 2}, | |
61 | + { .name = "pck_scif", .devnum = CK_SCIF, .reg = 0, .offset = 3}, | |
62 | + { .name = "pck_dmac", .devnum = CK_DMAC, .reg = 0, .offset = 4}, | |
63 | + { .name = "pck_ubc", .devnum = CK_UBC, .reg = 1, .offset = 0}, | |
64 | + { .name = "pck_sq", .devnum = CK_SQ, .reg = 1, .offset = 1}, | |
65 | + { .name = "pck_intc", .devnum = CK_INTC, .reg = 2, .offset = 0}, | |
66 | + { .name = "pck_tmu-1", .devnum = CK_TMU_1, .reg = 2, .offset = 1}, | |
67 | + { .name = "pck_pcic", .devnum = CK_PCIC, .reg = 2, .offset = 2}, | |
68 | + { }, | |
69 | +}; | |
70 | + | |
71 | +static void set_clock_in(SH7751CPGBaseState *cpg, const dev_clock_t *ck) | |
72 | +{ | |
73 | + Clock *out; | |
74 | + uint64_t period; | |
75 | + | |
76 | + out = qdev_get_clock_out(DEVICE(cpg), ck->name); | |
77 | + g_assert(out); | |
78 | + period = 0; | |
79 | + switch (ck->reg) { | |
80 | + case 0: | |
81 | + case 1: | |
82 | + if (extract8(cpg->stbcr[ck->reg], ck->offset, 1) == 0) { | |
83 | + period = clock_get(cpg->clk_ick); | |
84 | + } | |
85 | + break; | |
86 | + case 2: | |
87 | + if (extract32(cpg->clkstp00, ck->offset, 1) == 0) { | |
88 | + period = clock_get(cpg->clk_ick); | |
89 | + } | |
90 | + break; | |
91 | + } | |
92 | + if (clock_get(out) != period) { | |
93 | + clock_update(out, period); | |
94 | + } | |
95 | +} | |
96 | + | |
97 | +static void update_divrate(SH7751CPGBaseState *cpg) | |
98 | +{ | |
99 | + SH7751CPGBaseClass *k = SH7751CPG_GET_CLASS(cpg); | |
100 | + int ick = FIELD_EX32(cpg->freqcr, FREQCR, IFC); | |
101 | + int bck = FIELD_EX32(cpg->freqcr, FREQCR, BFC); | |
102 | + int pck = FIELD_EX32(cpg->freqcr, FREQCR, PFC); | |
103 | + const dev_clock_t *p = dev_clock_list; | |
104 | + uint32_t divinput; | |
105 | + | |
106 | + divinput = cpg->xtal_freq_hz * k->pll1mul(cpg->clock_mode, cpg->freqcr); | |
107 | + | |
108 | + ick = ick < 4 ? ick + 1 : (ick - 1) * 2; | |
109 | + clock_update_hz(cpg->clk_ick, divinput / ick); | |
110 | + bck = bck < 4 ? bck + 1 : (bck - 1) * 2; | |
111 | + clock_update_hz(cpg->clk_bck, divinput / bck); | |
112 | + pck = pck < 3 ? pck + 2 : pck * 2; | |
113 | + clock_update_hz(cpg->clk_pck, divinput / pck); | |
114 | + | |
115 | + while (p->name) { | |
116 | + set_clock_in(cpg, p); | |
117 | + p++; | |
118 | + } | |
119 | +} | |
120 | + | |
121 | +static const dev_clock_t *find_clock_list(int crno, int bit) | |
122 | +{ | |
123 | + const dev_clock_t *ret = dev_clock_list; | |
124 | + while (ret->name) { | |
125 | + if (ret->reg == crno && ret->offset == bit) { | |
126 | + return ret; | |
127 | + } | |
128 | + ret++; | |
129 | + } | |
130 | + return NULL; | |
131 | +} | |
132 | + | |
133 | +static void update_stbcr(SH7751CPGBaseState *cpg, int no, uint32_t diff) | |
134 | +{ | |
135 | + int bit = 0; | |
136 | + const dev_clock_t *p; | |
137 | + static const char *reg[] = {"STBCR", "STBCR2", "CLKSTP00"}; | |
138 | + | |
139 | + while (diff) { | |
140 | + if (diff & 1) { | |
141 | + p = find_clock_list(no, bit); | |
142 | + if (p) { | |
143 | + set_clock_in(cpg, p); | |
144 | + } else { | |
145 | + qemu_log_mask(LOG_UNIMP, "sh7751-cpg: %s " | |
146 | + " bit %d is not implement.\n", reg[no], bit); | |
147 | + } | |
148 | + } | |
149 | + bit++; | |
150 | + diff >>= 1; | |
151 | + } | |
152 | +} | |
153 | + | |
154 | +static uint64_t cpg_read(void *opaque, hwaddr addr, unsigned size) | |
155 | +{ | |
156 | + SH7751CPGBaseState *cpg = SH7751CPGBase(opaque); | |
157 | + int reg; | |
158 | + | |
159 | + switch (addr) { | |
160 | + case A_FREQCR: | |
161 | + if (size != 2) { | |
162 | + qemu_log_mask(LOG_GUEST_ERROR, "sh7751-cpg: Register 0x%" | |
163 | + HWADDR_PRIX " Invalid access size.\n", addr); | |
164 | + return UINT64_MAX; | |
165 | + } | |
166 | + return cpg->freqcr; | |
167 | + case A_STBCR: | |
168 | + case A_STBCR2: | |
169 | + if (size != 1) { | |
170 | + qemu_log_mask(LOG_GUEST_ERROR, "sh7751-cpg: Register 0x%" | |
171 | + HWADDR_PRIX " Invalid access size.\n", addr); | |
172 | + return UINT64_MAX; | |
173 | + } | |
174 | + reg = extract32(addr, 4, 1); /* STBCR -> 0x04 / STBCR2 -> 0x10 */ | |
175 | + return cpg->stbcr[reg]; | |
176 | + break; | |
177 | + default: | |
178 | + qemu_log_mask(LOG_GUEST_ERROR, "sh7751-cpg: Register 0x%" | |
179 | + HWADDR_PRIX " Invalid address.\n", addr); | |
180 | + return UINT64_MAX; | |
181 | + } | |
182 | + | |
183 | +} | |
184 | + | |
185 | +static void cpg_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) | |
186 | +{ | |
187 | + SH7751CPGBaseState *cpg = SH7751CPGBase(opaque); | |
188 | + uint32_t old_stbcr; | |
189 | + int reg; | |
190 | + | |
191 | + switch (addr) { | |
192 | + case A_FREQCR: | |
193 | + if (size != 2) { | |
194 | + qemu_log_mask(LOG_GUEST_ERROR, "sh7751-cpg: Register 0x%" | |
195 | + HWADDR_PRIX " Invalid access size.\n", addr); | |
196 | + return; | |
197 | + } | |
198 | + if ((cpg->freqcr ^ val) & 0x0600) { | |
199 | + qemu_log_mask(LOG_UNIMP, | |
200 | + "sh7751-cpg: PLL operation not supported.\n"); | |
201 | + } | |
202 | + cpg->freqcr = val; | |
203 | + update_divrate(cpg); | |
204 | + break; | |
205 | + case A_STBCR: | |
206 | + case A_STBCR2: | |
207 | + if (size != 1) { | |
208 | + qemu_log_mask(LOG_GUEST_ERROR, "sh7751-cpg: Register 0x%" | |
209 | + HWADDR_PRIX " Invalid access size.\n", addr); | |
210 | + return; | |
211 | + } | |
212 | + reg = extract32(addr, 4, 1); /* STBCR -> 0x04 / STBCR2 -> 0x10 */ | |
213 | + old_stbcr = cpg->stbcr[reg]; | |
214 | + old_stbcr ^= val; | |
215 | + cpg->stbcr[reg] = val; | |
216 | + update_stbcr(cpg, reg, old_stbcr); | |
217 | + break; | |
218 | + default: | |
219 | + qemu_log_mask(LOG_GUEST_ERROR, "sh7751-cpg: Register 0x%" | |
220 | + HWADDR_PRIX " Invalid address.\n", addr); | |
221 | + } | |
222 | +} | |
223 | + | |
224 | +static uint64_t stp_read(void *opaque, hwaddr addr, unsigned size) | |
225 | +{ | |
226 | + SH7751CPGBaseState *cpg = SH7751CPGBase(opaque); | |
227 | + | |
228 | + switch (addr) { | |
229 | + case A_CLKSTP00: | |
230 | + return cpg->clkstp00; | |
231 | + case A_CLKSTPCLR00: | |
232 | + qemu_log_mask(LOG_GUEST_ERROR, | |
233 | + "sh7751-cpg: CLKSTPCLR00 is write only.\n"); | |
234 | + return UINT64_MAX; | |
235 | + default: | |
236 | + qemu_log_mask(LOG_GUEST_ERROR, "sh7751-cpg: Register 0x%" | |
237 | + HWADDR_PRIX " Invalid address.\n", addr); | |
238 | + return UINT64_MAX; | |
239 | + } | |
240 | +} | |
241 | + | |
242 | +static void stp_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) | |
243 | +{ | |
244 | + SH7751CPGBaseState *cpg = SH7751CPGBase(opaque); | |
245 | + | |
246 | + val &= 0x7; | |
247 | + switch (addr) { | |
248 | + case A_CLKSTP00: | |
249 | + cpg->clkstp00 |= val; | |
250 | + update_stbcr(cpg, 2, val); | |
251 | + break; | |
252 | + case A_CLKSTPCLR00: | |
253 | + cpg->clkstp00 &= ~val; | |
254 | + update_stbcr(cpg, 2, val); | |
255 | + break; | |
256 | + default: | |
257 | + qemu_log_mask(LOG_GUEST_ERROR, "sh7751-cpg: Register 0x%" | |
258 | + HWADDR_PRIX " Invalid address.\n", addr); | |
259 | + } | |
260 | +} | |
261 | + | |
262 | +static int sh7751_pll1mul(int mode, uint16_t freqcr) | |
263 | +{ | |
264 | + int div1; | |
265 | + int pll1; | |
266 | + switch (mode) { | |
267 | + case 3: | |
268 | + case 5: | |
269 | + case 6: | |
270 | + div1 = 2; | |
271 | + break; | |
272 | + default: | |
273 | + div1 = 1; | |
274 | + } | |
275 | + if (FIELD_EX16(freqcr, FREQCR, PLL1EN)) { | |
276 | + pll1 = 6; | |
277 | + } else { | |
278 | + pll1 = 1; | |
279 | + } | |
280 | + return pll1 / div1; | |
281 | +} | |
282 | + | |
283 | +static int sh7751r_pll1mul(int mode, uint16_t freqcr) | |
284 | +{ | |
285 | + int pll1; | |
286 | + switch (mode) { | |
287 | + case 0: | |
288 | + case 1: | |
289 | + case 3: | |
290 | + case 5: | |
291 | + pll1 = 12; | |
292 | + break; | |
293 | + case 2: | |
294 | + case 4: | |
295 | + case 6: | |
296 | + pll1 = 6; | |
297 | + break; | |
298 | + default: | |
299 | + g_assert_not_reached(); | |
300 | + } | |
301 | + if (!FIELD_EX16(freqcr, FREQCR, PLL1EN)) { | |
302 | + pll1 = 1; | |
303 | + } | |
304 | + return pll1; | |
305 | +} | |
306 | + | |
307 | +static const MemoryRegionOps cpg_ops = { | |
308 | + .write = cpg_write, | |
309 | + .read = cpg_read, | |
310 | + .endianness = DEVICE_NATIVE_ENDIAN, | |
311 | + .impl = { | |
312 | + .min_access_size = 1, | |
313 | + .max_access_size = 4, | |
314 | + }, | |
315 | +}; | |
316 | + | |
317 | +static const MemoryRegionOps stp_ops = { | |
318 | + .write = stp_write, | |
319 | + .read = stp_read, | |
320 | + .endianness = DEVICE_NATIVE_ENDIAN, | |
321 | + .impl = { | |
322 | + .min_access_size = 4, | |
323 | + .max_access_size = 4, | |
324 | + }, | |
325 | +}; | |
326 | + | |
327 | +static const ClockPortInitArray sh7751_cpg_clocks = { | |
328 | + QDEV_CLOCK_OUT(SH7751CPGBaseState, clk_ick), | |
329 | + QDEV_CLOCK_OUT(SH7751CPGBaseState, clk_bck), | |
330 | + QDEV_CLOCK_OUT(SH7751CPGBaseState, clk_pck), | |
331 | + QDEV_CLOCK_END | |
332 | +}; | |
333 | + | |
334 | +static void sh7751cpg_realize(DeviceState *dev, Error **errp) | |
335 | +{ | |
336 | + SH7751CPGBaseState *cpg = SH7751CPGBase(dev); | |
337 | + SH7751CPGBaseClass *k = SH7751CPG_GET_CLASS(cpg); | |
338 | + | |
339 | + if (cpg->xtal_freq_hz == 0) { | |
340 | + error_setg(errp, "\"xtal-frequency-hz\" property must be provided."); | |
341 | + return; | |
342 | + } | |
343 | + /* XTAL range: 1-34 MHz */ | |
344 | + if (cpg->xtal_freq_hz < SH7751_XTAL_MIN_HZ || | |
345 | + cpg->xtal_freq_hz > SH7751_XTAL_MAX_HZ) { | |
346 | + error_setg(errp, "\"xtal-frequency-hz\" property in incorrect range."); | |
347 | + return; | |
348 | + } | |
349 | + /* Clock mode: 0 - 6 */ | |
350 | + if (cpg->clock_mode > 6) { | |
351 | + error_setg(errp, "\"clock-mode\" property in incorrect range."); | |
352 | + return; | |
353 | + } | |
354 | + | |
355 | + cpg->freqcr = k->initfreqcr[cpg->clock_mode]; | |
356 | + update_divrate(cpg); | |
357 | +} | |
358 | + | |
359 | +static void sh7751_cpg_init(Object *obj) | |
360 | +{ | |
361 | + SH7751CPGBaseState *cpg = SH7751CPGBase(obj); | |
362 | + const dev_clock_t *p = dev_clock_list; | |
363 | + qdev_init_clocks(DEVICE(obj), sh7751_cpg_clocks); | |
364 | + /* connect parent clock */ | |
365 | + while (p->name) { | |
366 | + cpg->dev_clocks[p->devnum] = qdev_init_clock_out(DEVICE(obj), | |
367 | + p->name); | |
368 | + p++; | |
369 | + } | |
370 | + | |
371 | + memory_region_init_io(&cpg->memory[0], OBJECT(cpg), &cpg_ops, | |
372 | + cpg, "sh7751-cpg", 0x14); | |
373 | + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &cpg->memory[0]); | |
374 | + memory_region_init_alias(&cpg->memory[1], NULL, | |
375 | + "sh7751-cpg-a4", &cpg->memory[0], 0, 0x14); | |
376 | + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &cpg->memory[1]); | |
377 | + memory_region_init_alias(&cpg->memory[2], NULL, | |
378 | + "sh7751-cpg-p7", &cpg->memory[0], 0, 0x14); | |
379 | + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &cpg->memory[2]); | |
380 | + memory_region_init_io(&cpg->memory[3], OBJECT(cpg), &stp_ops, | |
381 | + cpg, "sh7751-stp", 0x10); | |
382 | + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &cpg->memory[3]); | |
383 | + memory_region_init_alias(&cpg->memory[4], NULL, | |
384 | + "sh7751-stp-a4", &cpg->memory[3], 0, 0x10); | |
385 | + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &cpg->memory[4]); | |
386 | + memory_region_init_alias(&cpg->memory[5], NULL, | |
387 | + "sh7751-stp-p7", &cpg->memory[3], 0, 0x10u); | |
388 | + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &cpg->memory[5]); | |
389 | +} | |
390 | + | |
391 | +static Property sh7751_cpg_properties[] = { | |
392 | + DEFINE_PROP_UINT32("xtal-frequency-hz", | |
393 | + SH7751CPGBaseState, xtal_freq_hz, 0), | |
394 | + DEFINE_PROP_UINT32("clock-mode", | |
395 | + SH7751CPGBaseState, clock_mode, 0), | |
396 | + DEFINE_PROP_END_OF_LIST(), | |
397 | +}; | |
398 | + | |
399 | +static void sh7751cpg_base_class_init(ObjectClass *klass, void *data) | |
400 | +{ | |
401 | + DeviceClass *dc = DEVICE_CLASS(klass); | |
402 | + | |
403 | + device_class_set_props(dc, sh7751_cpg_properties); | |
404 | +} | |
405 | + | |
406 | +static void sh7751cpg_class_init(ObjectClass *klass, void *data) | |
407 | +{ | |
408 | + SH7751CPGBaseClass *base = SH7751CPGBaseClass(klass); | |
409 | + DeviceClass *dc = DEVICE_CLASS(klass); | |
410 | + static uint16_t initfreqcr[] = {0x0e1a, 0x0e23, 0x0e13, 0x0e13, | |
411 | + 0x0e0a, 0x0e0a, 0x0808}; | |
412 | + | |
413 | + base->pll1mul = sh7751_pll1mul; | |
414 | + base->initfreqcr = initfreqcr; | |
415 | + dc->realize = sh7751cpg_realize; | |
416 | +} | |
417 | + | |
418 | +static void sh7751rcpg_class_init(ObjectClass *klass, void *data) | |
419 | +{ | |
420 | + SH7751CPGBaseClass *base = SH7751CPGBaseClass(klass); | |
421 | + DeviceClass *dc = DEVICE_CLASS(klass); | |
422 | + static uint16_t initfreqcr[] = {0x0e1a, 0x0e2c, 0x0e13, 0x0e13, | |
423 | + 0x0e0a, 0x0e0a, 0x0808}; | |
424 | + | |
425 | + base->pll1mul = sh7751r_pll1mul; | |
426 | + base->initfreqcr = initfreqcr; | |
427 | + dc->realize = sh7751cpg_realize; | |
428 | +} | |
429 | + | |
430 | +static const TypeInfo sh7751cpg_info[] = { | |
431 | + { | |
432 | + .name = TYPE_SH7751_CPG_BASE, | |
433 | + .parent = TYPE_SYS_BUS_DEVICE, | |
434 | + .instance_size = sizeof(SH7751CPGBaseState), | |
435 | + .class_init = sh7751cpg_base_class_init, | |
436 | + .class_size = sizeof(SH7751CPGBaseClass), | |
437 | + .abstract = true, | |
438 | + }, | |
439 | + { | |
440 | + .name = TYPE_SH7751_CPG, | |
441 | + .parent = TYPE_SH7751_CPG_BASE, | |
442 | + .instance_size = sizeof(SH7751CPGState), | |
443 | + .instance_init = sh7751_cpg_init, | |
444 | + .class_init = sh7751cpg_class_init, | |
445 | + .class_size = sizeof(SH7751CPGClass), | |
446 | + }, | |
447 | + { | |
448 | + .name = TYPE_SH7751R_CPG, | |
449 | + .parent = TYPE_SH7751_CPG_BASE, | |
450 | + .instance_size = sizeof(SH7751RCPGState), | |
451 | + .instance_init = sh7751_cpg_init, | |
452 | + .class_init = sh7751rcpg_class_init, | |
453 | + .class_size = sizeof(SH7751RCPGClass), | |
454 | + }, | |
455 | +}; | |
456 | + | |
457 | +DEFINE_TYPES(sh7751cpg_info) |
@@ -0,0 +1,94 @@ | ||
1 | +/* | |
2 | + * SH7751(R) Clock generator circuit | |
3 | + * | |
4 | + * Datasheet: SH7751 Group, SH7751R Group User's Manual: Hardware | |
5 | + * (Rev.4.01 R01UH0457EJ0401) | |
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_SH4_SH7751_CPG_H | |
23 | +#define HW_SH4_SH7751_CPG_H | |
24 | + | |
25 | +#include "hw/sysbus.h" | |
26 | +#include "hw/qdev-clock.h" | |
27 | + | |
28 | +#define TYPE_SH7751_CPG_BASE "sh7751-cpg-base" | |
29 | +#define SH7751CPGBase(obj) \ | |
30 | + OBJECT_CHECK(SH7751CPGBaseState, (obj), TYPE_SH7751_CPG_BASE) | |
31 | +#define TYPE_SH7751_CPG "sh7751-cpg" | |
32 | +#define SH7751CPG(obj) OBJECT_CHECK(SH7751CPGState, (obj), TYPE_SH7751_CPG) | |
33 | +#define TYPE_SH7751R_CPG "sh7751r-cpg" | |
34 | +#define SH7751RCPG(obj) OBJECT_CHECK(SH7751RCPGState, (obj), TYPE_SH7751R_CPG) | |
35 | +#define SH7751CPG_GET_CLASS(obj) \ | |
36 | + OBJECT_GET_CLASS(SH7751CPGBaseClass, obj, TYPE_SH7751_CPG_BASE) | |
37 | +#define SH7751CPGBaseClass(klass) \ | |
38 | + OBJECT_CLASS_CHECK(SH7751CPGBaseClass, klass, TYPE_SH7751_CPG_BASE) | |
39 | + | |
40 | +enum { | |
41 | + CK_DMAC, | |
42 | + CK_SCIF, | |
43 | + CK_TMU_0, | |
44 | + CK_RTC, | |
45 | + CK_SCI, | |
46 | + CK_SQ, | |
47 | + CK_UBC, | |
48 | + CK_PCIC, | |
49 | + CK_TMU_1, | |
50 | + CK_INTC, | |
51 | + NUM_SUBCLOCK, | |
52 | +}; | |
53 | + | |
54 | +typedef struct SH7751CPGBaseState { | |
55 | + SysBusDevice parent_obj; | |
56 | + uint8_t stbcr[2]; | |
57 | + uint32_t clkstp00; | |
58 | + uint16_t freqcr; | |
59 | + | |
60 | + uint32_t clock_mode; | |
61 | + int ick; | |
62 | + Clock *clk_ick; | |
63 | + int bck; | |
64 | + Clock *clk_bck; | |
65 | + int pck; | |
66 | + Clock *clk_pck; | |
67 | + Clock *dev_clocks[NUM_SUBCLOCK]; | |
68 | + uint32_t xtal_freq_hz; | |
69 | + MemoryRegion memory[3 * 2]; | |
70 | +} SH7751CPGBaseState; | |
71 | + | |
72 | +typedef struct { | |
73 | + SH7751CPGBaseState parent_obj; | |
74 | +} SH7751CPGState; | |
75 | + | |
76 | +typedef struct { | |
77 | + SH7751CPGBaseState parent_obj; | |
78 | +} SH7751RCPGState; | |
79 | + | |
80 | +typedef struct { | |
81 | + SysBusDeviceClass parent; | |
82 | + int (*pll1mul)(int mode, uint16_t freqcr); | |
83 | + uint16_t *initfreqcr; | |
84 | +} SH7751CPGBaseClass; | |
85 | + | |
86 | +typedef struct { | |
87 | + SH7751CPGBaseClass parent; | |
88 | +} SH7751CPGClass; | |
89 | + | |
90 | +typedef struct { | |
91 | + SH7751CPGBaseClass parent; | |
92 | +} SH7751RCPGClass; | |
93 | + | |
94 | +#endif |