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