• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

Commit MetaInfo

Revisión75a502e22cf20b852f107fe521c2600e22cb3a08 (tree)
Tiempo2020-06-01 00:49:33
AutorYoshinori Sato <ysato@user...>
CommiterYoshinori Sato

Log Message

hw/timer: Renesas TMU/CMT module.

TMU - SH4 Timer module.
CMT - Compare and match timer used by some Renesas MCUs.

The two modules have similar interfaces and have been merged.

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>

Cambiar Resumen

Diferencia incremental

--- a/hw/timer/Kconfig
+++ b/hw/timer/Kconfig
@@ -38,3 +38,6 @@ config CMSDK_APB_DUALTIMER
3838
3939 config RENESAS_8TMR
4040 bool
41+
42+config RENESAS_TIMER
43+ bool
--- a/hw/timer/Makefile.objs
+++ b/hw/timer/Makefile.objs
@@ -37,3 +37,4 @@ common-obj-$(CONFIG_MSF2) += mss-timer.o
3737 common-obj-$(CONFIG_RASPI) += bcm2835_systmr.o
3838
3939 common-obj-$(CONFIG_RENESAS_8TMR) += renesas_8timer.o
40+common-obj-$(CONFIG_RENESAS_TIMER) += renesas_timer.o
--- /dev/null
+++ b/hw/timer/renesas_timer.c
@@ -0,0 +1,421 @@
1+/*
2+ * Renesas 16bit Compare-match timer
3+ *
4+ * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
5+ * (Rev.1.40 R01UH0033EJ0140)
6+ *
7+ * Copyright (c) 2019 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 "qemu-common.h"
24+#include "qemu/log.h"
25+#include "qapi/error.h"
26+#include "qemu/timer.h"
27+#include "hw/hw.h"
28+#include "hw/irq.h"
29+#include "hw/sysbus.h"
30+#include "hw/registerfields.h"
31+#include "hw/qdev-properties.h"
32+#include "hw/timer/renesas_timer.h"
33+#include "migration/vmstate.h"
34+#include "qemu/error-report.h"
35+
36+REG32(TOCR, 0)
37+ FIELD(TOCR, TCOE, 0, 1)
38+REG32(TSTR, 4)
39+REG32(TCOR, 8)
40+REG32(TCNT, 12)
41+REG32(TCR, 16)
42+ FIELD(TCR, TPSC, 0, 3)
43+ FIELD(TCR, CKEG, 3, 2)
44+ FIELD(TCR, UNIE, 5, 1)
45+ FIELD(TCR, ICPE, 6, 2)
46+ FIELD(TCR, UNF, 8, 1)
47+ FIELD(TCR, ICPF, 9, 1)
48+REG32(CMCR, 16)
49+ FIELD(CMCR, CKS, 0, 2)
50+ FIELD(CMCR, CMIE, 6, 1)
51+REG32(TCPR, 20)
52+
53+#define IS_CMT(t) (t->feature == RTIMER_FEAT_CMT)
54+
55+static int clkdiv(RTIMERState *tmr, int ch)
56+{
57+ if (IS_CMT(tmr)) {
58+ return 8 << (2 * FIELD_EX16(tmr->ch[ch].ctrl, CMCR, CKS));
59+ } else {
60+ if (FIELD_EX16(tmr->ch[ch].ctrl, TCR, TPSC) <= 5) {
61+ return 4 << (2 * FIELD_EX16(tmr->ch[ch].ctrl, TCR, TPSC));
62+ } else {
63+ return 0;
64+ }
65+ }
66+}
67+
68+static void set_next_event(struct channel_rtimer *ch, int64_t now)
69+{
70+ int64_t next;
71+ RTIMERState *tmr = ch->tmrp;
72+ if (IS_CMT(tmr)) {
73+ next = ch->cor - ch->cnt;
74+ } else {
75+ next = ch->cnt;
76+ }
77+ next *= ch->clk;
78+ ch->base = now;
79+ ch->next = now + next;
80+ timer_mod(ch->timer, ch->next);
81+}
82+
83+static void timer_event(void *opaque)
84+{
85+ struct channel_rtimer *ch = opaque;
86+ RTIMERState *tmr = ch->tmrp;
87+
88+ if (IS_CMT(tmr)) {
89+ ch->cnt = 0;
90+ if (FIELD_EX16(ch->ctrl, CMCR, CMIE)) {
91+ qemu_irq_pulse(ch->irq);
92+ }
93+ } else {
94+ ch->cnt = ch->cor;
95+ if (!FIELD_EX16(ch->ctrl, TCR, UNF)) {
96+ ch->ctrl = FIELD_DP16(ch->ctrl, TCR, UNF, 1);
97+ qemu_set_irq(ch->irq, FIELD_EX16(ch->ctrl, TCR, UNIE));
98+ }
99+ }
100+ set_next_event(ch, ch->next);
101+}
102+
103+static int64_t read_tcnt(RTIMERState *tmr, int ch)
104+{
105+ int64_t delta, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
106+
107+ if (tmr->ch[ch].clk > 0) {
108+ delta = (now - tmr->ch[ch].base);
109+ delta /= tmr->ch[ch].clk;
110+ if (IS_CMT(tmr)) {
111+ return delta;
112+ } else {
113+ return tmr->ch[ch].cnt - delta;
114+ }
115+ } else {
116+ return tmr->ch[ch].cnt;
117+ }
118+}
119+
120+static void tmr_start_stop(RTIMERState *tmr, int ch, int start)
121+{
122+ int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
123+ tmr->ch[ch].start = start;
124+ if (start) {
125+ if (!tmr->ch[ch].timer) {
126+ tmr->ch[ch].timer =
127+ timer_new_ns(QEMU_CLOCK_VIRTUAL, timer_event, &tmr->ch[ch]);
128+ }
129+ set_next_event(&tmr->ch[ch], now);
130+ } else {
131+ tmr->ch[ch].cnt = read_tcnt(tmr, ch);
132+ tmr->ch[ch].next = 0;
133+ if (tmr->ch[ch].timer) {
134+ timer_del(tmr->ch[ch].timer);
135+ }
136+ }
137+}
138+
139+static void timer_register(RTIMERState *tmr, hwaddr addr, int *ch, int *reg)
140+{
141+ if (IS_CMT(tmr)) {
142+ /* +0 - CMSTR (TSTR) */
143+ /* +2 - CMCR0 (TCR) */
144+ /* +4 - CMCNT0 (TCNT) */
145+ /* +6 - CMCOR0 (TCOR) */
146+ /* +8 - CMCR1 (TCR) */
147+ /* +10 - CMCNT1 (TCNT) */
148+ /* +12 - CMCOR1 (TCOR) */
149+ addr /= 2;
150+ if (addr > 6) {
151+ /* Out of register area */
152+ *reg = -1;
153+ return;
154+ }
155+ if (addr == 0) {
156+ *ch = -1;
157+ *reg = R_TSTR;
158+ } else {
159+ *ch = addr / 4;
160+ if (addr < 4) {
161+ /* skip CMSTR */
162+ addr--;
163+ }
164+ *reg = 2 - (addr % 4);
165+ }
166+ } else {
167+ /* +0 - TCOR */
168+ /* +4 - TSTR */
169+ /* +8 - TCOR0 */
170+ /* +12 - TCNT0 */
171+ /* +16 - TCR0 */
172+ /* +20 - TCOR1 */
173+ /* +24 - TCNT1 */
174+ /* +28 - TCR1 */
175+ /* +32 - TCOR2 */
176+ /* +36 - TCNT2 */
177+ /* +40 - TCR2 */
178+ /* +44 - TCPR2 */
179+ if (tmr->feature == RTIMER_FEAT_TMU_HIGH && addr >= 8) {
180+ *reg = -1;
181+ return;
182+ }
183+ addr /= 4;
184+ if (addr < 2) {
185+ *ch = -1;
186+ *reg = addr;
187+ } else if (addr < 11) {
188+ *ch = (addr - 2) / 3;
189+ *reg = (addr - 2) % 3 + 2;
190+ } else {
191+ *ch = 2;
192+ *reg = R_TCPR;
193+ }
194+ }
195+}
196+
197+static uint64_t read_tstr(RTIMERState *tmr)
198+{
199+ uint64_t ret = 0;
200+ int ch;
201+ for (ch = 0; ch < tmr->num_ch; ch++) {
202+ ret = deposit64(ret, ch, 1, tmr->ch[ch].start);
203+ }
204+ return ret;
205+}
206+
207+static void update_clk(RTIMERState *tmr, int ch)
208+{
209+ int tpsc;
210+ int t;
211+ if (!IS_CMT(tmr)) {
212+ /* Clock setting validation */
213+ tpsc = FIELD_EX16(tmr->ch[ch].ctrl, TCR, TPSC);
214+ switch (tpsc) {
215+ case 5:
216+ qemu_log_mask(LOG_GUEST_ERROR,
217+ "renesas_timer: Invalid TPSC valule %d.", tpsc);
218+ break;
219+ case 6:
220+ case 7:
221+ qemu_log_mask(LOG_UNIMP,
222+ "renesas_timer: External clock not implemented.");
223+ break;
224+ }
225+ /* Interrupt clear */
226+ if (FIELD_EX16(tmr->ch[ch].ctrl, TCR, UNF) == 0) {
227+ qemu_set_irq(tmr->ch[ch].irq, 0);
228+ }
229+ }
230+ t = clkdiv(tmr, ch);
231+ if (t > 0) {
232+ t = tmr->input_freq / t;
233+ tmr->ch[ch].clk = NANOSECONDS_PER_SECOND / t;
234+ } else {
235+ tmr->ch[ch].clk = 0;
236+ }
237+}
238+
239+static uint64_t tmr_read(void *opaque, hwaddr addr, unsigned size)
240+{
241+ RTIMERState *tmr = opaque;
242+ int ch = -1, reg = -1;
243+
244+ timer_register(tmr, addr, &ch, &reg);
245+ switch (reg) {
246+ case R_TOCR:
247+ return tmr->tocr;
248+ case R_TSTR:
249+ return read_tstr(tmr);
250+ case R_TCR:
251+ return tmr->ch[ch].ctrl;
252+ case R_TCNT:
253+ if (tmr->ch[ch].start) {
254+ return read_tcnt(tmr, ch);
255+ } else {
256+ return tmr->ch[ch].cnt;
257+ }
258+ case R_TCOR:
259+ return tmr->ch[ch].cor;
260+ case R_TCPR:
261+ qemu_log_mask(LOG_UNIMP,
262+ "renesas_timer: Input capture not implemented\n");
263+ return 0;
264+ default:
265+ qemu_log_mask(LOG_UNIMP, "renesas_timer: Register 0x%"
266+ HWADDR_PRIX " not implemented\n", addr);
267+ }
268+ return UINT64_MAX;
269+}
270+
271+static void tmr_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
272+{
273+ RTIMERState *tmr = opaque;
274+ int ch = -1, reg = -1;
275+ uint16_t tcr_mask;
276+
277+ timer_register(tmr, addr, &ch, &reg);
278+ switch (reg) {
279+ case R_TOCR:
280+ tmr->tocr = FIELD_DP8(tmr->tocr, TOCR, TCOE,
281+ FIELD_EX8(val, TOCR, TCOE));
282+ break;
283+ case R_TSTR:
284+ for (ch = 0; ch < tmr->num_ch; ch++) {
285+ tmr_start_stop(tmr, ch, extract32(val, ch, 1));
286+ }
287+ break;
288+ case R_TCR:
289+ switch (tmr->feature) {
290+ case RTIMER_FEAT_CMT:
291+ tcr_mask = 0x00a3;
292+ /* bit7 always 1 */
293+ val |= 0x0080;
294+ break;
295+ case RTIMER_FEAT_TMU_LOW:
296+ tcr_mask = (ch < 2) ? 0x013f : 0x03ff;
297+ break;
298+ case RTIMER_FEAT_TMU_HIGH:
299+ tcr_mask = 0x0127;
300+ break;
301+ default:
302+ tcr_mask = 0x00ff;
303+ break;
304+ }
305+ /* Upper byte write only 0 */
306+ tmr->ch[ch].ctrl |= (tcr_mask & 0x00ff);
307+ tmr->ch[ch].ctrl &= val & tcr_mask;
308+ update_clk(tmr, ch);
309+ break;
310+ case R_TCNT:
311+ tmr->ch[ch].cnt = val;
312+ break;
313+ case R_TCOR:
314+ tmr->ch[ch].cor = val;
315+ break;
316+ case R_TCPR:
317+ qemu_log_mask(LOG_GUEST_ERROR,
318+ "renesas_timer: TCPR is read only.");
319+ break;
320+ default:
321+ qemu_log_mask(LOG_UNIMP, "renesas_timer: Register 0x%"
322+ HWADDR_PRIX " not implemented\n", addr);
323+ }
324+}
325+
326+static const MemoryRegionOps tmr_ops = {
327+ .write = tmr_write,
328+ .read = tmr_read,
329+ .endianness = DEVICE_NATIVE_ENDIAN,
330+ .impl = {
331+ .min_access_size = 2,
332+ .max_access_size = 4,
333+ },
334+};
335+
336+static void rtimer_realize(DeviceState *dev, Error **errp)
337+{
338+ SysBusDevice *d = SYS_BUS_DEVICE(dev);
339+ RTIMERState *tmr = RTIMER(dev);
340+ int i;
341+ int ch;
342+
343+ if (tmr->input_freq == 0) {
344+ qemu_log_mask(LOG_GUEST_ERROR,
345+ "renesas_timer: input-freq property must be set.");
346+ return;
347+ }
348+ if (IS_CMT(tmr)) {
349+ memory_region_init_io(&tmr->memory, OBJECT(tmr), &tmr_ops,
350+ tmr, "renesas-cmt", 0x10);
351+ sysbus_init_mmio(d, &tmr->memory);
352+
353+ for (i = 0; i < TIMER_CH_CMT; i++) {
354+ sysbus_init_irq(d, &tmr->ch[i].irq);
355+ }
356+ tmr->num_ch = 2;
357+ } else {
358+ memory_region_init_io(&tmr->memory, OBJECT(tmr), &tmr_ops,
359+ tmr, "renesas-tmu", 0x30);
360+ sysbus_init_mmio(d, &tmr->memory);
361+ memory_region_init_alias(&tmr->memory_p4, NULL, "renesas-tmu-p4",
362+ &tmr->memory, 0, 0x30);
363+ sysbus_init_mmio(d, &tmr->memory_p4);
364+ memory_region_init_alias(&tmr->memory_a7, NULL, "renesas-tmu-a7",
365+ &tmr->memory, 0, 0x30);
366+ sysbus_init_mmio(d, &tmr->memory_a7);
367+ ch = (tmr->feature == RTIMER_FEAT_TMU_LOW) ?
368+ TIMER_CH_TMU : TIMER_CH_TMU - 1;
369+ for (i = 0; i < ch; i++) {
370+ sysbus_init_irq(d, &tmr->ch[i].irq);
371+ }
372+ tmr->num_ch = (tmr->feature == RTIMER_FEAT_TMU_LOW) ? 3 : 2;
373+ }
374+ for (ch = 0; ch < tmr->num_ch; ch++) {
375+ tmr->ch[ch].tmrp = tmr;
376+ update_clk(tmr, ch);
377+ if (IS_CMT(tmr)) {
378+ tmr->ch[ch].cor = 0xffff;
379+ } else {
380+ tmr->ch[ch].cor = tmr->ch[ch].cnt = 0xffffffff;
381+ }
382+ }
383+}
384+
385+static const VMStateDescription vmstate_rtimer = {
386+ .name = "rx-cmt",
387+ .version_id = 1,
388+ .minimum_version_id = 1,
389+ .fields = (VMStateField[]) {
390+ VMSTATE_END_OF_LIST()
391+ }
392+};
393+
394+static Property rtimer_properties[] = {
395+ DEFINE_PROP_UINT32("feature", RTIMERState, feature, 0),
396+ DEFINE_PROP_UINT64("input-freq", RTIMERState, input_freq, 0),
397+ DEFINE_PROP_END_OF_LIST(),
398+};
399+
400+static void rtimer_class_init(ObjectClass *klass, void *data)
401+{
402+ DeviceClass *dc = DEVICE_CLASS(klass);
403+
404+ dc->vmsd = &vmstate_rtimer;
405+ dc->realize = rtimer_realize;
406+ device_class_set_props(dc, rtimer_properties);
407+}
408+
409+static const TypeInfo rtimer_info = {
410+ .name = TYPE_RENESAS_TIMER,
411+ .parent = TYPE_SYS_BUS_DEVICE,
412+ .instance_size = sizeof(RTIMERState),
413+ .class_init = rtimer_class_init,
414+};
415+
416+static void rtimer_register_types(void)
417+{
418+ type_register_static(&rtimer_info);
419+}
420+
421+type_init(rtimer_register_types)
--- /dev/null
+++ b/include/hw/timer/renesas_timer.h
@@ -0,0 +1,59 @@
1+/*
2+ * Renesas Timer unit Object
3+ *
4+ * Copyright (c) 2020 Yoshinori Sato
5+ *
6+ * This code is licensed under the GPL version 2 or later.
7+ *
8+ */
9+
10+#ifndef HW_RENESAS_TIMER_H
11+#define HW_RENESAS_TIMER_H
12+
13+#include "hw/sysbus.h"
14+
15+#define TYPE_RENESAS_TIMER "renesas-timer"
16+#define RTIMER(obj) OBJECT_CHECK(RTIMERState, (obj), TYPE_RENESAS_TIMER)
17+
18+enum {
19+ TIMER_CH_CMT = 2,
20+ /* TMU have 5channels. It separated 0-2 and 3-4. */
21+ TIMER_CH_TMU = 3,
22+};
23+
24+enum {
25+ RTIMER_FEAT_CMT,
26+ RTIMER_FEAT_TMU_LOW,
27+ RTIMER_FEAT_TMU_HIGH,
28+};
29+
30+struct RTIMERState;
31+
32+struct channel_rtimer {
33+ uint32_t cnt;
34+ uint32_t cor;
35+ uint16_t ctrl;
36+ qemu_irq irq;
37+ int64_t base;
38+ int64_t next;
39+ uint64_t clk;
40+ bool start;
41+ QEMUTimer *timer;
42+ struct RTIMERState *tmrp;
43+};
44+
45+typedef struct RTIMERState {
46+ SysBusDevice parent_obj;
47+
48+ uint64_t input_freq;
49+ MemoryRegion memory;
50+ MemoryRegion memory_p4;
51+ MemoryRegion memory_a7;
52+
53+ uint8_t tocr;
54+ struct channel_rtimer ch[TIMER_CH_TMU];
55+ uint32_t feature;
56+ int num_ch;
57+} RTIMERState;
58+
59+#endif