xref: /qemu/hw/timer/renesas_tmr.c (revision 06b40d250ecfa1633209c2e431a7a38acfd03a98)
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 
4981b3ddafSPeter Maydell #define CSS_EXTERNAL  0x00
5002f8fe11SPeter Maydell #define CSS_INTERNAL  0x01
5181b3ddafSPeter Maydell #define CSS_INVALID   0x02
5202f8fe11SPeter Maydell #define CSS_CASCADING 0x03
537adca78eSYoshinori Sato #define CCLR_A    0x01
547adca78eSYoshinori Sato #define CCLR_B    0x02
557adca78eSYoshinori Sato 
567adca78eSYoshinori Sato static const int clkdiv[] = {0, 1, 2, 8, 32, 64, 1024, 8192};
577adca78eSYoshinori Sato 
concat_reg(uint8_t * reg)587adca78eSYoshinori Sato static uint8_t concat_reg(uint8_t *reg)
597adca78eSYoshinori Sato {
607adca78eSYoshinori Sato     return (reg[0] << 8) | reg[1];
617adca78eSYoshinori Sato }
627adca78eSYoshinori Sato 
update_events(RTMRState * tmr,int ch)637adca78eSYoshinori Sato static void update_events(RTMRState *tmr, int ch)
647adca78eSYoshinori Sato {
657adca78eSYoshinori Sato     uint16_t diff[TMR_NR_EVENTS], min;
667adca78eSYoshinori Sato     int64_t next_time;
677adca78eSYoshinori Sato     int i, event;
687adca78eSYoshinori Sato 
697adca78eSYoshinori Sato     if (tmr->tccr[ch] == 0) {
707adca78eSYoshinori Sato         return;
717adca78eSYoshinori Sato     }
727adca78eSYoshinori Sato     if (FIELD_EX8(tmr->tccr[ch], TCCR, CSS) == 0) {
737adca78eSYoshinori Sato         /* external clock mode */
747adca78eSYoshinori Sato         /* event not happened */
757adca78eSYoshinori Sato         return;
767adca78eSYoshinori Sato     }
7702f8fe11SPeter Maydell     if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) == CSS_CASCADING) {
787adca78eSYoshinori Sato         /* cascading mode */
797adca78eSYoshinori Sato         if (ch == 1) {
807adca78eSYoshinori Sato             tmr->next[ch] = none;
817adca78eSYoshinori Sato             return;
827adca78eSYoshinori Sato         }
837adca78eSYoshinori Sato         diff[cmia] = concat_reg(tmr->tcora) - concat_reg(tmr->tcnt);
847adca78eSYoshinori Sato         diff[cmib] = concat_reg(tmr->tcorb) - concat_reg(tmr->tcnt);
857adca78eSYoshinori Sato         diff[ovi] = 0x10000 - concat_reg(tmr->tcnt);
867adca78eSYoshinori Sato     } else {
877adca78eSYoshinori Sato         /* separate mode */
887adca78eSYoshinori Sato         diff[cmia] = tmr->tcora[ch] - tmr->tcnt[ch];
897adca78eSYoshinori Sato         diff[cmib] = tmr->tcorb[ch] - tmr->tcnt[ch];
907adca78eSYoshinori Sato         diff[ovi] = 0x100 - tmr->tcnt[ch];
917adca78eSYoshinori Sato     }
927adca78eSYoshinori Sato     /* Search for the most recently occurring event. */
937adca78eSYoshinori Sato     for (event = 0, min = diff[0], i = 1; i < none; i++) {
947adca78eSYoshinori Sato         if (min > diff[i]) {
957adca78eSYoshinori Sato             event = i;
967adca78eSYoshinori Sato             min = diff[i];
977adca78eSYoshinori Sato         }
987adca78eSYoshinori Sato     }
997adca78eSYoshinori Sato     tmr->next[ch] = event;
1007adca78eSYoshinori Sato     next_time = diff[event];
1017adca78eSYoshinori Sato     next_time *= clkdiv[FIELD_EX8(tmr->tccr[ch], TCCR, CKS)];
1027adca78eSYoshinori Sato     next_time *= NANOSECONDS_PER_SECOND;
1037adca78eSYoshinori Sato     next_time /= tmr->input_freq;
1047adca78eSYoshinori Sato     next_time += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
1057adca78eSYoshinori Sato     timer_mod(&tmr->timer[ch], next_time);
1067adca78eSYoshinori Sato }
1077adca78eSYoshinori Sato 
elapsed_time(RTMRState * tmr,int ch,int64_t delta)1087adca78eSYoshinori Sato static int elapsed_time(RTMRState *tmr, int ch, int64_t delta)
1097adca78eSYoshinori Sato {
1107adca78eSYoshinori Sato     int divrate = clkdiv[FIELD_EX8(tmr->tccr[ch], TCCR, CKS)];
1117adca78eSYoshinori Sato     int et;
1127adca78eSYoshinori Sato 
1137adca78eSYoshinori Sato     tmr->div_round[ch] += delta;
1147adca78eSYoshinori Sato     if (divrate > 0) {
1157adca78eSYoshinori Sato         et = tmr->div_round[ch] / divrate;
1167adca78eSYoshinori Sato         tmr->div_round[ch] %= divrate;
1177adca78eSYoshinori Sato     } else {
1189b4b4e51SMichael Tokarev         /* disable clock. so no update */
1197adca78eSYoshinori Sato         et = 0;
1207adca78eSYoshinori Sato     }
1217adca78eSYoshinori Sato     return et;
1227adca78eSYoshinori Sato }
1237adca78eSYoshinori Sato 
read_tcnt(RTMRState * tmr,unsigned size,int ch)1247adca78eSYoshinori Sato static uint16_t read_tcnt(RTMRState *tmr, unsigned size, int ch)
1257adca78eSYoshinori Sato {
1267adca78eSYoshinori Sato     int64_t delta, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
1277adca78eSYoshinori Sato     int elapsed, ovf = 0;
1287adca78eSYoshinori Sato     uint16_t tcnt[2];
1297adca78eSYoshinori Sato     uint32_t ret;
1307adca78eSYoshinori Sato 
1317adca78eSYoshinori Sato     delta = (now - tmr->tick) * NANOSECONDS_PER_SECOND / tmr->input_freq;
1327adca78eSYoshinori Sato     if (delta > 0) {
1337adca78eSYoshinori Sato         tmr->tick = now;
1347adca78eSYoshinori Sato 
13581b3ddafSPeter Maydell         switch (FIELD_EX8(tmr->tccr[1], TCCR, CSS)) {
13681b3ddafSPeter Maydell         case CSS_INTERNAL:
1377adca78eSYoshinori Sato             /* timer1 count update */
1387adca78eSYoshinori Sato             elapsed = elapsed_time(tmr, 1, delta);
1397adca78eSYoshinori Sato             if (elapsed >= 0x100) {
1407adca78eSYoshinori Sato                 ovf = elapsed >> 8;
1417adca78eSYoshinori Sato             }
1427adca78eSYoshinori Sato             tcnt[1] = tmr->tcnt[1] + (elapsed & 0xff);
14381b3ddafSPeter Maydell             break;
14481b3ddafSPeter Maydell         case CSS_INVALID: /* guest error to have set this */
14581b3ddafSPeter Maydell         case CSS_EXTERNAL: /* QEMU doesn't implement these */
14681b3ddafSPeter Maydell         case CSS_CASCADING:
14781b3ddafSPeter Maydell             tcnt[1] = tmr->tcnt[1];
14881b3ddafSPeter Maydell             break;
149b9e3f157SPeter Maydell         default:
150b9e3f157SPeter Maydell             g_assert_not_reached();
1517adca78eSYoshinori Sato         }
1527adca78eSYoshinori Sato         switch (FIELD_EX8(tmr->tccr[0], TCCR, CSS)) {
15302f8fe11SPeter Maydell         case CSS_INTERNAL:
1547adca78eSYoshinori Sato             elapsed = elapsed_time(tmr, 0, delta);
1557adca78eSYoshinori Sato             tcnt[0] = tmr->tcnt[0] + elapsed;
1567adca78eSYoshinori Sato             break;
15702f8fe11SPeter Maydell         case CSS_CASCADING:
1587adca78eSYoshinori Sato             tcnt[0] = tmr->tcnt[0] + ovf;
15981b3ddafSPeter Maydell             break;
16081b3ddafSPeter Maydell         case CSS_INVALID: /* guest error to have set this */
16181b3ddafSPeter Maydell         case CSS_EXTERNAL: /* QEMU doesn't implement this */
16281b3ddafSPeter Maydell             tcnt[0] = tmr->tcnt[0];
1637adca78eSYoshinori Sato             break;
164b9e3f157SPeter Maydell         default:
165b9e3f157SPeter Maydell             g_assert_not_reached();
1667adca78eSYoshinori Sato         }
1677adca78eSYoshinori Sato     } else {
1687adca78eSYoshinori Sato         tcnt[0] = tmr->tcnt[0];
1697adca78eSYoshinori Sato         tcnt[1] = tmr->tcnt[1];
1707adca78eSYoshinori Sato     }
1717adca78eSYoshinori Sato     if (size == 1) {
1727adca78eSYoshinori Sato         return tcnt[ch];
1737adca78eSYoshinori Sato     } else {
1747adca78eSYoshinori Sato         ret = 0;
1757adca78eSYoshinori Sato         ret = deposit32(ret, 0, 8, tcnt[1]);
1767adca78eSYoshinori Sato         ret = deposit32(ret, 8, 8, tcnt[0]);
1777adca78eSYoshinori Sato         return ret;
1787adca78eSYoshinori Sato     }
1797adca78eSYoshinori Sato }
1807adca78eSYoshinori Sato 
read_tccr(uint8_t r)1817adca78eSYoshinori Sato static uint8_t read_tccr(uint8_t r)
1827adca78eSYoshinori Sato {
1837adca78eSYoshinori Sato     uint8_t tccr = 0;
1847adca78eSYoshinori Sato     tccr = FIELD_DP8(tccr, TCCR, TMRIS,
1857adca78eSYoshinori Sato                      FIELD_EX8(r, TCCR, TMRIS));
1867adca78eSYoshinori Sato     tccr = FIELD_DP8(tccr, TCCR, CSS,
1877adca78eSYoshinori Sato                      FIELD_EX8(r, TCCR, CSS));
1887adca78eSYoshinori Sato     tccr = FIELD_DP8(tccr, TCCR, CKS,
1897adca78eSYoshinori Sato                      FIELD_EX8(r, TCCR, CKS));
1907adca78eSYoshinori Sato     return tccr;
1917adca78eSYoshinori Sato }
1927adca78eSYoshinori Sato 
tmr_read(void * opaque,hwaddr addr,unsigned size)1937adca78eSYoshinori Sato static uint64_t tmr_read(void *opaque, hwaddr addr, unsigned size)
1947adca78eSYoshinori Sato {
1957adca78eSYoshinori Sato     RTMRState *tmr = opaque;
1967adca78eSYoshinori Sato     int ch = addr & 1;
1977adca78eSYoshinori Sato     uint64_t ret;
1987adca78eSYoshinori Sato 
1997adca78eSYoshinori Sato     if (size == 2 && (ch != 0 || addr == A_TCR || addr == A_TCSR)) {
2007adca78eSYoshinori Sato         qemu_log_mask(LOG_GUEST_ERROR, "renesas_tmr: Invalid read size 0x%"
2017adca78eSYoshinori Sato                                        HWADDR_PRIX "\n",
2027adca78eSYoshinori Sato                       addr);
2037adca78eSYoshinori Sato         return UINT64_MAX;
2047adca78eSYoshinori Sato     }
2057adca78eSYoshinori Sato     switch (addr & 0x0e) {
2067adca78eSYoshinori Sato     case A_TCR:
2077adca78eSYoshinori Sato         ret = 0;
2087adca78eSYoshinori Sato         ret = FIELD_DP8(ret, TCR, CCLR,
2097adca78eSYoshinori Sato                         FIELD_EX8(tmr->tcr[ch], TCR, CCLR));
2107adca78eSYoshinori Sato         ret = FIELD_DP8(ret, TCR, OVIE,
2117adca78eSYoshinori Sato                         FIELD_EX8(tmr->tcr[ch], TCR, OVIE));
2127adca78eSYoshinori Sato         ret = FIELD_DP8(ret, TCR, CMIEA,
2137adca78eSYoshinori Sato                         FIELD_EX8(tmr->tcr[ch], TCR, CMIEA));
2147adca78eSYoshinori Sato         ret = FIELD_DP8(ret, TCR, CMIEB,
2157adca78eSYoshinori Sato                         FIELD_EX8(tmr->tcr[ch], TCR, CMIEB));
2167adca78eSYoshinori Sato         return ret;
2177adca78eSYoshinori Sato     case A_TCSR:
2187adca78eSYoshinori Sato         ret = 0;
2197adca78eSYoshinori Sato         ret = FIELD_DP8(ret, TCSR, OSA,
2207adca78eSYoshinori Sato                         FIELD_EX8(tmr->tcsr[ch], TCSR, OSA));
2217adca78eSYoshinori Sato         ret = FIELD_DP8(ret, TCSR, OSB,
2227adca78eSYoshinori Sato                         FIELD_EX8(tmr->tcsr[ch], TCSR, OSB));
2237adca78eSYoshinori Sato         switch (ch) {
2247adca78eSYoshinori Sato         case 0:
2257adca78eSYoshinori Sato             ret = FIELD_DP8(ret, TCSR, ADTE,
2267adca78eSYoshinori Sato                             FIELD_EX8(tmr->tcsr[ch], TCSR, ADTE));
2277adca78eSYoshinori Sato             break;
2287adca78eSYoshinori Sato         case 1: /* CH1 ADTE unimplement always 1 */
2297adca78eSYoshinori Sato             ret = FIELD_DP8(ret, TCSR, ADTE, 1);
2307adca78eSYoshinori Sato             break;
2317adca78eSYoshinori Sato         }
2327adca78eSYoshinori Sato         return ret;
2337adca78eSYoshinori Sato     case A_TCORA:
2347adca78eSYoshinori Sato         if (size == 1) {
2357adca78eSYoshinori Sato             return tmr->tcora[ch];
2367adca78eSYoshinori Sato         } else if (ch == 0) {
2377adca78eSYoshinori Sato             return concat_reg(tmr->tcora);
2387adca78eSYoshinori Sato         }
23930982862SChen Qun         /* fall through */
2407adca78eSYoshinori Sato     case A_TCORB:
2417adca78eSYoshinori Sato         if (size == 1) {
2427adca78eSYoshinori Sato             return tmr->tcorb[ch];
2437adca78eSYoshinori Sato         } else {
2447adca78eSYoshinori Sato             return concat_reg(tmr->tcorb);
2457adca78eSYoshinori Sato         }
2467adca78eSYoshinori Sato     case A_TCNT:
2477adca78eSYoshinori Sato         return read_tcnt(tmr, size, ch);
2487adca78eSYoshinori Sato     case A_TCCR:
2497adca78eSYoshinori Sato         if (size == 1) {
2507adca78eSYoshinori Sato             return read_tccr(tmr->tccr[ch]);
2517adca78eSYoshinori Sato         } else {
2527adca78eSYoshinori Sato             return read_tccr(tmr->tccr[0]) << 8 | read_tccr(tmr->tccr[1]);
2537adca78eSYoshinori Sato         }
2547adca78eSYoshinori Sato     default:
2557adca78eSYoshinori Sato         qemu_log_mask(LOG_UNIMP, "renesas_tmr: Register 0x%" HWADDR_PRIX
2567adca78eSYoshinori Sato                                  " not implemented\n",
2577adca78eSYoshinori Sato                       addr);
2587adca78eSYoshinori Sato         break;
2597adca78eSYoshinori Sato     }
2607adca78eSYoshinori Sato     return UINT64_MAX;
2617adca78eSYoshinori Sato }
2627adca78eSYoshinori Sato 
tmr_write_count(RTMRState * tmr,int ch,unsigned size,uint8_t * reg,uint64_t val)2637adca78eSYoshinori Sato static void tmr_write_count(RTMRState *tmr, int ch, unsigned size,
2647adca78eSYoshinori Sato                             uint8_t *reg, uint64_t val)
2657adca78eSYoshinori Sato {
2667adca78eSYoshinori Sato     if (size == 1) {
2677adca78eSYoshinori Sato         reg[ch] = val;
2687adca78eSYoshinori Sato         update_events(tmr, ch);
2697adca78eSYoshinori Sato     } else {
2707adca78eSYoshinori Sato         reg[0] = extract32(val, 8, 8);
2717adca78eSYoshinori Sato         reg[1] = extract32(val, 0, 8);
2727adca78eSYoshinori Sato         update_events(tmr, 0);
2737adca78eSYoshinori Sato         update_events(tmr, 1);
2747adca78eSYoshinori Sato     }
2757adca78eSYoshinori Sato }
2767adca78eSYoshinori Sato 
tmr_write(void * opaque,hwaddr addr,uint64_t val,unsigned size)2777adca78eSYoshinori Sato static void tmr_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
2787adca78eSYoshinori Sato {
2797adca78eSYoshinori Sato     RTMRState *tmr = opaque;
2807adca78eSYoshinori Sato     int ch = addr & 1;
2817adca78eSYoshinori Sato 
2827adca78eSYoshinori Sato     if (size == 2 && (ch != 0 || addr == A_TCR || addr == A_TCSR)) {
2837adca78eSYoshinori Sato         qemu_log_mask(LOG_GUEST_ERROR,
2847adca78eSYoshinori Sato                       "renesas_tmr: Invalid write size 0x%" HWADDR_PRIX "\n",
2857adca78eSYoshinori Sato                       addr);
2867adca78eSYoshinori Sato         return;
2877adca78eSYoshinori Sato     }
2887adca78eSYoshinori Sato     switch (addr & 0x0e) {
2897adca78eSYoshinori Sato     case A_TCR:
2907adca78eSYoshinori Sato         tmr->tcr[ch] = val;
2917adca78eSYoshinori Sato         break;
2927adca78eSYoshinori Sato     case A_TCSR:
2937adca78eSYoshinori Sato         tmr->tcsr[ch] = val;
2947adca78eSYoshinori Sato         break;
2957adca78eSYoshinori Sato     case A_TCORA:
2967adca78eSYoshinori Sato         tmr_write_count(tmr, ch, size, tmr->tcora, val);
2977adca78eSYoshinori Sato         break;
2987adca78eSYoshinori Sato     case A_TCORB:
2997adca78eSYoshinori Sato         tmr_write_count(tmr, ch, size, tmr->tcorb, val);
3007adca78eSYoshinori Sato         break;
3017adca78eSYoshinori Sato     case A_TCNT:
3027adca78eSYoshinori Sato         tmr_write_count(tmr, ch, size, tmr->tcnt, val);
3037adca78eSYoshinori Sato         break;
3047adca78eSYoshinori Sato     case A_TCCR:
3057adca78eSYoshinori Sato         tmr_write_count(tmr, ch, size, tmr->tccr, val);
3067adca78eSYoshinori Sato         break;
3077adca78eSYoshinori Sato     default:
3087adca78eSYoshinori Sato         qemu_log_mask(LOG_UNIMP, "renesas_tmr: Register 0x%" HWADDR_PRIX
3097adca78eSYoshinori Sato                                  " not implemented\n",
3107adca78eSYoshinori Sato                       addr);
3117adca78eSYoshinori Sato         break;
3127adca78eSYoshinori Sato     }
3137adca78eSYoshinori Sato }
3147adca78eSYoshinori Sato 
3157adca78eSYoshinori Sato static const MemoryRegionOps tmr_ops = {
3167adca78eSYoshinori Sato     .write = tmr_write,
3177adca78eSYoshinori Sato     .read  = tmr_read,
3187adca78eSYoshinori Sato     .endianness = DEVICE_LITTLE_ENDIAN,
3197adca78eSYoshinori Sato     .impl = {
3207adca78eSYoshinori Sato         .min_access_size = 1,
3217adca78eSYoshinori Sato         .max_access_size = 2,
3227adca78eSYoshinori Sato     },
3237adca78eSYoshinori Sato     .valid = {
3247adca78eSYoshinori Sato         .min_access_size = 1,
3257adca78eSYoshinori Sato         .max_access_size = 2,
3267adca78eSYoshinori Sato     },
3277adca78eSYoshinori Sato };
3287adca78eSYoshinori Sato 
3297adca78eSYoshinori Sato static void timer_events(RTMRState *tmr, int ch);
3307adca78eSYoshinori Sato 
issue_event(RTMRState * tmr,int ch,int sz,uint16_t tcnt,uint16_t tcora,uint16_t tcorb)3317adca78eSYoshinori Sato static uint16_t issue_event(RTMRState *tmr, int ch, int sz,
3327adca78eSYoshinori Sato                         uint16_t tcnt, uint16_t tcora, uint16_t tcorb)
3337adca78eSYoshinori Sato {
3347adca78eSYoshinori Sato     uint16_t ret = tcnt;
3357adca78eSYoshinori Sato 
3367adca78eSYoshinori Sato     switch (tmr->next[ch]) {
3377adca78eSYoshinori Sato     case none:
3387adca78eSYoshinori Sato         break;
3397adca78eSYoshinori Sato     case cmia:
3407adca78eSYoshinori Sato         if (tcnt >= tcora) {
3417adca78eSYoshinori Sato             if (FIELD_EX8(tmr->tcr[ch], TCR, CCLR) == CCLR_A) {
3427adca78eSYoshinori Sato                 ret = tcnt - tcora;
3437adca78eSYoshinori Sato             }
3447adca78eSYoshinori Sato             if (FIELD_EX8(tmr->tcr[ch], TCR, CMIEA)) {
3457adca78eSYoshinori Sato                 qemu_irq_pulse(tmr->cmia[ch]);
3467adca78eSYoshinori Sato             }
3477adca78eSYoshinori Sato             if (sz == 8 && ch == 0 &&
34802f8fe11SPeter Maydell                 FIELD_EX8(tmr->tccr[1], TCCR, CSS) == CSS_CASCADING) {
3497adca78eSYoshinori Sato                 tmr->tcnt[1]++;
3507adca78eSYoshinori Sato                 timer_events(tmr, 1);
3517adca78eSYoshinori Sato             }
3527adca78eSYoshinori Sato         }
3537adca78eSYoshinori Sato         break;
3547adca78eSYoshinori Sato     case cmib:
3557adca78eSYoshinori Sato         if (tcnt >= tcorb) {
3567adca78eSYoshinori Sato             if (FIELD_EX8(tmr->tcr[ch], TCR, CCLR) == CCLR_B) {
3577adca78eSYoshinori Sato                 ret = tcnt - tcorb;
3587adca78eSYoshinori Sato             }
3597adca78eSYoshinori Sato             if (FIELD_EX8(tmr->tcr[ch], TCR, CMIEB)) {
3607adca78eSYoshinori Sato                 qemu_irq_pulse(tmr->cmib[ch]);
3617adca78eSYoshinori Sato             }
3627adca78eSYoshinori Sato         }
3637adca78eSYoshinori Sato         break;
3647adca78eSYoshinori Sato     case ovi:
3657adca78eSYoshinori Sato         if ((tcnt >= (1 << sz)) && FIELD_EX8(tmr->tcr[ch], TCR, OVIE)) {
3667adca78eSYoshinori Sato             qemu_irq_pulse(tmr->ovi[ch]);
3677adca78eSYoshinori Sato         }
3687adca78eSYoshinori Sato         break;
3697adca78eSYoshinori Sato     default:
3707adca78eSYoshinori Sato         g_assert_not_reached();
3717adca78eSYoshinori Sato     }
3727adca78eSYoshinori Sato     return ret;
3737adca78eSYoshinori Sato }
3747adca78eSYoshinori Sato 
timer_events(RTMRState * tmr,int ch)3757adca78eSYoshinori Sato static void timer_events(RTMRState *tmr, int ch)
3767adca78eSYoshinori Sato {
3777adca78eSYoshinori Sato     uint16_t tcnt;
3787adca78eSYoshinori Sato 
3797adca78eSYoshinori Sato     tmr->tcnt[ch] = read_tcnt(tmr, 1, ch);
38002f8fe11SPeter Maydell     if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) != CSS_CASCADING) {
3817adca78eSYoshinori Sato         tmr->tcnt[ch] = issue_event(tmr, ch, 8,
3827adca78eSYoshinori Sato                                     tmr->tcnt[ch],
3837adca78eSYoshinori Sato                                     tmr->tcora[ch],
3847adca78eSYoshinori Sato                                     tmr->tcorb[ch]) & 0xff;
3857adca78eSYoshinori Sato     } else {
3867adca78eSYoshinori Sato         if (ch == 1) {
3877adca78eSYoshinori Sato             return;
3887adca78eSYoshinori Sato         }
3897adca78eSYoshinori Sato         tcnt = issue_event(tmr, ch, 16,
3907adca78eSYoshinori Sato                            concat_reg(tmr->tcnt),
3917adca78eSYoshinori Sato                            concat_reg(tmr->tcora),
3927adca78eSYoshinori Sato                            concat_reg(tmr->tcorb));
3937adca78eSYoshinori Sato         tmr->tcnt[0] = (tcnt >> 8) & 0xff;
3947adca78eSYoshinori Sato         tmr->tcnt[1] = tcnt & 0xff;
3957adca78eSYoshinori Sato     }
3967adca78eSYoshinori Sato     update_events(tmr, ch);
3977adca78eSYoshinori Sato }
3987adca78eSYoshinori Sato 
timer_event0(void * opaque)3997adca78eSYoshinori Sato static void timer_event0(void *opaque)
4007adca78eSYoshinori Sato {
4017adca78eSYoshinori Sato     RTMRState *tmr = opaque;
4027adca78eSYoshinori Sato 
4037adca78eSYoshinori Sato     timer_events(tmr, 0);
4047adca78eSYoshinori Sato }
4057adca78eSYoshinori Sato 
timer_event1(void * opaque)4067adca78eSYoshinori Sato static void timer_event1(void *opaque)
4077adca78eSYoshinori Sato {
4087adca78eSYoshinori Sato     RTMRState *tmr = opaque;
4097adca78eSYoshinori Sato 
4107adca78eSYoshinori Sato     timer_events(tmr, 1);
4117adca78eSYoshinori Sato }
4127adca78eSYoshinori Sato 
rtmr_reset(DeviceState * dev)4137adca78eSYoshinori Sato static void rtmr_reset(DeviceState *dev)
4147adca78eSYoshinori Sato {
4157adca78eSYoshinori Sato     RTMRState *tmr = RTMR(dev);
4167adca78eSYoshinori Sato     tmr->tcr[0]   = tmr->tcr[1]   = 0x00;
4177adca78eSYoshinori Sato     tmr->tcsr[0]  = 0x00;
4187adca78eSYoshinori Sato     tmr->tcsr[1]  = 0x10;
4197adca78eSYoshinori Sato     tmr->tcnt[0]  = tmr->tcnt[1]  = 0x00;
4207adca78eSYoshinori Sato     tmr->tcora[0] = tmr->tcora[1] = 0xff;
4217adca78eSYoshinori Sato     tmr->tcorb[0] = tmr->tcorb[1] = 0xff;
4227adca78eSYoshinori Sato     tmr->tccr[0]  = tmr->tccr[1]  = 0x00;
4237adca78eSYoshinori Sato     tmr->next[0]  = tmr->next[1]  = none;
4247adca78eSYoshinori Sato     tmr->tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
4257adca78eSYoshinori Sato }
4267adca78eSYoshinori Sato 
rtmr_init(Object * obj)4277adca78eSYoshinori Sato static void rtmr_init(Object *obj)
4287adca78eSYoshinori Sato {
4297adca78eSYoshinori Sato     SysBusDevice *d = SYS_BUS_DEVICE(obj);
4307adca78eSYoshinori Sato     RTMRState *tmr = RTMR(obj);
4317adca78eSYoshinori Sato     int i;
4327adca78eSYoshinori Sato 
4337adca78eSYoshinori Sato     memory_region_init_io(&tmr->memory, OBJECT(tmr), &tmr_ops,
4347adca78eSYoshinori Sato                           tmr, "renesas-tmr", 0x10);
4357adca78eSYoshinori Sato     sysbus_init_mmio(d, &tmr->memory);
4367adca78eSYoshinori Sato 
4377adca78eSYoshinori Sato     for (i = 0; i < ARRAY_SIZE(tmr->ovi); i++) {
4387adca78eSYoshinori Sato         sysbus_init_irq(d, &tmr->cmia[i]);
4397adca78eSYoshinori Sato         sysbus_init_irq(d, &tmr->cmib[i]);
4407adca78eSYoshinori Sato         sysbus_init_irq(d, &tmr->ovi[i]);
4417adca78eSYoshinori Sato     }
4427adca78eSYoshinori Sato     timer_init_ns(&tmr->timer[0], QEMU_CLOCK_VIRTUAL, timer_event0, tmr);
4437adca78eSYoshinori Sato     timer_init_ns(&tmr->timer[1], QEMU_CLOCK_VIRTUAL, timer_event1, tmr);
4447adca78eSYoshinori Sato }
4457adca78eSYoshinori Sato 
4467adca78eSYoshinori Sato static const VMStateDescription vmstate_rtmr = {
4477adca78eSYoshinori Sato     .name = "rx-tmr",
4487adca78eSYoshinori Sato     .version_id = 1,
4497adca78eSYoshinori Sato     .minimum_version_id = 1,
450ba324b3fSRichard Henderson     .fields = (const VMStateField[]) {
4517adca78eSYoshinori Sato         VMSTATE_INT64(tick, RTMRState),
4527adca78eSYoshinori Sato         VMSTATE_UINT8_ARRAY(tcnt, RTMRState, TMR_CH),
4537adca78eSYoshinori Sato         VMSTATE_UINT8_ARRAY(tcora, RTMRState, TMR_CH),
4547adca78eSYoshinori Sato         VMSTATE_UINT8_ARRAY(tcorb, RTMRState, TMR_CH),
4557adca78eSYoshinori Sato         VMSTATE_UINT8_ARRAY(tcr, RTMRState, TMR_CH),
4567adca78eSYoshinori Sato         VMSTATE_UINT8_ARRAY(tccr, RTMRState, TMR_CH),
4577adca78eSYoshinori Sato         VMSTATE_UINT8_ARRAY(tcor, RTMRState, TMR_CH),
4587adca78eSYoshinori Sato         VMSTATE_UINT8_ARRAY(tcsr, RTMRState, TMR_CH),
4597adca78eSYoshinori Sato         VMSTATE_INT64_ARRAY(div_round, RTMRState, TMR_CH),
4607adca78eSYoshinori Sato         VMSTATE_UINT8_ARRAY(next, RTMRState, TMR_CH),
4617adca78eSYoshinori Sato         VMSTATE_TIMER_ARRAY(timer, RTMRState, TMR_CH),
4627adca78eSYoshinori Sato         VMSTATE_END_OF_LIST()
4637adca78eSYoshinori Sato     }
4647adca78eSYoshinori Sato };
4657adca78eSYoshinori Sato 
46674734e2bSRichard Henderson static const Property rtmr_properties[] = {
4677adca78eSYoshinori Sato     DEFINE_PROP_UINT64("input-freq", RTMRState, input_freq, 0),
4687adca78eSYoshinori Sato };
4697adca78eSYoshinori Sato 
rtmr_class_init(ObjectClass * klass,const void * data)470*12d1a768SPhilippe Mathieu-Daudé static void rtmr_class_init(ObjectClass *klass, const void *data)
4717adca78eSYoshinori Sato {
4727adca78eSYoshinori Sato     DeviceClass *dc = DEVICE_CLASS(klass);
4737adca78eSYoshinori Sato 
4747adca78eSYoshinori Sato     dc->vmsd = &vmstate_rtmr;
475e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, rtmr_reset);
4767adca78eSYoshinori Sato     device_class_set_props(dc, rtmr_properties);
4777adca78eSYoshinori Sato }
4787adca78eSYoshinori Sato 
4797adca78eSYoshinori Sato static const TypeInfo rtmr_info = {
4807adca78eSYoshinori Sato     .name = TYPE_RENESAS_TMR,
4817adca78eSYoshinori Sato     .parent = TYPE_SYS_BUS_DEVICE,
4827adca78eSYoshinori Sato     .instance_size = sizeof(RTMRState),
4837adca78eSYoshinori Sato     .instance_init = rtmr_init,
4847adca78eSYoshinori Sato     .class_init = rtmr_class_init,
4857adca78eSYoshinori Sato };
4867adca78eSYoshinori Sato 
rtmr_register_types(void)4877adca78eSYoshinori Sato static void rtmr_register_types(void)
4887adca78eSYoshinori Sato {
4897adca78eSYoshinori Sato     type_register_static(&rtmr_info);
4907adca78eSYoshinori Sato }
4917adca78eSYoshinori Sato 
4927adca78eSYoshinori Sato type_init(rtmr_register_types)
493