[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH 13/20] hw/timer: Add Renesas MTU2
From: |
Yoshinori Sato |
Subject: |
[PATCH 13/20] hw/timer: Add Renesas MTU2 |
Date: |
Thu, 27 Aug 2020 21:38:52 +0900 |
Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
include/hw/timer/renesas_mtu.h | 90 +++
hw/timer/renesas_mtu.c | 1312 ++++++++++++++++++++++++++++++++
hw/timer/Kconfig | 2 +
hw/timer/meson.build | 1 +
4 files changed, 1405 insertions(+)
create mode 100644 include/hw/timer/renesas_mtu.h
create mode 100644 hw/timer/renesas_mtu.c
diff --git a/include/hw/timer/renesas_mtu.h b/include/hw/timer/renesas_mtu.h
new file mode 100644
index 0000000000..27df14b308
--- /dev/null
+++ b/include/hw/timer/renesas_mtu.h
@@ -0,0 +1,90 @@
+/*
+ * Renesas Multi-function Timer Uint Object
+ *
+ * Copyright (c) 2020 Yoshinori Sato
+ *
+ * This code is licensed under the GPL version 2 or later.
+ *
+ */
+
+#ifndef HW_RENESAS_MTU_H
+#define HW_RENESAS_MTU_H
+
+#include "hw/sysbus.h"
+#include "hw/qdev-clock.h"
+
+#define TYPE_RENESAS_MTU2 "renesas-mtu2"
+#define RenesasMTU2(obj) \
+ OBJECT_CHECK(RenesasMTU2State, (obj), TYPE_RENESAS_MTU2)
+
+#define MTU2Class(klass) \
+ OBJECT_CLASS_CHECK(RenesasMTU2Class, klass, TYPE_RENESAS_MTU2)
+
+enum {
+ NR_MAX_IRQ = 7,
+ MTU_NR_IRQ = 7 + 4 + 4 + 5 + 5 + 3,
+};
+
+struct RenesasMTU2State;
+
+typedef struct {
+ uint8_t tcr;
+ uint8_t tmdr;
+ uint8_t tsr;
+ uint16_t tior;
+ uint16_t tier;
+ uint32_t tcnt;
+ uint16_t tgr[6];
+
+ int num_gr;
+ int64_t base;
+ int64_t next;
+ int64_t clk;
+ bool start;
+ bool cntclr;
+ bool ier;
+ QEMUTimer *timer;
+ int ch;
+ qemu_irq irq[NR_MAX_IRQ];
+ int next_cnt;
+ struct RenesasMTU2State *mtu;
+} RenesasMTURegs;
+
+typedef struct RenesasMTU2State {
+ SysBusDevice parent_obj;
+ RenesasMTURegs r[5];
+ RenesasMTURegs r5[3];
+ uint8_t tbtm;
+ uint8_t ticcr;
+ uint16_t tadcr;
+ uint16_t tadcor[2];
+ uint16_t tadcobr[2];
+ /* CH A registers */
+ uint8_t toer;
+ uint8_t tgcr;
+ uint8_t tocr[2];
+ uint16_t tcdr;
+ uint16_t tddr;
+ uint16_t tcnts;
+ uint16_t tcbr;
+ uint8_t titcr;
+ uint8_t titcnt;
+ uint8_t tbter;
+ uint8_t tder;
+ uint8_t tolbr;
+ uint8_t twcr;
+ uint8_t trwer;
+ uint8_t tsyr;
+
+ Clock *pck;
+ int64_t input_freq;
+ MemoryRegion memory[3];
+ uint8_t trwer_r;
+ uint32_t unit;
+} RenesasMTU2State;
+
+typedef struct {
+ SysBusDeviceClass parent;
+} RenesasMTU2Class;
+
+#endif
diff --git a/hw/timer/renesas_mtu.c b/hw/timer/renesas_mtu.c
new file mode 100644
index 0000000000..fc204f10ce
--- /dev/null
+++ b/hw/timer/renesas_mtu.c
@@ -0,0 +1,1312 @@
+/*
+ * Renesas Multi-function Timer Uint
+ *
+ * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
+ * (Rev.1.40 R01UH0033EJ0140)
+ *
+ * Copyright (c) 2019 Yoshinori Sato
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "qemu/timer.h"
+#include "hw/hw.h"
+#include "hw/irq.h"
+#include "hw/sysbus.h"
+#include "hw/registerfields.h"
+#include "hw/qdev-properties.h"
+#include "hw/timer/renesas_mtu.h"
+#include "qemu/error-report.h"
+
+REG8(TCR_012, 0)
+REG8(TMDR_012, 1)
+REG8(TIORH_012, 2)
+REG8(TIORL_012, 3)
+REG8(TIER_012, 4)
+REG8(TSR_012, 5)
+REG16(TCNT_012, 6)
+REG16(TGRA_012, 8)
+REG16(TGRB_012, 10)
+REG16(TGRC_012, 12)
+REG16(TGRD_012, 14)
+REG8(TICCR_1, 16)
+REG16(TGRE_0, 32)
+REG16(TGRF_0, 34)
+REG8(TIER2_0, 36)
+REG8(TBTM_0, 38)
+
+REG8(TCR_3, 0)
+REG8(TCR_4, 1)
+REG8(TMDR_3, 2)
+REG8(TMDR_4, 3)
+REG8(TIORH_3, 4)
+REG8(TIORL_3, 5)
+REG8(TIORH_4, 6)
+REG8(TIORL_4, 7)
+REG8(TIER_3, 8)
+REG8(TIER_4, 9)
+REG8(TOER, 10)
+ FIELD(TOER, OE3B, 0, 1)
+ FIELD(TOER, OE4A, 1, 1)
+ FIELD(TOER, OE4B, 2, 1)
+ FIELD(TOER, OE3D, 3, 1)
+ FIELD(TOER, OE4C, 4, 1)
+ FIELD(TOER, OE4D, 5, 1)
+REG8(TGCR, 13)
+ FIELD(TGCR, BDC, 6, 1)
+ FIELD(TGCR, N, 5, 1)
+ FIELD(TGCR, P, 4, 1)
+ FIELD(TGCR, FB, 3, 1)
+ FIELD(TGCR, WF, 2, 1)
+ FIELD(TGCR, VF, 1, 1)
+ FIELD(TGCR, UF, 0, 1)
+REG8(TOCR1, 14)
+ FIELD(TOCR1, OLSP, 0, 1)
+ FIELD(TOCR1, OLSN, 1, 1)
+ FIELD(TOCR1, TOCS, 2, 1)
+ FIELD(TOCR1, TOCL, 3, 1)
+ FIELD(TOCR1, PSYE, 6, 1)
+REG8(TOCR2, 15)
+ FIELD(TOCR2, OLS1P, 0, 1)
+ FIELD(TOCR2, OLS1N, 1, 1)
+ FIELD(TOCR2, OLS2P, 2, 1)
+ FIELD(TOCR2, OLS2N, 3, 1)
+ FIELD(TOCR2, OLS3P, 4, 1)
+ FIELD(TOCR2, OLS3N, 5, 1)
+ FIELD(TOCR2, BF, 6, 2)
+REG16(TCNT_3, 16)
+REG16(TCNT_4, 18)
+REG16(TCDR, 20)
+REG16(TDDR, 22)
+REG16(TGRA_3, 24)
+REG16(TGRB_3, 26)
+REG16(TGRA_4, 28)
+REG16(TGRB_4, 30)
+REG16(TCNTS, 32)
+REG16(TCBR, 34)
+REG16(TGRC_3, 36)
+REG16(TGRD_3, 38)
+REG16(TGRC_4, 40)
+REG16(TGRD_4, 42)
+REG8(TSR_3, 44)
+REG8(TSR_4, 45)
+REG8(TITCR, 48)
+ FIELD(TITCR, T4VCOR, 0, 3)
+ FIELD(TITCR, T4VEN, 3, 1)
+ FIELD(TITCR, T3ACOR, 4, 3)
+ FIELD(TITCR, T3AEN, 7, 1)
+REG8(TITCNT, 49)
+ FIELD(TITCNT, T4VCNT, 0, 3)
+ FIELD(TITCNT, T3ACNT, 4, 3)
+REG8(TBTER, 50)
+ FIELD(TBTER, BTE, 0, 2)
+REG8(TDER, 52)
+ FIELD(TDER, TDER, 0, 1)
+REG8(TOLBR, 54)
+ FIELD(TOLBR, OLS1P, 0, 1)
+ FIELD(TOLBR, OLS1N, 1, 1)
+ FIELD(TOLBR, OLS2P, 2, 1)
+ FIELD(TOLBR, OLS2N, 3, 1)
+ FIELD(TOLBR, OLS3P, 4, 1)
+ FIELD(TOLBR, OLS3N, 5, 1)
+REG8(TBTM_3, 56)
+REG8(TBTM_4, 57)
+REG16(TADCR_4, 64)
+REG16(TADCORA_4, 68)
+REG16(TADCORB_4, 70)
+REG16(TADCOBRA_4, 72)
+REG16(TADCOBRB_4, 74)
+REG8(TWCR, 96)
+ FIELD(TWCR, WRE, 0, 1)
+ FIELD(TWCR, CCE, 7, 1)
+REG8(TSTR, 128)
+ FIELD(TSTR, CST0, 0, 1)
+ FIELD(TSTR, CST1, 1, 1)
+ FIELD(TSTR, CST2, 2, 1)
+ FIELD(TSTR, CSTL, 0, 3)
+ FIELD(TSTR, CST3, 6, 1)
+ FIELD(TSTR, CST4, 7, 1)
+ FIELD(TSTR, CSTH, 6, 2)
+REG8(TSYR, 129)
+ FIELD(TSYR, SYNC0, 0, 1)
+ FIELD(TSYR, SYNC1, 1, 1)
+ FIELD(TSYR, SYNC2, 2, 1)
+ FIELD(TSYR, SYNC3, 6, 1)
+ FIELD(TSYR, SYNC4, 7, 1)
+REG8(TRWER, 132)
+ FIELD(TRWER, RWE, 0, 1)
+
+REG16(TCNTU_5, 0)
+REG16(TGRU_5, 2)
+REG16(TCRU_5, 4)
+REG8(TIORU_5, 6)
+REG16(TCNTV_5, 16)
+REG16(TGRV_5, 18)
+REG8(TCRV_5, 20)
+REG8(TIORV_5, 22)
+REG16(TCNTW_5, 32)
+REG16(TGRW_5, 34)
+REG8(TCRW_5, 36)
+REG8(TIORW_5, 38)
+REG8(TIER_5, 50)
+ FIELD(TIER_5, TGIEW5, 0, 1)
+ FIELD(TIER_5, TGIEV5, 1, 1)
+ FIELD(TIER_5, TGIEU5, 2, 1)
+ FIELD(TIER_5, TGIE5, 0, 3)
+REG8(TSTR_5, 52)
+ FIELD(TSTR_5, CSTW5, 0, 1)
+ FIELD(TSTR_5, CSTV5, 1, 1)
+ FIELD(TSTR_5, CSTU5, 2, 1)
+ FIELD(TSTR_5, CST5, 0, 3)
+REG8(TCNTCMPCLR_5, 54)
+ FIELD(TCNTCMPCLR_5, CMPCLRW5, 0, 1)
+ FIELD(TCNTCMPCLR_5, CMPCLRV5, 1, 1)
+ FIELD(TCNTCMPCLR_5, CMPCLRU5, 2, 1)
+ FIELD(TCNTCMPCLR_5, CMPCLR5, 0, 3)
+
+REG8(TCR, 1)
+ FIELD(TCR, TPSC, 0, 3)
+ FIELD(TCR, CKEG, 3, 2)
+ FIELD(TCR, CCLR, 5, 3)
+REG8(TMDR, 2)
+ FIELD(TMDR, MD, 0, 4)
+ FIELD(TMDR, BFA, 4, 1)
+ FIELD(TMDR, BFB, 5, 1)
+ FIELD(TMDR, BFE, 6, 1)
+REG16(TIOR, 3)
+ FIELD(TIOR, IOA, 0, 3)
+ FIELD(TIOR, IOB, 4, 3)
+ FIELD(TIOR, IOC, 8, 3)
+ FIELD(TIOR, IOD, 12, 3)
+REG8(TIOR5, 4)
+ FIELD(TIOR5, IOC, 0, 4)
+REG8(TCNTCMPCLR, 5)
+ FIELD(TCNTCMPCLR, CMPCLR5W, 0, 1)
+ FIELD(TCNTCMPCLR, CMPCLR5V, 1, 1)
+ FIELD(TCNTCMPCLR, CMPCLR5U, 2, 1)
+ FIELD(TCNTCMPCLR, CMPCLR5, 0, 3)
+REG8(TIER, 6)
+ FIELD(TIER, TGIEA, 0, 1)
+ FIELD(TIER, TGIEB, 1, 1)
+ FIELD(TIER, TGIEC, 2, 1)
+ FIELD(TIER, TGIED, 3, 1)
+ FIELD(TIER, TGIE, 0, 4)
+ FIELD(TIER, TCIEV, 4, 1)
+ FIELD(TIER, TCIEU, 5, 1)
+ FIELD(TIER, TTGE2, 6, 1)
+ FIELD(TIER, TTGE, 7, 1)
+REG8(TIER2, 7)
+ FIELD(TIER2, TGIEE, 0, 1)
+ FIELD(TIER2, TGIEF, 1, 1)
+ FIELD(TIER2, TGIE, 0, 2)
+REG8(TSR, 8)
+ FIELD(TSR, TCFD, 7, 1)
+REG8(TBTM, 9)
+ FIELD(TBTM, TTSA, 0, 1)
+ FIELD(TBTM, TTSB, 1, 1)
+ FIELD(TBTM, TTSE, 2, 1)
+REG8(TICCR, 10)
+ FIELD(TICCR, I1AE, 0, 1)
+ FIELD(TICCR, I1BE, 1, 1)
+ FIELD(TICCR, I2AE, 2, 1)
+ FIELD(TICCR, I2BE, 3, 1)
+REG16(TADCR, 11)
+ FIELD(TADCR, ITB4VE, 0, 1)
+ FIELD(TADCR, ITB3AE, 1, 1)
+ FIELD(TADCR, ITA4VE, 2, 1)
+ FIELD(TADCR, ITA3AE, 3, 1)
+ FIELD(TADCR, DT4BE, 4, 1)
+ FIELD(TADCR, UT4BE, 5, 1)
+ FIELD(TADCR, DT4AE, 6, 1)
+ FIELD(TADCR, UT4AE, 7, 1)
+ FIELD(TADCR, BF, 0, 1)
+REG16(TCNT, 12)
+REG16(TGRA, 13)
+REG16(TGRB, 14)
+REG16(TGRC, 15)
+REG16(TGRD, 16)
+REG16(TGRE, 17)
+REG16(TGRF, 18)
+REG8(TIORH, 19)
+REG8(TIORL, 20)
+REG16(TADCOBRA, 21)
+REG16(TADCOBRB, 22)
+REG16(TADCORA, 23)
+REG16(TADCORB, 24)
+
+static const int div_rate[6][8] = {
+ [0] = {1, 4, 16, 64, 0, 0, 0, 0, },
+ [1] = {1, 4, 16, 64, 0, 0, 256, 0, },
+ [2] = {1, 4, 16, 64, 0, 0, 0, 1024, },
+ [3] = {1, 4, 16, 64, 256, 1024, 0, 0, },
+ [4] = {1, 4, 16, 64, 256, 1024, 0, 0, },
+ [5] = {1, 4, 16, 64, 0, 0, 0, 0, },
+};
+
+static bool is_cascade(RenesasMTU2State *mtu)
+{
+ if (mtu == NULL) {
+ return false;
+ }
+ if (FIELD_EX8(mtu->r[1].tcr, TCR, TPSC) != 7 ||
+ mtu->r[2].ier) {
+ return false;
+ }
+ return true;
+}
+
+static void mtu2_event(void *opaque);
+static void set_next_event(RenesasMTURegs *r)
+{
+ int gr;
+ int64_t next;
+ uint32_t wcnt;
+ int ch = r->ch;
+ RenesasMTU2State *mtu = r->mtu;
+
+ if (ch == 1 && is_cascade(mtu)) {
+ /* If cascade count mode, skip ch1 event */
+ return;
+ }
+ if (r->start) {
+ if (ch != 2 || !is_cascade(mtu)) {
+ /* normal counter */
+ r->next_cnt = 0x10000;
+ for (gr = 0; gr < r->num_gr; gr++) {
+ if (r->tcnt <= r->tgr[gr]) {
+ r->next_cnt = MIN(r->next_cnt, r->tgr[gr] + 1);
+ }
+ }
+ next = (r->next_cnt - r->tcnt) * r->clk;
+ g_assert(next > 0);
+ } else {
+ /* 32bit freerun counter */
+ wcnt = mtu->r[2].tcnt;
+ wcnt = deposit32(wcnt, 16, 16, mtu->r[1].tcnt);
+ next = (0x100000000LL - wcnt) * r->clk;
+ }
+ g_assert(next > 0);
+ r->next = r->base + next;
+ if (r->timer == NULL) {
+ r->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+ mtu2_event, r);
+ }
+ timer_mod(r->timer, r->next);
+ } else {
+ if (r->timer) {
+ timer_del(r->timer);
+ }
+ }
+}
+
+static void mtu2_5_event(void *opaque);
+static void set_next_event5(RenesasMTURegs *r)
+{
+ int64_t next;
+
+ r->next_cnt = r->cntclr ? r->tgr[0] : 0x10000;
+ if (r->start) {
+ next = (r->next_cnt - r->tcnt) * r->clk;
+ g_assert(next > 0);
+ r->next = r->base + next;
+ if (r->timer == NULL) {
+ r->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+ mtu2_5_event, r);
+ }
+ timer_mod(r->timer, r->next);
+ } else {
+ if (r->timer) {
+ timer_del(r->timer);
+ }
+ }
+}
+
+static void tgr_match(RenesasMTURegs *r, int clr_gr)
+{
+ int gr;
+
+ for (gr = 0; gr < r->num_gr; gr++) {
+ if (r->next_cnt == r->tgr[gr]) {
+ /* TGR match */
+ if (clr_gr == gr) {
+ r->tcnt = 0;
+ } else {
+ r->tcnt = r->next_cnt;
+ }
+ if (extract16(r->tier, (gr < 4 ? gr : gr + 4), 1)) {
+ qemu_irq_pulse(r->irq[gr]);
+ }
+ }
+ }
+}
+
+static int clr_gr(uint8_t tcr, int ch)
+{
+ switch (FIELD_EX8(tcr, TCR, CCLR)) {
+ case 1:
+ return 0;
+ case 2:
+ return 1;
+ case 5:
+ return 2;
+ case 6:
+ return 3;
+ default:
+ return -1;
+ }
+}
+
+static void mtu2_event(void *opaque)
+{
+ RenesasMTURegs *r = opaque;
+ RenesasMTU2State *mtu = r->mtu;
+ uint32_t sync;
+ int ch;
+
+ if (r->ch != 2 || !is_cascade(mtu)) {
+ tgr_match(r, clr_gr(r->tcr, r->ch));
+ if (r->next_cnt == 0x10000) {
+ /* Count overflow */
+ r->tcnt = 0;
+ r->base = r->next;
+ if (FIELD_EX16(r->tier, TIER, TCIEV)) {
+ qemu_irq_pulse(r->irq[r->num_gr]);
+ }
+ if (r->ch == 2 && FIELD_EX8(mtu->r[1].tcr, TCR, TPSC) == 7) {
+ mtu->r[1].tcnt++;
+ tgr_match(&mtu->r[1], clr_gr(mtu->r[1].tcr, 1));
+ if (mtu->r[1].tcnt >= 0x10000) {
+ mtu->r[1].tcnt = 0;
+ if (FIELD_EX16(mtu->r[1].tier, TIER, TCIEV)) {
+ qemu_irq_pulse(mtu->r[1].irq[mtu->r[1].num_gr]);
+ }
+ }
+ }
+ }
+ } else {
+ r->tcnt = 0;
+ mtu->r[1].tcnt = 0;
+ r->base = r->next;
+ if (FIELD_EX16(mtu->r[1].tier, TIER, TCIEV)) {
+ qemu_irq_pulse(mtu->r[1].irq[mtu->r[1].num_gr]);
+ }
+ }
+ set_next_event(r);
+ if (r->tcnt == 0) {
+ sync = extract8(mtu->tsyr, 0, 3);
+ sync = deposit32(sync, 3, 2, extract8(mtu->tsyr, 6, 2));
+ if (extract32(sync, r->ch, 1)) {
+ /* Syncronus clear */
+ for (ch = 0; ch < 5; ch++) {
+ if (ch == r->ch || !extract8(sync, ch, 1)) {
+ continue;
+ }
+ if ((FIELD_EX8(mtu->r[ch].tcr, TCR, CCLR) & 3) == 3) {
+ mtu->r[ch].tcnt = 0;
+ set_next_event(&mtu->r[ch]);
+ }
+ }
+ }
+ }
+}
+
+static void mtu2_5_event(void *opaque)
+{
+ RenesasMTURegs *r = opaque;
+
+ if (r->next_cnt < 0x10000) {
+ if (r->ier) {
+ qemu_irq_pulse(r->irq[0]);
+ }
+ if (r->cntclr) {
+ r->tcnt = 0;
+ r->base = r->next;
+ }
+ } else {
+ r->tcnt = 0;
+ r->base = r->next;
+ }
+ set_next_event5(r);
+}
+
+static uint16_t read_tcnt(RenesasMTURegs *r)
+{
+ int64_t now;
+ uint32_t wcnt;
+
+ if (r->start) {
+ now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ if (!is_cascade(r->mtu)) {
+ if (r->ch == 1 && FIELD_EX8(r->mtu->r[1].tcr, TCR, TPSC) == 7) {
+ return r->tcnt;
+ } else {
+ return (r->tcnt + (now - r->base) / r->clk) & 0xffff;
+ }
+ } else {
+ wcnt = r->mtu->r[2].tcnt;
+ wcnt = deposit32(wcnt, 16, 16, r->mtu->r[1].tcnt);
+ wcnt += (now - r->mtu->r[2].base) / r->mtu->r[2].clk;
+ switch (r->ch) {
+ case 1:
+ return extract32(wcnt, 16, 16);
+ case 2:
+ return extract32(wcnt, 0, 16);
+ default:
+ g_assert_not_reached();
+ }
+ }
+ } else {
+ return r->tcnt;
+ }
+}
+
+static void mtu_pck_update(void *opaque)
+{
+ RenesasMTU2State *mtu = RenesasMTU2(opaque);
+ int ch;
+ for (ch = 0; ch < 5; ch++) {
+ mtu->r[ch].tcnt = read_tcnt(&mtu->r[ch]);
+ }
+ for (ch = 0; ch < 3; ch++) {
+ mtu->r5[ch].tcnt = read_tcnt(&mtu->r5[ch]);
+ }
+ mtu->input_freq = clock_get_hz(mtu->pck);
+ if (clock_is_enabled(mtu->pck)) {
+ for (ch = 0; ch < 5; ch++) {
+ set_next_event(&mtu->r[ch]);
+ }
+ for (ch = 0; ch < 3; ch++) {
+ set_next_event5(&mtu->r5[ch]);
+ }
+ } else {
+ for (ch = 0; ch < 5; ch++) {
+ if (mtu->r[ch].timer) {
+ timer_del(mtu->r[ch].timer);
+ }
+ }
+ for (ch = 0; ch < 3; ch++) {
+ if (mtu->r5[ch].timer) {
+ timer_del(mtu->r5[ch].timer);
+ }
+ }
+ }
+}
+
+static bool mtu2_low_valid_size(hwaddr addr, unsigned size)
+{
+ if ((A_TCNT_012 <= addr && addr < (A_TGRD_012 + 2)) ||
+ (A_TGRE_0 <= addr && addr < (A_TGRF_0 + 2))) {
+ if (size == 2) {
+ return true;
+ }
+ } else {
+ if (size == 1) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static uint64_t mtu2_low_read(void *opaque, hwaddr addr, unsigned size)
+{
+ RenesasMTU2State *mtu = RenesasMTU2(opaque);
+ int gr;
+ int ch = (addr >> 7) & 3;
+ addr &= 0x7f;
+
+ if (!mtu2_low_valid_size(addr, size)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "renesas_mtu: Invalid access size %d of 0x%"
+ HWADDR_PRIX "\n", size, addr);
+ return UINT64_MAX;
+ }
+ if (!clock_is_enabled(mtu->pck)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "renesas_mtu: Unit %d is stopped.\n", mtu->unit);
+ return UINT64_MAX;
+ }
+ switch (addr) {
+ case A_TCR_012:
+ return mtu->r[ch].tcr;
+ case A_TMDR_012:
+ return mtu->r[ch].tmdr;
+ case A_TIORL_012:
+ return extract16(mtu->r[ch].tior, 0, 8);
+ case A_TIORH_012:
+ return extract16(mtu->r[ch].tior, 8, 8);
+ case A_TIER_012:
+ return extract16(mtu->r[ch].tier, 0, 8);
+ case A_TIER2_0:
+ if (ch == 0) {
+ return extract16(mtu->r[ch].tier, 8, 8);
+ } else {
+ goto no_register;
+ }
+ case A_TSR_012:
+ return mtu->r[ch].tsr;
+ case A_TBTM_0:
+ if (ch == 0) {
+ return mtu->tbtm;
+ } else {
+ goto no_register;
+ }
+ case A_TICCR_1:
+ if (ch == 1) {
+ return mtu->ticcr;
+ } else {
+ goto no_register;
+ }
+ case A_TCNT_012:
+ return read_tcnt(&mtu->r[ch]);
+ case A_TGRA_012:
+ case A_TGRB_012:
+ case A_TGRC_012:
+ case A_TGRD_012:
+ gr = ((addr - A_TGRA_012) >> 1) & 3;
+ if (gr < mtu->r[ch].num_gr) {
+ return mtu->r[ch].tgr[gr];
+ } else {
+ goto no_register;
+ }
+ case A_TGRE_0:
+ case A_TGRF_0:
+ if (ch == 0) {
+ gr = (((addr - A_TGRE_0) >> 1) & 2) + 4;
+ return mtu->r[0].tgr[gr];
+ } else {
+ goto no_register;
+ }
+ no_register:
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "renesas_mtu: Unknown register %08lx\n",
+ addr);
+ return UINT64_MAX;
+ }
+}
+
+static bool mtu2_high_valid_size(hwaddr addr, unsigned size)
+{
+ if ((A_TCNT_3 <= addr && addr < (A_TGRD_4 + 2)) ||
+ (A_TADCR <= addr && addr < (A_TADCORB + 2)) ||
+ (A_TCDR <= addr && addr < (A_TCBR + 2))) {
+ if (size == 2) {
+ return true;
+ }
+ } else {
+ if (size == 1) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static uint64_t mtu2_high_read(void *opaque, hwaddr addr, unsigned size)
+{
+ RenesasMTU2State *mtu = RenesasMTU2(opaque);
+ int ch = 3 + (addr & 1);
+ int ch_w = 3 + ((addr >> 1) & 1);
+ uint32_t ret;
+
+ if (!mtu2_high_valid_size(addr, size)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "renesas_mtu: Invalid access size %d\n",
+ size);
+ return UINT64_MAX;
+ }
+ if (addr < 0x20 && ((mtu->trwer & 1) == 0)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "renesas_mtu: register read protected "
+ "0x%" HWADDR_PRIX "\n", addr);
+ return UINT64_MAX;
+ }
+ if (!clock_is_enabled(mtu->pck)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "renesas_mtu: Unit %d is stopped.\n", mtu->unit);
+ return UINT64_MAX;
+ }
+ switch (addr) {
+ case A_TCR_3:
+ case A_TCR_4:
+ return mtu->r[ch].tcr;
+ case A_TMDR_3:
+ case A_TMDR_4:
+ return mtu->r[ch].tmdr;
+ case A_TIORL_3:
+ case A_TIORL_4:
+ return extract32(mtu->r[ch_w].tior, 0, 8);
+ case A_TIORH_3:
+ case A_TIORH_4:
+ return extract32(mtu->r[ch_w].tior, 8, 8);
+ case A_TIER_3:
+ case A_TIER_4:
+ return mtu->r[ch].tier;
+ case A_TSR_3:
+ case A_TSR_4:
+ return mtu->r[ch].tsr;
+ case A_TCNT_3:
+ case A_TCNT_4:
+ return read_tcnt(&mtu->r[ch]);
+ case A_TGRA_3:
+ case A_TGRB_3:
+ case A_TGRA_4:
+ case A_TGRB_4:
+ return mtu->r[3 + ((addr >> 2) & 1)].tgr[(addr >> 1) & 1];
+ case A_TGRC_3:
+ case A_TGRD_3:
+ case A_TGRC_4:
+ case A_TGRD_4:
+ return mtu->r[3 + ((addr >> 2) & 1)].tgr[2 + ((addr >> 1) & 1)];
+ case A_TADCR_4:
+ return mtu->tadcr;
+ case A_TADCOBRA_4:
+ case A_TADCOBRB_4:
+ return mtu->tadcobr[(addr >> 1) & 1];
+ case A_TADCORA_4:
+ case A_TADCORB_4:
+ return mtu->tadcor[(addr >> 1) & 1];
+ case A_TOER:
+ return mtu->toer;
+ case A_TGCR:
+ return mtu->tgcr;
+ case A_TOCR1:
+ case A_TOCR2:
+ return mtu->tocr[addr & 1];
+ case A_TCDR:
+ return mtu->tcdr;
+ case A_TDDR:
+ return mtu->tddr;
+ case A_TCNTS:
+ return mtu->tcnts;
+ case A_TCBR:
+ return mtu->tcbr;
+ case A_TITCR:
+ return mtu->titcr;
+ case A_TITCNT:
+ return mtu->titcnt;
+ case A_TBTER:
+ return mtu->tbter;
+ case A_TDER:
+ return mtu->tder;
+ case A_TOLBR:
+ return mtu->tolbr;
+ case A_TWCR:
+ return mtu->twcr;
+ case A_TSTR:
+ ret = 0;
+ for (ch = 0; ch < 5; ch++) {
+ ret = deposit32(ret, (ch < 3 ? ch : ch + 3), 1, mtu->r[ch].start);
+ }
+ return ret;
+ case A_TSYR:
+ return mtu->tsyr;
+ case A_TRWER:
+ mtu->trwer_r = mtu->trwer;
+ return mtu->trwer;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "renesas_mtu: Unknown register 0x%" HWADDR_PRIX "\n",
+ addr);
+ return UINT64_MAX;
+ }
+}
+
+static bool mtu2_5_valid_size(hwaddr addr, unsigned size)
+{
+ if (addr < A_TIER_5) {
+ addr &= 0x0f;
+ if (addr < A_TCRU_5) {
+ if (size == 2) {
+ return true;
+ }
+ } else {
+ if (size == 1) {
+ return true;
+ }
+ }
+ } else {
+ if (size == 1) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static uint64_t mtu2_5_read(void *opaque, hwaddr addr, unsigned size)
+{
+ RenesasMTU2State *mtu = RenesasMTU2(opaque);
+ int ch;
+ uint32_t ret;
+ ch = addr >> 4;
+ if (!mtu2_5_valid_size(addr, size)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "renesas_mtu: Invalid access size at "
+ "0x%" HWADDR_PRIX "\n", addr);
+ }
+ if (!clock_is_enabled(mtu->pck)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "renesas_mtu: Unit %d is stopped.\n", mtu->unit);
+ return UINT64_MAX;
+ }
+ if (ch < 3) {
+ switch (addr & 0x0f) {
+ case A_TCNTU_5:
+ return read_tcnt(&mtu->r5[ch]);
+ case A_TGRU_5:
+ return mtu->r5[ch].tgr[0];
+ case A_TCRU_5:
+ return mtu->r5[ch].tcr;
+ case A_TIORU_5:
+ return mtu->r5[ch].tior;
+ }
+ } else {
+ switch (addr) {
+ case A_TIER_5:
+ ret = 0;
+ for (ch = 0; ch < 3; ch++) {
+ ret = deposit32(ret, ch, 1, mtu->r5[ch].ier);
+ }
+ return ret;
+ case A_TSTR_5:
+ ret = 0;
+ for (ch = 0; ch < 3; ch++) {
+ ret = deposit32(ret, ch, 1, mtu->r5[ch].start);
+ }
+ return ret;
+ case A_TCNTCMPCLR_5:
+ ret = 0;
+ for (ch = 0; ch < 3; ch++) {
+ ret = deposit32(ret, ch, 1, mtu->r5[ch].cntclr);
+ }
+ return ret;
+ }
+ }
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "renesas_mtu: Unknown register "
+ "0x%" HWADDR_PRIX "\n", addr);
+ return UINT64_MAX;
+}
+
+static bool is_ext_clock(int ch, int tcr)
+{
+ int tpsc = FIELD_EX8(tcr, TCR, TPSC);
+ if (ch == 1 && tcr == 7) {
+ return false;
+ } else {
+ return div_rate[ch][tpsc] == 0;
+ }
+}
+
+static void set_cnt_clock(int64_t input_freq, RenesasMTURegs *r)
+{
+ int tpsc = FIELD_EX8(r->tcr, TCR, TPSC);
+ int ckeg = FIELD_EX8(r->tcr, TCR, CKEG);
+ int div = div_rate[r->ch][tpsc];
+ int64_t clk;
+
+ if (div >= 4 && ckeg >= 2) {
+ div /= 2;
+ }
+ if (div > 0) {
+ clk = NANOSECONDS_PER_SECOND / input_freq;
+ r->clk = clk * div;
+ }
+}
+
+#define NOT_SUPPORT_REG_VAL(val, name) \
+ if (val != 0) { \
+ qemu_log_mask(LOG_UNIMP, \
+ "renesas_mtu: " #name " %02x is not supported.\n", \
+ (uint8_t)val); \
+ }
+
+static void mtu2_low_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ RenesasMTU2State *mtu = RenesasMTU2(opaque);
+ int ch = (addr >> 7) & 3;
+ addr &= 0x7f;
+ if (!mtu2_low_valid_size(addr, size)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "renesas_mtu: Invalid access size %d of "
+ "0x%" HWADDR_PRIX "\n", size, addr);
+ return;
+ }
+ if (!clock_is_enabled(mtu->pck)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "renesas_mtu: Unit %d is stopped.\n", mtu->unit);
+ return;
+ }
+
+ switch (addr) {
+ case A_TCR_012:
+ if (mtu->r[ch].start) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "renesas_mtu: CH %d is already started.\n", ch);
+ }
+ if (is_ext_clock(ch, val)) {
+ qemu_log_mask(LOG_UNIMP,
+ "renesas_mtu: External clock not supported.\n");
+ }
+ mtu->r[ch].tcr = val;
+ set_cnt_clock(mtu->input_freq, &mtu->r[ch]);
+ set_next_event(&mtu->r[ch]);
+ break;
+ case A_TMDR_012:
+ mtu->r[ch].tmdr = val;
+ break;
+ case A_TIORL_012:
+ mtu->r[ch].tior = deposit32(mtu->r[ch].tior, 0, 8, val);
+ NOT_SUPPORT_REG_VAL(val, TIORL);
+ break;
+ case A_TIORH_012:
+ mtu->r[ch].tior = deposit32(mtu->r[ch].tior, 8, 8, val);
+ NOT_SUPPORT_REG_VAL(val, TIORH);
+ break;
+ case A_TIER_012:
+ mtu->r[ch].tier = deposit32(mtu->r[ch].tier, 0, 8, val);
+ break;
+ case A_TIER2_0:
+ if (ch == 0) {
+ mtu->r[ch].tier = deposit32(mtu->r[ch].tior, 8, 8, val);
+ } else {
+ goto no_register;
+ }
+ break;
+ case A_TSR_012:
+ mtu->r[ch].tsr = deposit32(mtu->r[ch].tsr, 6, 1, extract32(val, 6, 1));
+ break;
+ case A_TBTM_0:
+ if (ch == 0) {
+ mtu->tbtm = val;
+ break;
+ } else {
+ goto no_register;
+ }
+ case A_TICCR_1:
+ if (ch == 1) {
+ mtu->ticcr = val;
+ break;
+ } else {
+ goto no_register;
+ }
+ case A_TCNT_012:
+ mtu->r[ch].tcnt = val;
+ if (mtu->r[ch].start) {
+ mtu->r[ch].base = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ }
+ set_next_event(&mtu->r[ch]);
+ break;
+ case A_TGRA_012:
+ case A_TGRB_012:
+ case A_TGRC_012:
+ case A_TGRD_012:
+ mtu->r[ch].tgr[((addr - A_TGRA_012) >> 1) & 3] = val;
+ set_next_event(&mtu->r[ch]);
+ break;
+ case A_TGRE_0:
+ case A_TGRF_0:
+ if (ch == 0) {
+ mtu->r[ch].tgr[(((addr - A_TGRE_0) >> 1) & 2) + 4] = val;
+ set_next_event(&mtu->r[ch]);
+ break;
+ } else {
+ goto no_register;
+ }
+ default:
+ no_register:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "renesas_mtu: Unknown register 0x%" HWADDR_PRIX "\n",
+ addr);
+ break;
+ }
+}
+
+static void mtu2_high_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ RenesasMTU2State *mtu = RenesasMTU2(opaque);
+ int ch = 3 + (addr & 1);
+ int ch_w = 3 + ((addr >> 1) & 1);
+ int64_t now;
+
+ if (!mtu2_high_valid_size(addr, size)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "renesas_mtu: Invalid access size %d\n",
+ size);
+ return;
+ }
+ if (addr < 0x20 && ((mtu->trwer & 1) == 0)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "renesas_mtu: register write protected "
+ "0x%" HWADDR_PRIX "\n", addr);
+ return;
+ }
+ if (!clock_is_enabled(mtu->pck)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "renesas_mtu: Unit %d is stopped.\n", mtu->unit);
+ return;
+ }
+
+ switch (addr) {
+ case A_TCR_3:
+ case A_TCR_4:
+ if (mtu->r[ch].start) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "renesas_mtu: CH %d is already started.\n", ch);
+ }
+ if (is_ext_clock(ch, val)) {
+ qemu_log_mask(LOG_UNIMP,
+ "renesas_mtu: External clock not supported.\n");
+ }
+ mtu->r[ch].tcr = val;
+ set_cnt_clock(mtu->input_freq, &mtu->r[ch]);
+ set_next_event(&mtu->r[ch]);
+ break;
+ case A_TMDR_3:
+ case A_TMDR_4:
+ mtu->r[ch].tmdr = val;
+ NOT_SUPPORT_REG_VAL(val, TMDR);
+ break;
+ case A_TIORL_3:
+ case A_TIORL_4:
+ mtu->r[ch_w].tior = deposit32(mtu->r[ch_w].tior, 0, 8, val);
+ NOT_SUPPORT_REG_VAL(val, TIORL);
+ break;
+ case A_TIORH_3:
+ case A_TIORH_4:
+ mtu->r[ch_w].tior = deposit32(mtu->r[ch_w].tior, 8, 8, val);
+ NOT_SUPPORT_REG_VAL(val, TIORH);
+ break;
+ case A_TIER_3:
+ case A_TIER_4:
+ mtu->r[ch].tier = val;
+ set_next_event(&mtu->r[ch]);
+ break;
+ case A_TSR_3:
+ case A_TSR_4:
+ mtu->r[ch].tsr = val;
+ break;
+ case A_TCNT_3:
+ case A_TCNT_4:
+ mtu->r[ch_w].tcnt = val;
+ if (mtu->r[ch].start) {
+ mtu->r[ch].base = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ }
+ set_next_event(&mtu->r[ch]);
+ break;
+ case A_TGRA_3:
+ case A_TGRA_4:
+ case A_TGRB_3:
+ case A_TGRB_4:
+ mtu->r[3 + ((addr >> 2) & 1)].tgr[(addr >> 1) & 1] = val;
+ set_next_event(&mtu->r[3 + ((addr >> 2) & 1)]);
+ break;
+ case A_TGRC_3:
+ case A_TGRD_3:
+ case A_TGRC_4:
+ case A_TGRD_4:
+ mtu->r[3 + ((addr >> 2) & 1)].tgr[2 + ((addr >> 1) & 1)] = val;
+ set_next_event(&mtu->r[3 + ((addr >> 2) & 1)]);
+ break;
+ case A_TADCR_4:
+ mtu->tadcr = val;
+ NOT_SUPPORT_REG_VAL(val, TADCR);
+ break;
+ case A_TADCOBRA_4:
+ case A_TADCOBRB_4:
+ mtu->tadcobr[(addr >> 1) & 1] = val;
+ case A_TADCORA_4:
+ case A_TADCORB_4:
+ mtu->tadcor[(addr >> 1) & 1] = val;
+ case A_TOER:
+ mtu->toer = val;
+ break;
+ case A_TGCR:
+ mtu->tgcr = val;
+ break;
+ case A_TOCR1:
+ case A_TOCR2:
+ mtu->tocr[addr & 1] = val;
+ break;
+ case A_TCDR:
+ mtu->tcdr = val;
+ break;
+ case A_TDDR:
+ mtu->tddr = val;
+ break;
+ case A_TCNTS:
+ mtu->tcnts = val;
+ break;
+ case A_TCBR:
+ mtu->tcbr = val;
+ break;
+ case A_TITCR:
+ mtu->titcr = val;
+ break;
+ case A_TITCNT:
+ mtu->titcnt = val;
+ break;
+ case A_TBTER:
+ mtu->tbter = val;
+ break;
+ case A_TDER:
+ mtu->tder = val;
+ break;
+ case A_TOLBR:
+ mtu->tolbr = val;
+ break;
+ case A_TWCR:
+ mtu->twcr = val;
+ break;
+ case A_TSTR:
+ val = deposit64(val, 3, 2, extract64(val, 6, 2));
+ now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ for (ch = 0; ch < 5; ch++) {
+ if (mtu->r[ch].start != extract32(val, ch, 1)) {
+ mtu->r[ch].start = extract32(val, ch, 1);
+ if (mtu->r[ch].start) {
+ mtu->r[ch].base = now;
+ }
+ set_next_event(&mtu->r[ch]);
+ }
+ }
+ break;
+ case A_TSYR:
+ mtu->tsyr = val;
+ break;
+ case A_TRWER:
+ if (mtu->trwer_r) {
+ mtu->trwer = FIELD_DP8(mtu->trwer, TRWER, RWE,
+ FIELD_EX8(val, TRWER, RWE));
+ mtu->trwer_r = 0;
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "renesas_mtu: TRWER protected.\n");
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "renesas_mtu: Unknown register "
+ "0x%" HWADDR_PRIX "\n", addr);
+ break;
+ }
+}
+
+static void mtu2_5_write(void *opaque, hwaddr addr, uint64_t val, unsigned
size)
+{
+ RenesasMTU2State *mtu = RenesasMTU2(opaque);
+ int ch;
+ int64_t now;
+
+ ch = addr >> 4;
+ if (!mtu2_5_valid_size(addr, size)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "renesas_mtu: Invalid access size at "
+ "0x%" HWADDR_PRIX "\n", addr);
+ return;
+ }
+ if (!clock_is_enabled(mtu->pck)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "renesas_mtu: Unit %d is stopped.\n", mtu->unit);
+ return;
+ }
+ if (ch < 3) {
+ switch (addr & 0x0f) {
+ case A_TCNTU_5:
+ mtu->r5[ch].tcnt = val;
+ set_next_event5(&mtu->r5[ch]);
+ break;
+ case A_TGRU_5:
+ mtu->r5[ch].tgr[0] = val;
+ set_next_event5(&mtu->r5[ch]);
+ break;
+ case A_TCRU_5:
+ mtu->r5[ch].tcr = val;
+ set_next_event5(&mtu->r5[ch]);
+ break;
+ case A_TIORU_5:
+ mtu->r5[ch].tior = val;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "renesas_mtu: Unknown register 0x%"
+ HWADDR_PRIX "\n", addr);
+ break;
+ }
+ } else {
+ switch (addr & 0xff) {
+ case A_TIER_5:
+ for (ch = 0; ch < 3; ch++) {
+ mtu->r5[ch].ier = extract64(val, ch, 1);
+ }
+ break;
+ case A_TSTR_5:
+ now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ for (ch = 0; ch < 3; ch++) {
+ if (mtu->r5[ch].start != extract64(val, ch, 1)) {
+ mtu->r5[ch].start = extract64(val, ch, 1);
+ if (mtu->r5[ch].start) {
+ mtu->r5[ch].base = now;
+ }
+ set_next_event5(&mtu->r5[ch]);
+ }
+ }
+ break;
+ case A_TCNTCMPCLR_5:
+ for (ch = 0; ch < 3; ch++) {
+ if (mtu->r5[ch].cntclr != extract64(val, ch, 1)) {
+ mtu->r5[ch].cntclr = extract64(val, ch, 1);
+ set_next_event5(&mtu->r5[ch]);
+ }
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "renesas_mtu: Unknown register %08lx\n",
+ addr);
+ break;
+ }
+ }
+}
+
+static const MemoryRegionOps mtu2_low_ops = {
+ .write = mtu2_low_write,
+ .read = mtu2_low_read,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 2,
+ },
+};
+
+static const MemoryRegionOps mtu2_high_ops = {
+ .write = mtu2_high_write,
+ .read = mtu2_high_read,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 2,
+ },
+};
+
+static const MemoryRegionOps mtu2_5_ops = {
+ .write = mtu2_5_write,
+ .read = mtu2_5_read,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 2,
+ },
+};
+
+static void mtu_reg_init(int channel, RenesasMTU2State *mtu, RenesasMTURegs *r)
+{
+ int grn;
+ static const int gr[] = {6, 2, 2, 4, 4};
+ r->ch = channel;
+ r->mtu = mtu;
+ r->tsr = 0xc0;
+ r->num_gr = gr[channel];
+ for (grn = 0; grn < r->num_gr; grn++) {
+ r->tgr[grn] = 0xffff;
+ }
+}
+
+static void mtu2_realize(DeviceState *dev, Error **errp)
+{
+ int ch;
+ RenesasMTU2State *mtu = RenesasMTU2(dev);
+
+ for (ch = 0; ch < 5; ch++) {
+ mtu_reg_init(ch, mtu, &mtu->r[ch]);
+ if (clock_is_enabled(mtu->pck)) {
+ set_cnt_clock(mtu->input_freq, &mtu->r[ch]);
+ }
+ }
+ for (ch = 0; ch < 3; ch++) {
+ mtu->r5[ch].mtu = NULL;
+ mtu->r5[ch].tgr[0] = 0xffff;
+ if (clock_is_enabled(mtu->pck)) {
+ set_cnt_clock(mtu->input_freq, &mtu->r5[ch]);
+ }
+ }
+ mtu->ticcr = 0x00;
+ mtu->toer = 0xc0;
+ mtu->tgcr = 0x80;
+ mtu->tcdr = mtu->tddr = 0xffff;
+ mtu->tcbr = 0xffff;
+ mtu->tder = 0x01;
+ mtu->trwer = 0x01;
+}
+
+static void mtu2_init(Object *obj)
+{
+ int ch, irq;
+ static int nr_irq[] = {7, 4, 4, 5, 5};
+ SysBusDevice *d = SYS_BUS_DEVICE(obj);
+ RenesasMTU2State *mtu = RenesasMTU2(obj);
+
+ memory_region_init_io(&mtu->memory[0], OBJECT(mtu), &mtu2_low_ops,
+ mtu, "renesas-mtu2-low", 0x180);
+ sysbus_init_mmio(d, &mtu->memory[0]);
+ memory_region_init_io(&mtu->memory[1], OBJECT(mtu), &mtu2_high_ops,
+ mtu, "renesas-mtu2-high", 0x90);
+ sysbus_init_mmio(d, &mtu->memory[1]);
+ memory_region_init_io(&mtu->memory[2], OBJECT(mtu), &mtu2_5_ops,
+ mtu, "renesas-mtu2-5", 0x40);
+ sysbus_init_mmio(d, &mtu->memory[2]);
+ for (ch = 0; ch < 5; ch++) {
+ for (irq = 0; irq < nr_irq[ch]; irq++) {
+ sysbus_init_irq(d, &mtu->r[ch].irq[irq]);
+ }
+ }
+ for (ch = 0; ch < 3; ch++) {
+ sysbus_init_irq(d, &mtu->r5[ch].irq[0]);
+ }
+ mtu->pck = qdev_init_clock_in(DEVICE(d), "pck",
+ mtu_pck_update, mtu);
+}
+
+static Property mtu_properties[] = {
+ DEFINE_PROP_UINT32("unit", RenesasMTU2State, unit, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void mtu2_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = mtu2_realize;
+ device_class_set_props(dc, mtu_properties);
+}
+
+static const TypeInfo renesas_mtu_info = {
+ .name = TYPE_RENESAS_MTU2,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(RenesasMTU2State),
+ .instance_init = mtu2_init,
+ .class_init = mtu2_class_init,
+ .class_size = sizeof(RenesasMTU2Class),
+};
+
+static void mtu_register_types(void)
+{
+ type_register_static(&renesas_mtu_info);
+}
+
+type_init(mtu_register_types)
diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig
index 4d21b50ab0..b4553d7847 100644
--- a/hw/timer/Kconfig
+++ b/hw/timer/Kconfig
@@ -45,3 +45,5 @@ config AVR_TIMER16
config RENESAS_TIMER
bool
+config RENESAS_MTU
+ bool
diff --git a/hw/timer/meson.build b/hw/timer/meson.build
index 6aed6d1e5f..4d16a59c02 100644
--- a/hw/timer/meson.build
+++ b/hw/timer/meson.build
@@ -10,6 +10,7 @@ softmmu_ss.add(when: 'CONFIG_CMSDK_APB_DUALTIMER', if_true:
files('cmsdk-apb-dua
softmmu_ss.add(when: 'CONFIG_CMSDK_APB_TIMER', if_true:
files('cmsdk-apb-timer.c'))
softmmu_ss.add(when: 'CONFIG_RENESAS_TMR8', if_true: files('renesas_tmr8.c'))
softmmu_ss.add(when: 'CONFIG_RENESAS_TIMER', if_true: files('renesas_timer.c'))
+softmmu_ss.add(when: 'CONFIG_RENESAS_MTU', if_true: files('renesas_mtu.c'))
softmmu_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic-timer.c'))
softmmu_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_timer.c'))
softmmu_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_mct.c'))
--
2.20.1
- [PATCH 09/20] hw/timer: Remove renesas_cmt., (continued)
- [PATCH 09/20] hw/timer: Remove renesas_cmt., Yoshinori Sato, 2020/08/27
- [PATCH 01/20] loader.c: Add support Motrola S-record format., Yoshinori Sato, 2020/08/27
- [PATCH 10/20] hw/rx: Convert to renesas_timer, Yoshinori Sato, 2020/08/27
- [PATCH 17/20] hw/rx/rx62n: Add Ethernet support., Yoshinori Sato, 2020/08/27
- [PATCH 08/20] hw/timer: Renesas TMU/CMT module., Yoshinori Sato, 2020/08/27
- [PATCH 04/20] hw/rx: New firmware loader., Yoshinori Sato, 2020/08/27
- [PATCH 05/20] hw/rx: Add RX62N Clock generator, Yoshinori Sato, 2020/08/27
- [PATCH 06/20] hw/timer: Renesas 8bit timer emulation., Yoshinori Sato, 2020/08/27
- [PATCH 19/20] hw/rx: Add CQ-FRK-RX62N target, Yoshinori Sato, 2020/08/27
- [PATCH 14/20] hw/rx/rx62n: RX62N Add MTU module, Yoshinori Sato, 2020/08/27
- [PATCH 13/20] hw/timer: Add Renesas MTU2,
Yoshinori Sato <=
- [PATCH 16/20] hw/net: Add Renesas On-chip Ethernet MAC, Yoshinori Sato, 2020/08/27
- [PATCH 18/20] hw/rx: Add Tokudenkairo TKDN-RX62N-BRD, Yoshinori Sato, 2020/08/27
- [PATCH 11/20] hw/char: Renesas SCI module., Yoshinori Sato, 2020/08/27
- [PATCH 12/20] hw/rx/rx62n: Use New SCI module., Yoshinori Sato, 2020/08/27
- [PATCH 20/20] MAINTAINERS: Update RX entry, Yoshinori Sato, 2020/08/27
- [PATCH 15/20] hw/net: Add generic Bit-bang MDIO PHY., Yoshinori Sato, 2020/08/27
- Re: [PATCH 00/20] RX target update, Philippe Mathieu-Daudé, 2020/08/31