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