xref: /qemu/hw/timer/renesas_tmr.c (revision 7adca78edaa91069f66e373f5b7e4e7d5fe14879)
1*7adca78eSYoshinori Sato /*
2*7adca78eSYoshinori Sato  * Renesas 8bit timer
3*7adca78eSYoshinori Sato  *
4*7adca78eSYoshinori Sato  * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
5*7adca78eSYoshinori Sato  *            (Rev.1.40 R01UH0033EJ0140)
6*7adca78eSYoshinori Sato  *
7*7adca78eSYoshinori Sato  * Copyright (c) 2019 Yoshinori Sato
8*7adca78eSYoshinori Sato  *
9*7adca78eSYoshinori Sato  * SPDX-License-Identifier: GPL-2.0-or-later
10*7adca78eSYoshinori Sato  *
11*7adca78eSYoshinori Sato  * This program is free software; you can redistribute it and/or modify it
12*7adca78eSYoshinori Sato  * under the terms and conditions of the GNU General Public License,
13*7adca78eSYoshinori Sato  * version 2 or later, as published by the Free Software Foundation.
14*7adca78eSYoshinori Sato  *
15*7adca78eSYoshinori Sato  * This program is distributed in the hope it will be useful, but WITHOUT
16*7adca78eSYoshinori Sato  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17*7adca78eSYoshinori Sato  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
18*7adca78eSYoshinori Sato  * more details.
19*7adca78eSYoshinori Sato  *
20*7adca78eSYoshinori Sato  * You should have received a copy of the GNU General Public License along with
21*7adca78eSYoshinori Sato  * this program.  If not, see <http://www.gnu.org/licenses/>.
22*7adca78eSYoshinori Sato  */
23*7adca78eSYoshinori Sato 
24*7adca78eSYoshinori Sato #include "qemu/osdep.h"
25*7adca78eSYoshinori Sato #include "qemu/log.h"
26*7adca78eSYoshinori Sato #include "hw/irq.h"
27*7adca78eSYoshinori Sato #include "hw/registerfields.h"
28*7adca78eSYoshinori Sato #include "hw/qdev-properties.h"
29*7adca78eSYoshinori Sato #include "hw/timer/renesas_tmr.h"
30*7adca78eSYoshinori Sato #include "migration/vmstate.h"
31*7adca78eSYoshinori Sato 
32*7adca78eSYoshinori Sato REG8(TCR, 0)
33*7adca78eSYoshinori Sato   FIELD(TCR, CCLR,  3, 2)
34*7adca78eSYoshinori Sato   FIELD(TCR, OVIE,  5, 1)
35*7adca78eSYoshinori Sato   FIELD(TCR, CMIEA, 6, 1)
36*7adca78eSYoshinori Sato   FIELD(TCR, CMIEB, 7, 1)
37*7adca78eSYoshinori Sato REG8(TCSR, 2)
38*7adca78eSYoshinori Sato   FIELD(TCSR, OSA,  0, 2)
39*7adca78eSYoshinori Sato   FIELD(TCSR, OSB,  2, 2)
40*7adca78eSYoshinori Sato   FIELD(TCSR, ADTE, 4, 2)
41*7adca78eSYoshinori Sato REG8(TCORA, 4)
42*7adca78eSYoshinori Sato REG8(TCORB, 6)
43*7adca78eSYoshinori Sato REG8(TCNT, 8)
44*7adca78eSYoshinori Sato REG8(TCCR, 10)
45*7adca78eSYoshinori Sato   FIELD(TCCR, CKS,   0, 3)
46*7adca78eSYoshinori Sato   FIELD(TCCR, CSS,   3, 2)
47*7adca78eSYoshinori Sato   FIELD(TCCR, TMRIS, 7, 1)
48*7adca78eSYoshinori Sato 
49*7adca78eSYoshinori Sato #define INTERNAL  0x01
50*7adca78eSYoshinori Sato #define CASCADING 0x03
51*7adca78eSYoshinori Sato #define CCLR_A    0x01
52*7adca78eSYoshinori Sato #define CCLR_B    0x02
53*7adca78eSYoshinori Sato 
54*7adca78eSYoshinori Sato static const int clkdiv[] = {0, 1, 2, 8, 32, 64, 1024, 8192};
55*7adca78eSYoshinori Sato 
56*7adca78eSYoshinori Sato static uint8_t concat_reg(uint8_t *reg)
57*7adca78eSYoshinori Sato {
58*7adca78eSYoshinori Sato     return (reg[0] << 8) | reg[1];
59*7adca78eSYoshinori Sato }
60*7adca78eSYoshinori Sato 
61*7adca78eSYoshinori Sato static void update_events(RTMRState *tmr, int ch)
62*7adca78eSYoshinori Sato {
63*7adca78eSYoshinori Sato     uint16_t diff[TMR_NR_EVENTS], min;
64*7adca78eSYoshinori Sato     int64_t next_time;
65*7adca78eSYoshinori Sato     int i, event;
66*7adca78eSYoshinori Sato 
67*7adca78eSYoshinori Sato     if (tmr->tccr[ch] == 0) {
68*7adca78eSYoshinori Sato         return ;
69*7adca78eSYoshinori Sato     }
70*7adca78eSYoshinori Sato     if (FIELD_EX8(tmr->tccr[ch], TCCR, CSS) == 0) {
71*7adca78eSYoshinori Sato         /* external clock mode */
72*7adca78eSYoshinori Sato         /* event not happened */
73*7adca78eSYoshinori Sato         return ;
74*7adca78eSYoshinori Sato     }
75*7adca78eSYoshinori Sato     if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) == CASCADING) {
76*7adca78eSYoshinori Sato         /* cascading mode */
77*7adca78eSYoshinori Sato         if (ch == 1) {
78*7adca78eSYoshinori Sato             tmr->next[ch] = none;
79*7adca78eSYoshinori Sato             return ;
80*7adca78eSYoshinori Sato         }
81*7adca78eSYoshinori Sato         diff[cmia] = concat_reg(tmr->tcora) - concat_reg(tmr->tcnt);
82*7adca78eSYoshinori Sato         diff[cmib] = concat_reg(tmr->tcorb) - concat_reg(tmr->tcnt);
83*7adca78eSYoshinori Sato         diff[ovi] = 0x10000 - concat_reg(tmr->tcnt);
84*7adca78eSYoshinori Sato     } else {
85*7adca78eSYoshinori Sato         /* separate mode */
86*7adca78eSYoshinori Sato         diff[cmia] = tmr->tcora[ch] - tmr->tcnt[ch];
87*7adca78eSYoshinori Sato         diff[cmib] = tmr->tcorb[ch] - tmr->tcnt[ch];
88*7adca78eSYoshinori Sato         diff[ovi] = 0x100 - tmr->tcnt[ch];
89*7adca78eSYoshinori Sato     }
90*7adca78eSYoshinori Sato     /* Search for the most recently occurring event. */
91*7adca78eSYoshinori Sato     for (event = 0, min = diff[0], i = 1; i < none; i++) {
92*7adca78eSYoshinori Sato         if (min > diff[i]) {
93*7adca78eSYoshinori Sato             event = i;
94*7adca78eSYoshinori Sato             min = diff[i];
95*7adca78eSYoshinori Sato         }
96*7adca78eSYoshinori Sato     }
97*7adca78eSYoshinori Sato     tmr->next[ch] = event;
98*7adca78eSYoshinori Sato     next_time = diff[event];
99*7adca78eSYoshinori Sato     next_time *= clkdiv[FIELD_EX8(tmr->tccr[ch], TCCR, CKS)];
100*7adca78eSYoshinori Sato     next_time *= NANOSECONDS_PER_SECOND;
101*7adca78eSYoshinori Sato     next_time /= tmr->input_freq;
102*7adca78eSYoshinori Sato     next_time += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
103*7adca78eSYoshinori Sato     timer_mod(&tmr->timer[ch], next_time);
104*7adca78eSYoshinori Sato }
105*7adca78eSYoshinori Sato 
106*7adca78eSYoshinori Sato static int elapsed_time(RTMRState *tmr, int ch, int64_t delta)
107*7adca78eSYoshinori Sato {
108*7adca78eSYoshinori Sato     int divrate = clkdiv[FIELD_EX8(tmr->tccr[ch], TCCR, CKS)];
109*7adca78eSYoshinori Sato     int et;
110*7adca78eSYoshinori Sato 
111*7adca78eSYoshinori Sato     tmr->div_round[ch] += delta;
112*7adca78eSYoshinori Sato     if (divrate > 0) {
113*7adca78eSYoshinori Sato         et = tmr->div_round[ch] / divrate;
114*7adca78eSYoshinori Sato         tmr->div_round[ch] %= divrate;
115*7adca78eSYoshinori Sato     } else {
116*7adca78eSYoshinori Sato         /* disble clock. so no update */
117*7adca78eSYoshinori Sato         et = 0;
118*7adca78eSYoshinori Sato     }
119*7adca78eSYoshinori Sato     return et;
120*7adca78eSYoshinori Sato }
121*7adca78eSYoshinori Sato 
122*7adca78eSYoshinori Sato static uint16_t read_tcnt(RTMRState *tmr, unsigned size, int ch)
123*7adca78eSYoshinori Sato {
124*7adca78eSYoshinori Sato     int64_t delta, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
125*7adca78eSYoshinori Sato     int elapsed, ovf = 0;
126*7adca78eSYoshinori Sato     uint16_t tcnt[2];
127*7adca78eSYoshinori Sato     uint32_t ret;
128*7adca78eSYoshinori Sato 
129*7adca78eSYoshinori Sato     delta = (now - tmr->tick) * NANOSECONDS_PER_SECOND / tmr->input_freq;
130*7adca78eSYoshinori Sato     if (delta > 0) {
131*7adca78eSYoshinori Sato         tmr->tick = now;
132*7adca78eSYoshinori Sato 
133*7adca78eSYoshinori Sato         if (FIELD_EX8(tmr->tccr[1], TCCR, CSS) == INTERNAL) {
134*7adca78eSYoshinori Sato             /* timer1 count update */
135*7adca78eSYoshinori Sato             elapsed = elapsed_time(tmr, 1, delta);
136*7adca78eSYoshinori Sato             if (elapsed >= 0x100) {
137*7adca78eSYoshinori Sato                 ovf = elapsed >> 8;
138*7adca78eSYoshinori Sato             }
139*7adca78eSYoshinori Sato             tcnt[1] = tmr->tcnt[1] + (elapsed & 0xff);
140*7adca78eSYoshinori Sato         }
141*7adca78eSYoshinori Sato         switch (FIELD_EX8(tmr->tccr[0], TCCR, CSS)) {
142*7adca78eSYoshinori Sato         case INTERNAL:
143*7adca78eSYoshinori Sato             elapsed = elapsed_time(tmr, 0, delta);
144*7adca78eSYoshinori Sato             tcnt[0] = tmr->tcnt[0] + elapsed;
145*7adca78eSYoshinori Sato             break;
146*7adca78eSYoshinori Sato         case CASCADING:
147*7adca78eSYoshinori Sato             if (ovf > 0) {
148*7adca78eSYoshinori Sato                 tcnt[0] = tmr->tcnt[0] + ovf;
149*7adca78eSYoshinori Sato             }
150*7adca78eSYoshinori Sato             break;
151*7adca78eSYoshinori Sato         }
152*7adca78eSYoshinori Sato     } else {
153*7adca78eSYoshinori Sato         tcnt[0] = tmr->tcnt[0];
154*7adca78eSYoshinori Sato         tcnt[1] = tmr->tcnt[1];
155*7adca78eSYoshinori Sato     }
156*7adca78eSYoshinori Sato     if (size == 1) {
157*7adca78eSYoshinori Sato         return tcnt[ch];
158*7adca78eSYoshinori Sato     } else {
159*7adca78eSYoshinori Sato         ret = 0;
160*7adca78eSYoshinori Sato         ret = deposit32(ret, 0, 8, tcnt[1]);
161*7adca78eSYoshinori Sato         ret = deposit32(ret, 8, 8, tcnt[0]);
162*7adca78eSYoshinori Sato         return ret;
163*7adca78eSYoshinori Sato     }
164*7adca78eSYoshinori Sato }
165*7adca78eSYoshinori Sato 
166*7adca78eSYoshinori Sato static uint8_t read_tccr(uint8_t r)
167*7adca78eSYoshinori Sato {
168*7adca78eSYoshinori Sato     uint8_t tccr = 0;
169*7adca78eSYoshinori Sato     tccr = FIELD_DP8(tccr, TCCR, TMRIS,
170*7adca78eSYoshinori Sato                      FIELD_EX8(r, TCCR, TMRIS));
171*7adca78eSYoshinori Sato     tccr = FIELD_DP8(tccr, TCCR, CSS,
172*7adca78eSYoshinori Sato                      FIELD_EX8(r, TCCR, CSS));
173*7adca78eSYoshinori Sato     tccr = FIELD_DP8(tccr, TCCR, CKS,
174*7adca78eSYoshinori Sato                      FIELD_EX8(r, TCCR, CKS));
175*7adca78eSYoshinori Sato     return tccr;
176*7adca78eSYoshinori Sato }
177*7adca78eSYoshinori Sato 
178*7adca78eSYoshinori Sato static uint64_t tmr_read(void *opaque, hwaddr addr, unsigned size)
179*7adca78eSYoshinori Sato {
180*7adca78eSYoshinori Sato     RTMRState *tmr = opaque;
181*7adca78eSYoshinori Sato     int ch = addr & 1;
182*7adca78eSYoshinori Sato     uint64_t ret;
183*7adca78eSYoshinori Sato 
184*7adca78eSYoshinori Sato     if (size == 2 && (ch != 0 || addr == A_TCR || addr == A_TCSR)) {
185*7adca78eSYoshinori Sato         qemu_log_mask(LOG_GUEST_ERROR, "renesas_tmr: Invalid read size 0x%"
186*7adca78eSYoshinori Sato                                        HWADDR_PRIX "\n",
187*7adca78eSYoshinori Sato                       addr);
188*7adca78eSYoshinori Sato         return UINT64_MAX;
189*7adca78eSYoshinori Sato     }
190*7adca78eSYoshinori Sato     switch (addr & 0x0e) {
191*7adca78eSYoshinori Sato     case A_TCR:
192*7adca78eSYoshinori Sato         ret = 0;
193*7adca78eSYoshinori Sato         ret = FIELD_DP8(ret, TCR, CCLR,
194*7adca78eSYoshinori Sato                         FIELD_EX8(tmr->tcr[ch], TCR, CCLR));
195*7adca78eSYoshinori Sato         ret = FIELD_DP8(ret, TCR, OVIE,
196*7adca78eSYoshinori Sato                         FIELD_EX8(tmr->tcr[ch], TCR, OVIE));
197*7adca78eSYoshinori Sato         ret = FIELD_DP8(ret, TCR, CMIEA,
198*7adca78eSYoshinori Sato                         FIELD_EX8(tmr->tcr[ch], TCR, CMIEA));
199*7adca78eSYoshinori Sato         ret = FIELD_DP8(ret, TCR, CMIEB,
200*7adca78eSYoshinori Sato                         FIELD_EX8(tmr->tcr[ch], TCR, CMIEB));
201*7adca78eSYoshinori Sato         return ret;
202*7adca78eSYoshinori Sato     case A_TCSR:
203*7adca78eSYoshinori Sato         ret = 0;
204*7adca78eSYoshinori Sato         ret = FIELD_DP8(ret, TCSR, OSA,
205*7adca78eSYoshinori Sato                         FIELD_EX8(tmr->tcsr[ch], TCSR, OSA));
206*7adca78eSYoshinori Sato         ret = FIELD_DP8(ret, TCSR, OSB,
207*7adca78eSYoshinori Sato                         FIELD_EX8(tmr->tcsr[ch], TCSR, OSB));
208*7adca78eSYoshinori Sato         switch (ch) {
209*7adca78eSYoshinori Sato         case 0:
210*7adca78eSYoshinori Sato             ret = FIELD_DP8(ret, TCSR, ADTE,
211*7adca78eSYoshinori Sato                             FIELD_EX8(tmr->tcsr[ch], TCSR, ADTE));
212*7adca78eSYoshinori Sato             break;
213*7adca78eSYoshinori Sato         case 1: /* CH1 ADTE unimplement always 1 */
214*7adca78eSYoshinori Sato             ret = FIELD_DP8(ret, TCSR, ADTE, 1);
215*7adca78eSYoshinori Sato             break;
216*7adca78eSYoshinori Sato         }
217*7adca78eSYoshinori Sato         return ret;
218*7adca78eSYoshinori Sato     case A_TCORA:
219*7adca78eSYoshinori Sato         if (size == 1) {
220*7adca78eSYoshinori Sato             return tmr->tcora[ch];
221*7adca78eSYoshinori Sato         } else if (ch == 0) {
222*7adca78eSYoshinori Sato             return concat_reg(tmr->tcora);
223*7adca78eSYoshinori Sato         }
224*7adca78eSYoshinori Sato     case A_TCORB:
225*7adca78eSYoshinori Sato         if (size == 1) {
226*7adca78eSYoshinori Sato             return tmr->tcorb[ch];
227*7adca78eSYoshinori Sato         } else {
228*7adca78eSYoshinori Sato             return concat_reg(tmr->tcorb);
229*7adca78eSYoshinori Sato         }
230*7adca78eSYoshinori Sato     case A_TCNT:
231*7adca78eSYoshinori Sato         return read_tcnt(tmr, size, ch);
232*7adca78eSYoshinori Sato     case A_TCCR:
233*7adca78eSYoshinori Sato         if (size == 1) {
234*7adca78eSYoshinori Sato             return read_tccr(tmr->tccr[ch]);
235*7adca78eSYoshinori Sato         } else {
236*7adca78eSYoshinori Sato             return read_tccr(tmr->tccr[0]) << 8 | read_tccr(tmr->tccr[1]);
237*7adca78eSYoshinori Sato         }
238*7adca78eSYoshinori Sato     default:
239*7adca78eSYoshinori Sato         qemu_log_mask(LOG_UNIMP, "renesas_tmr: Register 0x%" HWADDR_PRIX
240*7adca78eSYoshinori Sato                                  " not implemented\n",
241*7adca78eSYoshinori Sato                       addr);
242*7adca78eSYoshinori Sato         break;
243*7adca78eSYoshinori Sato     }
244*7adca78eSYoshinori Sato     return UINT64_MAX;
245*7adca78eSYoshinori Sato }
246*7adca78eSYoshinori Sato 
247*7adca78eSYoshinori Sato static void tmr_write_count(RTMRState *tmr, int ch, unsigned size,
248*7adca78eSYoshinori Sato                             uint8_t *reg, uint64_t val)
249*7adca78eSYoshinori Sato {
250*7adca78eSYoshinori Sato     if (size == 1) {
251*7adca78eSYoshinori Sato         reg[ch] = val;
252*7adca78eSYoshinori Sato         update_events(tmr, ch);
253*7adca78eSYoshinori Sato     } else {
254*7adca78eSYoshinori Sato         reg[0] = extract32(val, 8, 8);
255*7adca78eSYoshinori Sato         reg[1] = extract32(val, 0, 8);
256*7adca78eSYoshinori Sato         update_events(tmr, 0);
257*7adca78eSYoshinori Sato         update_events(tmr, 1);
258*7adca78eSYoshinori Sato     }
259*7adca78eSYoshinori Sato }
260*7adca78eSYoshinori Sato 
261*7adca78eSYoshinori Sato static void tmr_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
262*7adca78eSYoshinori Sato {
263*7adca78eSYoshinori Sato     RTMRState *tmr = opaque;
264*7adca78eSYoshinori Sato     int ch = addr & 1;
265*7adca78eSYoshinori Sato 
266*7adca78eSYoshinori Sato     if (size == 2 && (ch != 0 || addr == A_TCR || addr == A_TCSR)) {
267*7adca78eSYoshinori Sato         qemu_log_mask(LOG_GUEST_ERROR,
268*7adca78eSYoshinori Sato                       "renesas_tmr: Invalid write size 0x%" HWADDR_PRIX "\n",
269*7adca78eSYoshinori Sato                       addr);
270*7adca78eSYoshinori Sato         return;
271*7adca78eSYoshinori Sato     }
272*7adca78eSYoshinori Sato     switch (addr & 0x0e) {
273*7adca78eSYoshinori Sato     case A_TCR:
274*7adca78eSYoshinori Sato         tmr->tcr[ch] = val;
275*7adca78eSYoshinori Sato         break;
276*7adca78eSYoshinori Sato     case A_TCSR:
277*7adca78eSYoshinori Sato         tmr->tcsr[ch] = val;
278*7adca78eSYoshinori Sato         break;
279*7adca78eSYoshinori Sato     case A_TCORA:
280*7adca78eSYoshinori Sato         tmr_write_count(tmr, ch, size, tmr->tcora, val);
281*7adca78eSYoshinori Sato         break;
282*7adca78eSYoshinori Sato     case A_TCORB:
283*7adca78eSYoshinori Sato         tmr_write_count(tmr, ch, size, tmr->tcorb, val);
284*7adca78eSYoshinori Sato         break;
285*7adca78eSYoshinori Sato     case A_TCNT:
286*7adca78eSYoshinori Sato         tmr_write_count(tmr, ch, size, tmr->tcnt, val);
287*7adca78eSYoshinori Sato         break;
288*7adca78eSYoshinori Sato     case A_TCCR:
289*7adca78eSYoshinori Sato         tmr_write_count(tmr, ch, size, tmr->tccr, val);
290*7adca78eSYoshinori Sato         break;
291*7adca78eSYoshinori Sato     default:
292*7adca78eSYoshinori Sato         qemu_log_mask(LOG_UNIMP, "renesas_tmr: Register 0x%" HWADDR_PRIX
293*7adca78eSYoshinori Sato                                  " not implemented\n",
294*7adca78eSYoshinori Sato                       addr);
295*7adca78eSYoshinori Sato         break;
296*7adca78eSYoshinori Sato     }
297*7adca78eSYoshinori Sato }
298*7adca78eSYoshinori Sato 
299*7adca78eSYoshinori Sato static const MemoryRegionOps tmr_ops = {
300*7adca78eSYoshinori Sato     .write = tmr_write,
301*7adca78eSYoshinori Sato     .read  = tmr_read,
302*7adca78eSYoshinori Sato     .endianness = DEVICE_LITTLE_ENDIAN,
303*7adca78eSYoshinori Sato     .impl = {
304*7adca78eSYoshinori Sato         .min_access_size = 1,
305*7adca78eSYoshinori Sato         .max_access_size = 2,
306*7adca78eSYoshinori Sato     },
307*7adca78eSYoshinori Sato     .valid = {
308*7adca78eSYoshinori Sato         .min_access_size = 1,
309*7adca78eSYoshinori Sato         .max_access_size = 2,
310*7adca78eSYoshinori Sato     },
311*7adca78eSYoshinori Sato };
312*7adca78eSYoshinori Sato 
313*7adca78eSYoshinori Sato static void timer_events(RTMRState *tmr, int ch);
314*7adca78eSYoshinori Sato 
315*7adca78eSYoshinori Sato static uint16_t issue_event(RTMRState *tmr, int ch, int sz,
316*7adca78eSYoshinori Sato                         uint16_t tcnt, uint16_t tcora, uint16_t tcorb)
317*7adca78eSYoshinori Sato {
318*7adca78eSYoshinori Sato     uint16_t ret = tcnt;
319*7adca78eSYoshinori Sato 
320*7adca78eSYoshinori Sato     switch (tmr->next[ch]) {
321*7adca78eSYoshinori Sato     case none:
322*7adca78eSYoshinori Sato         break;
323*7adca78eSYoshinori Sato     case cmia:
324*7adca78eSYoshinori Sato         if (tcnt >= tcora) {
325*7adca78eSYoshinori Sato             if (FIELD_EX8(tmr->tcr[ch], TCR, CCLR) == CCLR_A) {
326*7adca78eSYoshinori Sato                 ret = tcnt - tcora;
327*7adca78eSYoshinori Sato             }
328*7adca78eSYoshinori Sato             if (FIELD_EX8(tmr->tcr[ch], TCR, CMIEA)) {
329*7adca78eSYoshinori Sato                 qemu_irq_pulse(tmr->cmia[ch]);
330*7adca78eSYoshinori Sato             }
331*7adca78eSYoshinori Sato             if (sz == 8 && ch == 0 &&
332*7adca78eSYoshinori Sato                 FIELD_EX8(tmr->tccr[1], TCCR, CSS) == CASCADING) {
333*7adca78eSYoshinori Sato                 tmr->tcnt[1]++;
334*7adca78eSYoshinori Sato                 timer_events(tmr, 1);
335*7adca78eSYoshinori Sato             }
336*7adca78eSYoshinori Sato         }
337*7adca78eSYoshinori Sato         break;
338*7adca78eSYoshinori Sato     case cmib:
339*7adca78eSYoshinori Sato         if (tcnt >= tcorb) {
340*7adca78eSYoshinori Sato             if (FIELD_EX8(tmr->tcr[ch], TCR, CCLR) == CCLR_B) {
341*7adca78eSYoshinori Sato                 ret = tcnt - tcorb;
342*7adca78eSYoshinori Sato             }
343*7adca78eSYoshinori Sato             if (FIELD_EX8(tmr->tcr[ch], TCR, CMIEB)) {
344*7adca78eSYoshinori Sato                 qemu_irq_pulse(tmr->cmib[ch]);
345*7adca78eSYoshinori Sato             }
346*7adca78eSYoshinori Sato         }
347*7adca78eSYoshinori Sato         break;
348*7adca78eSYoshinori Sato     case ovi:
349*7adca78eSYoshinori Sato         if ((tcnt >= (1 << sz)) && FIELD_EX8(tmr->tcr[ch], TCR, OVIE)) {
350*7adca78eSYoshinori Sato             qemu_irq_pulse(tmr->ovi[ch]);
351*7adca78eSYoshinori Sato         }
352*7adca78eSYoshinori Sato         break;
353*7adca78eSYoshinori Sato     default:
354*7adca78eSYoshinori Sato         g_assert_not_reached();
355*7adca78eSYoshinori Sato     }
356*7adca78eSYoshinori Sato     return ret;
357*7adca78eSYoshinori Sato }
358*7adca78eSYoshinori Sato 
359*7adca78eSYoshinori Sato static void timer_events(RTMRState *tmr, int ch)
360*7adca78eSYoshinori Sato {
361*7adca78eSYoshinori Sato     uint16_t tcnt;
362*7adca78eSYoshinori Sato 
363*7adca78eSYoshinori Sato     tmr->tcnt[ch] = read_tcnt(tmr, 1, ch);
364*7adca78eSYoshinori Sato     if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) != CASCADING) {
365*7adca78eSYoshinori Sato         tmr->tcnt[ch] = issue_event(tmr, ch, 8,
366*7adca78eSYoshinori Sato                                     tmr->tcnt[ch],
367*7adca78eSYoshinori Sato                                     tmr->tcora[ch],
368*7adca78eSYoshinori Sato                                     tmr->tcorb[ch]) & 0xff;
369*7adca78eSYoshinori Sato     } else {
370*7adca78eSYoshinori Sato         if (ch == 1) {
371*7adca78eSYoshinori Sato             return ;
372*7adca78eSYoshinori Sato         }
373*7adca78eSYoshinori Sato         tcnt = issue_event(tmr, ch, 16,
374*7adca78eSYoshinori Sato                            concat_reg(tmr->tcnt),
375*7adca78eSYoshinori Sato                            concat_reg(tmr->tcora),
376*7adca78eSYoshinori Sato                            concat_reg(tmr->tcorb));
377*7adca78eSYoshinori Sato         tmr->tcnt[0] = (tcnt >> 8) & 0xff;
378*7adca78eSYoshinori Sato         tmr->tcnt[1] = tcnt & 0xff;
379*7adca78eSYoshinori Sato     }
380*7adca78eSYoshinori Sato     update_events(tmr, ch);
381*7adca78eSYoshinori Sato }
382*7adca78eSYoshinori Sato 
383*7adca78eSYoshinori Sato static void timer_event0(void *opaque)
384*7adca78eSYoshinori Sato {
385*7adca78eSYoshinori Sato     RTMRState *tmr = opaque;
386*7adca78eSYoshinori Sato 
387*7adca78eSYoshinori Sato     timer_events(tmr, 0);
388*7adca78eSYoshinori Sato }
389*7adca78eSYoshinori Sato 
390*7adca78eSYoshinori Sato static void timer_event1(void *opaque)
391*7adca78eSYoshinori Sato {
392*7adca78eSYoshinori Sato     RTMRState *tmr = opaque;
393*7adca78eSYoshinori Sato 
394*7adca78eSYoshinori Sato     timer_events(tmr, 1);
395*7adca78eSYoshinori Sato }
396*7adca78eSYoshinori Sato 
397*7adca78eSYoshinori Sato static void rtmr_reset(DeviceState *dev)
398*7adca78eSYoshinori Sato {
399*7adca78eSYoshinori Sato     RTMRState *tmr = RTMR(dev);
400*7adca78eSYoshinori Sato     tmr->tcr[0]   = tmr->tcr[1]   = 0x00;
401*7adca78eSYoshinori Sato     tmr->tcsr[0]  = 0x00;
402*7adca78eSYoshinori Sato     tmr->tcsr[1]  = 0x10;
403*7adca78eSYoshinori Sato     tmr->tcnt[0]  = tmr->tcnt[1]  = 0x00;
404*7adca78eSYoshinori Sato     tmr->tcora[0] = tmr->tcora[1] = 0xff;
405*7adca78eSYoshinori Sato     tmr->tcorb[0] = tmr->tcorb[1] = 0xff;
406*7adca78eSYoshinori Sato     tmr->tccr[0]  = tmr->tccr[1]  = 0x00;
407*7adca78eSYoshinori Sato     tmr->next[0]  = tmr->next[1]  = none;
408*7adca78eSYoshinori Sato     tmr->tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
409*7adca78eSYoshinori Sato }
410*7adca78eSYoshinori Sato 
411*7adca78eSYoshinori Sato static void rtmr_init(Object *obj)
412*7adca78eSYoshinori Sato {
413*7adca78eSYoshinori Sato     SysBusDevice *d = SYS_BUS_DEVICE(obj);
414*7adca78eSYoshinori Sato     RTMRState *tmr = RTMR(obj);
415*7adca78eSYoshinori Sato     int i;
416*7adca78eSYoshinori Sato 
417*7adca78eSYoshinori Sato     memory_region_init_io(&tmr->memory, OBJECT(tmr), &tmr_ops,
418*7adca78eSYoshinori Sato                           tmr, "renesas-tmr", 0x10);
419*7adca78eSYoshinori Sato     sysbus_init_mmio(d, &tmr->memory);
420*7adca78eSYoshinori Sato 
421*7adca78eSYoshinori Sato     for (i = 0; i < ARRAY_SIZE(tmr->ovi); i++) {
422*7adca78eSYoshinori Sato         sysbus_init_irq(d, &tmr->cmia[i]);
423*7adca78eSYoshinori Sato         sysbus_init_irq(d, &tmr->cmib[i]);
424*7adca78eSYoshinori Sato         sysbus_init_irq(d, &tmr->ovi[i]);
425*7adca78eSYoshinori Sato     }
426*7adca78eSYoshinori Sato     timer_init_ns(&tmr->timer[0], QEMU_CLOCK_VIRTUAL, timer_event0, tmr);
427*7adca78eSYoshinori Sato     timer_init_ns(&tmr->timer[1], QEMU_CLOCK_VIRTUAL, timer_event1, tmr);
428*7adca78eSYoshinori Sato }
429*7adca78eSYoshinori Sato 
430*7adca78eSYoshinori Sato static const VMStateDescription vmstate_rtmr = {
431*7adca78eSYoshinori Sato     .name = "rx-tmr",
432*7adca78eSYoshinori Sato     .version_id = 1,
433*7adca78eSYoshinori Sato     .minimum_version_id = 1,
434*7adca78eSYoshinori Sato     .fields = (VMStateField[]) {
435*7adca78eSYoshinori Sato         VMSTATE_INT64(tick, RTMRState),
436*7adca78eSYoshinori Sato         VMSTATE_UINT8_ARRAY(tcnt, RTMRState, TMR_CH),
437*7adca78eSYoshinori Sato         VMSTATE_UINT8_ARRAY(tcora, RTMRState, TMR_CH),
438*7adca78eSYoshinori Sato         VMSTATE_UINT8_ARRAY(tcorb, RTMRState, TMR_CH),
439*7adca78eSYoshinori Sato         VMSTATE_UINT8_ARRAY(tcr, RTMRState, TMR_CH),
440*7adca78eSYoshinori Sato         VMSTATE_UINT8_ARRAY(tccr, RTMRState, TMR_CH),
441*7adca78eSYoshinori Sato         VMSTATE_UINT8_ARRAY(tcor, RTMRState, TMR_CH),
442*7adca78eSYoshinori Sato         VMSTATE_UINT8_ARRAY(tcsr, RTMRState, TMR_CH),
443*7adca78eSYoshinori Sato         VMSTATE_INT64_ARRAY(div_round, RTMRState, TMR_CH),
444*7adca78eSYoshinori Sato         VMSTATE_UINT8_ARRAY(next, RTMRState, TMR_CH),
445*7adca78eSYoshinori Sato         VMSTATE_TIMER_ARRAY(timer, RTMRState, TMR_CH),
446*7adca78eSYoshinori Sato         VMSTATE_END_OF_LIST()
447*7adca78eSYoshinori Sato     }
448*7adca78eSYoshinori Sato };
449*7adca78eSYoshinori Sato 
450*7adca78eSYoshinori Sato static Property rtmr_properties[] = {
451*7adca78eSYoshinori Sato     DEFINE_PROP_UINT64("input-freq", RTMRState, input_freq, 0),
452*7adca78eSYoshinori Sato     DEFINE_PROP_END_OF_LIST(),
453*7adca78eSYoshinori Sato };
454*7adca78eSYoshinori Sato 
455*7adca78eSYoshinori Sato static void rtmr_class_init(ObjectClass *klass, void *data)
456*7adca78eSYoshinori Sato {
457*7adca78eSYoshinori Sato     DeviceClass *dc = DEVICE_CLASS(klass);
458*7adca78eSYoshinori Sato 
459*7adca78eSYoshinori Sato     dc->vmsd = &vmstate_rtmr;
460*7adca78eSYoshinori Sato     dc->reset = rtmr_reset;
461*7adca78eSYoshinori Sato     device_class_set_props(dc, rtmr_properties);
462*7adca78eSYoshinori Sato }
463*7adca78eSYoshinori Sato 
464*7adca78eSYoshinori Sato static const TypeInfo rtmr_info = {
465*7adca78eSYoshinori Sato     .name = TYPE_RENESAS_TMR,
466*7adca78eSYoshinori Sato     .parent = TYPE_SYS_BUS_DEVICE,
467*7adca78eSYoshinori Sato     .instance_size = sizeof(RTMRState),
468*7adca78eSYoshinori Sato     .instance_init = rtmr_init,
469*7adca78eSYoshinori Sato     .class_init = rtmr_class_init,
470*7adca78eSYoshinori Sato };
471*7adca78eSYoshinori Sato 
472*7adca78eSYoshinori Sato static void rtmr_register_types(void)
473*7adca78eSYoshinori Sato {
474*7adca78eSYoshinori Sato     type_register_static(&rtmr_info);
475*7adca78eSYoshinori Sato }
476*7adca78eSYoshinori Sato 
477*7adca78eSYoshinori Sato type_init(rtmr_register_types)
478