xref: /qemu/hw/timer/renesas_tmr.c (revision 30982862b2d81e7b4a58bac319075d343c36e06a)
17adca78eSYoshinori Sato /*
27adca78eSYoshinori Sato  * Renesas 8bit timer
37adca78eSYoshinori Sato  *
47adca78eSYoshinori Sato  * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
57adca78eSYoshinori Sato  *            (Rev.1.40 R01UH0033EJ0140)
67adca78eSYoshinori Sato  *
77adca78eSYoshinori Sato  * Copyright (c) 2019 Yoshinori Sato
87adca78eSYoshinori Sato  *
97adca78eSYoshinori Sato  * SPDX-License-Identifier: GPL-2.0-or-later
107adca78eSYoshinori Sato  *
117adca78eSYoshinori Sato  * This program is free software; you can redistribute it and/or modify it
127adca78eSYoshinori Sato  * under the terms and conditions of the GNU General Public License,
137adca78eSYoshinori Sato  * version 2 or later, as published by the Free Software Foundation.
147adca78eSYoshinori Sato  *
157adca78eSYoshinori Sato  * This program is distributed in the hope it will be useful, but WITHOUT
167adca78eSYoshinori Sato  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
177adca78eSYoshinori Sato  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
187adca78eSYoshinori Sato  * more details.
197adca78eSYoshinori Sato  *
207adca78eSYoshinori Sato  * You should have received a copy of the GNU General Public License along with
217adca78eSYoshinori Sato  * this program.  If not, see <http://www.gnu.org/licenses/>.
227adca78eSYoshinori Sato  */
237adca78eSYoshinori Sato 
247adca78eSYoshinori Sato #include "qemu/osdep.h"
257adca78eSYoshinori Sato #include "qemu/log.h"
267adca78eSYoshinori Sato #include "hw/irq.h"
277adca78eSYoshinori Sato #include "hw/registerfields.h"
287adca78eSYoshinori Sato #include "hw/qdev-properties.h"
297adca78eSYoshinori Sato #include "hw/timer/renesas_tmr.h"
307adca78eSYoshinori Sato #include "migration/vmstate.h"
317adca78eSYoshinori Sato 
327adca78eSYoshinori Sato REG8(TCR, 0)
337adca78eSYoshinori Sato   FIELD(TCR, CCLR,  3, 2)
347adca78eSYoshinori Sato   FIELD(TCR, OVIE,  5, 1)
357adca78eSYoshinori Sato   FIELD(TCR, CMIEA, 6, 1)
367adca78eSYoshinori Sato   FIELD(TCR, CMIEB, 7, 1)
377adca78eSYoshinori Sato REG8(TCSR, 2)
387adca78eSYoshinori Sato   FIELD(TCSR, OSA,  0, 2)
397adca78eSYoshinori Sato   FIELD(TCSR, OSB,  2, 2)
407adca78eSYoshinori Sato   FIELD(TCSR, ADTE, 4, 2)
417adca78eSYoshinori Sato REG8(TCORA, 4)
427adca78eSYoshinori Sato REG8(TCORB, 6)
437adca78eSYoshinori Sato REG8(TCNT, 8)
447adca78eSYoshinori Sato REG8(TCCR, 10)
457adca78eSYoshinori Sato   FIELD(TCCR, CKS,   0, 3)
467adca78eSYoshinori Sato   FIELD(TCCR, CSS,   3, 2)
477adca78eSYoshinori Sato   FIELD(TCCR, TMRIS, 7, 1)
487adca78eSYoshinori Sato 
497adca78eSYoshinori Sato #define INTERNAL  0x01
507adca78eSYoshinori Sato #define CASCADING 0x03
517adca78eSYoshinori Sato #define CCLR_A    0x01
527adca78eSYoshinori Sato #define CCLR_B    0x02
537adca78eSYoshinori Sato 
547adca78eSYoshinori Sato static const int clkdiv[] = {0, 1, 2, 8, 32, 64, 1024, 8192};
557adca78eSYoshinori Sato 
567adca78eSYoshinori Sato static uint8_t concat_reg(uint8_t *reg)
577adca78eSYoshinori Sato {
587adca78eSYoshinori Sato     return (reg[0] << 8) | reg[1];
597adca78eSYoshinori Sato }
607adca78eSYoshinori Sato 
617adca78eSYoshinori Sato static void update_events(RTMRState *tmr, int ch)
627adca78eSYoshinori Sato {
637adca78eSYoshinori Sato     uint16_t diff[TMR_NR_EVENTS], min;
647adca78eSYoshinori Sato     int64_t next_time;
657adca78eSYoshinori Sato     int i, event;
667adca78eSYoshinori Sato 
677adca78eSYoshinori Sato     if (tmr->tccr[ch] == 0) {
687adca78eSYoshinori Sato         return ;
697adca78eSYoshinori Sato     }
707adca78eSYoshinori Sato     if (FIELD_EX8(tmr->tccr[ch], TCCR, CSS) == 0) {
717adca78eSYoshinori Sato         /* external clock mode */
727adca78eSYoshinori Sato         /* event not happened */
737adca78eSYoshinori Sato         return ;
747adca78eSYoshinori Sato     }
757adca78eSYoshinori Sato     if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) == CASCADING) {
767adca78eSYoshinori Sato         /* cascading mode */
777adca78eSYoshinori Sato         if (ch == 1) {
787adca78eSYoshinori Sato             tmr->next[ch] = none;
797adca78eSYoshinori Sato             return ;
807adca78eSYoshinori Sato         }
817adca78eSYoshinori Sato         diff[cmia] = concat_reg(tmr->tcora) - concat_reg(tmr->tcnt);
827adca78eSYoshinori Sato         diff[cmib] = concat_reg(tmr->tcorb) - concat_reg(tmr->tcnt);
837adca78eSYoshinori Sato         diff[ovi] = 0x10000 - concat_reg(tmr->tcnt);
847adca78eSYoshinori Sato     } else {
857adca78eSYoshinori Sato         /* separate mode */
867adca78eSYoshinori Sato         diff[cmia] = tmr->tcora[ch] - tmr->tcnt[ch];
877adca78eSYoshinori Sato         diff[cmib] = tmr->tcorb[ch] - tmr->tcnt[ch];
887adca78eSYoshinori Sato         diff[ovi] = 0x100 - tmr->tcnt[ch];
897adca78eSYoshinori Sato     }
907adca78eSYoshinori Sato     /* Search for the most recently occurring event. */
917adca78eSYoshinori Sato     for (event = 0, min = diff[0], i = 1; i < none; i++) {
927adca78eSYoshinori Sato         if (min > diff[i]) {
937adca78eSYoshinori Sato             event = i;
947adca78eSYoshinori Sato             min = diff[i];
957adca78eSYoshinori Sato         }
967adca78eSYoshinori Sato     }
977adca78eSYoshinori Sato     tmr->next[ch] = event;
987adca78eSYoshinori Sato     next_time = diff[event];
997adca78eSYoshinori Sato     next_time *= clkdiv[FIELD_EX8(tmr->tccr[ch], TCCR, CKS)];
1007adca78eSYoshinori Sato     next_time *= NANOSECONDS_PER_SECOND;
1017adca78eSYoshinori Sato     next_time /= tmr->input_freq;
1027adca78eSYoshinori Sato     next_time += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
1037adca78eSYoshinori Sato     timer_mod(&tmr->timer[ch], next_time);
1047adca78eSYoshinori Sato }
1057adca78eSYoshinori Sato 
1067adca78eSYoshinori Sato static int elapsed_time(RTMRState *tmr, int ch, int64_t delta)
1077adca78eSYoshinori Sato {
1087adca78eSYoshinori Sato     int divrate = clkdiv[FIELD_EX8(tmr->tccr[ch], TCCR, CKS)];
1097adca78eSYoshinori Sato     int et;
1107adca78eSYoshinori Sato 
1117adca78eSYoshinori Sato     tmr->div_round[ch] += delta;
1127adca78eSYoshinori Sato     if (divrate > 0) {
1137adca78eSYoshinori Sato         et = tmr->div_round[ch] / divrate;
1147adca78eSYoshinori Sato         tmr->div_round[ch] %= divrate;
1157adca78eSYoshinori Sato     } else {
1167adca78eSYoshinori Sato         /* disble clock. so no update */
1177adca78eSYoshinori Sato         et = 0;
1187adca78eSYoshinori Sato     }
1197adca78eSYoshinori Sato     return et;
1207adca78eSYoshinori Sato }
1217adca78eSYoshinori Sato 
1227adca78eSYoshinori Sato static uint16_t read_tcnt(RTMRState *tmr, unsigned size, int ch)
1237adca78eSYoshinori Sato {
1247adca78eSYoshinori Sato     int64_t delta, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
1257adca78eSYoshinori Sato     int elapsed, ovf = 0;
1267adca78eSYoshinori Sato     uint16_t tcnt[2];
1277adca78eSYoshinori Sato     uint32_t ret;
1287adca78eSYoshinori Sato 
1297adca78eSYoshinori Sato     delta = (now - tmr->tick) * NANOSECONDS_PER_SECOND / tmr->input_freq;
1307adca78eSYoshinori Sato     if (delta > 0) {
1317adca78eSYoshinori Sato         tmr->tick = now;
1327adca78eSYoshinori Sato 
1337adca78eSYoshinori Sato         if (FIELD_EX8(tmr->tccr[1], TCCR, CSS) == INTERNAL) {
1347adca78eSYoshinori Sato             /* timer1 count update */
1357adca78eSYoshinori Sato             elapsed = elapsed_time(tmr, 1, delta);
1367adca78eSYoshinori Sato             if (elapsed >= 0x100) {
1377adca78eSYoshinori Sato                 ovf = elapsed >> 8;
1387adca78eSYoshinori Sato             }
1397adca78eSYoshinori Sato             tcnt[1] = tmr->tcnt[1] + (elapsed & 0xff);
1407adca78eSYoshinori Sato         }
1417adca78eSYoshinori Sato         switch (FIELD_EX8(tmr->tccr[0], TCCR, CSS)) {
1427adca78eSYoshinori Sato         case INTERNAL:
1437adca78eSYoshinori Sato             elapsed = elapsed_time(tmr, 0, delta);
1447adca78eSYoshinori Sato             tcnt[0] = tmr->tcnt[0] + elapsed;
1457adca78eSYoshinori Sato             break;
1467adca78eSYoshinori Sato         case CASCADING:
1477adca78eSYoshinori Sato             if (ovf > 0) {
1487adca78eSYoshinori Sato                 tcnt[0] = tmr->tcnt[0] + ovf;
1497adca78eSYoshinori Sato             }
1507adca78eSYoshinori Sato             break;
1517adca78eSYoshinori Sato         }
1527adca78eSYoshinori Sato     } else {
1537adca78eSYoshinori Sato         tcnt[0] = tmr->tcnt[0];
1547adca78eSYoshinori Sato         tcnt[1] = tmr->tcnt[1];
1557adca78eSYoshinori Sato     }
1567adca78eSYoshinori Sato     if (size == 1) {
1577adca78eSYoshinori Sato         return tcnt[ch];
1587adca78eSYoshinori Sato     } else {
1597adca78eSYoshinori Sato         ret = 0;
1607adca78eSYoshinori Sato         ret = deposit32(ret, 0, 8, tcnt[1]);
1617adca78eSYoshinori Sato         ret = deposit32(ret, 8, 8, tcnt[0]);
1627adca78eSYoshinori Sato         return ret;
1637adca78eSYoshinori Sato     }
1647adca78eSYoshinori Sato }
1657adca78eSYoshinori Sato 
1667adca78eSYoshinori Sato static uint8_t read_tccr(uint8_t r)
1677adca78eSYoshinori Sato {
1687adca78eSYoshinori Sato     uint8_t tccr = 0;
1697adca78eSYoshinori Sato     tccr = FIELD_DP8(tccr, TCCR, TMRIS,
1707adca78eSYoshinori Sato                      FIELD_EX8(r, TCCR, TMRIS));
1717adca78eSYoshinori Sato     tccr = FIELD_DP8(tccr, TCCR, CSS,
1727adca78eSYoshinori Sato                      FIELD_EX8(r, TCCR, CSS));
1737adca78eSYoshinori Sato     tccr = FIELD_DP8(tccr, TCCR, CKS,
1747adca78eSYoshinori Sato                      FIELD_EX8(r, TCCR, CKS));
1757adca78eSYoshinori Sato     return tccr;
1767adca78eSYoshinori Sato }
1777adca78eSYoshinori Sato 
1787adca78eSYoshinori Sato static uint64_t tmr_read(void *opaque, hwaddr addr, unsigned size)
1797adca78eSYoshinori Sato {
1807adca78eSYoshinori Sato     RTMRState *tmr = opaque;
1817adca78eSYoshinori Sato     int ch = addr & 1;
1827adca78eSYoshinori Sato     uint64_t ret;
1837adca78eSYoshinori Sato 
1847adca78eSYoshinori Sato     if (size == 2 && (ch != 0 || addr == A_TCR || addr == A_TCSR)) {
1857adca78eSYoshinori Sato         qemu_log_mask(LOG_GUEST_ERROR, "renesas_tmr: Invalid read size 0x%"
1867adca78eSYoshinori Sato                                        HWADDR_PRIX "\n",
1877adca78eSYoshinori Sato                       addr);
1887adca78eSYoshinori Sato         return UINT64_MAX;
1897adca78eSYoshinori Sato     }
1907adca78eSYoshinori Sato     switch (addr & 0x0e) {
1917adca78eSYoshinori Sato     case A_TCR:
1927adca78eSYoshinori Sato         ret = 0;
1937adca78eSYoshinori Sato         ret = FIELD_DP8(ret, TCR, CCLR,
1947adca78eSYoshinori Sato                         FIELD_EX8(tmr->tcr[ch], TCR, CCLR));
1957adca78eSYoshinori Sato         ret = FIELD_DP8(ret, TCR, OVIE,
1967adca78eSYoshinori Sato                         FIELD_EX8(tmr->tcr[ch], TCR, OVIE));
1977adca78eSYoshinori Sato         ret = FIELD_DP8(ret, TCR, CMIEA,
1987adca78eSYoshinori Sato                         FIELD_EX8(tmr->tcr[ch], TCR, CMIEA));
1997adca78eSYoshinori Sato         ret = FIELD_DP8(ret, TCR, CMIEB,
2007adca78eSYoshinori Sato                         FIELD_EX8(tmr->tcr[ch], TCR, CMIEB));
2017adca78eSYoshinori Sato         return ret;
2027adca78eSYoshinori Sato     case A_TCSR:
2037adca78eSYoshinori Sato         ret = 0;
2047adca78eSYoshinori Sato         ret = FIELD_DP8(ret, TCSR, OSA,
2057adca78eSYoshinori Sato                         FIELD_EX8(tmr->tcsr[ch], TCSR, OSA));
2067adca78eSYoshinori Sato         ret = FIELD_DP8(ret, TCSR, OSB,
2077adca78eSYoshinori Sato                         FIELD_EX8(tmr->tcsr[ch], TCSR, OSB));
2087adca78eSYoshinori Sato         switch (ch) {
2097adca78eSYoshinori Sato         case 0:
2107adca78eSYoshinori Sato             ret = FIELD_DP8(ret, TCSR, ADTE,
2117adca78eSYoshinori Sato                             FIELD_EX8(tmr->tcsr[ch], TCSR, ADTE));
2127adca78eSYoshinori Sato             break;
2137adca78eSYoshinori Sato         case 1: /* CH1 ADTE unimplement always 1 */
2147adca78eSYoshinori Sato             ret = FIELD_DP8(ret, TCSR, ADTE, 1);
2157adca78eSYoshinori Sato             break;
2167adca78eSYoshinori Sato         }
2177adca78eSYoshinori Sato         return ret;
2187adca78eSYoshinori Sato     case A_TCORA:
2197adca78eSYoshinori Sato         if (size == 1) {
2207adca78eSYoshinori Sato             return tmr->tcora[ch];
2217adca78eSYoshinori Sato         } else if (ch == 0) {
2227adca78eSYoshinori Sato             return concat_reg(tmr->tcora);
2237adca78eSYoshinori Sato         }
224*30982862SChen Qun         /* fall through */
2257adca78eSYoshinori Sato     case A_TCORB:
2267adca78eSYoshinori Sato         if (size == 1) {
2277adca78eSYoshinori Sato             return tmr->tcorb[ch];
2287adca78eSYoshinori Sato         } else {
2297adca78eSYoshinori Sato             return concat_reg(tmr->tcorb);
2307adca78eSYoshinori Sato         }
2317adca78eSYoshinori Sato     case A_TCNT:
2327adca78eSYoshinori Sato         return read_tcnt(tmr, size, ch);
2337adca78eSYoshinori Sato     case A_TCCR:
2347adca78eSYoshinori Sato         if (size == 1) {
2357adca78eSYoshinori Sato             return read_tccr(tmr->tccr[ch]);
2367adca78eSYoshinori Sato         } else {
2377adca78eSYoshinori Sato             return read_tccr(tmr->tccr[0]) << 8 | read_tccr(tmr->tccr[1]);
2387adca78eSYoshinori Sato         }
2397adca78eSYoshinori Sato     default:
2407adca78eSYoshinori Sato         qemu_log_mask(LOG_UNIMP, "renesas_tmr: Register 0x%" HWADDR_PRIX
2417adca78eSYoshinori Sato                                  " not implemented\n",
2427adca78eSYoshinori Sato                       addr);
2437adca78eSYoshinori Sato         break;
2447adca78eSYoshinori Sato     }
2457adca78eSYoshinori Sato     return UINT64_MAX;
2467adca78eSYoshinori Sato }
2477adca78eSYoshinori Sato 
2487adca78eSYoshinori Sato static void tmr_write_count(RTMRState *tmr, int ch, unsigned size,
2497adca78eSYoshinori Sato                             uint8_t *reg, uint64_t val)
2507adca78eSYoshinori Sato {
2517adca78eSYoshinori Sato     if (size == 1) {
2527adca78eSYoshinori Sato         reg[ch] = val;
2537adca78eSYoshinori Sato         update_events(tmr, ch);
2547adca78eSYoshinori Sato     } else {
2557adca78eSYoshinori Sato         reg[0] = extract32(val, 8, 8);
2567adca78eSYoshinori Sato         reg[1] = extract32(val, 0, 8);
2577adca78eSYoshinori Sato         update_events(tmr, 0);
2587adca78eSYoshinori Sato         update_events(tmr, 1);
2597adca78eSYoshinori Sato     }
2607adca78eSYoshinori Sato }
2617adca78eSYoshinori Sato 
2627adca78eSYoshinori Sato static void tmr_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
2637adca78eSYoshinori Sato {
2647adca78eSYoshinori Sato     RTMRState *tmr = opaque;
2657adca78eSYoshinori Sato     int ch = addr & 1;
2667adca78eSYoshinori Sato 
2677adca78eSYoshinori Sato     if (size == 2 && (ch != 0 || addr == A_TCR || addr == A_TCSR)) {
2687adca78eSYoshinori Sato         qemu_log_mask(LOG_GUEST_ERROR,
2697adca78eSYoshinori Sato                       "renesas_tmr: Invalid write size 0x%" HWADDR_PRIX "\n",
2707adca78eSYoshinori Sato                       addr);
2717adca78eSYoshinori Sato         return;
2727adca78eSYoshinori Sato     }
2737adca78eSYoshinori Sato     switch (addr & 0x0e) {
2747adca78eSYoshinori Sato     case A_TCR:
2757adca78eSYoshinori Sato         tmr->tcr[ch] = val;
2767adca78eSYoshinori Sato         break;
2777adca78eSYoshinori Sato     case A_TCSR:
2787adca78eSYoshinori Sato         tmr->tcsr[ch] = val;
2797adca78eSYoshinori Sato         break;
2807adca78eSYoshinori Sato     case A_TCORA:
2817adca78eSYoshinori Sato         tmr_write_count(tmr, ch, size, tmr->tcora, val);
2827adca78eSYoshinori Sato         break;
2837adca78eSYoshinori Sato     case A_TCORB:
2847adca78eSYoshinori Sato         tmr_write_count(tmr, ch, size, tmr->tcorb, val);
2857adca78eSYoshinori Sato         break;
2867adca78eSYoshinori Sato     case A_TCNT:
2877adca78eSYoshinori Sato         tmr_write_count(tmr, ch, size, tmr->tcnt, val);
2887adca78eSYoshinori Sato         break;
2897adca78eSYoshinori Sato     case A_TCCR:
2907adca78eSYoshinori Sato         tmr_write_count(tmr, ch, size, tmr->tccr, val);
2917adca78eSYoshinori Sato         break;
2927adca78eSYoshinori Sato     default:
2937adca78eSYoshinori Sato         qemu_log_mask(LOG_UNIMP, "renesas_tmr: Register 0x%" HWADDR_PRIX
2947adca78eSYoshinori Sato                                  " not implemented\n",
2957adca78eSYoshinori Sato                       addr);
2967adca78eSYoshinori Sato         break;
2977adca78eSYoshinori Sato     }
2987adca78eSYoshinori Sato }
2997adca78eSYoshinori Sato 
3007adca78eSYoshinori Sato static const MemoryRegionOps tmr_ops = {
3017adca78eSYoshinori Sato     .write = tmr_write,
3027adca78eSYoshinori Sato     .read  = tmr_read,
3037adca78eSYoshinori Sato     .endianness = DEVICE_LITTLE_ENDIAN,
3047adca78eSYoshinori Sato     .impl = {
3057adca78eSYoshinori Sato         .min_access_size = 1,
3067adca78eSYoshinori Sato         .max_access_size = 2,
3077adca78eSYoshinori Sato     },
3087adca78eSYoshinori Sato     .valid = {
3097adca78eSYoshinori Sato         .min_access_size = 1,
3107adca78eSYoshinori Sato         .max_access_size = 2,
3117adca78eSYoshinori Sato     },
3127adca78eSYoshinori Sato };
3137adca78eSYoshinori Sato 
3147adca78eSYoshinori Sato static void timer_events(RTMRState *tmr, int ch);
3157adca78eSYoshinori Sato 
3167adca78eSYoshinori Sato static uint16_t issue_event(RTMRState *tmr, int ch, int sz,
3177adca78eSYoshinori Sato                         uint16_t tcnt, uint16_t tcora, uint16_t tcorb)
3187adca78eSYoshinori Sato {
3197adca78eSYoshinori Sato     uint16_t ret = tcnt;
3207adca78eSYoshinori Sato 
3217adca78eSYoshinori Sato     switch (tmr->next[ch]) {
3227adca78eSYoshinori Sato     case none:
3237adca78eSYoshinori Sato         break;
3247adca78eSYoshinori Sato     case cmia:
3257adca78eSYoshinori Sato         if (tcnt >= tcora) {
3267adca78eSYoshinori Sato             if (FIELD_EX8(tmr->tcr[ch], TCR, CCLR) == CCLR_A) {
3277adca78eSYoshinori Sato                 ret = tcnt - tcora;
3287adca78eSYoshinori Sato             }
3297adca78eSYoshinori Sato             if (FIELD_EX8(tmr->tcr[ch], TCR, CMIEA)) {
3307adca78eSYoshinori Sato                 qemu_irq_pulse(tmr->cmia[ch]);
3317adca78eSYoshinori Sato             }
3327adca78eSYoshinori Sato             if (sz == 8 && ch == 0 &&
3337adca78eSYoshinori Sato                 FIELD_EX8(tmr->tccr[1], TCCR, CSS) == CASCADING) {
3347adca78eSYoshinori Sato                 tmr->tcnt[1]++;
3357adca78eSYoshinori Sato                 timer_events(tmr, 1);
3367adca78eSYoshinori Sato             }
3377adca78eSYoshinori Sato         }
3387adca78eSYoshinori Sato         break;
3397adca78eSYoshinori Sato     case cmib:
3407adca78eSYoshinori Sato         if (tcnt >= tcorb) {
3417adca78eSYoshinori Sato             if (FIELD_EX8(tmr->tcr[ch], TCR, CCLR) == CCLR_B) {
3427adca78eSYoshinori Sato                 ret = tcnt - tcorb;
3437adca78eSYoshinori Sato             }
3447adca78eSYoshinori Sato             if (FIELD_EX8(tmr->tcr[ch], TCR, CMIEB)) {
3457adca78eSYoshinori Sato                 qemu_irq_pulse(tmr->cmib[ch]);
3467adca78eSYoshinori Sato             }
3477adca78eSYoshinori Sato         }
3487adca78eSYoshinori Sato         break;
3497adca78eSYoshinori Sato     case ovi:
3507adca78eSYoshinori Sato         if ((tcnt >= (1 << sz)) && FIELD_EX8(tmr->tcr[ch], TCR, OVIE)) {
3517adca78eSYoshinori Sato             qemu_irq_pulse(tmr->ovi[ch]);
3527adca78eSYoshinori Sato         }
3537adca78eSYoshinori Sato         break;
3547adca78eSYoshinori Sato     default:
3557adca78eSYoshinori Sato         g_assert_not_reached();
3567adca78eSYoshinori Sato     }
3577adca78eSYoshinori Sato     return ret;
3587adca78eSYoshinori Sato }
3597adca78eSYoshinori Sato 
3607adca78eSYoshinori Sato static void timer_events(RTMRState *tmr, int ch)
3617adca78eSYoshinori Sato {
3627adca78eSYoshinori Sato     uint16_t tcnt;
3637adca78eSYoshinori Sato 
3647adca78eSYoshinori Sato     tmr->tcnt[ch] = read_tcnt(tmr, 1, ch);
3657adca78eSYoshinori Sato     if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) != CASCADING) {
3667adca78eSYoshinori Sato         tmr->tcnt[ch] = issue_event(tmr, ch, 8,
3677adca78eSYoshinori Sato                                     tmr->tcnt[ch],
3687adca78eSYoshinori Sato                                     tmr->tcora[ch],
3697adca78eSYoshinori Sato                                     tmr->tcorb[ch]) & 0xff;
3707adca78eSYoshinori Sato     } else {
3717adca78eSYoshinori Sato         if (ch == 1) {
3727adca78eSYoshinori Sato             return ;
3737adca78eSYoshinori Sato         }
3747adca78eSYoshinori Sato         tcnt = issue_event(tmr, ch, 16,
3757adca78eSYoshinori Sato                            concat_reg(tmr->tcnt),
3767adca78eSYoshinori Sato                            concat_reg(tmr->tcora),
3777adca78eSYoshinori Sato                            concat_reg(tmr->tcorb));
3787adca78eSYoshinori Sato         tmr->tcnt[0] = (tcnt >> 8) & 0xff;
3797adca78eSYoshinori Sato         tmr->tcnt[1] = tcnt & 0xff;
3807adca78eSYoshinori Sato     }
3817adca78eSYoshinori Sato     update_events(tmr, ch);
3827adca78eSYoshinori Sato }
3837adca78eSYoshinori Sato 
3847adca78eSYoshinori Sato static void timer_event0(void *opaque)
3857adca78eSYoshinori Sato {
3867adca78eSYoshinori Sato     RTMRState *tmr = opaque;
3877adca78eSYoshinori Sato 
3887adca78eSYoshinori Sato     timer_events(tmr, 0);
3897adca78eSYoshinori Sato }
3907adca78eSYoshinori Sato 
3917adca78eSYoshinori Sato static void timer_event1(void *opaque)
3927adca78eSYoshinori Sato {
3937adca78eSYoshinori Sato     RTMRState *tmr = opaque;
3947adca78eSYoshinori Sato 
3957adca78eSYoshinori Sato     timer_events(tmr, 1);
3967adca78eSYoshinori Sato }
3977adca78eSYoshinori Sato 
3987adca78eSYoshinori Sato static void rtmr_reset(DeviceState *dev)
3997adca78eSYoshinori Sato {
4007adca78eSYoshinori Sato     RTMRState *tmr = RTMR(dev);
4017adca78eSYoshinori Sato     tmr->tcr[0]   = tmr->tcr[1]   = 0x00;
4027adca78eSYoshinori Sato     tmr->tcsr[0]  = 0x00;
4037adca78eSYoshinori Sato     tmr->tcsr[1]  = 0x10;
4047adca78eSYoshinori Sato     tmr->tcnt[0]  = tmr->tcnt[1]  = 0x00;
4057adca78eSYoshinori Sato     tmr->tcora[0] = tmr->tcora[1] = 0xff;
4067adca78eSYoshinori Sato     tmr->tcorb[0] = tmr->tcorb[1] = 0xff;
4077adca78eSYoshinori Sato     tmr->tccr[0]  = tmr->tccr[1]  = 0x00;
4087adca78eSYoshinori Sato     tmr->next[0]  = tmr->next[1]  = none;
4097adca78eSYoshinori Sato     tmr->tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
4107adca78eSYoshinori Sato }
4117adca78eSYoshinori Sato 
4127adca78eSYoshinori Sato static void rtmr_init(Object *obj)
4137adca78eSYoshinori Sato {
4147adca78eSYoshinori Sato     SysBusDevice *d = SYS_BUS_DEVICE(obj);
4157adca78eSYoshinori Sato     RTMRState *tmr = RTMR(obj);
4167adca78eSYoshinori Sato     int i;
4177adca78eSYoshinori Sato 
4187adca78eSYoshinori Sato     memory_region_init_io(&tmr->memory, OBJECT(tmr), &tmr_ops,
4197adca78eSYoshinori Sato                           tmr, "renesas-tmr", 0x10);
4207adca78eSYoshinori Sato     sysbus_init_mmio(d, &tmr->memory);
4217adca78eSYoshinori Sato 
4227adca78eSYoshinori Sato     for (i = 0; i < ARRAY_SIZE(tmr->ovi); i++) {
4237adca78eSYoshinori Sato         sysbus_init_irq(d, &tmr->cmia[i]);
4247adca78eSYoshinori Sato         sysbus_init_irq(d, &tmr->cmib[i]);
4257adca78eSYoshinori Sato         sysbus_init_irq(d, &tmr->ovi[i]);
4267adca78eSYoshinori Sato     }
4277adca78eSYoshinori Sato     timer_init_ns(&tmr->timer[0], QEMU_CLOCK_VIRTUAL, timer_event0, tmr);
4287adca78eSYoshinori Sato     timer_init_ns(&tmr->timer[1], QEMU_CLOCK_VIRTUAL, timer_event1, tmr);
4297adca78eSYoshinori Sato }
4307adca78eSYoshinori Sato 
4317adca78eSYoshinori Sato static const VMStateDescription vmstate_rtmr = {
4327adca78eSYoshinori Sato     .name = "rx-tmr",
4337adca78eSYoshinori Sato     .version_id = 1,
4347adca78eSYoshinori Sato     .minimum_version_id = 1,
4357adca78eSYoshinori Sato     .fields = (VMStateField[]) {
4367adca78eSYoshinori Sato         VMSTATE_INT64(tick, RTMRState),
4377adca78eSYoshinori Sato         VMSTATE_UINT8_ARRAY(tcnt, RTMRState, TMR_CH),
4387adca78eSYoshinori Sato         VMSTATE_UINT8_ARRAY(tcora, RTMRState, TMR_CH),
4397adca78eSYoshinori Sato         VMSTATE_UINT8_ARRAY(tcorb, RTMRState, TMR_CH),
4407adca78eSYoshinori Sato         VMSTATE_UINT8_ARRAY(tcr, RTMRState, TMR_CH),
4417adca78eSYoshinori Sato         VMSTATE_UINT8_ARRAY(tccr, RTMRState, TMR_CH),
4427adca78eSYoshinori Sato         VMSTATE_UINT8_ARRAY(tcor, RTMRState, TMR_CH),
4437adca78eSYoshinori Sato         VMSTATE_UINT8_ARRAY(tcsr, RTMRState, TMR_CH),
4447adca78eSYoshinori Sato         VMSTATE_INT64_ARRAY(div_round, RTMRState, TMR_CH),
4457adca78eSYoshinori Sato         VMSTATE_UINT8_ARRAY(next, RTMRState, TMR_CH),
4467adca78eSYoshinori Sato         VMSTATE_TIMER_ARRAY(timer, RTMRState, TMR_CH),
4477adca78eSYoshinori Sato         VMSTATE_END_OF_LIST()
4487adca78eSYoshinori Sato     }
4497adca78eSYoshinori Sato };
4507adca78eSYoshinori Sato 
4517adca78eSYoshinori Sato static Property rtmr_properties[] = {
4527adca78eSYoshinori Sato     DEFINE_PROP_UINT64("input-freq", RTMRState, input_freq, 0),
4537adca78eSYoshinori Sato     DEFINE_PROP_END_OF_LIST(),
4547adca78eSYoshinori Sato };
4557adca78eSYoshinori Sato 
4567adca78eSYoshinori Sato static void rtmr_class_init(ObjectClass *klass, void *data)
4577adca78eSYoshinori Sato {
4587adca78eSYoshinori Sato     DeviceClass *dc = DEVICE_CLASS(klass);
4597adca78eSYoshinori Sato 
4607adca78eSYoshinori Sato     dc->vmsd = &vmstate_rtmr;
4617adca78eSYoshinori Sato     dc->reset = rtmr_reset;
4627adca78eSYoshinori Sato     device_class_set_props(dc, rtmr_properties);
4637adca78eSYoshinori Sato }
4647adca78eSYoshinori Sato 
4657adca78eSYoshinori Sato static const TypeInfo rtmr_info = {
4667adca78eSYoshinori Sato     .name = TYPE_RENESAS_TMR,
4677adca78eSYoshinori Sato     .parent = TYPE_SYS_BUS_DEVICE,
4687adca78eSYoshinori Sato     .instance_size = sizeof(RTMRState),
4697adca78eSYoshinori Sato     .instance_init = rtmr_init,
4707adca78eSYoshinori Sato     .class_init = rtmr_class_init,
4717adca78eSYoshinori Sato };
4727adca78eSYoshinori Sato 
4737adca78eSYoshinori Sato static void rtmr_register_types(void)
4747adca78eSYoshinori Sato {
4757adca78eSYoshinori Sato     type_register_static(&rtmr_info);
4767adca78eSYoshinori Sato }
4777adca78eSYoshinori Sato 
4787adca78eSYoshinori Sato type_init(rtmr_register_types)
479