All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/11] Unified peripheral emulation for Renesas chips
@ 2021-05-27  5:21 Yoshinori Sato
  2021-05-27  5:21 ` [PATCH 01/11] hw/char: Renesas SCI module Yoshinori Sato
                   ` (11 more replies)
  0 siblings, 12 replies; 21+ messages in thread
From: Yoshinori Sato @ 2021-05-27  5:21 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

Renesas SH4 and RX have similar peripherals.
Integrate the emulation code for these peripherals.

Migrate peripherals.
SH4 sh_timer(TMU) -> renesas_timer
SH4 sh_serial(SCI/SCIF) -> renesas_sci
RX renesas_cmt(CMT) -> renesas_timer
RX renesas_sci(SCIa) -> renesas_sci

I want to process the peripheral clocks of SH4 and RX in the same way,
so I added a new clock generator. This will generate a peripheral clock
from the master clock.

Yoshinori Sato (11):
  hw/char: Renesas SCI module.
  hw/char: remove sh_serial.
  hw/timer: Renesas TMU/CMT module.
  hw/timer: Remove sh_timer.
  hw/timer: Remove renesas_cmt.
  hw/rx: Add RX62N Clock generator
  hw/timer: Renesas 8bit timer.
  hw/rx: rx62n use new hw modules.
  hw/sh4: sh7750 Add CPG.
  hw/sh4: sh7750 use new hw modules.
  hw/rx: rx-gdbsim Add bootstrup for linux

 include/hw/char/renesas_sci.h    |  129 +++-
 include/hw/rx/rx62n-cpg.h        |   72 +++
 include/hw/rx/rx62n.h            |   34 +-
 include/hw/sh4/sh.h              |    8 -
 include/hw/sh4/sh7751-cpg.h      |   94 +++
 include/hw/timer/renesas_cmt.h   |   43 --
 include/hw/timer/renesas_timer.h |   96 +++
 include/hw/timer/renesas_tmr.h   |   58 --
 include/hw/timer/renesas_tmr8.h  |   67 ++
 include/hw/timer/tmu012.h        |   23 -
 hw/char/renesas_sci.c            | 1039 +++++++++++++++++++++++++-----
 hw/char/sh_serial.c              |  431 -------------
 hw/rx/rx-gdbsim.c                |   89 +--
 hw/rx/rx62n-cpg.c                |  344 ++++++++++
 hw/rx/rx62n.c                    |   64 +-
 hw/sh4/sh7750.c                  |  110 +++-
 hw/sh4/sh7751-cpg.c              |  457 +++++++++++++
 hw/timer/renesas_cmt.c           |  283 --------
 hw/timer/renesas_timer.c         |  644 ++++++++++++++++++
 hw/timer/renesas_tmr.c           |  493 --------------
 hw/timer/renesas_tmr8.c          |  540 ++++++++++++++++
 hw/timer/sh_timer.c              |  368 -----------
 MAINTAINERS                      |    8 +-
 hw/char/Kconfig                  |    3 -
 hw/char/meson.build              |    1 -
 hw/rx/Kconfig                    |    4 +-
 hw/rx/meson.build                |    2 +-
 hw/sh4/Kconfig                   |    4 +-
 hw/sh4/meson.build               |    1 +
 hw/timer/Kconfig                 |   12 +-
 hw/timer/meson.build             |    5 +-
 31 files changed, 3501 insertions(+), 2025 deletions(-)
 create mode 100644 include/hw/rx/rx62n-cpg.h
 create mode 100644 include/hw/sh4/sh7751-cpg.h
 delete mode 100644 include/hw/timer/renesas_cmt.h
 create mode 100644 include/hw/timer/renesas_timer.h
 delete mode 100644 include/hw/timer/renesas_tmr.h
 create mode 100644 include/hw/timer/renesas_tmr8.h
 delete mode 100644 include/hw/timer/tmu012.h
 delete mode 100644 hw/char/sh_serial.c
 create mode 100644 hw/rx/rx62n-cpg.c
 create mode 100644 hw/sh4/sh7751-cpg.c
 delete mode 100644 hw/timer/renesas_cmt.c
 create mode 100644 hw/timer/renesas_timer.c
 delete mode 100644 hw/timer/renesas_tmr.c
 create mode 100644 hw/timer/renesas_tmr8.c
 delete mode 100644 hw/timer/sh_timer.c

-- 
2.20.1



^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH 01/11] hw/char: Renesas SCI module.
  2021-05-27  5:21 [PATCH 00/11] Unified peripheral emulation for Renesas chips Yoshinori Sato
@ 2021-05-27  5:21 ` Yoshinori Sato
  2021-06-04  9:09   ` Peter Maydell
  2021-05-27  5:21 ` [PATCH 02/11] hw/char: remove sh_serial Yoshinori Sato
                   ` (10 subsequent siblings)
  11 siblings, 1 reply; 21+ messages in thread
From: Yoshinori Sato @ 2021-05-27  5:21 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

This module supported SCI / SCIa / SCIF.

Hardware manual.
SCI / SCIF
https://www.renesas.com/us/en/doc/products/mpumcu/001/r01uh0457ej0401_sh7751.pdf
SCIa
https://www.renesas.com/us/en/doc/products/mpumcu/doc/rx_family/r01uh0033ej0140_rx62n.pdf

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 include/hw/char/renesas_sci.h |  129 +++-
 hw/char/renesas_sci.c         | 1039 +++++++++++++++++++++++++++------
 2 files changed, 966 insertions(+), 202 deletions(-)

diff --git a/include/hw/char/renesas_sci.h b/include/hw/char/renesas_sci.h
index a4764e3eee..ae9554db60 100644
--- a/include/hw/char/renesas_sci.h
+++ b/include/hw/char/renesas_sci.h
@@ -1,54 +1,137 @@
 /*
  * Renesas Serial Communication Interface
  *
- * Copyright (c) 2018 Yoshinori Sato
+ * Copyright (c) 2020 Yoshinori Sato
+ *
+ * This code is licensed under the GPL version 2 or later.
  *
- * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
-#ifndef HW_CHAR_RENESAS_SCI_H
-#define HW_CHAR_RENESAS_SCI_H
-
 #include "chardev/char-fe.h"
+#include "qemu/timer.h"
+#include "qemu/fifo8.h"
 #include "hw/sysbus.h"
 #include "qom/object.h"
 
+#define TYPE_RENESAS_SCI_BASE "renesas-sci-base"
+OBJECT_DECLARE_TYPE(RenesasSCIBaseState, RenesasSCIBaseClass,
+                    RENESAS_SCI_BASE)
 #define TYPE_RENESAS_SCI "renesas-sci"
-typedef struct RSCIState RSCIState;
-DECLARE_INSTANCE_CHECKER(RSCIState, RSCI,
-                         TYPE_RENESAS_SCI)
+OBJECT_DECLARE_TYPE(RenesasSCIState, RenesasSCIClass,
+                    RENESAS_SCI)
+#define TYPE_RENESAS_SCIA "renesas-scia"
+OBJECT_DECLARE_TYPE(RenesasSCIAState, RenesasSCIAClass,
+                    RENESAS_SCIA)
+#define TYPE_RENESAS_SCIF "renesas-scif"
+OBJECT_DECLARE_TYPE(RenesasSCIFState, RenesasSCIFClass,
+                    RENESAS_SCIF)
 
 enum {
     ERI = 0,
     RXI = 1,
     TXI = 2,
-    TEI = 3,
-    SCI_NR_IRQ = 4
+    BRI_TEI = 3,
+    SCI_NR_IRQ = 4,
+};
+
+enum {
+    RXTOUT,
+    RXNEXT,
+    TXEMPTY,
+    TXEND,
+    NR_SCI_EVENT,
 };
 
-struct RSCIState {
+enum {
+    SCI_REGSIZE_8 = 0,
+    SCI_REGSIZE_16 = 1,
+    SCI_REGSIZE_32 = 2,
+};
+
+typedef struct RenesasSCIBaseState {
     /*< private >*/
     SysBusDevice parent_obj;
-    /*< public >*/
-
     MemoryRegion memory;
-    QEMUTimer timer;
-    CharBackend chr;
-    qemu_irq irq[SCI_NR_IRQ];
+    MemoryRegion memory_p4;
+    MemoryRegion memory_a7;
 
+    /* SCI register */
     uint8_t smr;
     uint8_t brr;
     uint8_t scr;
     uint8_t tdr;
-    uint8_t ssr;
-    uint8_t rdr;
-    uint8_t scmr;
-    uint8_t semr;
+    uint16_t Xsr;
 
-    uint8_t read_ssr;
+    /* internal use */
+    uint16_t read_Xsr;
+    int64_t etu;
     int64_t trtime;
-    int64_t rx_next;
+    int64_t tx_start_time;
+    struct {
+        int64_t time;
+        int64_t (*handler)(struct RenesasSCIBaseState *sci);
+    } event[NR_SCI_EVENT];
+    QEMUTimer *event_timer;
+    CharBackend chr;
     uint64_t input_freq;
+    qemu_irq irq[SCI_NR_IRQ];
+    Fifo8 rxfifo;
+    int regshift;
+    uint32_t unit;
+    Clock *pck;
+} RenesasSCIBaseState;
+
+struct RenesasSCIState {
+    RenesasSCIBaseState parent_obj;
+
+    /* SCI specific register */
+    uint8_t sptr;
 };
 
-#endif
+struct RenesasSCIAState {
+    RenesasSCIBaseState parent_obj;
+
+    /* SCIa specific register */
+    uint8_t scmr;
+    uint8_t semr;
+};
+
+struct RenesasSCIFState {
+    RenesasSCIBaseState parent_obj;
+
+    /* SCIF specific register */
+    uint16_t fcr;
+    uint16_t sptr;
+    uint16_t lsr;
+
+    /* private */
+    uint16_t read_lsr;
+    int tdcnt;
+};
+
+typedef struct RenesasSCIBaseClass {
+    SysBusDeviceClass parent;
+
+    const struct MemoryRegionOps *ops;
+    void (*irq_fn)(struct RenesasSCIBaseState *sci, int request);
+    int (*divrate)(struct RenesasSCIBaseState *sci);
+} RenesasSCIBaseClass;
+
+typedef struct RenesasSCIClass {
+    RenesasSCIBaseClass parent;
+
+    void (*p_irq_fn)(struct RenesasSCIBaseState *sci, int request);
+} RenesasSCIClass;
+
+typedef struct RenesasSCIAClass {
+    RenesasSCIBaseClass parent;
+
+    void (*p_irq_fn)(struct RenesasSCIBaseState *sci, int request);
+    int (*p_divrate)(struct RenesasSCIBaseState *sci);
+} RenesasSCIAClass;
+
+typedef struct RenesasSCIFClass {
+    RenesasSCIBaseClass parent;
+
+    void (*p_irq_fn)(struct RenesasSCIBaseState *sci, int request);
+} RenesasSCIFClass;
diff --git a/hw/char/renesas_sci.c b/hw/char/renesas_sci.c
index 1c63467290..f8ab00362a 100644
--- a/hw/char/renesas_sci.c
+++ b/hw/char/renesas_sci.c
@@ -1,10 +1,12 @@
 /*
- * Renesas Serial Communication Interface
+ * Renesas Serial Communication Interface (SCI / SCIa / SCIF)
  *
  * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
  *            (Rev.1.40 R01UH0033EJ0140)
+ *        And SH7751 Group, SH7751R Group User's Manual: Hardware
+ *            (Rev.4.01 R01UH0457EJ0401)
  *
- * Copyright (c) 2019 Yoshinori Sato
+ * Copyright (c) 2020 Yoshinori Sato
  *
  * SPDX-License-Identifier: GPL-2.0-or-later
  *
@@ -23,15 +25,25 @@
 
 #include "qemu/osdep.h"
 #include "qemu/log.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "hw/hw.h"
 #include "hw/irq.h"
+#include "hw/sysbus.h"
 #include "hw/registerfields.h"
-#include "hw/qdev-properties.h"
 #include "hw/qdev-properties-system.h"
+#include "hw/qdev-clock.h"
 #include "hw/char/renesas_sci.h"
 #include "migration/vmstate.h"
+#include "qemu/error-report.h"
 
-/* SCI register map */
-REG8(SMR, 0)
+/*
+ * SCI register map
+ * SCI(a) register size all 8bit.
+ * SCIF regsister size 8bit and 16bit.
+ * Allocate 16bit to match the larger one.
+ */
+REG32(SMR, 0) /* 8bit */
   FIELD(SMR, CKS,  0, 2)
   FIELD(SMR, MP,   2, 1)
   FIELD(SMR, STOP, 3, 1)
@@ -39,263 +51,840 @@ REG8(SMR, 0)
   FIELD(SMR, PE,   5, 1)
   FIELD(SMR, CHR,  6, 1)
   FIELD(SMR, CM,   7, 1)
-REG8(BRR, 1)
-REG8(SCR, 2)
-  FIELD(SCR, CKE,  0, 2)
+REG32(BRR, 4) /* 8bit */
+REG32(SCR, 8)
+  FIELD(SCR, CKE, 0, 2)
   FIELD(SCR, TEIE, 2, 1)
   FIELD(SCR, MPIE, 3, 1)
+  FIELD(SCR, REIE, 3, 1)
   FIELD(SCR, RE,   4, 1)
   FIELD(SCR, TE,   5, 1)
   FIELD(SCR, RIE,  6, 1)
   FIELD(SCR, TIE,  7, 1)
-REG8(TDR, 3)
-REG8(SSR, 4)
+REG32(TDR, 12) /* 8bit */
+REG32(SSR, 16) /* 8bit */
   FIELD(SSR, MPBT, 0, 1)
   FIELD(SSR, MPB,  1, 1)
   FIELD(SSR, TEND, 2, 1)
-  FIELD(SSR, ERR,  3, 3)
+  FIELD(SSR, ERR, 3, 3)
     FIELD(SSR, PER,  3, 1)
     FIELD(SSR, FER,  4, 1)
     FIELD(SSR, ORER, 5, 1)
   FIELD(SSR, RDRF, 6, 1)
   FIELD(SSR, TDRE, 7, 1)
-REG8(RDR, 5)
-REG8(SCMR, 6)
+REG32(FSR, 16)
+  FIELD(FSR, DR, 0, 1)
+  FIELD(FSR, RDF, 1, 1)
+  FIELD(FSR, RDF_DR, 0, 2)
+  FIELD(FSR, PER, 2, 1)
+  FIELD(FSR, FER, 3, 1)
+  FIELD(FSR, BRK, 4, 1)
+  FIELD(FSR, TDFE, 5, 1)
+  FIELD(FSR, TEND, 6, 1)
+  FIELD(FSR, ER, 7, 1)
+  FIELD(FSR, FERn, 8, 4)
+  FIELD(FSR, PERn, 12, 4)
+REG32(RDR, 20) /* 8bit */
+REG32(SCMR, 24) /* 8bit */
   FIELD(SCMR, SMIF, 0, 1)
   FIELD(SCMR, SINV, 2, 1)
   FIELD(SCMR, SDIR, 3, 1)
   FIELD(SCMR, BCP2, 7, 1)
-REG8(SEMR, 7)
+REG32(FCR, 24)
+  FIELD(FCR, LOOP, 0, 1)
+  FIELD(FCR, RFRST, 1, 1)
+  FIELD(FCR, TFRST, 2, 1)
+  FIELD(FCR, MCE, 3, 1)
+  FIELD(FCR, TTRG, 4, 2)
+  FIELD(FCR, RTRG, 6, 2)
+  FIELD(FCR, RSTRG, 8, 3)
+REG32(SEMR, 28) /* 8bit */
   FIELD(SEMR, ACS0, 0, 1)
   FIELD(SEMR, ABCS, 4, 1)
+REG32(FDR, 28)
+  FIELD(FDR, Rn, 0, 4)
+  FIELD(FDR, Tn, 8, 4)
+REG32(SPTR, 32)
+  FIELD(SPTR, SPB2DT, 0, 1)
+  FIELD(SPTR, SPB2IO, 1, 1)
+  FIELD(SPTR, SCKDT, 2, 1)
+  FIELD(SPTR, SCKIO, 3, 1)
+  FIELD(SPTR, CTSDT, 4, 1)
+  FIELD(SPTR, CTSIO, 5, 1)
+  FIELD(SPTR, RTSDT, 6, 1)
+  FIELD(SPTR, RTSIO, 7, 1)
+  FIELD(SPTR, EIO, 7, 1)
+REG32(LSR, 36)
+  FIELD(LSR, ORER, 0, 1)
+
+#define SCIF_FIFO_DEPTH 16
 
-static int can_receive(void *opaque)
+static const int sci_rtrg[] = {1, 4, 8, 14};
+
+static void update_event_time(RenesasSCIBaseState *sci, int evt, int64_t t)
 {
-    RSCIState *sci = RSCI(opaque);
-    if (sci->rx_next > qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) {
-        return 0;
+    if (t > 0) {
+        t +=  qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+        sci->event[evt].time = t;
+        if (timer_expire_time_ns(sci->event_timer) > t) {
+            timer_mod(sci->event_timer, t);
+        }
     } else {
-        return FIELD_EX8(sci->scr, SCR, RE);
+        sci->event[evt].time = 0;
+    }
+}
+
+static void sci_irq(RenesasSCIBaseState *sci_common, int req)
+{
+    int irq = 0;
+    int rie;
+    int tie;
+    RenesasSCIState *sci = RENESAS_SCI(sci_common);
+
+    rie = FIELD_EX16(sci_common->scr, SCR, RIE);
+    tie = FIELD_EX16(sci_common->scr, SCR, TIE);
+    switch (req) {
+    case ERI:
+        irq = rie && (FIELD_EX16(sci_common->Xsr, SSR, ERR) != 0);
+        break;
+    case RXI:
+        irq = FIELD_EX16(sci_common->Xsr, SSR, RDRF) && rie  &&
+            !FIELD_EX16(sci->sptr, SPTR, EIO);
+        break;
+    case TXI:
+        irq = FIELD_EX16(sci_common->Xsr, SSR, TDRE) && tie;
+        break;
+    case BRI_TEI:
+        irq = FIELD_EX16(sci_common->Xsr, SSR, TEND) &&
+            FIELD_EX16(sci_common->scr, SCR, TEIE);
+        break;
     }
+    qemu_set_irq(sci_common->irq[req], irq);
 }
 
-static void receive(void *opaque, const uint8_t *buf, int size)
+static void scia_irq(RenesasSCIBaseState *sci, int req)
 {
-    RSCIState *sci = RSCI(opaque);
-    sci->rx_next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + sci->trtime;
-    if (FIELD_EX8(sci->ssr, SSR, RDRF) || size > 1) {
-        sci->ssr = FIELD_DP8(sci->ssr, SSR, ORER, 1);
-        if (FIELD_EX8(sci->scr, SCR, RIE)) {
-            qemu_set_irq(sci->irq[ERI], 1);
+    int irq = 0;
+    int rie;
+    int tie;
+
+    rie = FIELD_EX16(sci->scr, SCR, RIE);
+    tie = FIELD_EX16(sci->scr, SCR, TIE);
+    switch (req) {
+    case ERI:
+        irq = (FIELD_EX16(sci->Xsr, SSR, ERR) != 0) && rie;
+        qemu_set_irq(sci->irq[req], irq);
+        break;
+    case RXI:
+        if (FIELD_EX16(sci->Xsr, SSR, RDRF) && rie) {
+            qemu_irq_pulse(sci->irq[req]);
         }
-    } else {
-        sci->rdr = buf[0];
-        sci->ssr = FIELD_DP8(sci->ssr, SSR, RDRF, 1);
-        if (FIELD_EX8(sci->scr, SCR, RIE)) {
-            qemu_irq_pulse(sci->irq[RXI]);
+        break;
+    case TXI:
+        if (FIELD_EX16(sci->Xsr, SSR, TDRE) && tie) {
+            qemu_irq_pulse(sci->irq[req]);
+        }
+        break;
+    case BRI_TEI:
+        irq = FIELD_EX16(sci->Xsr, SSR, TEND) &&
+            FIELD_EX16(sci->scr, SCR, TEIE);
+        qemu_set_irq(sci->irq[req], irq);
+        break;
+    }
+}
+
+static void scif_irq(RenesasSCIBaseState *sci, int req)
+{
+    int irq = 0;
+    int rie;
+    int reie;
+    int tie;
+
+    rie = FIELD_EX16(sci->scr, SCR, RIE);
+    reie = FIELD_EX16(sci->scr, SCR, REIE);
+    tie = FIELD_EX16(sci->scr, SCR, TIE);
+    switch (req) {
+    case ERI:
+        irq = (rie || reie) && FIELD_EX16(sci->Xsr, FSR, ER);
+        break;
+    case RXI:
+        irq = (FIELD_EX16(sci->Xsr, FSR, RDF_DR) != 0) && rie;
+        break;
+    case TXI:
+        irq = FIELD_EX16(sci->Xsr, FSR, TDFE) & tie;
+        break;
+    case BRI_TEI:
+        irq = (rie || reie) && FIELD_EX16(sci->Xsr, FSR, BRK);
+        break;
+    }
+    qemu_set_irq(sci->irq[req], irq);
+}
+
+static int sci_can_receive(void *opaque)
+{
+    RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque);
+    int fifo_free = 0;
+    if (clock_is_enabled(sci->pck) && FIELD_EX16(sci->scr, SCR, RE)) {
+        /* Receiver enabled */
+        fifo_free = fifo8_num_free(&sci->rxfifo);
+    }
+    return fifo_free;
+}
+
+static void sci_receive(void *opaque, const uint8_t *buf, int size)
+{
+    RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque);
+    RenesasSCIBaseClass *rc = RENESAS_SCI_BASE_GET_CLASS(sci);
+    fifo8_push_all(&sci->rxfifo, buf, size);
+    if (sci->event[RXNEXT].time == 0) {
+        sci->Xsr = FIELD_DP16(sci->Xsr, SSR, RDRF, 1);
+        update_event_time(sci, RXNEXT, sci->trtime);
+        rc->irq_fn(sci, RXI);
+    }
+}
+
+static int scif_can_receive(void *opaque)
+{
+    RenesasSCIFState *scif = RENESAS_SCIF(opaque);
+    RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque);
+    int fifo_free = 0;
+    if (clock_is_enabled(sci->pck) && FIELD_EX16(sci->scr, SCR, RE)) {
+        /* Receiver enabled */
+        fifo_free = fifo8_num_free(&sci->rxfifo);
+        if (fifo_free == 0) {
+            /* FIFO overrun */
+            scif->lsr = FIELD_DP16(scif->lsr, LSR, ORER, 1);
+            scif_irq(sci, ERI);
+        }
+    }
+    return fifo_free;
+}
+
+static void scif_receive(void *opaque, const uint8_t *buf, int size)
+{
+    RenesasSCIFState *scif = RENESAS_SCIF(opaque);
+    RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque);
+    int rtrg;
+
+    fifo8_push_all(&sci->rxfifo, buf, size);
+    if (sci->event[RXNEXT].time == 0) {
+        rtrg = sci_rtrg[FIELD_EX16(scif->fcr, FCR, RTRG)];
+        if (fifo8_num_used(&sci->rxfifo) >= rtrg) {
+            sci->Xsr = FIELD_DP16(sci->Xsr, FSR, RDF, 1);
+        } else {
+            update_event_time(sci, RXTOUT, 15 * sci->etu);
         }
+        scif_irq(sci, RXI);
     }
 }
 
-static void send_byte(RSCIState *sci)
+static void sci_send_byte(RenesasSCIBaseState *sci)
 {
     if (qemu_chr_fe_backend_connected(&sci->chr)) {
         qemu_chr_fe_write_all(&sci->chr, &sci->tdr, 1);
     }
-    timer_mod(&sci->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + sci->trtime);
-    sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 0);
-    sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 1);
-    qemu_set_irq(sci->irq[TEI], 0);
-    if (FIELD_EX8(sci->scr, SCR, TIE)) {
-        qemu_irq_pulse(sci->irq[TXI]);
+    sci->Xsr = FIELD_DP16(sci->Xsr, SSR, TEND, 0);
+    sci->Xsr = FIELD_DP16(sci->Xsr, SSR, TDRE, 1);
+}
+
+static int transmit_byte(RenesasSCIFState *scif)
+{
+    RenesasSCIBaseState *sci = RENESAS_SCI_BASE(scif);
+    int64_t elapsed;
+    int byte = 0;
+    if (sci->tx_start_time > 0) {
+        elapsed = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - sci->tx_start_time;
+        byte = elapsed / sci->trtime;
+        if (byte > scif->tdcnt) {
+            byte = scif->tdcnt;
+        }
     }
+    return byte;
 }
 
-static void txend(void *opaque)
+static int64_t scif_rx_timeout(RenesasSCIBaseState *sci)
 {
-    RSCIState *sci = RSCI(opaque);
-    if (!FIELD_EX8(sci->ssr, SSR, TDRE)) {
-        send_byte(sci);
+    sci->Xsr = FIELD_DP16(sci->Xsr, FSR, DR, 1);
+    scif_irq(sci, RXI);
+    return 0;
+}
+
+static int64_t sci_rx_next(RenesasSCIBaseState *sci)
+{
+    int64_t next_event = 0;
+    RenesasSCIBaseClass *rc = RENESAS_SCI_BASE_GET_CLASS(sci);
+    if (FIELD_EX16(sci->Xsr, SSR, RDRF)) {
+        /* Receiver overrun */
+        sci->Xsr = FIELD_DP16(sci->Xsr, SSR, ORER, 1);
+        rc->irq_fn(sci, ERI);
     } else {
-        sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 1);
-        if (FIELD_EX8(sci->scr, SCR, TEIE)) {
-            qemu_set_irq(sci->irq[TEI], 1);
+        if (!fifo8_is_empty(&sci->rxfifo)) {
+            /* set next event */
+            sci->Xsr = FIELD_DP16(sci->Xsr, SSR, RDRF, 1);
+            rc->irq_fn(sci, RXI);
+            next_event = sci->trtime;
         }
     }
+    return next_event;
 }
 
-static void update_trtime(RSCIState *sci)
+static int64_t sci_tx_empty(RenesasSCIBaseState *sci)
 {
-    /* char per bits */
-    sci->trtime = 8 - FIELD_EX8(sci->smr, SMR, CHR);
-    sci->trtime += FIELD_EX8(sci->smr, SMR, PE);
-    sci->trtime += FIELD_EX8(sci->smr, SMR, STOP) + 1;
-    /* x bit transmit time (32 * divrate * brr) / base freq */
-    sci->trtime *= 32 * sci->brr;
-    sci->trtime *= 1 << (2 * FIELD_EX8(sci->smr, SMR, CKS));
-    sci->trtime *= NANOSECONDS_PER_SECOND;
-    sci->trtime /= sci->input_freq;
+    int64_t ret = 0;
+    RenesasSCIBaseClass *rc = RENESAS_SCI_BASE_GET_CLASS(sci);
+    if (!FIELD_EX16(sci->Xsr, SSR, TDRE)) {
+        sci_send_byte(sci);
+        ret = sci->trtime;
+        rc->irq_fn(sci, TXI);
+    } else {
+        sci->Xsr = FIELD_DP16(sci->Xsr, SSR, TEND, 1);
+        rc->irq_fn(sci, BRI_TEI);
+    }
+    return ret;
 }
 
-static bool sci_is_tr_enabled(RSCIState *sci)
+static int64_t scif_tx_empty(RenesasSCIBaseState *sci)
 {
-    return FIELD_EX8(sci->scr, SCR, TE) || FIELD_EX8(sci->scr, SCR, RE);
+    RenesasSCIFState *scif = RENESAS_SCIF(sci);
+    scif->tdcnt -= transmit_byte(scif);
+    sci->Xsr = FIELD_DP16(sci->Xsr, FSR, TDFE, 1);
+    scif_irq(sci, TXI);
+    return 0;
 }
 
-static void sci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
+static int64_t scif_tx_end(RenesasSCIBaseState *sci)
 {
-    RSCIState *sci = RSCI(opaque);
+    RenesasSCIFState *scif = RENESAS_SCIF(sci);
+    scif->tdcnt = 0;
+    sci->Xsr = FIELD_DP16(sci->Xsr, FSR, TEND, 1);
+    return 0;
+}
 
-    switch (offset) {
-    case A_SMR:
-        if (!sci_is_tr_enabled(sci)) {
-            sci->smr = val;
-            update_trtime(sci);
+static void sci_timer_event(void *opaque)
+{
+    RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque);
+    int64_t now, next, t;
+    int i;
+
+    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+    next = INT64_MAX;
+    for (i = 0; i < NR_SCI_EVENT; i++) {
+        if (sci->event[i].time > 0 && sci->event[i].time <= now) {
+            t = sci->event[i].handler(sci);
+            sci->event[i].time = (t > 0) ? now + t : 0;
         }
-        break;
-    case A_BRR:
-        if (!sci_is_tr_enabled(sci)) {
-            sci->brr = val;
-            update_trtime(sci);
+        if (sci->event[i].time > 0) {
+            next = MIN(next, sci->event[i].time);
         }
-        break;
+    }
+    if (next < INT64_MAX) {
+        timer_mod(sci->event_timer, next);
+    } else {
+        timer_del(sci->event_timer);
+    }
+}
+
+static int static_divrate(RenesasSCIBaseState *sci)
+{
+    /* SCI / SCIF have static divide rate */
+    return 32;
+}
+
+static int scia_divrate(RenesasSCIBaseState *sci)
+{
+    /*
+     * SEMR.ABCS = 0 -> 32
+     * SEMR.ABCS = 1 -> 16
+     */
+    RenesasSCIAState *scia = RENESAS_SCIA(sci);
+    return 16 * (2 - FIELD_EX8(scia->semr, SEMR, ABCS));
+}
+
+static void update_trtime(RenesasSCIBaseState *sci)
+{
+    RenesasSCIBaseClass *rc = RENESAS_SCI_BASE_GET_CLASS(sci);
+    int cks = 1 << (2 * FIELD_EX16(sci->smr, SMR, CKS));
+    if (sci->input_freq > 0) {
+        /* x bit transmit time (divrate * brr) / base freq */
+        sci->etu = rc->divrate(sci) * cks;
+        sci->etu *= sci->brr + 1;
+        sci->etu *= NANOSECONDS_PER_SECOND;
+        sci->etu /= sci->input_freq;
+
+        /* char per bits */
+        sci->trtime = 8 - FIELD_EX16(sci->smr, SMR, CHR);
+        sci->trtime += FIELD_EX16(sci->smr, SMR, PE);
+        sci->trtime += FIELD_EX16(sci->smr, SMR, STOP) + 1 + 1;
+        sci->trtime *= sci->etu;
+    }
+}
+
+static void sci_pck_update(void *opaque, ClockEvent evt)
+{
+    RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque);
+
+    sci->input_freq = clock_get_hz(sci->pck);
+    update_trtime(sci);
+}
+
+#define IS_TR_ENABLED(scr) \
+    (FIELD_EX16(scr, SCR, TE) || FIELD_EX16(scr, SCR, RE))
+
+static hwaddr map_address(RenesasSCIBaseState *sci, hwaddr addr)
+{
+    return addr << (2 - sci->regshift);
+}
+
+static void sci_common_write(void *opaque, hwaddr addr,
+                             uint64_t val, unsigned size)
+{
+    RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque);
+    RenesasSCIBaseClass *rc = RENESAS_SCI_BASE_GET_CLASS(opaque);
+    switch (addr) {
     case A_SCR:
         sci->scr = val;
-        if (FIELD_EX8(sci->scr, SCR, TE)) {
-            sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 1);
-            sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 1);
-            if (FIELD_EX8(sci->scr, SCR, TIE)) {
-                qemu_irq_pulse(sci->irq[TXI]);
-            }
+        if (FIELD_EX16(sci->scr, SCR, TE)) {
+            /* Transmitter enable */
+            sci->Xsr = FIELD_DP16(sci->Xsr, SSR, TDRE, 1);
+            sci->Xsr = FIELD_DP16(sci->Xsr, SSR, TEND, 1);
+            rc->irq_fn(sci, TXI);
+            rc->irq_fn(sci, BRI_TEI);
+        } else {
+            /* Transmitter disable  */
+            update_event_time(sci, TXEND, 0);
+            update_event_time(sci, TXEMPTY, 0);
+        }
+        break;
+    case A_SMR:
+        sci->smr = val;
+        update_trtime(sci);
+        break;
+    case A_BRR:
+        sci->brr = val;
+        update_trtime(sci);
+        break;
+    default:
+        qemu_log_mask(LOG_UNIMP, "renesas_sci: Register 0x%" HWADDR_PRIX
+                      " not implemented\n", addr);
+    }
+}
+
+static void sci_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque);
+    RenesasSCIBaseClass *rc = RENESAS_SCI_BASE_GET_CLASS(sci);
+    bool tx_start;
+
+    if (!clock_is_enabled(sci->pck)) {
+        qemu_log_mask(LOG_GUEST_ERROR, "renesas_sci: SCI %d is stopped.\n",
+                      sci->unit);
+        return ;
+    }
+    addr = map_address(sci, addr);
+    switch (addr) {
+    case A_TDR:
+        sci->tdr = val;
+        break;
+    case A_SSR:
+        /* Mask for read only bits */
+        sci->Xsr = FIELD_DP16(RENESAS_SCI_BASE(sci)->Xsr, SSR, MPBT,
+                              FIELD_EX16(val, SSR, MPBT));
+        sci->Xsr &= (val | 0x07);
+        /* Clear ERI */
+        rc->irq_fn(sci, ERI);
+        tx_start = FIELD_EX16(sci->read_Xsr, SSR, TDRE) &&
+            !FIELD_EX16(sci->Xsr, SSR, TDRE) &&
+            (FIELD_EX16(sci->Xsr, SSR, ERR) == 0);
+        if (tx_start) {
+            sci_send_byte(sci);
+            update_event_time(sci, TXEMPTY, sci->trtime);
+            rc->irq_fn(sci, TXI);
         }
-        if (!FIELD_EX8(sci->scr, SCR, TEIE)) {
-            qemu_set_irq(sci->irq[TEI], 0);
+        break;
+    case A_SPTR:
+        RENESAS_SCI(sci)->sptr = val;
+        break;
+    default:
+        sci_common_write(sci, addr, val, size);
+    }
+}
+
+static void scia_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque);
+    RenesasSCIAState *scia = RENESAS_SCIA(opaque);
+
+    if (!clock_is_enabled(sci->pck)) {
+        qemu_log_mask(LOG_GUEST_ERROR, "renesas_sci: SCIa %d is stopped.\n",
+                      sci->unit);
+        return ;
+    }
+    addr = map_address(sci, addr);
+    switch (addr) {
+    case A_SMR:
+        if (IS_TR_ENABLED(sci->scr)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "reneas_sci: SMR write protected.\n");
+        } else {
+            sci_common_write(sci, addr, val, size);
         }
-        if (!FIELD_EX8(sci->scr, SCR, RIE)) {
-            qemu_set_irq(sci->irq[ERI], 0);
+        break;
+    case A_BRR:
+        if (IS_TR_ENABLED(sci->scr)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "reneas_sci: BRR write protected.\n");
+            break;
+        } else {
+            sci_common_write(sci, addr, val, size);
         }
         break;
     case A_TDR:
         sci->tdr = val;
-        if (FIELD_EX8(sci->ssr, SSR, TEND)) {
-            send_byte(sci);
+        if (FIELD_EX16(sci->Xsr, SSR, TEND)) {
+            update_event_time(sci, TXEMPTY, sci->trtime);
+            sci_send_byte(sci);
         } else {
-            sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 0);
+            sci->Xsr = FIELD_DP16(sci->Xsr, SSR, TDRE, 0);
         }
+        scia_irq(sci, TXI);
+        scia_irq(sci, BRI_TEI);
         break;
     case A_SSR:
-        sci->ssr = FIELD_DP8(sci->ssr, SSR, MPBT,
-                             FIELD_EX8(val, SSR, MPBT));
-        sci->ssr = FIELD_DP8(sci->ssr, SSR, ERR,
-                             FIELD_EX8(val, SSR, ERR) & 0x07);
-        if (FIELD_EX8(sci->read_ssr, SSR, ERR) &&
-            FIELD_EX8(sci->ssr, SSR, ERR) == 0) {
-            qemu_set_irq(sci->irq[ERI], 0);
-        }
-        break;
-    case A_RDR:
-        qemu_log_mask(LOG_GUEST_ERROR, "reneas_sci: RDR is read only.\n");
+        /* Mask for read only bits */
+        sci->Xsr = FIELD_DP16(sci->Xsr, SSR, MPBT,
+                              FIELD_EX16(val, SSR, MPBT));
+        sci->Xsr &= (val | 0xc7);
+        /* Clear ERI */
+        scia_irq(sci, ERI);
         break;
     case A_SCMR:
-        sci->scmr = val; break;
-    case A_SEMR: /* SEMR */
-        sci->semr = val; break;
+        scia->scmr = val;
+        break;
+    case A_SEMR:
+        scia->semr = val;
+        break;
     default:
-        qemu_log_mask(LOG_UNIMP, "renesas_sci: Register 0x%" HWADDR_PRIX " "
-                                 "not implemented\n",
-                      offset);
+        sci_common_write(sci, addr, val, size);
+        break;
     }
 }
 
-static uint64_t sci_read(void *opaque, hwaddr offset, unsigned size)
+static void scif_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
 {
-    RSCIState *sci = RSCI(opaque);
+    RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque);
+    RenesasSCIFState *scif = RENESAS_SCIF(opaque);
+    int txtrg;
+    int rxtrg;
+    uint16_t ssr_mask;
+    uint8_t txd;
+
+    if (!clock_is_enabled(sci->pck)) {
+        qemu_log_mask(LOG_GUEST_ERROR, "renesas_sci: SCIF %d is stopped.\n",
+                      sci->unit);
+        return ;
+    }
+    txtrg = 1 << (3 - FIELD_EX16(scif->fcr, FCR, TTRG));
+    addr = map_address(sci, addr);
+    switch (addr) {
+    case A_SCR:
+        sci->scr = val;
+        if (FIELD_EX16(sci->scr, SCR, TE)) {
+            /* Transmitter enable */
+            sci->Xsr = FIELD_DP16(sci->Xsr, FSR, TEND, 1);
+            sci->Xsr = FIELD_DP16(sci->Xsr, FSR, TDFE, 1);
+            sci->tx_start_time = 0;
+            scif_irq(sci, TXI);
+        } else {
+            /* Transmitter disable  */
+            update_event_time(sci, TXEND, 0);
+            update_event_time(sci, TXEMPTY, 0);
+        }
+        break;
+    case A_TDR:
+        if (sci->tx_start_time > 0) {
+            scif->tdcnt -= transmit_byte(scif);
+        } else {
+            sci->tx_start_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+        }
+        if (scif->tdcnt >= SCIF_FIFO_DEPTH) {
+            break;
+        }
+        txd = val;
+        if (qemu_chr_fe_backend_connected(&sci->chr)) {
+            qemu_chr_fe_write_all(&sci->chr, &txd, 1);
+        }
+        if (FIELD_EX16(scif->fcr, FCR, LOOP) && scif_can_receive(sci) > 0) {
+            /* Loopback mode */
+            scif_receive(sci, &txd, 1);
+        }
+        scif->tdcnt++;
+        sci->Xsr = FIELD_DP16(sci->Xsr, FSR, TEND, 0);
+        update_event_time(sci, TXEND, scif->tdcnt);
+        if (scif->tdcnt > txtrg) {
+            sci->Xsr = FIELD_DP16(sci->Xsr, FSR, TDFE, 0);
+            update_event_time(sci, TXEMPTY, scif->tdcnt - txtrg + 1);
+            scif_irq(sci, TXI);
+        }
+        break;
+    case A_FSR:
+        rxtrg = sci_rtrg[FIELD_EX16(scif->fcr, FCR, RTRG)];
+        ssr_mask = ~(sci->read_Xsr & 0xf3);
+        scif->tdcnt -= transmit_byte(scif);
+        if (scif->tdcnt < txtrg) {
+            ssr_mask = FIELD_DP16(ssr_mask, FSR, TDFE, 1);
+        }
+        if (fifo8_num_used(&sci->rxfifo) >= rxtrg) {
+            ssr_mask = FIELD_DP16(ssr_mask, FSR, RDF, 1);
+        }
+        sci->Xsr &= (val | ssr_mask);
+        scif_irq(sci, ERI);
+        scif_irq(sci, RXI);
+        scif_irq(sci, TXI);
+        break;
+    case A_FCR:
+        scif->fcr = val;
+        if (FIELD_EX16(scif->fcr, FCR, RFRST)) {
+            fifo8_reset(&sci->rxfifo);
+            update_event_time(sci, RXTOUT, 0);
+            update_event_time(sci, RXNEXT, 0);
+        }
+        if (FIELD_EX16(scif->fcr, FCR, TFRST)) {
+            scif->tdcnt = 0;
+        }
+        break;
+    case A_FDR:
+        qemu_log_mask(LOG_GUEST_ERROR, "reneas_sci: FDR is read only.\n");
+        break;
+    case A_SPTR:
+        scif->sptr = val;
+        break;
+    case A_LSR:
+        if (FIELD_EX16(scif->read_lsr, LSR, ORER) != 1) {
+            val = FIELD_DP16(val, LSR, ORER, 1);
+        }
+        scif->lsr &= val;
+        scif_irq(sci, ERI);
+        break;
+    default:
+        sci_common_write(sci, addr, val, size);
+        break;
+    }
+}
 
-    switch (offset) {
+static uint64_t sci_common_read(void *opaque, hwaddr addr, unsigned size)
+{
+    RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque);
+    switch (addr) {
     case A_SMR:
         return sci->smr;
     case A_BRR:
         return sci->brr;
     case A_SCR:
         return sci->scr;
-    case A_TDR:
-        return sci->tdr;
-    case A_SSR:
-        sci->read_ssr = sci->ssr;
-        return sci->ssr;
+    case A_FSR: /* A_SSR */
+        sci->read_Xsr = sci->Xsr;
+        return sci->Xsr;
     case A_RDR:
-        sci->ssr = FIELD_DP8(sci->ssr, SSR, RDRF, 0);
-        return sci->rdr;
-    case A_SCMR:
-        return sci->scmr;
-    case A_SEMR:
-        return sci->semr;
+        if (fifo8_num_used(&sci->rxfifo) > 0) {
+            return fifo8_pop(&sci->rxfifo);
+        } else {
+            return 0xff;
+        }
     default:
         qemu_log_mask(LOG_UNIMP, "renesas_sci: Register 0x%" HWADDR_PRIX
-                      " not implemented.\n", offset);
+                      " not implemented.\n", addr);
     }
     return UINT64_MAX;
 }
 
-static const MemoryRegionOps sci_ops = {
-    .write = sci_write,
-    .read  = sci_read,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-    .impl.max_access_size = 1,
-    .valid.max_access_size = 1,
-};
+static uint64_t sci_read(void *opaque, hwaddr addr, unsigned size)
+{
+    RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque);
+    addr = map_address(sci, addr);
+
+    if (clock_is_enabled(sci->pck)) {
+        switch (addr) {
+        case A_TDR:
+            return sci->tdr;
+        case A_SPTR:
+            return RENESAS_SCI(sci)->sptr;
+        default:
+            return sci_common_read(sci, addr, size);
+        }
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR, "renesas_sci: SCI %d is stopped.\n",
+                      sci->unit);
+    }
+    return UINT64_MAX;
+}
 
-static void rsci_reset(DeviceState *dev)
+static uint64_t scia_read(void *opaque, hwaddr addr, unsigned size)
 {
-    RSCIState *sci = RSCI(dev);
-    sci->smr = sci->scr = 0x00;
-    sci->brr = 0xff;
-    sci->tdr = 0xff;
-    sci->rdr = 0x00;
-    sci->ssr = 0x84;
-    sci->scmr = 0x00;
-    sci->semr = 0x00;
-    sci->rx_next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+    RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque);
+    RenesasSCIAState *scia = RENESAS_SCIA(opaque);
+
+    if (clock_is_enabled(sci->pck)) {
+        addr = map_address(sci, addr);
+        switch (addr) {
+        case A_TDR:
+            return sci->tdr;
+        case A_RDR:
+            sci->Xsr = FIELD_DP16(sci->Xsr, SSR, RDRF, 0);
+            return fifo8_pop(&sci->rxfifo);
+        case A_SCMR:
+            return scia->scmr;
+        default:
+            return sci_common_read(sci, addr, size);
+        }
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR, "renesas_sci: SCIa %d is stopped.\n",
+                      sci->unit);
+    }
+    return UINT64_MAX;
+}
+
+static uint64_t scif_read(void *opaque, hwaddr addr, unsigned size)
+{
+    RenesasSCIFState *scif = RENESAS_SCIF(opaque);
+    RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque);
+    uint64_t ret;
+
+    if (clock_is_enabled(sci->pck)) {
+        addr = map_address(sci, addr);
+        switch (addr) {
+        case A_FCR:
+            return scif->fcr & 0x7ff;
+        case A_FDR:
+            ret = 0;
+            ret = FIELD_DP16(ret, FDR, Rn, fifo8_num_used(&sci->rxfifo));
+            ret = FIELD_DP16(ret, FDR, Tn, scif->tdcnt - transmit_byte(scif));
+            return ret;
+        case A_SPTR:
+            return scif->sptr;
+        case A_LSR:
+            scif->read_lsr = scif->lsr;
+            return scif->lsr;
+        default:
+            return sci_common_read(sci, addr, size);
+        }
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR, "renesas_sci: SCIF %d is stopped.\n",
+                      sci->unit);
+    }
+    return UINT64_MAX;
+}
+
+static void rsci_common_init(Object *obj)
+{
+    RenesasSCIBaseState *sci = RENESAS_SCI_BASE(obj);
+    SysBusDevice *d = SYS_BUS_DEVICE(obj);
+    int i;
+
+    for (i = 0; i < SCI_NR_IRQ; i++) {
+        sysbus_init_irq(d, &sci->irq[i]);
+    }
+    fifo8_create(&sci->rxfifo, SCIF_FIFO_DEPTH);
+    sci->event_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sci_timer_event, sci);
+    sci->pck = qdev_init_clock_in(DEVICE(d), "pck",
+                                  sci_pck_update, sci, ClockUpdate);
 }
 
 static void sci_event(void *opaque, QEMUChrEvent event)
 {
-    RSCIState *sci = RSCI(opaque);
+    RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque);
+    RenesasSCIBaseClass *rc = RENESAS_SCI_BASE_GET_CLASS(sci);
+    if (clock_is_enabled(sci->pck) && event == CHR_EVENT_BREAK) {
+        sci->Xsr = FIELD_DP16(sci->Xsr, SSR, FER, 1);
+        rc->irq_fn(sci, BRI_TEI);
+    }
+}
+
+static void scif_event(void *opaque, QEMUChrEvent event)
+{
+    RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque);
     if (event == CHR_EVENT_BREAK) {
-        sci->ssr = FIELD_DP8(sci->ssr, SSR, FER, 1);
-        if (FIELD_EX8(sci->scr, SCR, RIE)) {
-            qemu_set_irq(sci->irq[ERI], 1);
-        }
+        sci->Xsr = FIELD_DP16(sci->Xsr, FSR, BRK, 1);
+        scif_irq(sci, BRI_TEI);
     }
 }
 
-static void rsci_realize(DeviceState *dev, Error **errp)
+static void rsci_common_realize(DeviceState *dev, Error **errp)
 {
-    RSCIState *sci = RSCI(dev);
+    RenesasSCIBaseState *sci = RENESAS_SCI_BASE(dev);
 
-    if (sci->input_freq == 0) {
+    if (sci->regshift != 8 && sci->regshift != 16 && sci->regshift != 32) {
         qemu_log_mask(LOG_GUEST_ERROR,
-                      "renesas_sci: input-freq property must be set.");
+                      "renesas_sci: Invalid register size.");
         return;
     }
-    qemu_chr_fe_set_handlers(&sci->chr, can_receive, receive,
-                             sci_event, NULL, sci, NULL, true);
+
+    sci->smr = sci->scr = 0x00;
+    sci->brr = 0xff;
+    sci->tdr = 0xff;
+    sci->Xsr = 0x84;
+    update_trtime(sci);
+
 }
 
-static void rsci_init(Object *obj)
+static void register_mmio(RenesasSCIBaseState *sci, int size)
 {
-    SysBusDevice *d = SYS_BUS_DEVICE(obj);
-    RSCIState *sci = RSCI(obj);
-    int i;
+    SysBusDevice *d = SYS_BUS_DEVICE(sci);
+    RenesasSCIBaseClass *rc = RENESAS_SCI_BASE_GET_CLASS(sci);
 
-    memory_region_init_io(&sci->memory, OBJECT(sci), &sci_ops,
-                          sci, "renesas-sci", 0x8);
+    memory_region_init_io(&sci->memory, OBJECT(sci), rc->ops,
+                          sci, "renesas-sci", size);
     sysbus_init_mmio(d, &sci->memory);
+    memory_region_init_alias(&sci->memory_p4, NULL, "renesas-sci-p4",
+                             &sci->memory, 0, size);
+    sysbus_init_mmio(d, &sci->memory_p4);
+    memory_region_init_alias(&sci->memory_a7, NULL, "renesas-sci-a7",
+                             &sci->memory, 0, size);
+    sysbus_init_mmio(d, &sci->memory_a7);
+}
 
-    for (i = 0; i < SCI_NR_IRQ; i++) {
-        sysbus_init_irq(d, &sci->irq[i]);
-    }
-    timer_init_ns(&sci->timer, QEMU_CLOCK_VIRTUAL, txend, sci);
+static void rsci_realize(DeviceState *dev, Error **errp)
+{
+    RenesasSCIState *sci = RENESAS_SCI(dev);
+    RenesasSCIBaseState *common = RENESAS_SCI_BASE(dev);
+
+    rsci_common_realize(dev, errp);
+
+    register_mmio(common, 8 * (1 << common->regshift));
+    qemu_chr_fe_set_handlers(&common->chr, sci_can_receive, sci_receive,
+                             sci_event, NULL, sci, NULL, true);
+
+    sci->sptr = 0x00;
+}
+
+static void rscia_realize(DeviceState *dev, Error **errp)
+{
+    RenesasSCIAState *sci = RENESAS_SCIA(dev);
+    RenesasSCIBaseState *common = RENESAS_SCI_BASE(dev);
+
+    rsci_common_realize(dev, errp);
+
+    register_mmio(common, 8 * (1 << common->regshift));
+    qemu_chr_fe_set_handlers(&common->chr, sci_can_receive, sci_receive,
+                             sci_event, NULL, sci, NULL, true);
+
+    sci->scmr = 0x00;
+    sci->semr = 0x00;
+}
+
+static void rscif_realize(DeviceState *dev, Error **errp)
+{
+    RenesasSCIFState *sci = RENESAS_SCIF(dev);
+    RenesasSCIBaseState *common = RENESAS_SCI_BASE(sci);
+
+    rsci_common_realize(dev, errp);
+
+    register_mmio(common, 10 * (1 << common->regshift));
+    qemu_chr_fe_set_handlers(&common->chr, scif_can_receive, scif_receive,
+                             scif_event, NULL, sci, NULL, true);
+    common->Xsr = 0x0060;
+    sci->fcr = 0x0000;
+    sci->sptr = 0x0000;
+    sci->lsr = 0x0000;
 }
 
 static const VMStateDescription vmstate_rsci = {
@@ -303,49 +892,141 @@ static const VMStateDescription vmstate_rsci = {
     .version_id = 1,
     .minimum_version_id = 1,
     .fields = (VMStateField[]) {
-        VMSTATE_INT64(trtime, RSCIState),
-        VMSTATE_INT64(rx_next, RSCIState),
-        VMSTATE_UINT8(smr, RSCIState),
-        VMSTATE_UINT8(brr, RSCIState),
-        VMSTATE_UINT8(scr, RSCIState),
-        VMSTATE_UINT8(tdr, RSCIState),
-        VMSTATE_UINT8(ssr, RSCIState),
-        VMSTATE_UINT8(rdr, RSCIState),
-        VMSTATE_UINT8(scmr, RSCIState),
-        VMSTATE_UINT8(semr, RSCIState),
-        VMSTATE_UINT8(read_ssr, RSCIState),
-        VMSTATE_TIMER(timer, RSCIState),
         VMSTATE_END_OF_LIST()
     }
 };
 
 static Property rsci_properties[] = {
-    DEFINE_PROP_UINT64("input-freq", RSCIState, input_freq, 0),
-    DEFINE_PROP_CHR("chardev", RSCIState, chr),
+    DEFINE_PROP_INT32("register-size", RenesasSCIBaseState,
+                      regshift, SCI_REGSIZE_32),
+    DEFINE_PROP_UINT32("unit", RenesasSCIBaseState, unit, 0),
+    DEFINE_PROP_CHR("chardev", RenesasSCIBaseState, chr),
     DEFINE_PROP_END_OF_LIST(),
 };
 
-static void rsci_class_init(ObjectClass *klass, void *data)
+static void rsci_init(Object *obj)
 {
+    RenesasSCIBaseState *sci = RENESAS_SCI_BASE(obj);
+    sci->event[RXNEXT].handler = sci_rx_next;
+    sci->event[TXEMPTY].handler = sci_tx_empty;
+}
+
+static void rscif_init(Object *obj)
+{
+    RenesasSCIBaseState *sci = RENESAS_SCI_BASE(obj);
+    sci->event[RXTOUT].handler = scif_rx_timeout;
+    sci->event[TXEMPTY].handler = scif_tx_empty;
+    sci->event[TXEND].handler = scif_tx_end;
+}
+
+static void rsci_common_class_init(ObjectClass *klass, void *data)
+{
+    RenesasSCIBaseClass *rc = RENESAS_SCI_BASE_CLASS(klass);
     DeviceClass *dc = DEVICE_CLASS(klass);
 
-    dc->realize = rsci_realize;
     dc->vmsd = &vmstate_rsci;
-    dc->reset = rsci_reset;
     device_class_set_props(dc, rsci_properties);
+    rc->divrate = static_divrate;
 }
 
-static const TypeInfo rsci_info = {
-    .name = TYPE_RENESAS_SCI,
-    .parent = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(RSCIState),
-    .instance_init = rsci_init,
-    .class_init = rsci_class_init,
+static const MemoryRegionOps sci_ops = {
+    .read = sci_read,
+    .write = sci_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
 };
 
-static void rsci_register_types(void)
+static void rsci_class_init(ObjectClass *klass, void *data)
 {
-    type_register_static(&rsci_info);
+    RenesasSCIBaseClass *comm_rc = RENESAS_SCI_BASE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    comm_rc->ops = &sci_ops;
+    comm_rc->irq_fn = sci_irq;
+    dc->realize = rsci_realize;
 }
 
-type_init(rsci_register_types)
+static const MemoryRegionOps scia_ops = {
+    .read = scia_read,
+    .write = scia_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+};
+
+static void rscia_class_init(ObjectClass *klass, void *data)
+{
+    RenesasSCIBaseClass *comm_rc = RENESAS_SCI_BASE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    comm_rc->ops = &scia_ops;
+    comm_rc->irq_fn = scia_irq;
+    comm_rc->divrate = scia_divrate;
+
+    dc->realize = rscia_realize;
+}
+
+static const MemoryRegionOps scif_ops = {
+    .read = scif_read,
+    .write = scif_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+};
+
+static void rscif_class_init(ObjectClass *klass, void *data)
+{
+    RenesasSCIBaseClass *comm_rc = RENESAS_SCI_BASE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    comm_rc->ops = &scif_ops;
+    comm_rc->irq_fn = scif_irq;
+
+    dc->realize = rscif_realize;
+}
+
+static const TypeInfo renesas_sci_info[] = {
+    {
+        .name       = TYPE_RENESAS_SCI_BASE,
+        .parent     = TYPE_SYS_BUS_DEVICE,
+        .instance_size = sizeof(RenesasSCIBaseState),
+        .instance_init = rsci_common_init,
+        .class_init = rsci_common_class_init,
+        .class_size = sizeof(RenesasSCIBaseClass),
+        .abstract = true,
+    },
+    {
+        .name       = TYPE_RENESAS_SCI,
+        .parent     = TYPE_RENESAS_SCI_BASE,
+        .instance_size = sizeof(RenesasSCIState),
+        .instance_init = rsci_init,
+        .class_init = rsci_class_init,
+        .class_size = sizeof(RenesasSCIClass),
+    },
+    {
+        .name       = TYPE_RENESAS_SCIA,
+        .parent     = TYPE_RENESAS_SCI_BASE,
+        .instance_size = sizeof(RenesasSCIAState),
+        /* Initializer same of SCI */
+        .instance_init = rsci_init,
+        .class_init = rscia_class_init,
+        .class_size = sizeof(RenesasSCIAClass),
+    },
+    {
+        .name       = TYPE_RENESAS_SCIF,
+        .parent     = TYPE_RENESAS_SCI_BASE,
+        .instance_size = sizeof(RenesasSCIFState),
+        .instance_init = rscif_init,
+        .class_init = rscif_class_init,
+        .class_size = sizeof(RenesasSCIFClass),
+    },
+};
+
+DEFINE_TYPES(renesas_sci_info)
-- 
2.20.1



^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 02/11] hw/char: remove sh_serial.
  2021-05-27  5:21 [PATCH 00/11] Unified peripheral emulation for Renesas chips Yoshinori Sato
  2021-05-27  5:21 ` [PATCH 01/11] hw/char: Renesas SCI module Yoshinori Sato
@ 2021-05-27  5:21 ` Yoshinori Sato
  2021-06-04 10:08   ` Peter Maydell
  2021-05-27  5:21 ` [PATCH 03/11] hw/timer: Renesas TMU/CMT module Yoshinori Sato
                   ` (9 subsequent siblings)
  11 siblings, 1 reply; 21+ messages in thread
From: Yoshinori Sato @ 2021-05-27  5:21 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

Migrate to renesas_sci.

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 hw/char/sh_serial.c | 431 --------------------------------------------
 MAINTAINERS         |   4 +-
 hw/char/Kconfig     |   3 -
 hw/char/meson.build |   1 -
 4 files changed, 2 insertions(+), 437 deletions(-)
 delete mode 100644 hw/char/sh_serial.c

diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c
deleted file mode 100644
index 167f4d8cb9..0000000000
--- a/hw/char/sh_serial.c
+++ /dev/null
@@ -1,431 +0,0 @@
-/*
- * QEMU SCI/SCIF serial port emulation
- *
- * Copyright (c) 2007 Magnus Damm
- *
- * Based on serial.c - QEMU 16450 UART emulation
- * Copyright (c) 2003-2004 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "qemu/osdep.h"
-#include "hw/irq.h"
-#include "hw/sh4/sh.h"
-#include "chardev/char-fe.h"
-#include "qapi/error.h"
-#include "qemu/timer.h"
-
-//#define DEBUG_SERIAL
-
-#define SH_SERIAL_FLAG_TEND (1 << 0)
-#define SH_SERIAL_FLAG_TDE  (1 << 1)
-#define SH_SERIAL_FLAG_RDF  (1 << 2)
-#define SH_SERIAL_FLAG_BRK  (1 << 3)
-#define SH_SERIAL_FLAG_DR   (1 << 4)
-
-#define SH_RX_FIFO_LENGTH (16)
-
-typedef struct {
-    MemoryRegion iomem;
-    MemoryRegion iomem_p4;
-    MemoryRegion iomem_a7;
-    uint8_t smr;
-    uint8_t brr;
-    uint8_t scr;
-    uint8_t dr; /* ftdr / tdr */
-    uint8_t sr; /* fsr / ssr */
-    uint16_t fcr;
-    uint8_t sptr;
-
-    uint8_t rx_fifo[SH_RX_FIFO_LENGTH]; /* frdr / rdr */
-    uint8_t rx_cnt;
-    uint8_t rx_tail;
-    uint8_t rx_head;
-
-    int freq;
-    int feat;
-    int flags;
-    int rtrg;
-
-    CharBackend chr;
-    QEMUTimer *fifo_timeout_timer;
-    uint64_t etu; /* Elementary Time Unit (ns) */
-
-    qemu_irq eri;
-    qemu_irq rxi;
-    qemu_irq txi;
-    qemu_irq tei;
-    qemu_irq bri;
-} sh_serial_state;
-
-static void sh_serial_clear_fifo(sh_serial_state * s)
-{
-    memset(s->rx_fifo, 0, SH_RX_FIFO_LENGTH);
-    s->rx_cnt = 0;
-    s->rx_head = 0;
-    s->rx_tail = 0;
-}
-
-static void sh_serial_write(void *opaque, hwaddr offs,
-                            uint64_t val, unsigned size)
-{
-    sh_serial_state *s = opaque;
-    unsigned char ch;
-
-#ifdef DEBUG_SERIAL
-    printf("sh_serial: write offs=0x%02x val=0x%02x\n",
-           offs, val);
-#endif
-    switch(offs) {
-    case 0x00: /* SMR */
-        s->smr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0x7b : 0xff);
-        return;
-    case 0x04: /* BRR */
-        s->brr = val;
-        return;
-    case 0x08: /* SCR */
-        /* TODO : For SH7751, SCIF mask should be 0xfb. */
-        s->scr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0xfa : 0xff);
-        if (!(val & (1 << 5)))
-            s->flags |= SH_SERIAL_FLAG_TEND;
-        if ((s->feat & SH_SERIAL_FEAT_SCIF) && s->txi) {
-            qemu_set_irq(s->txi, val & (1 << 7));
-        }
-        if (!(val & (1 << 6))) {
-            qemu_set_irq(s->rxi, 0);
-        }
-        return;
-    case 0x0c: /* FTDR / TDR */
-        if (qemu_chr_fe_backend_connected(&s->chr)) {
-            ch = val;
-            /* XXX this blocks entire thread. Rewrite to use
-             * qemu_chr_fe_write and background I/O callbacks */
-            qemu_chr_fe_write_all(&s->chr, &ch, 1);
-        }
-        s->dr = val;
-        s->flags &= ~SH_SERIAL_FLAG_TDE;
-        return;
-#if 0
-    case 0x14: /* FRDR / RDR */
-        ret = 0;
-        break;
-#endif
-    }
-    if (s->feat & SH_SERIAL_FEAT_SCIF) {
-        switch(offs) {
-        case 0x10: /* FSR */
-            if (!(val & (1 << 6)))
-                s->flags &= ~SH_SERIAL_FLAG_TEND;
-            if (!(val & (1 << 5)))
-                s->flags &= ~SH_SERIAL_FLAG_TDE;
-            if (!(val & (1 << 4)))
-                s->flags &= ~SH_SERIAL_FLAG_BRK;
-            if (!(val & (1 << 1)))
-                s->flags &= ~SH_SERIAL_FLAG_RDF;
-            if (!(val & (1 << 0)))
-                s->flags &= ~SH_SERIAL_FLAG_DR;
-
-            if (!(val & (1 << 1)) || !(val & (1 << 0))) {
-                if (s->rxi) {
-                    qemu_set_irq(s->rxi, 0);
-                }
-            }
-            return;
-        case 0x18: /* FCR */
-            s->fcr = val;
-            switch ((val >> 6) & 3) {
-            case 0:
-                s->rtrg = 1;
-                break;
-            case 1:
-                s->rtrg = 4;
-                break;
-            case 2:
-                s->rtrg = 8;
-                break;
-            case 3:
-                s->rtrg = 14;
-                break;
-            }
-            if (val & (1 << 1)) {
-                sh_serial_clear_fifo(s);
-                s->sr &= ~(1 << 1);
-            }
-
-            return;
-        case 0x20: /* SPTR */
-            s->sptr = val & 0xf3;
-            return;
-        case 0x24: /* LSR */
-            return;
-        }
-    }
-    else {
-        switch(offs) {
-#if 0
-        case 0x0c:
-            ret = s->dr;
-            break;
-        case 0x10:
-            ret = 0;
-            break;
-#endif
-        case 0x1c:
-            s->sptr = val & 0x8f;
-            return;
-        }
-    }
-
-    fprintf(stderr, "sh_serial: unsupported write to 0x%02"
-            HWADDR_PRIx "\n", offs);
-    abort();
-}
-
-static uint64_t sh_serial_read(void *opaque, hwaddr offs,
-                               unsigned size)
-{
-    sh_serial_state *s = opaque;
-    uint32_t ret = ~0;
-
-#if 0
-    switch(offs) {
-    case 0x00:
-        ret = s->smr;
-        break;
-    case 0x04:
-        ret = s->brr;
-        break;
-    case 0x08:
-        ret = s->scr;
-        break;
-    case 0x14:
-        ret = 0;
-        break;
-    }
-#endif
-    if (s->feat & SH_SERIAL_FEAT_SCIF) {
-        switch(offs) {
-        case 0x00: /* SMR */
-            ret = s->smr;
-            break;
-        case 0x08: /* SCR */
-            ret = s->scr;
-            break;
-        case 0x10: /* FSR */
-            ret = 0;
-            if (s->flags & SH_SERIAL_FLAG_TEND)
-                ret |= (1 << 6);
-            if (s->flags & SH_SERIAL_FLAG_TDE)
-                ret |= (1 << 5);
-            if (s->flags & SH_SERIAL_FLAG_BRK)
-                ret |= (1 << 4);
-            if (s->flags & SH_SERIAL_FLAG_RDF)
-                ret |= (1 << 1);
-            if (s->flags & SH_SERIAL_FLAG_DR)
-                ret |= (1 << 0);
-
-            if (s->scr & (1 << 5))
-                s->flags |= SH_SERIAL_FLAG_TDE | SH_SERIAL_FLAG_TEND;
-
-            break;
-        case 0x14:
-            if (s->rx_cnt > 0) {
-                ret = s->rx_fifo[s->rx_tail++];
-                s->rx_cnt--;
-                if (s->rx_tail == SH_RX_FIFO_LENGTH)
-                    s->rx_tail = 0;
-                if (s->rx_cnt < s->rtrg)
-                    s->flags &= ~SH_SERIAL_FLAG_RDF;
-            }
-            break;
-        case 0x18:
-            ret = s->fcr;
-            break;
-        case 0x1c:
-            ret = s->rx_cnt;
-            break;
-        case 0x20:
-            ret = s->sptr;
-            break;
-        case 0x24:
-            ret = 0;
-            break;
-        }
-    }
-    else {
-        switch(offs) {
-#if 0
-        case 0x0c:
-            ret = s->dr;
-            break;
-        case 0x10:
-            ret = 0;
-            break;
-        case 0x14:
-            ret = s->rx_fifo[0];
-            break;
-#endif
-        case 0x1c:
-            ret = s->sptr;
-            break;
-        }
-    }
-#ifdef DEBUG_SERIAL
-    printf("sh_serial: read offs=0x%02x val=0x%x\n",
-           offs, ret);
-#endif
-
-    if (ret & ~((1 << 16) - 1)) {
-        fprintf(stderr, "sh_serial: unsupported read from 0x%02"
-                HWADDR_PRIx "\n", offs);
-        abort();
-    }
-
-    return ret;
-}
-
-static int sh_serial_can_receive(sh_serial_state *s)
-{
-    return s->scr & (1 << 4);
-}
-
-static void sh_serial_receive_break(sh_serial_state *s)
-{
-    if (s->feat & SH_SERIAL_FEAT_SCIF)
-        s->sr |= (1 << 4);
-}
-
-static int sh_serial_can_receive1(void *opaque)
-{
-    sh_serial_state *s = opaque;
-    return sh_serial_can_receive(s);
-}
-
-static void sh_serial_timeout_int(void *opaque)
-{
-    sh_serial_state *s = opaque;
-
-    s->flags |= SH_SERIAL_FLAG_RDF;
-    if (s->scr & (1 << 6) && s->rxi) {
-        qemu_set_irq(s->rxi, 1);
-    }
-}
-
-static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size)
-{
-    sh_serial_state *s = opaque;
-
-    if (s->feat & SH_SERIAL_FEAT_SCIF) {
-        int i;
-        for (i = 0; i < size; i++) {
-            if (s->rx_cnt < SH_RX_FIFO_LENGTH) {
-                s->rx_fifo[s->rx_head++] = buf[i];
-                if (s->rx_head == SH_RX_FIFO_LENGTH) {
-                    s->rx_head = 0;
-                }
-                s->rx_cnt++;
-                if (s->rx_cnt >= s->rtrg) {
-                    s->flags |= SH_SERIAL_FLAG_RDF;
-                    if (s->scr & (1 << 6) && s->rxi) {
-                        timer_del(s->fifo_timeout_timer);
-                        qemu_set_irq(s->rxi, 1);
-                    }
-                } else {
-                    timer_mod(s->fifo_timeout_timer,
-                        qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 15 * s->etu);
-                }
-            }
-        }
-    } else {
-        s->rx_fifo[0] = buf[0];
-    }
-}
-
-static void sh_serial_event(void *opaque, QEMUChrEvent event)
-{
-    sh_serial_state *s = opaque;
-    if (event == CHR_EVENT_BREAK)
-        sh_serial_receive_break(s);
-}
-
-static const MemoryRegionOps sh_serial_ops = {
-    .read = sh_serial_read,
-    .write = sh_serial_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-void sh_serial_init(MemoryRegion *sysmem,
-                    hwaddr base, int feat,
-                    uint32_t freq, Chardev *chr,
-                    qemu_irq eri_source,
-                    qemu_irq rxi_source,
-                    qemu_irq txi_source,
-                    qemu_irq tei_source,
-                    qemu_irq bri_source)
-{
-    sh_serial_state *s;
-
-    s = g_malloc0(sizeof(sh_serial_state));
-
-    s->feat = feat;
-    s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE;
-    s->rtrg = 1;
-
-    s->smr = 0;
-    s->brr = 0xff;
-    s->scr = 1 << 5; /* pretend that TX is enabled so early printk works */
-    s->sptr = 0;
-
-    if (feat & SH_SERIAL_FEAT_SCIF) {
-        s->fcr = 0;
-    }
-    else {
-        s->dr = 0xff;
-    }
-
-    sh_serial_clear_fifo(s);
-
-    memory_region_init_io(&s->iomem, NULL, &sh_serial_ops, s,
-                          "serial", 0x100000000ULL);
-
-    memory_region_init_alias(&s->iomem_p4, NULL, "serial-p4", &s->iomem,
-                             0, 0x28);
-    memory_region_add_subregion(sysmem, P4ADDR(base), &s->iomem_p4);
-
-    memory_region_init_alias(&s->iomem_a7, NULL, "serial-a7", &s->iomem,
-                             0, 0x28);
-    memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7);
-
-    if (chr) {
-        qemu_chr_fe_init(&s->chr, chr, &error_abort);
-        qemu_chr_fe_set_handlers(&s->chr, sh_serial_can_receive1,
-                                 sh_serial_receive1,
-                                 sh_serial_event, NULL, s, NULL, true);
-    }
-
-    s->fifo_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
-                                         sh_serial_timeout_int, s);
-    s->etu = NANOSECONDS_PER_SECOND / 9600;
-    s->eri = eri_source;
-    s->rxi = rxi_source;
-    s->txi = txi_source;
-    s->tei = tei_source;
-    s->bri = bri_source;
-}
diff --git a/MAINTAINERS b/MAINTAINERS
index a77f246569..ecfa97cefa 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1410,7 +1410,7 @@ R2D
 R: Yoshinori Sato <ysato@users.sourceforge.jp>
 R: Magnus Damm <magnus.damm@gmail.com>
 S: Odd Fixes
-F: hw/char/sh_serial.c
+F: hw/char/renesas_sci.c
 F: hw/sh4/r2d.c
 F: hw/intc/sh_intc.c
 F: hw/pci-host/sh_pci.c
@@ -1422,7 +1422,7 @@ R: Yoshinori Sato <ysato@users.sourceforge.jp>
 R: Magnus Damm <magnus.damm@gmail.com>
 S: Odd Fixes
 F: hw/block/tc58128.c
-F: hw/char/sh_serial.c
+F: hw/char/reness_sci.c
 F: hw/sh4/shix.c
 F: hw/intc/sh_intc.c
 F: hw/timer/sh_timer.c
diff --git a/hw/char/Kconfig b/hw/char/Kconfig
index 4cf36ac637..a8bf0c6a77 100644
--- a/hw/char/Kconfig
+++ b/hw/char/Kconfig
@@ -50,9 +50,6 @@ config SCLPCONSOLE
 config TERMINAL3270
     bool
 
-config SH_SCI
-    bool
-
 config RENESAS_SCI
     bool
 
diff --git a/hw/char/meson.build b/hw/char/meson.build
index 8361d0ab28..b9a88aa200 100644
--- a/hw/char/meson.build
+++ b/hw/char/meson.build
@@ -29,7 +29,6 @@ softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_uart.c'))
 softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_aux.c'))
 softmmu_ss.add(when: 'CONFIG_RENESAS_SCI', if_true: files('renesas_sci.c'))
 softmmu_ss.add(when: 'CONFIG_SIFIVE_UART', if_true: files('sifive_uart.c'))
-softmmu_ss.add(when: 'CONFIG_SH_SCI', if_true: files('sh_serial.c'))
 softmmu_ss.add(when: 'CONFIG_STM32F2XX_USART', if_true: files('stm32f2xx_usart.c'))
 softmmu_ss.add(when: 'CONFIG_MCHP_PFSOC_MMUART', if_true: files('mchp_pfsoc_mmuart.c'))
 
-- 
2.20.1



^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 03/11] hw/timer: Renesas TMU/CMT module.
  2021-05-27  5:21 [PATCH 00/11] Unified peripheral emulation for Renesas chips Yoshinori Sato
  2021-05-27  5:21 ` [PATCH 01/11] hw/char: Renesas SCI module Yoshinori Sato
  2021-05-27  5:21 ` [PATCH 02/11] hw/char: remove sh_serial Yoshinori Sato
@ 2021-05-27  5:21 ` Yoshinori Sato
  2021-05-27  5:21 ` [PATCH 04/11] hw/timer: Remove sh_timer Yoshinori Sato
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 21+ messages in thread
From: Yoshinori Sato @ 2021-05-27  5:21 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

TMU - SH4 Timer module.
CMT - Compare and match timer used by some Renesas MCUs.

The two modules have similar interfaces and have been merged.

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 include/hw/timer/renesas_timer.h |  96 +++++
 hw/timer/renesas_timer.c         | 644 +++++++++++++++++++++++++++++++
 hw/timer/Kconfig                 |   3 +
 hw/timer/meson.build             |   1 +
 4 files changed, 744 insertions(+)
 create mode 100644 include/hw/timer/renesas_timer.h
 create mode 100644 hw/timer/renesas_timer.c

diff --git a/include/hw/timer/renesas_timer.h b/include/hw/timer/renesas_timer.h
new file mode 100644
index 0000000000..d71feec54e
--- /dev/null
+++ b/include/hw/timer/renesas_timer.h
@@ -0,0 +1,96 @@
+/*
+ * Renesas Timer unit Object
+ *
+ * Copyright (c) 2020 Yoshinori Sato
+ *
+ * This code is licensed under the GPL version 2 or later.
+ *
+ */
+
+#ifndef HW_RENESAS_TIMER_H
+#define HW_RENESAS_TIMER_H
+
+#include "hw/sysbus.h"
+#include "hw/qdev-clock.h"
+
+#define TYPE_RENESAS_TIMER_BASE "renesas-timer"
+OBJECT_DECLARE_TYPE(RenesasTimerBaseState, RenesasTimerBaseClass,
+                    RENESAS_TIMER_BASE)
+#define TYPE_RENESAS_CMT "renesas-cmt"
+OBJECT_DECLARE_TYPE(RenesasCMTState, RenesasCMTClass,
+                    RENESAS_CMT)
+#define TYPE_RENESAS_TMU "renesas-tmu"
+OBJECT_DECLARE_TYPE(RenesasTMUState, RenesasTMUClass,
+                    RENESAS_TMU)
+
+enum {
+    TIMER_CH_CMT = 2,
+    TIMER_CH_TMU = 3,
+};
+
+enum {
+    CMT_NR_IRQ = 1 * TIMER_CH_CMT,
+};
+
+struct RenesasTimerBaseState;
+
+enum dirction {
+    countup, countdown,
+};
+
+struct rtimer_ch {
+    uint32_t cnt;
+    uint32_t cor;
+    uint16_t ctrl;
+    qemu_irq irq;
+    int64_t base;
+    int64_t next;
+    uint64_t clk;
+    bool start;
+    QEMUTimer *timer;
+    struct RenesasTimerBaseState *tmrp;
+};
+
+typedef struct RenesasTimerBaseState {
+    SysBusDevice parent_obj;
+
+    uint64_t input_freq;
+    MemoryRegion memory;
+    Clock *pck;
+
+    struct rtimer_ch ch[TIMER_CH_TMU];
+    int num_ch;
+    enum dirction direction;
+    int unit;
+} RenesasTimerBaseState;
+
+typedef struct RenesasCMTState {
+    RenesasTimerBaseState parent_obj;
+} RenesasCMTState;
+
+typedef struct RenesasTMUState {
+    RenesasTimerBaseState parent_obj;
+    uint8_t tocr;
+    MemoryRegion memory_p4;
+    MemoryRegion memory_a7;
+} RenesasTMUState;
+
+typedef struct RenesasTimerBaseClass {
+    SysBusDeviceClass parent;
+    int (*divrate)(RenesasTimerBaseState *tmr, int ch);
+    void (*timer_event)(void *opaque);
+    int64_t (*delta_to_tcnt)(RenesasTimerBaseState *tmr, int ch, int64_t delta);
+    int64_t (*get_next)(RenesasTimerBaseState *tmr, int ch);
+    void (*update_clk)(RenesasTimerBaseState *tmr, int ch);
+} RenesasTimerBaseClass;
+
+typedef struct RenesasCMTClass {
+    RenesasTimerBaseClass parent;
+} RenesasCMTClass;
+
+typedef struct RenesasTMUClass {
+    RenesasTimerBaseClass parent;
+    void (*p_update_clk)(RenesasTimerBaseState *tmr, int ch);
+} RenesasTMUClass;
+
+#endif
diff --git a/hw/timer/renesas_timer.c b/hw/timer/renesas_timer.c
new file mode 100644
index 0000000000..b1224e1e6b
--- /dev/null
+++ b/hw/timer/renesas_timer.c
@@ -0,0 +1,644 @@
+/*
+ * Renesas 16bit/32bit Compare-match timer (CMT/TMU)
+ *
+ * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
+ *            (Rev.1.40 R01UH0033EJ0140)
+ *        And SH7751 Group, SH7751R Group User's Manual: Hardware
+ *            (Rev.4.01 R01UH0457EJ0401)
+ *
+ * Copyright (c) 2021 Yoshinori Sato
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * 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_timer.h"
+#include "migration/vmstate.h"
+#include "qemu/error-report.h"
+
+REG32(TOCR, 0)
+  FIELD(TOCR, TCOE, 0, 1)
+REG32(CMSTR, 0)
+REG32(TSTR, 4)
+REG32(TCOR, 8)
+REG32(TCNT, 12)
+REG32(TCR, 16)
+  FIELD(TCR, TPSC, 0, 3)
+  FIELD(TCR, CKEG, 3, 2)
+  FIELD(TCR, UNIE, 5, 1)
+  FIELD(TCR, ICPE, 6, 2)
+  FIELD(TCR, UNF, 8, 1)
+  FIELD(TCR, ICPF, 9, 1)
+REG32(CMCR, 16)
+  FIELD(CMCR, CKS, 0, 2)
+  FIELD(CMCR, CMIE, 6, 1)
+REG32(TCPR, 20)
+
+static int cmt_div(RenesasTimerBaseState *tmr, int ch)
+{
+    return 8 << (2 * FIELD_EX16(tmr->ch[ch].ctrl, CMCR, CKS));
+}
+
+static int tmu_div(RenesasTimerBaseState *tmr, int ch)
+{
+    if (FIELD_EX16(tmr->ch[ch].ctrl, TCR, TPSC) <= 5) {
+        return 4 << (2 * FIELD_EX16(tmr->ch[ch].ctrl, TCR, TPSC));
+    } else {
+        return 0;
+    }
+
+}
+
+static int64_t cmt_get_next(RenesasTimerBaseState *tmr, int ch)
+{
+    return tmr->ch[ch].cor - tmr->ch[ch].cnt;
+}
+
+static int64_t tmu_get_next(RenesasTimerBaseState *tmr, int ch)
+{
+    return tmr->ch[ch].cnt;
+}
+
+static void cmt_timer_event(void *opaque)
+{
+    struct rtimer_ch *ch = opaque;
+    ch->cnt = 0;
+    if (FIELD_EX16(ch->ctrl, CMCR, CMIE)) {
+        qemu_irq_pulse(ch->irq);
+    }
+    ch->base = ch->next;
+    ch->next += (ch->cor - ch->cnt) * ch->clk;
+    timer_mod(ch->timer, ch->next);
+}
+
+static void tmu_timer_event(void *opaque)
+{
+    struct rtimer_ch *ch = opaque;
+    ch->cnt = ch->cor;
+    if (!FIELD_EX16(ch->ctrl, TCR, UNF)) {
+        ch->ctrl = FIELD_DP16(ch->ctrl, TCR, UNF, 1);
+        qemu_set_irq(ch->irq, FIELD_EX16(ch->ctrl, TCR, UNIE));
+    }
+    ch->base = ch->next;
+    ch->next += ch->cnt * ch->clk;
+    timer_mod(ch->timer, ch->next);
+}
+
+static int64_t cmt_delta_to_cnt(RenesasTimerBaseState *tmr,
+                                int ch, int64_t delta)
+{
+    return tmr->ch[ch].cnt + delta;
+}
+
+static int64_t tmu_delta_to_cnt(RenesasTimerBaseState *tmr,
+                                int ch, int64_t delta)
+{
+    return tmr->ch[ch].cnt - delta;
+}
+
+static int64_t read_tcnt(RenesasTimerBaseState *tmr, int ch)
+{
+    int64_t delta, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+    RenesasTimerBaseClass *tc = RENESAS_TIMER_BASE_GET_CLASS(tmr);
+
+    if (tmr->ch[ch].clk > 0) {
+        delta = (now - tmr->ch[ch].base);
+        delta /= tmr->ch[ch].clk;
+        return tc->delta_to_tcnt(tmr, ch, delta);
+    } else {
+        return tmr->ch[ch].cnt;
+    }
+}
+
+static void tmr_start_stop(RenesasTimerBaseState *tmr, int ch, int start)
+{
+    RenesasTimerBaseClass *tc = RENESAS_TIMER_BASE_GET_CLASS(tmr);
+    int64_t now;
+    if (tmr->ch[ch].start != start) {
+        now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+        if (start) {
+            if (!tmr->ch[ch].timer) {
+                tmr->ch[ch].timer =
+                    timer_new_ns(QEMU_CLOCK_VIRTUAL,
+                                 tc->timer_event, &tmr->ch[ch]);
+            }
+            tmr->ch[ch].base = now;
+            tmr->ch[ch].next = now + tc->get_next(tmr, ch) * tmr->ch[ch].clk;
+            timer_mod(tmr->ch[ch].timer, tmr->ch[ch].next);
+        } else {
+            tmr->ch[ch].cnt = read_tcnt(tmr, ch);
+            tmr->ch[ch].next = 0;
+            if (tmr->ch[ch].timer) {
+                timer_del(tmr->ch[ch].timer);
+            }
+        }
+        tmr->ch[ch].start = start;
+    }
+}
+
+static uint64_t read_tstr(RenesasTimerBaseState *tmr)
+{
+    uint64_t ret = 0;
+    int ch;
+    for (ch = 0; ch < tmr->num_ch; ch++) {
+        ret = deposit64(ret, ch, 1, tmr->ch[ch].start);
+    }
+    return ret;
+}
+
+static void update_clk(RenesasTimerBaseState *tmr, int ch)
+{
+    RenesasTimerBaseClass *tc = RENESAS_TIMER_BASE_GET_CLASS(tmr);
+    int t;
+    t = tc->divrate(tmr, ch);
+    if (t > 0) {
+        t = tmr->input_freq / t;
+        tmr->ch[ch].clk = NANOSECONDS_PER_SECOND / t;
+    } else {
+        tmr->ch[ch].clk = 0;
+    }
+}
+
+static void tmu_update_clk(RenesasTimerBaseState *tmr, int ch)
+{
+    /* Clock setting validation */
+    int tpsc = FIELD_EX16(tmr->ch[ch].ctrl, TCR, TPSC);
+    switch (tpsc) {
+    case 5:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "renesas_timer: Invalid TPSC valule %d.\n", tpsc);
+        break;
+    case 6:
+    case 7:
+        qemu_log_mask(LOG_UNIMP,
+                      "renesas_timer: External clock not implemented.\n");
+        break;
+    }
+    /* Interrupt clear */
+    if (FIELD_EX16(tmr->ch[ch].ctrl, TCR, UNF) == 0) {
+        qemu_set_irq(tmr->ch[ch].irq, 0);
+    }
+    update_clk(tmr, ch);
+}
+
+static uint64_t channel_read(RenesasTimerBaseState *tmr, int ch, int reg)
+{
+    switch (reg) {
+    case R_TCR:
+        return tmr->ch[ch].ctrl;
+    case R_TCNT:
+        if (tmr->ch[ch].start) {
+            return read_tcnt(tmr, ch);
+        } else {
+            return tmr->ch[ch].cnt;
+        }
+    case R_TCOR:
+        return tmr->ch[ch].cor;
+    }
+    return UINT64_MAX;
+}
+
+static void tmr_pck_update(void *opaque, ClockEvent evt)
+{
+    RenesasTimerBaseState *tmr = RENESAS_TIMER_BASE(opaque);
+    int64_t now;
+    int i;
+    struct rtimer_ch *ch;
+    for (i = 0; i < TIMER_CH_CMT; i++) {
+        if (tmr->ch[i].start) {
+            tmr->ch[i].cnt = read_tcnt(tmr, i);
+        }
+    }
+    if (clock_is_enabled(tmr->pck)) {
+        tmr->input_freq = clock_get_hz(tmr->pck);
+        now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+        for (i = 0; i < TIMER_CH_CMT; i++) {
+            update_clk(tmr, i);
+            ch = &tmr->ch[i];
+            if (ch->start) {
+                ch->next = ch->base = now;
+                if (tmr->direction == countup) {
+                    ch->next += (ch->cor - ch->cnt) * ch->clk;
+                } else {
+                    ch->next += ch->cnt * ch->clk;
+                }
+                timer_mod(ch->timer, ch->next);
+            }
+        }
+    } else {
+        for (i = 0; i < TIMER_CH_CMT; i++) {
+            if (tmr->ch[i].timer) {
+                timer_del(tmr->ch[i].timer);
+            }
+        }
+    }
+}
+
+static uint64_t cmt_read(void *opaque, hwaddr addr, unsigned size)
+{
+    RenesasCMTState *cmt = RENESAS_CMT(opaque);
+    RenesasTimerBaseState *tmr = RENESAS_TIMER_BASE(cmt);
+    int ch, reg;
+
+    /*  +0 - CMSTR (TSTR)  */
+    /*  +2 - CMCR0  (TCR)  */
+    /*  +4 - CMCNT0 (TCNT) */
+    /*  +6 - CMCOR0 (TCOR) */
+    /*  +8 - CMCR1  (TCR)  */
+    /* +10 - CMCNT1 (TCNT) */
+    /* +12 - CMCOR1 (TCOR) */
+    if (!clock_is_enabled(tmr->pck)) {
+        qemu_log_mask(LOG_UNIMP, "renesas_timer: Unit %d stopped.\n",
+                      tmr->unit);
+        return UINT64_MAX;
+    }
+    addr /= 2;
+    if (addr == R_CMSTR) {
+        return read_tstr(RENESAS_TIMER_BASE(cmt));
+    } else {
+        ch = addr / 4;
+        if (addr < 4) {
+            /* skip CMSTR */
+            addr--;
+        }
+        reg = 2 - (addr % 4);
+        return channel_read(RENESAS_TIMER_BASE(cmt), ch, reg);
+    }
+}
+
+static uint64_t tmu_read(void *opaque, hwaddr addr, unsigned size)
+{
+    RenesasTMUState *tmu = RENESAS_TMU(opaque);
+    RenesasTimerBaseState *tmr = RENESAS_TIMER_BASE(tmu);
+    int ch = -1, reg = -1;
+
+    /*  +0 - TCOR  */
+    /*  +4 - TSTR  */
+    /*  +8 - TCOR0 */
+    /* +12 - TCNT0 */
+    /* +16 - TCR0  */
+    /* +20 - TCOR1 */
+    /* +24 - TCNT1 */
+    /* +28 - TCR1  */
+    /* +32 - TCOR2 */
+    /* +36 - TCNT2 */
+    /* +40 - TCR2  */
+    /* +44 - TCPR2 */
+
+    if (tmr->unit != 0 && addr >= 32) {
+        /* UNIT1 channel2 is not exit */
+        qemu_log_mask(LOG_UNIMP, "renesas_timer: Register 0x%"
+                      HWADDR_PRIX " not implemented\n", addr);
+        return UINT64_MAX;
+    }
+    if (!clock_is_enabled(tmr->pck)) {
+        qemu_log_mask(LOG_UNIMP, "renesas_timer: Unit %d stopped.\n",
+                      tmr->unit);
+        return UINT64_MAX;
+    }
+    addr /= 4;
+    switch (addr) {
+    case R_TOCR:
+        return tmu->tocr;
+    case R_TSTR:
+        return read_tstr(RENESAS_TIMER_BASE(tmu));
+    case R_TCPR:
+        qemu_log_mask(LOG_UNIMP,
+                      "renesas_timer: Input capture not implemented.\n");
+        return UINT64_MAX;
+    default:
+        ch = (addr - 2) / 3;
+        reg = (addr - 2) % 3 + 2;
+        return channel_read(RENESAS_TIMER_BASE(tmu), ch, reg);
+    }
+}
+
+static void write_tstr(RenesasTimerBaseState *tmr, uint16_t val)
+{
+    int ch;
+    for (ch = 0; ch < tmr->num_ch; ch++) {
+        tmr_start_stop(tmr, ch, extract16(val, ch, 1));
+    }
+}
+
+static void write_tcr(RenesasTimerBaseState *tmr, int ch,
+                      uint16_t val, uint16_t mask)
+{
+    RenesasTimerBaseClass *tc = RENESAS_TIMER_BASE_GET_CLASS(tmr);
+    tmr->ch[ch].ctrl |= (mask & 0x00ff);
+    tmr->ch[ch].ctrl &= val & mask;
+    tc->update_clk(tmr, ch);
+}
+
+static void channel_write(RenesasTimerBaseState *tmr, int ch,
+                         int reg, uint64_t val)
+{
+    switch (reg) {
+    case R_TCNT:
+        tmr->ch[ch].cnt = val;
+        break;
+    case R_TCOR:
+        tmr->ch[ch].cor = val;
+        break;
+    }
+}
+
+static void cmt_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    RenesasTimerBaseState *tmr = RENESAS_TIMER_BASE(opaque);
+    int ch, reg;
+    uint32_t next_timeout;
+    uint16_t cnt;
+
+    if (!clock_is_enabled(tmr->pck)) {
+        qemu_log_mask(LOG_UNIMP, "renesas_timer: Unit %d stopped.\n",
+                      tmr->unit);
+        return;
+    }
+    addr /= 2;
+    if (addr == R_CMSTR) {
+        write_tstr(tmr, val);
+    } else {
+        ch = addr / 4;
+        if (addr < 4) {
+            /* skip CMSTR */
+            addr--;
+        }
+        reg = (2 - (addr % 4)) + 2;
+        if (reg == R_TCR) {
+            /* bit7 always 1 */
+            val |= 0x0080;
+            write_tcr(RENESAS_TIMER_BASE(tmr), ch, val, 0x0043);
+        } else {
+            channel_write(RENESAS_TIMER_BASE(tmr), ch, reg, val);
+            if (tmr->ch[ch].start) {
+                if (reg == R_TCNT) {
+                    cnt = tmr->ch[ch].cnt;
+                } else {
+                    cnt = read_tcnt(tmr, ch);
+                }
+                if (tmr->ch[ch].cor < cnt) {
+                    next_timeout = 0x10000 + tmr->ch[ch].cor - cnt;
+                } else {
+                    next_timeout = tmr->ch[ch].cor - cnt;
+                }
+                tmr->ch[ch].next = tmr->ch[ch].base +
+                    next_timeout * tmr->ch[ch].clk;
+                timer_mod(tmr->ch[ch].timer, tmr->ch[ch].next);
+            }
+        }
+    }
+}
+
+static void tmu_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    RenesasTMUState *tmu = RENESAS_TMU(opaque);
+    RenesasTimerBaseState *tmr = RENESAS_TIMER_BASE(tmu);
+
+    int ch, reg;
+    uint16_t tcr_mask;
+
+    if (tmr->unit != 0 && addr >= 32) {
+        /* UNIT1 channel2 is not exit */
+        qemu_log_mask(LOG_UNIMP, "renesas_timer: Register 0x%"
+                      HWADDR_PRIX " not implemented\n", addr);
+        return;
+    }
+    if (!clock_is_enabled(tmr->pck)) {
+        qemu_log_mask(LOG_UNIMP, "renesas_timer: Unit %d stopped.\n",
+                      tmr->unit);
+        return;
+    }
+    addr /= 4;
+    switch (addr) {
+    case R_TOCR:
+        tmu->tocr = FIELD_DP8(tmu->tocr, TOCR, TCOE,
+                              FIELD_EX8(val, TOCR, TCOE));
+        break;
+    case R_TSTR:
+        write_tstr(tmr, val);
+        break;
+    case R_TCPR:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "renesas_timer: TCPR is read only.\n");
+        break;
+    default:
+        ch = (addr - 2) / 3;
+        reg = (addr - 2) % 3 + 2;
+        if (reg == R_TCR) {
+            if (tmr->unit == 0) {
+                tcr_mask = (ch < 2) ? 0x013f : 0x03ff;
+            } else {
+                tcr_mask = 0x0127;
+            }
+            write_tcr(tmr, ch, val, tcr_mask);
+        } else {
+            channel_write(tmr, ch, reg, val);
+            if (reg == R_TCNT && tmr->ch[ch].start) {
+                tmr->ch[ch].next = tmr->ch[ch].base +
+                    tmr->ch[ch].cnt * tmr->ch[ch].clk;
+                timer_mod(tmr->ch[ch].timer, tmr->ch[ch].next);
+            }
+        }
+        break;
+    }
+}
+
+static const MemoryRegionOps cmt_ops = {
+    .write = cmt_write,
+    .read  = cmt_read,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl = {
+        .min_access_size = 2,
+        .max_access_size = 2,
+    },
+};
+
+static const MemoryRegionOps tmu_ops = {
+    .write = tmu_write,
+    .read  = tmu_read,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl = {
+        .min_access_size = 2,
+        .max_access_size = 4,
+    },
+};
+
+static void timer_base_realize(RenesasTimerBaseState *tmr, int num_ch)
+{
+    tmr->num_ch = num_ch;
+}
+
+static void cmt_realize(DeviceState *dev, Error **errp)
+{
+    RenesasCMTState *cmt = RENESAS_CMT(dev);
+    RenesasTimerBaseState *tmr = RENESAS_TIMER_BASE(cmt);
+    int i;
+
+    timer_base_realize(tmr, TIMER_CH_CMT);
+
+    for (i = 0; i < TIMER_CH_CMT; i++) {
+        tmr->ch[i].cor = 0xffff;
+        if (clock_is_enabled(tmr->pck)) {
+            update_clk(tmr, i);
+        }
+    }
+}
+
+static void cmt_init(Object *obj)
+{
+    SysBusDevice *d = SYS_BUS_DEVICE(obj);
+    RenesasCMTState *cmt = RENESAS_CMT(obj);
+    RenesasTimerBaseState *tmr = RENESAS_TIMER_BASE(cmt);
+    int i;
+
+    tmr->direction = countup;
+    memory_region_init_io(&tmr->memory, obj, &cmt_ops,
+                          tmr, "renesas-cmt", 0x10);
+    sysbus_init_mmio(d, &tmr->memory);
+
+    for (i = 0; i < TIMER_CH_CMT; i++) {
+        sysbus_init_irq(d, &tmr->ch[i].irq);
+    }
+    tmr->pck = qdev_init_clock_in(DEVICE(obj), "pck",
+                                  tmr_pck_update, tmr, ClockUpdate);
+}
+
+static void tmu_realize(DeviceState *dev, Error **errp)
+{
+    SysBusDevice *d = SYS_BUS_DEVICE(dev);
+    RenesasTMUState *tmu = RENESAS_TMU(dev);
+    RenesasTimerBaseState *tmr = RENESAS_TIMER_BASE(tmu);
+    int i;
+    int num_ch;
+
+    /* Unit0 have 3ch, Unit1 have 2ch */
+    num_ch = TIMER_CH_TMU - tmr->unit;
+    timer_base_realize(tmr, num_ch);
+    for (i = 0; i < num_ch; i++) {
+        sysbus_init_irq(d, &tmr->ch[i].irq);
+        tmr->ch[i].cor = tmr->ch[i].cnt = 0xffffffff;
+        if (clock_is_enabled(tmr->pck)) {
+            update_clk(tmr, i);
+        }
+    }
+}
+
+static void tmu_init(Object *obj)
+{
+    SysBusDevice *d = SYS_BUS_DEVICE(obj);
+    RenesasTimerBaseState *tmr = RENESAS_TIMER_BASE(obj);
+    RenesasTMUState *tmu = RENESAS_TMU(obj);
+
+    tmr->direction = countdown;
+    memory_region_init_io(&tmr->memory, obj, &tmu_ops,
+                          tmr, "renesas-tmu", 0x30);
+    sysbus_init_mmio(d, &tmr->memory);
+    memory_region_init_alias(&tmu->memory_p4, NULL, "renesas-tmu-p4",
+                             &tmr->memory, 0, 0x30);
+    sysbus_init_mmio(d, &tmu->memory_p4);
+    memory_region_init_alias(&tmu->memory_a7, NULL, "renesas-tmu-a7",
+                             &tmr->memory, 0, 0x30);
+    sysbus_init_mmio(d, &tmu->memory_a7);
+    tmr->pck = qdev_init_clock_in(DEVICE(obj), "pck",
+                                  tmr_pck_update, tmr, ClockUpdate);
+}
+
+static const VMStateDescription vmstate_rtimer = {
+    .name = "rx-cmt",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property renesas_timer_properties[] = {
+    DEFINE_PROP_INT32("unit", RenesasTimerBaseState, unit, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void renesas_timer_base_class_init(ObjectClass *klass, void *data)
+{
+    RenesasTimerBaseClass *base = RENESAS_TIMER_BASE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd = &vmstate_rtimer;
+    base->update_clk = update_clk;
+    device_class_set_props(dc, renesas_timer_properties);
+}
+
+static void cmt_class_init(ObjectClass *klass, void *data)
+{
+    RenesasTimerBaseClass *base = RENESAS_TIMER_BASE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    base->divrate = cmt_div;
+    base->timer_event = cmt_timer_event;
+    base->delta_to_tcnt = cmt_delta_to_cnt;
+    base->get_next = cmt_get_next;
+    dc->realize = cmt_realize;
+}
+
+static void tmu_class_init(ObjectClass *klass, void *data)
+{
+    RenesasTimerBaseClass *base = RENESAS_TIMER_BASE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    base->divrate = tmu_div;
+    base->timer_event = tmu_timer_event;
+    base->delta_to_tcnt = tmu_delta_to_cnt;
+    base->get_next = tmu_get_next;
+    base->update_clk = tmu_update_clk;
+    dc->realize = tmu_realize;
+}
+
+static const TypeInfo renesas_timer_info[] = {
+    {
+        .name       = TYPE_RENESAS_TIMER_BASE,
+        .parent     = TYPE_SYS_BUS_DEVICE,
+        .instance_size = sizeof(RenesasTimerBaseState),
+        .class_init = renesas_timer_base_class_init,
+        .class_size = sizeof(RenesasTimerBaseClass),
+        .abstract = true,
+    },
+    {
+        .name       = TYPE_RENESAS_CMT,
+        .parent     = TYPE_RENESAS_TIMER_BASE,
+        .instance_size = sizeof(RenesasCMTState),
+        .instance_init = cmt_init,
+        .class_init = cmt_class_init,
+        .class_size = sizeof(RenesasCMTClass),
+    },
+    {
+        .name       = TYPE_RENESAS_TMU,
+        .parent     = TYPE_RENESAS_TIMER_BASE,
+        .instance_size = sizeof(RenesasTMUState),
+        .instance_init = tmu_init,
+        .class_init = tmu_class_init,
+        .class_size = sizeof(RenesasTMUClass),
+    },
+};
+
+DEFINE_TYPES(renesas_timer_info)
diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig
index bac2511715..347add12dd 100644
--- a/hw/timer/Kconfig
+++ b/hw/timer/Kconfig
@@ -54,3 +54,6 @@ config SSE_TIMER
 
 config AVR_TIMER16
     bool
+
+config RENESAS_TIMER
+    bool
diff --git a/hw/timer/meson.build b/hw/timer/meson.build
index 157f540ecd..9019dce993 100644
--- a/hw/timer/meson.build
+++ b/hw/timer/meson.build
@@ -33,5 +33,6 @@ softmmu_ss.add(when: 'CONFIG_SSE_COUNTER', if_true: files('sse-counter.c'))
 softmmu_ss.add(when: 'CONFIG_SSE_TIMER', if_true: files('sse-timer.c'))
 softmmu_ss.add(when: 'CONFIG_STM32F2XX_TIMER', if_true: files('stm32f2xx_timer.c'))
 softmmu_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_timer.c'))
+softmmu_ss.add(when: 'CONFIG_RENESAS_TIMER', if_true: files('renesas_timer.c'))
 
 specific_ss.add(when: 'CONFIG_AVR_TIMER16', if_true: files('avr_timer16.c'))
-- 
2.20.1



^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 04/11] hw/timer: Remove sh_timer.
  2021-05-27  5:21 [PATCH 00/11] Unified peripheral emulation for Renesas chips Yoshinori Sato
                   ` (2 preceding siblings ...)
  2021-05-27  5:21 ` [PATCH 03/11] hw/timer: Renesas TMU/CMT module Yoshinori Sato
@ 2021-05-27  5:21 ` Yoshinori Sato
  2021-05-27  5:21 ` [PATCH 05/11] hw/timer: Remove renesas_cmt Yoshinori Sato
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 21+ messages in thread
From: Yoshinori Sato @ 2021-05-27  5:21 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

Migrate to renesas_timer.

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 include/hw/timer/tmu012.h |  23 ---
 hw/timer/sh_timer.c       | 368 --------------------------------------
 MAINTAINERS               |   4 +-
 hw/timer/Kconfig          |   4 -
 hw/timer/meson.build      |   1 -
 5 files changed, 2 insertions(+), 398 deletions(-)
 delete mode 100644 include/hw/timer/tmu012.h
 delete mode 100644 hw/timer/sh_timer.c

diff --git a/include/hw/timer/tmu012.h b/include/hw/timer/tmu012.h
deleted file mode 100644
index 808ed8de1d..0000000000
--- a/include/hw/timer/tmu012.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * SuperH Timer
- *
- * Copyright (c) 2007 Magnus Damm
- *
- * This code is licensed under the GPL.
- */
-
-#ifndef HW_TIMER_TMU012_H
-#define HW_TIMER_TMU012_H
-
-#include "exec/hwaddr.h"
-
-#define TMU012_FEAT_TOCR   (1 << 0)
-#define TMU012_FEAT_3CHAN  (1 << 1)
-#define TMU012_FEAT_EXTCLK (1 << 2)
-
-void tmu012_init(MemoryRegion *sysmem, hwaddr base,
-                 int feat, uint32_t freq,
-                 qemu_irq ch0_irq, qemu_irq ch1_irq,
-                 qemu_irq ch2_irq0, qemu_irq ch2_irq1);
-
-#endif
diff --git a/hw/timer/sh_timer.c b/hw/timer/sh_timer.c
deleted file mode 100644
index 58af1a1edb..0000000000
--- a/hw/timer/sh_timer.c
+++ /dev/null
@@ -1,368 +0,0 @@
-/*
- * SuperH Timer modules.
- *
- * Copyright (c) 2007 Magnus Damm
- * Based on arm_timer.c by Paul Brook
- * Copyright (c) 2005-2006 CodeSourcery.
- *
- * This code is licensed under the GPL.
- */
-
-#include "qemu/osdep.h"
-#include "exec/memory.h"
-#include "hw/hw.h"
-#include "hw/irq.h"
-#include "hw/sh4/sh.h"
-#include "hw/timer/tmu012.h"
-#include "hw/ptimer.h"
-
-//#define DEBUG_TIMER
-
-#define TIMER_TCR_TPSC          (7 << 0)
-#define TIMER_TCR_CKEG          (3 << 3)
-#define TIMER_TCR_UNIE          (1 << 5)
-#define TIMER_TCR_ICPE          (3 << 6)
-#define TIMER_TCR_UNF           (1 << 8)
-#define TIMER_TCR_ICPF          (1 << 9)
-#define TIMER_TCR_RESERVED      (0x3f << 10)
-
-#define TIMER_FEAT_CAPT   (1 << 0)
-#define TIMER_FEAT_EXTCLK (1 << 1)
-
-#define OFFSET_TCOR   0
-#define OFFSET_TCNT   1
-#define OFFSET_TCR    2
-#define OFFSET_TCPR   3
-
-typedef struct {
-    ptimer_state *timer;
-    uint32_t tcnt;
-    uint32_t tcor;
-    uint32_t tcr;
-    uint32_t tcpr;
-    int freq;
-    int int_level;
-    int old_level;
-    int feat;
-    int enabled;
-    qemu_irq irq;
-} sh_timer_state;
-
-/* Check all active timers, and schedule the next timer interrupt. */
-
-static void sh_timer_update(sh_timer_state *s)
-{
-    int new_level = s->int_level && (s->tcr & TIMER_TCR_UNIE);
-
-    if (new_level != s->old_level)
-      qemu_set_irq (s->irq, new_level);
-
-    s->old_level = s->int_level;
-    s->int_level = new_level;
-}
-
-static uint32_t sh_timer_read(void *opaque, hwaddr offset)
-{
-    sh_timer_state *s = (sh_timer_state *)opaque;
-
-    switch (offset >> 2) {
-    case OFFSET_TCOR:
-        return s->tcor;
-    case OFFSET_TCNT:
-        return ptimer_get_count(s->timer);
-    case OFFSET_TCR:
-        return s->tcr | (s->int_level ? TIMER_TCR_UNF : 0);
-    case OFFSET_TCPR:
-        if (s->feat & TIMER_FEAT_CAPT)
-            return s->tcpr;
-        /* fall through */
-    default:
-        hw_error("sh_timer_read: Bad offset %x\n", (int)offset);
-        return 0;
-    }
-}
-
-static void sh_timer_write(void *opaque, hwaddr offset,
-                            uint32_t value)
-{
-    sh_timer_state *s = (sh_timer_state *)opaque;
-    int freq;
-
-    switch (offset >> 2) {
-    case OFFSET_TCOR:
-        s->tcor = value;
-        ptimer_transaction_begin(s->timer);
-        ptimer_set_limit(s->timer, s->tcor, 0);
-        ptimer_transaction_commit(s->timer);
-        break;
-    case OFFSET_TCNT:
-        s->tcnt = value;
-        ptimer_transaction_begin(s->timer);
-        ptimer_set_count(s->timer, s->tcnt);
-        ptimer_transaction_commit(s->timer);
-        break;
-    case OFFSET_TCR:
-        ptimer_transaction_begin(s->timer);
-        if (s->enabled) {
-            /* Pause the timer if it is running.  This may cause some
-               inaccuracy dure to rounding, but avoids a whole lot of other
-               messyness.  */
-            ptimer_stop(s->timer);
-        }
-        freq = s->freq;
-        /* ??? Need to recalculate expiry time after changing divisor.  */
-        switch (value & TIMER_TCR_TPSC) {
-        case 0: freq >>= 2; break;
-        case 1: freq >>= 4; break;
-        case 2: freq >>= 6; break;
-        case 3: freq >>= 8; break;
-        case 4: freq >>= 10; break;
-        case 6:
-        case 7:
-            if (s->feat & TIMER_FEAT_EXTCLK) {
-                break;
-            }
-            /* fallthrough */
-        default:
-            hw_error("sh_timer_write: Reserved TPSC value\n");
-        }
-        switch ((value & TIMER_TCR_CKEG) >> 3) {
-        case 0:
-            break;
-        case 1:
-        case 2:
-        case 3:
-            if (s->feat & TIMER_FEAT_EXTCLK) {
-                break;
-            }
-            /* fallthrough */
-        default:
-            hw_error("sh_timer_write: Reserved CKEG value\n");
-        }
-        switch ((value & TIMER_TCR_ICPE) >> 6) {
-        case 0:
-            break;
-        case 2:
-        case 3:
-            if (s->feat & TIMER_FEAT_CAPT) {
-                break;
-            }
-            /* fallthrough */
-        default:
-            hw_error("sh_timer_write: Reserved ICPE value\n");
-        }
-        if ((value & TIMER_TCR_UNF) == 0) {
-            s->int_level = 0;
-        }
-
-        value &= ~TIMER_TCR_UNF;
-
-        if ((value & TIMER_TCR_ICPF) && (!(s->feat & TIMER_FEAT_CAPT))) {
-            hw_error("sh_timer_write: Reserved ICPF value\n");
-        }
-
-        value &= ~TIMER_TCR_ICPF; /* capture not supported */
-
-        if (value & TIMER_TCR_RESERVED) {
-            hw_error("sh_timer_write: Reserved TCR bits set\n");
-        }
-        s->tcr = value;
-        ptimer_set_limit(s->timer, s->tcor, 0);
-        ptimer_set_freq(s->timer, freq);
-        if (s->enabled) {
-            /* Restart the timer if still enabled.  */
-            ptimer_run(s->timer, 0);
-        }
-        ptimer_transaction_commit(s->timer);
-        break;
-    case OFFSET_TCPR:
-        if (s->feat & TIMER_FEAT_CAPT) {
-            s->tcpr = value;
-            break;
-        }
-        /* fallthrough */
-    default:
-        hw_error("sh_timer_write: Bad offset %x\n", (int)offset);
-    }
-    sh_timer_update(s);
-}
-
-static void sh_timer_start_stop(void *opaque, int enable)
-{
-    sh_timer_state *s = (sh_timer_state *)opaque;
-
-#ifdef DEBUG_TIMER
-    printf("sh_timer_start_stop %d (%d)\n", enable, s->enabled);
-#endif
-
-    ptimer_transaction_begin(s->timer);
-    if (s->enabled && !enable) {
-        ptimer_stop(s->timer);
-    }
-    if (!s->enabled && enable) {
-        ptimer_run(s->timer, 0);
-    }
-    ptimer_transaction_commit(s->timer);
-    s->enabled = !!enable;
-
-#ifdef DEBUG_TIMER
-    printf("sh_timer_start_stop done %d\n", s->enabled);
-#endif
-}
-
-static void sh_timer_tick(void *opaque)
-{
-    sh_timer_state *s = (sh_timer_state *)opaque;
-    s->int_level = s->enabled;
-    sh_timer_update(s);
-}
-
-static void *sh_timer_init(uint32_t freq, int feat, qemu_irq irq)
-{
-    sh_timer_state *s;
-
-    s = (sh_timer_state *)g_malloc0(sizeof(sh_timer_state));
-    s->freq = freq;
-    s->feat = feat;
-    s->tcor = 0xffffffff;
-    s->tcnt = 0xffffffff;
-    s->tcpr = 0xdeadbeef;
-    s->tcr = 0;
-    s->enabled = 0;
-    s->irq = irq;
-
-    s->timer = ptimer_init(sh_timer_tick, s, PTIMER_POLICY_DEFAULT);
-
-    sh_timer_write(s, OFFSET_TCOR >> 2, s->tcor);
-    sh_timer_write(s, OFFSET_TCNT >> 2, s->tcnt);
-    sh_timer_write(s, OFFSET_TCPR >> 2, s->tcpr);
-    sh_timer_write(s, OFFSET_TCR  >> 2, s->tcpr);
-    /* ??? Save/restore.  */
-    return s;
-}
-
-typedef struct {
-    MemoryRegion iomem;
-    MemoryRegion iomem_p4;
-    MemoryRegion iomem_a7;
-    void *timer[3];
-    int level[3];
-    uint32_t tocr;
-    uint32_t tstr;
-    int feat;
-} tmu012_state;
-
-static uint64_t tmu012_read(void *opaque, hwaddr offset,
-                            unsigned size)
-{
-    tmu012_state *s = (tmu012_state *)opaque;
-
-#ifdef DEBUG_TIMER
-    printf("tmu012_read 0x%lx\n", (unsigned long) offset);
-#endif
-
-    if (offset >= 0x20) {
-        if (!(s->feat & TMU012_FEAT_3CHAN)) {
-            hw_error("tmu012_write: Bad channel offset %x\n", (int)offset);
-        }
-        return sh_timer_read(s->timer[2], offset - 0x20);
-    }
-
-    if (offset >= 0x14)
-        return sh_timer_read(s->timer[1], offset - 0x14);
-
-    if (offset >= 0x08)
-        return sh_timer_read(s->timer[0], offset - 0x08);
-
-    if (offset == 4)
-        return s->tstr;
-
-    if ((s->feat & TMU012_FEAT_TOCR) && offset == 0)
-        return s->tocr;
-
-    hw_error("tmu012_write: Bad offset %x\n", (int)offset);
-    return 0;
-}
-
-static void tmu012_write(void *opaque, hwaddr offset,
-                        uint64_t value, unsigned size)
-{
-    tmu012_state *s = (tmu012_state *)opaque;
-
-#ifdef DEBUG_TIMER
-    printf("tmu012_write 0x%lx 0x%08x\n", (unsigned long) offset, value);
-#endif
-
-    if (offset >= 0x20) {
-        if (!(s->feat & TMU012_FEAT_3CHAN)) {
-            hw_error("tmu012_write: Bad channel offset %x\n", (int)offset);
-        }
-        sh_timer_write(s->timer[2], offset - 0x20, value);
-        return;
-    }
-
-    if (offset >= 0x14) {
-        sh_timer_write(s->timer[1], offset - 0x14, value);
-        return;
-    }
-
-    if (offset >= 0x08) {
-        sh_timer_write(s->timer[0], offset - 0x08, value);
-        return;
-    }
-
-    if (offset == 4) {
-        sh_timer_start_stop(s->timer[0], value & (1 << 0));
-        sh_timer_start_stop(s->timer[1], value & (1 << 1));
-        if (s->feat & TMU012_FEAT_3CHAN) {
-            sh_timer_start_stop(s->timer[2], value & (1 << 2));
-        } else {
-            if (value & (1 << 2)) {
-                hw_error("tmu012_write: Bad channel\n");
-            }
-        }
-
-        s->tstr = value;
-        return;
-    }
-
-    if ((s->feat & TMU012_FEAT_TOCR) && offset == 0) {
-        s->tocr = value & (1 << 0);
-    }
-}
-
-static const MemoryRegionOps tmu012_ops = {
-    .read = tmu012_read,
-    .write = tmu012_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-void tmu012_init(MemoryRegion *sysmem, hwaddr base,
-                 int feat, uint32_t freq,
-                 qemu_irq ch0_irq, qemu_irq ch1_irq,
-                 qemu_irq ch2_irq0, qemu_irq ch2_irq1)
-{
-    tmu012_state *s;
-    int timer_feat = (feat & TMU012_FEAT_EXTCLK) ? TIMER_FEAT_EXTCLK : 0;
-
-    s = (tmu012_state *)g_malloc0(sizeof(tmu012_state));
-    s->feat = feat;
-    s->timer[0] = sh_timer_init(freq, timer_feat, ch0_irq);
-    s->timer[1] = sh_timer_init(freq, timer_feat, ch1_irq);
-    if (feat & TMU012_FEAT_3CHAN) {
-        s->timer[2] = sh_timer_init(freq, timer_feat | TIMER_FEAT_CAPT,
-                                    ch2_irq0); /* ch2_irq1 not supported */
-    }
-
-    memory_region_init_io(&s->iomem, NULL, &tmu012_ops, s,
-                          "timer", 0x100000000ULL);
-
-    memory_region_init_alias(&s->iomem_p4, NULL, "timer-p4",
-                             &s->iomem, 0, 0x1000);
-    memory_region_add_subregion(sysmem, P4ADDR(base), &s->iomem_p4);
-
-    memory_region_init_alias(&s->iomem_a7, NULL, "timer-a7",
-                             &s->iomem, 0, 0x1000);
-    memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7);
-    /* ??? Save/restore.  */
-}
diff --git a/MAINTAINERS b/MAINTAINERS
index ecfa97cefa..4e41c3ff91 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1414,7 +1414,7 @@ F: hw/char/renesas_sci.c
 F: hw/sh4/r2d.c
 F: hw/intc/sh_intc.c
 F: hw/pci-host/sh_pci.c
-F: hw/timer/sh_timer.c
+F: hw/timer/renesas_timer.c
 F: include/hw/sh4/sh_intc.h
 
 Shix
@@ -1425,7 +1425,7 @@ F: hw/block/tc58128.c
 F: hw/char/reness_sci.c
 F: hw/sh4/shix.c
 F: hw/intc/sh_intc.c
-F: hw/timer/sh_timer.c
+F: hw/timer/renesas_timer.c
 F: include/hw/sh4/sh_intc.h
 
 SPARC Machines
diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig
index 347add12dd..ec19708a4d 100644
--- a/hw/timer/Kconfig
+++ b/hw/timer/Kconfig
@@ -36,10 +36,6 @@ config CMSDK_APB_DUALTIMER
     bool
     select PTIMER
 
-config SH_TIMER
-    bool
-    select PTIMER
-
 config RENESAS_TMR
     bool
 
diff --git a/hw/timer/meson.build b/hw/timer/meson.build
index 9019dce993..ec70821c0b 100644
--- a/hw/timer/meson.build
+++ b/hw/timer/meson.build
@@ -27,7 +27,6 @@ softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_gptimer.c'))
 softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_synctimer.c'))
 softmmu_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_timer.c'))
 softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_systmr.c'))
-softmmu_ss.add(when: 'CONFIG_SH_TIMER', if_true: files('sh_timer.c'))
 softmmu_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_timer.c'))
 softmmu_ss.add(when: 'CONFIG_SSE_COUNTER', if_true: files('sse-counter.c'))
 softmmu_ss.add(when: 'CONFIG_SSE_TIMER', if_true: files('sse-timer.c'))
-- 
2.20.1



^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 05/11] hw/timer: Remove renesas_cmt.
  2021-05-27  5:21 [PATCH 00/11] Unified peripheral emulation for Renesas chips Yoshinori Sato
                   ` (3 preceding siblings ...)
  2021-05-27  5:21 ` [PATCH 04/11] hw/timer: Remove sh_timer Yoshinori Sato
@ 2021-05-27  5:21 ` Yoshinori Sato
  2021-05-27  5:21 ` [PATCH 06/11] hw/rx: Add RX62N Clock generator Yoshinori Sato
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 21+ messages in thread
From: Yoshinori Sato @ 2021-05-27  5:21 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

Migrate to renesas_timer.

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 include/hw/timer/renesas_cmt.h |  43 -----
 hw/timer/renesas_cmt.c         | 283 ---------------------------------
 hw/timer/meson.build           |   1 -
 3 files changed, 327 deletions(-)
 delete mode 100644 include/hw/timer/renesas_cmt.h
 delete mode 100644 hw/timer/renesas_cmt.c

diff --git a/include/hw/timer/renesas_cmt.h b/include/hw/timer/renesas_cmt.h
deleted file mode 100644
index 1c0b65c1d5..0000000000
--- a/include/hw/timer/renesas_cmt.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Renesas Compare-match timer Object
- *
- * Copyright (c) 2019 Yoshinori Sato
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-
-#ifndef HW_TIMER_RENESAS_CMT_H
-#define HW_TIMER_RENESAS_CMT_H
-
-#include "qemu/timer.h"
-#include "hw/sysbus.h"
-#include "qom/object.h"
-
-#define TYPE_RENESAS_CMT "renesas-cmt"
-typedef struct RCMTState RCMTState;
-DECLARE_INSTANCE_CHECKER(RCMTState, RCMT,
-                         TYPE_RENESAS_CMT)
-
-enum {
-    CMT_CH = 2,
-    CMT_NR_IRQ = 1 * CMT_CH
-};
-
-struct RCMTState {
-    /*< private >*/
-    SysBusDevice parent_obj;
-    /*< public >*/
-
-    uint64_t input_freq;
-    MemoryRegion memory;
-
-    uint16_t cmstr;
-    uint16_t cmcr[CMT_CH];
-    uint16_t cmcnt[CMT_CH];
-    uint16_t cmcor[CMT_CH];
-    int64_t tick[CMT_CH];
-    qemu_irq cmi[CMT_CH];
-    QEMUTimer timer[CMT_CH];
-};
-
-#endif
diff --git a/hw/timer/renesas_cmt.c b/hw/timer/renesas_cmt.c
deleted file mode 100644
index 2e0fd21a36..0000000000
--- a/hw/timer/renesas_cmt.c
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Renesas 16bit Compare-match timer
- *
- * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
- *            (Rev.1.40 R01UH0033EJ0140)
- *
- * Copyright (c) 2019 Yoshinori Sato
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- *
- * 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/log.h"
-#include "hw/irq.h"
-#include "hw/registerfields.h"
-#include "hw/qdev-properties.h"
-#include "hw/timer/renesas_cmt.h"
-#include "migration/vmstate.h"
-
-/*
- *  +0 CMSTR - common control
- *  +2 CMCR  - ch0
- *  +4 CMCNT - ch0
- *  +6 CMCOR - ch0
- *  +8 CMCR  - ch1
- * +10 CMCNT - ch1
- * +12 CMCOR - ch1
- * If we think that the address of CH 0 has an offset of +2,
- * we can treat it with the same address as CH 1, so define it like that.
- */
-REG16(CMSTR, 0)
-  FIELD(CMSTR, STR0, 0, 1)
-  FIELD(CMSTR, STR1, 1, 1)
-  FIELD(CMSTR, STR,  0, 2)
-/* This addeess is channel offset */
-REG16(CMCR, 0)
-  FIELD(CMCR, CKS,  0, 2)
-  FIELD(CMCR, CMIE, 6, 1)
-REG16(CMCNT, 2)
-REG16(CMCOR, 4)
-
-static void update_events(RCMTState *cmt, int ch)
-{
-    int64_t next_time;
-
-    if ((cmt->cmstr & (1 << ch)) == 0) {
-        /* count disable, so not happened next event. */
-        return ;
-    }
-    next_time = cmt->cmcor[ch] - cmt->cmcnt[ch];
-    next_time *= NANOSECONDS_PER_SECOND;
-    next_time /= cmt->input_freq;
-    /*
-     * CKS -> div rate
-     *  0 -> 8 (1 << 3)
-     *  1 -> 32 (1 << 5)
-     *  2 -> 128 (1 << 7)
-     *  3 -> 512 (1 << 9)
-     */
-    next_time *= 1 << (3 + FIELD_EX16(cmt->cmcr[ch], CMCR, CKS) * 2);
-    next_time += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-    timer_mod(&cmt->timer[ch], next_time);
-}
-
-static int64_t read_cmcnt(RCMTState *cmt, int ch)
-{
-    int64_t delta, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-
-    if (cmt->cmstr & (1 << ch)) {
-        delta = (now - cmt->tick[ch]);
-        delta /= NANOSECONDS_PER_SECOND;
-        delta /= cmt->input_freq;
-        delta /= 1 << (3 + FIELD_EX16(cmt->cmcr[ch], CMCR, CKS) * 2);
-        cmt->tick[ch] = now;
-        return cmt->cmcnt[ch] + delta;
-    } else {
-        return cmt->cmcnt[ch];
-    }
-}
-
-static uint64_t cmt_read(void *opaque, hwaddr offset, unsigned size)
-{
-    RCMTState *cmt = opaque;
-    int ch = offset / 0x08;
-    uint64_t ret;
-
-    if (offset == A_CMSTR) {
-        ret = 0;
-        ret = FIELD_DP16(ret, CMSTR, STR,
-                         FIELD_EX16(cmt->cmstr, CMSTR, STR));
-        return ret;
-    } else {
-        offset &= 0x07;
-        if (ch == 0) {
-            offset -= 0x02;
-        }
-        switch (offset) {
-        case A_CMCR:
-            ret = 0;
-            ret = FIELD_DP16(ret, CMCR, CKS,
-                             FIELD_EX16(cmt->cmstr, CMCR, CKS));
-            ret = FIELD_DP16(ret, CMCR, CMIE,
-                             FIELD_EX16(cmt->cmstr, CMCR, CMIE));
-            return ret;
-        case A_CMCNT:
-            return read_cmcnt(cmt, ch);
-        case A_CMCOR:
-            return cmt->cmcor[ch];
-        }
-    }
-    qemu_log_mask(LOG_UNIMP, "renesas_cmt: Register 0x%" HWADDR_PRIX " "
-                             "not implemented\n",
-                  offset);
-    return UINT64_MAX;
-}
-
-static void start_stop(RCMTState *cmt, int ch, int st)
-{
-    if (st) {
-        update_events(cmt, ch);
-    } else {
-        timer_del(&cmt->timer[ch]);
-    }
-}
-
-static void cmt_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
-{
-    RCMTState *cmt = opaque;
-    int ch = offset / 0x08;
-
-    if (offset == A_CMSTR) {
-        cmt->cmstr = FIELD_EX16(val, CMSTR, STR);
-        start_stop(cmt, 0, FIELD_EX16(cmt->cmstr, CMSTR, STR0));
-        start_stop(cmt, 1, FIELD_EX16(cmt->cmstr, CMSTR, STR1));
-    } else {
-        offset &= 0x07;
-        if (ch == 0) {
-            offset -= 0x02;
-        }
-        switch (offset) {
-        case A_CMCR:
-            cmt->cmcr[ch] = FIELD_DP16(cmt->cmcr[ch], CMCR, CKS,
-                                       FIELD_EX16(val, CMCR, CKS));
-            cmt->cmcr[ch] = FIELD_DP16(cmt->cmcr[ch], CMCR, CMIE,
-                                       FIELD_EX16(val, CMCR, CMIE));
-            break;
-        case 2:
-            cmt->cmcnt[ch] = val;
-            break;
-        case 4:
-            cmt->cmcor[ch] = val;
-            break;
-        default:
-            qemu_log_mask(LOG_UNIMP, "renesas_cmt: Register 0x%" HWADDR_PRIX " "
-                                     "not implemented\n",
-                          offset);
-            return;
-        }
-        if (FIELD_EX16(cmt->cmstr, CMSTR, STR) & (1 << ch)) {
-            update_events(cmt, ch);
-        }
-    }
-}
-
-static const MemoryRegionOps cmt_ops = {
-    .write = cmt_write,
-    .read  = cmt_read,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-    .impl = {
-        .min_access_size = 2,
-        .max_access_size = 2,
-    },
-    .valid = {
-        .min_access_size = 2,
-        .max_access_size = 2,
-    },
-};
-
-static void timer_events(RCMTState *cmt, int ch)
-{
-    cmt->cmcnt[ch] = 0;
-    cmt->tick[ch] = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-    update_events(cmt, ch);
-    if (FIELD_EX16(cmt->cmcr[ch], CMCR, CMIE)) {
-        qemu_irq_pulse(cmt->cmi[ch]);
-    }
-}
-
-static void timer_event0(void *opaque)
-{
-    RCMTState *cmt = opaque;
-
-    timer_events(cmt, 0);
-}
-
-static void timer_event1(void *opaque)
-{
-    RCMTState *cmt = opaque;
-
-    timer_events(cmt, 1);
-}
-
-static void rcmt_reset(DeviceState *dev)
-{
-    RCMTState *cmt = RCMT(dev);
-    cmt->cmstr = 0;
-    cmt->cmcr[0] = cmt->cmcr[1] = 0;
-    cmt->cmcnt[0] = cmt->cmcnt[1] = 0;
-    cmt->cmcor[0] = cmt->cmcor[1] = 0xffff;
-}
-
-static void rcmt_init(Object *obj)
-{
-    SysBusDevice *d = SYS_BUS_DEVICE(obj);
-    RCMTState *cmt = RCMT(obj);
-    int i;
-
-    memory_region_init_io(&cmt->memory, OBJECT(cmt), &cmt_ops,
-                          cmt, "renesas-cmt", 0x10);
-    sysbus_init_mmio(d, &cmt->memory);
-
-    for (i = 0; i < ARRAY_SIZE(cmt->cmi); i++) {
-        sysbus_init_irq(d, &cmt->cmi[i]);
-    }
-    timer_init_ns(&cmt->timer[0], QEMU_CLOCK_VIRTUAL, timer_event0, cmt);
-    timer_init_ns(&cmt->timer[1], QEMU_CLOCK_VIRTUAL, timer_event1, cmt);
-}
-
-static const VMStateDescription vmstate_rcmt = {
-    .name = "rx-cmt",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT16(cmstr, RCMTState),
-        VMSTATE_UINT16_ARRAY(cmcr, RCMTState, CMT_CH),
-        VMSTATE_UINT16_ARRAY(cmcnt, RCMTState, CMT_CH),
-        VMSTATE_UINT16_ARRAY(cmcor, RCMTState, CMT_CH),
-        VMSTATE_INT64_ARRAY(tick, RCMTState, CMT_CH),
-        VMSTATE_TIMER_ARRAY(timer, RCMTState, CMT_CH),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static Property rcmt_properties[] = {
-    DEFINE_PROP_UINT64("input-freq", RCMTState, input_freq, 0),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void rcmt_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-
-    dc->vmsd = &vmstate_rcmt;
-    dc->reset = rcmt_reset;
-    device_class_set_props(dc, rcmt_properties);
-}
-
-static const TypeInfo rcmt_info = {
-    .name = TYPE_RENESAS_CMT,
-    .parent = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(RCMTState),
-    .instance_init = rcmt_init,
-    .class_init = rcmt_class_init,
-};
-
-static void rcmt_register_types(void)
-{
-    type_register_static(&rcmt_info);
-}
-
-type_init(rcmt_register_types)
diff --git a/hw/timer/meson.build b/hw/timer/meson.build
index ec70821c0b..03b40cfbee 100644
--- a/hw/timer/meson.build
+++ b/hw/timer/meson.build
@@ -9,7 +9,6 @@ softmmu_ss.add(when: 'CONFIG_CADENCE', if_true: files('cadence_ttc.c'))
 softmmu_ss.add(when: 'CONFIG_CMSDK_APB_DUALTIMER', if_true: files('cmsdk-apb-dualtimer.c'))
 softmmu_ss.add(when: 'CONFIG_CMSDK_APB_TIMER', if_true: files('cmsdk-apb-timer.c'))
 softmmu_ss.add(when: 'CONFIG_RENESAS_TMR', if_true: files('renesas_tmr.c'))
-softmmu_ss.add(when: 'CONFIG_RENESAS_CMT', if_true: files('renesas_cmt.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



^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 06/11] hw/rx: Add RX62N Clock generator
  2021-05-27  5:21 [PATCH 00/11] Unified peripheral emulation for Renesas chips Yoshinori Sato
                   ` (4 preceding siblings ...)
  2021-05-27  5:21 ` [PATCH 05/11] hw/timer: Remove renesas_cmt Yoshinori Sato
@ 2021-05-27  5:21 ` Yoshinori Sato
  2021-05-27  5:21 ` [PATCH 07/11] hw/timer: Renesas 8bit timer Yoshinori Sato
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 21+ messages in thread
From: Yoshinori Sato @ 2021-05-27  5:21 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

This module generated core and peripheral clock.

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 include/hw/rx/rx62n-cpg.h |  72 ++++++++
 include/hw/rx/rx62n.h     |   8 +-
 hw/rx/rx62n-cpg.c         | 344 ++++++++++++++++++++++++++++++++++++++
 hw/rx/rx62n.c             |  39 +++--
 hw/rx/meson.build         |   2 +-
 5 files changed, 449 insertions(+), 16 deletions(-)
 create mode 100644 include/hw/rx/rx62n-cpg.h
 create mode 100644 hw/rx/rx62n-cpg.c

diff --git a/include/hw/rx/rx62n-cpg.h b/include/hw/rx/rx62n-cpg.h
new file mode 100644
index 0000000000..d90a067313
--- /dev/null
+++ b/include/hw/rx/rx62n-cpg.h
@@ -0,0 +1,72 @@
+/*
+ * RX62N Clock generator circuit
+ *
+ * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
+ * (Rev.1.40 R01UH0033EJ0140)
+ *
+ * Copyright (c) 2020 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/>.
+ */
+
+#ifndef HW_RX_RX62N_CPG_H
+#define HW_RX_RX62N_CPG_H
+
+#include "hw/sysbus.h"
+#include "hw/qdev-clock.h"
+
+#define TYPE_RX62N_CPG "rx62n-cpg"
+#define RX62NCPG(obj) OBJECT_CHECK(RX62NCPGState, (obj), TYPE_RX62N_CPG)
+
+enum {
+    CK_TMR8_1,
+    CK_TMR8_0,
+    CK_MTU_1,
+    CK_MTU_0,
+    CK_CMT_1,
+    CK_CMT_0,
+    CK_EDMAC,
+    CK_SCI6,
+    CK_SCI5,
+    CK_SCI3,
+    CK_SCI2,
+    CK_SCI1,
+    CK_SCI0,
+    NUM_SUBCLOCK,
+};
+
+typedef struct RX62NCPGState {
+    SysBusDevice parent_obj;
+    uint32_t mstpcr[3];
+    uint32_t sckcr;
+    uint8_t  bckcr;
+    uint8_t  ostdcr;
+
+    int ick;
+    Clock *clk_ick;
+    int bck;
+    Clock *clk_bck;
+    int pck;
+    Clock *clk_pck;
+    Clock *dev_clocks[NUM_SUBCLOCK];
+    uint32_t xtal_freq_hz;
+    MemoryRegion memory;
+} RX62NCPGState;
+
+typedef struct RX62NCPGClass {
+    SysBusDeviceClass parent;
+} RX62NCPGClass;
+
+#define OSTDCR_KEY 0xac
+
+#endif
diff --git a/include/hw/rx/rx62n.h b/include/hw/rx/rx62n.h
index 3ed80dba0d..44f5fcc74d 100644
--- a/include/hw/rx/rx62n.h
+++ b/include/hw/rx/rx62n.h
@@ -29,6 +29,7 @@
 #include "hw/timer/renesas_tmr.h"
 #include "hw/timer/renesas_cmt.h"
 #include "hw/char/renesas_sci.h"
+#include "hw/rx/rx62n-cpg.h"
 #include "qemu/units.h"
 #include "qom/object.h"
 
@@ -58,9 +59,9 @@ struct RX62NState {
     RTMRState tmr[RX62N_NR_TMR];
     RCMTState cmt[RX62N_NR_CMT];
     RSCIState sci[RX62N_NR_SCI];
+    RX62NCPGState cpg;
 
     MemoryRegion *sysmem;
-    bool kernel;
 
     MemoryRegion iram;
     MemoryRegion iomem1;
@@ -72,8 +73,7 @@ struct RX62NState {
 
     /* Input Clock (XTAL) frequency */
     uint32_t xtal_freq_hz;
-    /* Peripheral Module Clock frequency */
-    uint32_t pclk_freq_hz;
-};
+} RX62NState;
+
 
 #endif
diff --git a/hw/rx/rx62n-cpg.c b/hw/rx/rx62n-cpg.c
new file mode 100644
index 0000000000..9d70004302
--- /dev/null
+++ b/hw/rx/rx62n-cpg.c
@@ -0,0 +1,344 @@
+/*
+ * RX62N Clock Generation Circuit
+ *
+ * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
+ * (Rev.1.40 R01UH0033EJ0140)
+ *
+ * Copyright (c) 2020 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 "qapi/error.h"
+#include "qemu/log.h"
+#include "hw/hw.h"
+#include "hw/rx/rx62n-cpg.h"
+#include "hw/sysbus.h"
+#include "hw/qdev-properties.h"
+#include "hw/registerfields.h"
+#include "hw/qdev-properties.h"
+#include "hw/clock.h"
+#include "migration/vmstate.h"
+
+#define RX62N_XTAL_MIN_HZ  (8 * 1000 * 1000)
+#define RX62N_XTAL_MAX_HZ (14 * 1000 * 1000)
+
+REG32(MSTPCRA, 0)
+REG32(MSTPCRB, 4)
+REG32(MSTPCRC, 8)
+REG32(SCKCR, 16)
+  FIELD(SCKCR, PCK,  8, 3)
+  FIELD(SCKCR, BCK, 16, 3)
+  FIELD(SCKCR, PSTOP, 22, 2)
+  FIELD(SCKCR, ICK, 24, 3)
+REG8(BCKCR, 32)
+  FIELD(BCKCR, BCLKDIV, 0, 1)
+REG16(OSTDCR, 48)
+  FIELD(OSTDCR, OSTDF, 6, 1)
+  FIELD(OSTDCR, OSTDE, 7, 1)
+
+static const int access_size[] = {4, 4, 1, 2};
+
+typedef struct {
+    const char *name;
+    int devnum;
+    int reg;
+    int offset;
+    int parentck;
+} dev_clock_t;
+
+enum {
+    parent_ick, parent_bck, parent_pck,
+};
+
+static const dev_clock_t dev_clock_list[] = {
+    { .name = "pck_tmr8-1",
+      .devnum = CK_TMR8_1, .reg = 0, .offset = 4, .parentck = parent_pck, },
+    { .name = "pck_tmr8-0",
+      .devnum = CK_TMR8_0, .reg = 0, .offset = 5, .parentck = parent_pck, },
+    { .name = "pck_mtu-1",
+      .devnum = CK_MTU_1, .reg = 0, .offset = 8, .parentck = parent_pck, },
+    { .name = "pck_mtu-0",
+      .devnum = CK_MTU_0, .reg = 0, .offset = 9, .parentck = parent_pck, },
+    { .name = "pck_cmt-1",
+      .devnum = CK_CMT_1, .reg = 0, .offset = 14, .parentck = parent_pck, },
+    { .name = "pck_cmt-0",
+      .devnum = CK_CMT_0, .reg = 0, .offset = 15, .parentck = parent_pck, },
+    { .name = "ick_edmac",
+      .devnum = CK_EDMAC, .reg = 1, .offset = 15, .parentck = parent_ick, },
+    { .name = "pck_sci-6",
+      .devnum = CK_SCI6, .reg = 1, .offset = 25, .parentck = parent_pck, },
+    { .name = "pck_sci-5",
+      .devnum = CK_SCI5, .reg = 1, .offset = 26, .parentck = parent_pck, },
+    { .name = "pck_sci-3",
+      .devnum = CK_SCI3, .reg = 1, .offset = 28, .parentck = parent_pck, },
+    { .name = "pck_sci-2",
+      .devnum = CK_SCI2, .reg = 1, .offset = 29, .parentck = parent_pck, },
+    { .name = "pck_sci-1",
+      .devnum = CK_SCI1, .reg = 1, .offset = 30, .parentck = parent_pck, },
+    { .name = "pck_sci-0",
+      .devnum = CK_SCI0, .reg = 1, .offset = 31, .parentck = parent_pck, },
+    { },
+};
+
+static void set_clock_in(RX62NCPGState *cpg, const dev_clock_t *ck)
+{
+    Clock *out;
+    uint64_t period;
+
+    out = qdev_get_clock_out(DEVICE(cpg), ck->name);
+    g_assert(out);
+    period = 0;
+    if (extract32(cpg->mstpcr[ck->reg], ck->offset, 1) == 0) {
+        switch (ck->parentck) {
+        case parent_ick:
+            period = clock_get(cpg->clk_ick);
+            break;
+        case parent_pck:
+            period = clock_get(cpg->clk_pck);
+            break;
+        }
+    }
+    if (clock_get(out) != period) {
+        clock_update(out, period);
+    }
+}
+
+#define update_ck(ckname)                                             \
+    if (cpg->ckname != ckname) {                                      \
+        cpg->ckname = ckname;                                         \
+        ckname =  8 / (1 << ckname);                                  \
+        clock_update_hz(cpg->clk_ ## ckname,                          \
+                        cpg->xtal_freq_hz * ckname);                  \
+    }
+
+#define validate_setting(ckname)                                 \
+    if (ick > ckname) {                                         \
+        qemu_log_mask(LOG_GUEST_ERROR,                           \
+                      "rx62n-cpg: Invalid " #ckname " setting."   \
+                      " (ick=%d " #ckname "=%d)\n", ick, ckname); \
+        cpg->ckname = ckname = ick;                              \
+    }
+
+static void update_divrate(RX62NCPGState *cpg)
+{
+    int ick = FIELD_EX32(cpg->sckcr, SCKCR, ICK);
+    int bck = FIELD_EX32(cpg->sckcr, SCKCR, BCK);
+    int pck = FIELD_EX32(cpg->sckcr, SCKCR, PCK);
+    const dev_clock_t *p = dev_clock_list;
+    validate_setting(pck);
+    validate_setting(bck);
+    update_ck(ick);
+    update_ck(bck);
+    update_ck(pck);
+    while (p->name) {
+        set_clock_in(cpg, p);
+        p++;
+    }
+}
+
+static const dev_clock_t *find_clock_list(int crno, int bit)
+{
+    const dev_clock_t *ret = dev_clock_list;
+    while (ret->name) {
+        if (ret->reg == crno && ret->offset == bit) {
+            return ret;
+        }
+        ret++;
+    }
+    return NULL;
+}
+
+static void update_mstpcr(RX62NCPGState *cpg, int crno, uint32_t diff)
+{
+    int bit = 0;
+    const dev_clock_t *p;
+
+    while (diff) {
+        if (diff & 1) {
+            p = find_clock_list(crno, bit);
+            if (p) {
+                set_clock_in(cpg, p);
+            } else {
+                qemu_log_mask(LOG_UNIMP, "rx62n-cpg: MSTPCR%c "
+                              " bit %d is not implement.\n", 'A' + crno, bit);
+            }
+        }
+        bit++;
+        diff >>= 1;
+    }
+}
+
+static uint64_t cpg_read(void *opaque, hwaddr addr, unsigned size)
+{
+    RX62NCPGState *cpg = RX62NCPG(opaque);
+
+    if (access_size[addr >> 4] != size) {
+        qemu_log_mask(LOG_GUEST_ERROR, "rx62n-cpg: Register 0x%"
+                      HWADDR_PRIX " Invalid access size.\n", addr);
+        return UINT64_MAX;
+    }
+    switch (addr) {
+    case A_MSTPCRA:
+        return cpg->mstpcr[0] | 0x473530cf;
+    case A_MSTPCRB:
+        return cpg->mstpcr[1] | 0x09407ffe;
+    case A_MSTPCRC:
+        return (cpg->mstpcr[2] | 0xffff0000) & 0xffff0003;
+    case A_SCKCR:
+        return cpg->sckcr & 0x0fcf0f00;
+    case A_BCKCR:
+        return cpg->bckcr & 0x01;
+    case A_OSTDCR:
+        /* Main OSC always good */
+        return cpg->ostdcr & 0x0080;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "rx62n-cpg: Register 0x%"
+                      HWADDR_PRIX " Invalid address.\n", addr);
+        return UINT64_MAX;
+    }
+}
+
+static void cpg_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    RX62NCPGState *cpg = RX62NCPG(opaque);
+    uint32_t old_mstpcr;
+    int cr_no;
+    if (access_size[addr >> 4] != size) {
+        qemu_log_mask(LOG_GUEST_ERROR, "rx62n-cpg: Register 0x%"
+                      HWADDR_PRIX " Invalid access size.\n", addr);
+        return;
+    }
+    switch (addr) {
+    case A_MSTPCRA:
+    case A_MSTPCRB:
+    case A_MSTPCRC:
+        cr_no = (addr & 0x0f) >> 2;
+        old_mstpcr = cpg->mstpcr[cr_no];
+        old_mstpcr ^= val;
+        cpg->mstpcr[cr_no] = val;
+        update_mstpcr(cpg, cr_no, old_mstpcr);
+        break;
+    case A_SCKCR:
+        cpg->sckcr = val;
+        update_divrate(cpg);
+        break;
+    case A_BCKCR:
+        cpg->bckcr = val;
+        break;
+    case A_OSTDCR:
+        if (extract16(val, 8, 8) == OSTDCR_KEY) {
+            cpg->ostdcr = val;
+        } else {
+            qemu_log_mask(LOG_GUEST_ERROR, "rx62n-cpg: Register 0x%"
+                          HWADDR_PRIX " Invalid key value.\n", addr);
+        }
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "rx62n-cpg: Register 0x%"
+                      HWADDR_PRIX " Invalid address.\n", addr);
+    }
+}
+
+static const MemoryRegionOps cpg_ops = {
+    .write = cpg_write,
+    .read  = cpg_read,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+};
+
+static const ClockPortInitArray rx62n_cpg_clocks = {
+    QDEV_CLOCK_OUT(RX62NCPGState, clk_ick),
+    QDEV_CLOCK_OUT(RX62NCPGState, clk_bck),
+    QDEV_CLOCK_OUT(RX62NCPGState, clk_pck),
+    QDEV_CLOCK_END
+};
+
+static void cpg_realize(DeviceState *dev, Error **errp)
+{
+    RX62NCPGState *cpg = RX62NCPG(dev);
+    const dev_clock_t *p = dev_clock_list;
+
+    if (cpg->xtal_freq_hz == 0) {
+        error_setg(errp, "\"xtal-frequency-hz\" property must be provided.");
+        return;
+    }
+    /* XTAL range: 8-14 MHz */
+    if (cpg->xtal_freq_hz < RX62N_XTAL_MIN_HZ ||
+        cpg->xtal_freq_hz > RX62N_XTAL_MAX_HZ) {
+        error_setg(errp, "\"xtal-frequency-hz\" property in incorrect range.");
+        return;
+    }
+
+    cpg->sckcr = FIELD_DP32(cpg->sckcr, SCKCR, ICK, 2);
+    cpg->sckcr = FIELD_DP32(cpg->sckcr, SCKCR, BCK, 2);
+    cpg->sckcr = FIELD_DP32(cpg->sckcr, SCKCR, PCK, 2);
+    cpg->ostdcr = FIELD_DP8(cpg->ostdcr, OSTDCR, OSTDE, 1);
+    cpg->mstpcr[0] = 0x47ffffff;
+    cpg->mstpcr[1] = 0xffffffff;
+    cpg->mstpcr[2] = 0xffff0000;
+
+    /* set initial state */
+    while (p->name) {
+        set_clock_in(cpg, p);
+        p++;
+    }
+    update_divrate(cpg);
+}
+
+static void rx62n_cpg_init(Object *obj)
+{
+    RX62NCPGState *cpg = RX62NCPG(obj);
+    const dev_clock_t *p = dev_clock_list;
+    qdev_init_clocks(DEVICE(obj), rx62n_cpg_clocks);
+    /* connect parent clock */
+    while (p->name) {
+        cpg->dev_clocks[p->devnum] = qdev_init_clock_out(DEVICE(obj),
+                                                         p->name);
+        p++;
+    }
+
+    memory_region_init_io(&cpg->memory, OBJECT(cpg), &cpg_ops,
+                          cpg, "rx62n-cpg", 0x40);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &cpg->memory);
+}
+
+static Property rx62n_cpg_properties[] = {
+    DEFINE_PROP_UINT32("xtal-frequency-hz", RX62NCPGState, xtal_freq_hz, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void rx62n_cpg_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = cpg_realize;
+    device_class_set_props(dc, rx62n_cpg_properties);
+}
+
+static const TypeInfo rx62n_cpg_info[] = {
+    {
+        .name       = TYPE_RX62N_CPG,
+        .parent     = TYPE_SYS_BUS_DEVICE,
+        .instance_size = sizeof(RX62NCPGState),
+        .instance_init = rx62n_cpg_init,
+        .class_init = rx62n_cpg_class_init,
+        .class_size = sizeof(RX62NCPGClass),
+    },
+};
+
+DEFINE_TYPES(rx62n_cpg_info)
diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c
index fa5add9f9d..cfd41930bf 100644
--- a/hw/rx/rx62n.c
+++ b/hw/rx/rx62n.c
@@ -45,6 +45,7 @@
 #define RX62N_TMR_BASE  0x00088200
 #define RX62N_CMT_BASE  0x00088000
 #define RX62N_SCI_BASE  0x00088240
+#define RX62N_CPG_BASE  0x00080010
 
 /*
  * RX62N Peripheral IRQ
@@ -56,7 +57,6 @@
 
 #define RX62N_XTAL_MIN_HZ  (8 * 1000 * 1000)
 #define RX62N_XTAL_MAX_HZ (14 * 1000 * 1000)
-#define RX62N_PCLK_MAX_HZ (50 * 1000 * 1000)
 
 struct RX62NClass {
     /*< private >*/
@@ -161,36 +161,45 @@ static void register_tmr(RX62NState *s, int unit)
 {
     SysBusDevice *tmr;
     int i, irqbase;
+    char ckname[16];
 
     object_initialize_child(OBJECT(s), "tmr[*]",
                             &s->tmr[unit], TYPE_RENESAS_TMR);
     tmr = SYS_BUS_DEVICE(&s->tmr[unit]);
-    qdev_prop_set_uint64(DEVICE(tmr), "input-freq", s->pclk_freq_hz);
-    sysbus_realize(tmr, &error_abort);
 
     irqbase = RX62N_TMR_IRQ + TMR_NR_IRQ * unit;
     for (i = 0; i < TMR_NR_IRQ; i++) {
         sysbus_connect_irq(tmr, i, s->irq[irqbase + i]);
     }
     sysbus_mmio_map(tmr, 0, RX62N_TMR_BASE + unit * 0x10);
+
+    qdev_prop_set_uint32(DEVICE(tmr), "unit", unit);
+    sysbus_realize(tmr, &error_abort);
+    snprintf(ckname, sizeof(ckname), "pck_tmr8-%d", unit);
+    qdev_connect_clock_in(DEVICE(tmr), "pck",
+                          qdev_get_clock_out(DEVICE(&s->cpg), ckname));
 }
 
 static void register_cmt(RX62NState *s, int unit)
 {
     SysBusDevice *cmt;
     int i, irqbase;
+    char ckname[16];
 
     object_initialize_child(OBJECT(s), "cmt[*]",
                             &s->cmt[unit], TYPE_RENESAS_CMT);
     cmt = SYS_BUS_DEVICE(&s->cmt[unit]);
-    qdev_prop_set_uint64(DEVICE(cmt), "input-freq", s->pclk_freq_hz);
-    sysbus_realize(cmt, &error_abort);
+    qdev_prop_set_uint32(DEVICE(cmt), "unit", unit);
 
     irqbase = RX62N_CMT_IRQ + CMT_NR_IRQ * unit;
     for (i = 0; i < CMT_NR_IRQ; i++) {
         sysbus_connect_irq(cmt, i, s->irq[irqbase + i]);
     }
     sysbus_mmio_map(cmt, 0, RX62N_CMT_BASE + unit * 0x10);
+    sysbus_realize(cmt, &error_abort);
+    snprintf(ckname, sizeof(ckname), "pck_cmt-%d", unit);
+    qdev_connect_clock_in(DEVICE(cmt), "pck",
+                          qdev_get_clock_out(DEVICE(&s->cpg), ckname));
 }
 
 static void register_sci(RX62NState *s, int unit)
@@ -202,7 +211,6 @@ static void register_sci(RX62NState *s, int unit)
                             &s->sci[unit], TYPE_RENESAS_SCI);
     sci = SYS_BUS_DEVICE(&s->sci[unit]);
     qdev_prop_set_chr(DEVICE(sci), "chardev", serial_hd(unit));
-    qdev_prop_set_uint64(DEVICE(sci), "input-freq", s->pclk_freq_hz);
     sysbus_realize(sci, &error_abort);
 
     irqbase = RX62N_SCI_IRQ + SCI_NR_IRQ * unit;
@@ -212,6 +220,18 @@ static void register_sci(RX62NState *s, int unit)
     sysbus_mmio_map(sci, 0, RX62N_SCI_BASE + unit * 0x08);
 }
 
+static void register_cpg(RX62NState *s)
+{
+    SysBusDevice *cpg;
+
+    object_initialize_child(OBJECT(s), "rx62n-cpg", &s->cpg,
+                            TYPE_RX62N_CPG);
+    cpg = SYS_BUS_DEVICE(&s->cpg);
+    qdev_prop_set_uint64(DEVICE(cpg), "xtal-frequency-hz", s->xtal_freq_hz);
+
+    sysbus_mmio_map(cpg, 0, RX62N_CPG_BASE);
+}
+
 static void rx62n_realize(DeviceState *dev, Error **errp)
 {
     RX62NState *s = RX62N_MCU(dev);
@@ -227,11 +247,6 @@ static void rx62n_realize(DeviceState *dev, Error **errp)
         error_setg(errp, "\"xtal-frequency-hz\" property in incorrect range.");
         return;
     }
-    /* Use a 4x fixed multiplier */
-    s->pclk_freq_hz = 4 * s->xtal_freq_hz;
-    /* PCLK range: 8-50 MHz */
-    assert(s->pclk_freq_hz <= RX62N_PCLK_MAX_HZ);
-
     memory_region_init_ram(&s->iram, OBJECT(dev), "iram",
                            rxc->ram_size, &error_abort);
     memory_region_add_subregion(s->sysmem, RX62N_IRAM_BASE, &s->iram);
@@ -248,11 +263,13 @@ static void rx62n_realize(DeviceState *dev, Error **errp)
 
     register_icu(s);
     s->cpu.env.ack = qdev_get_gpio_in_named(DEVICE(&s->icu), "ack", 0);
+    register_cpg(s);
     register_tmr(s, 0);
     register_tmr(s, 1);
     register_cmt(s, 0);
     register_cmt(s, 1);
     register_sci(s, 0);
+    sysbus_realize(SYS_BUS_DEVICE(&s->cpg), &error_abort);
 }
 
 static Property rx62n_properties[] = {
diff --git a/hw/rx/meson.build b/hw/rx/meson.build
index d223512a78..e1c5278b6f 100644
--- a/hw/rx/meson.build
+++ b/hw/rx/meson.build
@@ -1,5 +1,5 @@
 rx_ss = ss.source_set()
 rx_ss.add(when: 'CONFIG_RX_GDBSIM', if_true: files('rx-gdbsim.c'))
-rx_ss.add(when: 'CONFIG_RX62N_MCU', if_true: files('rx62n.c'))
+rx_ss.add(when: 'CONFIG_RX62N_MCU', if_true: files('rx62n.c', 'rx62n-cpg.c'))
 
 hw_arch += {'rx': rx_ss}
-- 
2.20.1



^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 07/11] hw/timer: Renesas 8bit timer.
  2021-05-27  5:21 [PATCH 00/11] Unified peripheral emulation for Renesas chips Yoshinori Sato
                   ` (5 preceding siblings ...)
  2021-05-27  5:21 ` [PATCH 06/11] hw/rx: Add RX62N Clock generator Yoshinori Sato
@ 2021-05-27  5:21 ` Yoshinori Sato
  2021-06-04 10:12   ` Peter Maydell
  2021-05-27  5:21 ` [PATCH 08/11] hw/rx: rx62n use new hw modules Yoshinori Sato
                   ` (4 subsequent siblings)
  11 siblings, 1 reply; 21+ messages in thread
From: Yoshinori Sato @ 2021-05-27  5:21 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

Rewrite timer api.

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 include/hw/timer/renesas_tmr.h  |  58 ----
 include/hw/timer/renesas_tmr8.h |  67 ++++
 hw/timer/renesas_tmr.c          | 493 -----------------------------
 hw/timer/renesas_tmr8.c         | 540 ++++++++++++++++++++++++++++++++
 hw/timer/Kconfig                |   5 +-
 hw/timer/meson.build            |   2 +-
 6 files changed, 609 insertions(+), 556 deletions(-)
 delete mode 100644 include/hw/timer/renesas_tmr.h
 create mode 100644 include/hw/timer/renesas_tmr8.h
 delete mode 100644 hw/timer/renesas_tmr.c
 create mode 100644 hw/timer/renesas_tmr8.c

diff --git a/include/hw/timer/renesas_tmr.h b/include/hw/timer/renesas_tmr.h
deleted file mode 100644
index caf7eec0dc..0000000000
--- a/include/hw/timer/renesas_tmr.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Renesas 8bit timer Object
- *
- * Copyright (c) 2018 Yoshinori Sato
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-
-#ifndef HW_TIMER_RENESAS_TMR_H
-#define HW_TIMER_RENESAS_TMR_H
-
-#include "qemu/timer.h"
-#include "hw/sysbus.h"
-#include "qom/object.h"
-
-#define TYPE_RENESAS_TMR "renesas-tmr"
-typedef struct RTMRState RTMRState;
-DECLARE_INSTANCE_CHECKER(RTMRState, RTMR,
-                         TYPE_RENESAS_TMR)
-
-enum timer_event {
-    cmia = 0,
-    cmib = 1,
-    ovi = 2,
-    none = 3,
-    TMR_NR_EVENTS = 4
-};
-
-enum {
-    TMR_CH = 2,
-    TMR_NR_IRQ = 3 * TMR_CH
-};
-
-struct RTMRState {
-    /*< private >*/
-    SysBusDevice parent_obj;
-    /*< public >*/
-
-    uint64_t input_freq;
-    MemoryRegion memory;
-
-    int64_t tick;
-    uint8_t tcnt[TMR_CH];
-    uint8_t tcora[TMR_CH];
-    uint8_t tcorb[TMR_CH];
-    uint8_t tcr[TMR_CH];
-    uint8_t tccr[TMR_CH];
-    uint8_t tcor[TMR_CH];
-    uint8_t tcsr[TMR_CH];
-    int64_t div_round[TMR_CH];
-    uint8_t next[TMR_CH];
-    qemu_irq cmia[TMR_CH];
-    qemu_irq cmib[TMR_CH];
-    qemu_irq ovi[TMR_CH];
-    QEMUTimer timer[TMR_CH];
-};
-
-#endif
diff --git a/include/hw/timer/renesas_tmr8.h b/include/hw/timer/renesas_tmr8.h
new file mode 100644
index 0000000000..21e4337b0c
--- /dev/null
+++ b/include/hw/timer/renesas_tmr8.h
@@ -0,0 +1,67 @@
+/*
+ * Renesas 8bit timer Object
+ *
+ * Copyright (c) 2018 Yoshinori Sato
+ *
+ * This code is licensed under the GPL version 2 or later.
+ *
+ */
+
+#ifndef HW_RENESAS_TMR8_H
+#define HW_RENESAS_TMR8_H
+
+#include "hw/sysbus.h"
+
+#define TYPE_RENESAS_TMR8 "renesas-tmr8"
+OBJECT_DECLARE_TYPE(RenesasTMR8State, RenesasTMR8Class,
+                    RENESAS_TMR8)
+
+enum {
+    TMR_CH = 2,
+};
+
+enum {
+    IRQ_CMIA, IRQ_CMIB, IRQ_OVI,
+    TMR_NR_IRQ,
+};
+
+enum timer_event {
+    EVT_NONE, EVT_CMIA, EVT_CMIB, EVT_OVI, EVT_WOVI,
+    TMR_NR_EVENTS,
+};
+
+enum cor {
+    REG_A, REG_B, NR_COR,
+};
+
+struct RenesasTMR8State;
+
+struct tmr8_ch {
+    uint16_t cnt;
+    uint16_t cor[NR_COR];
+    uint8_t tcr;
+    uint8_t tccr;
+    uint8_t tcsr;
+    qemu_irq irq[TMR_NR_IRQ];
+    QEMUTimer *timer;
+    int64_t base;
+    int64_t next;
+    int64_t clk;
+    enum timer_event event;
+    int id;
+    struct RenesasTMR8State *tmrp;
+    bool word;
+};
+
+typedef struct RenesasTMR8State {
+    SysBusDevice parent_obj;
+
+    uint32_t unit;
+    Clock *pck;
+    uint64_t input_freq;
+    MemoryRegion memory;
+
+    struct tmr8_ch ch[TMR_CH];
+} RenesasTMR8State;
+
+#endif
diff --git a/hw/timer/renesas_tmr.c b/hw/timer/renesas_tmr.c
deleted file mode 100644
index d96002e1ee..0000000000
--- a/hw/timer/renesas_tmr.c
+++ /dev/null
@@ -1,493 +0,0 @@
-/*
- * Renesas 8bit timer
- *
- * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
- *            (Rev.1.40 R01UH0033EJ0140)
- *
- * Copyright (c) 2019 Yoshinori Sato
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- *
- * 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/log.h"
-#include "hw/irq.h"
-#include "hw/registerfields.h"
-#include "hw/qdev-properties.h"
-#include "hw/timer/renesas_tmr.h"
-#include "migration/vmstate.h"
-
-REG8(TCR, 0)
-  FIELD(TCR, CCLR,  3, 2)
-  FIELD(TCR, OVIE,  5, 1)
-  FIELD(TCR, CMIEA, 6, 1)
-  FIELD(TCR, CMIEB, 7, 1)
-REG8(TCSR, 2)
-  FIELD(TCSR, OSA,  0, 2)
-  FIELD(TCSR, OSB,  2, 2)
-  FIELD(TCSR, ADTE, 4, 2)
-REG8(TCORA, 4)
-REG8(TCORB, 6)
-REG8(TCNT, 8)
-REG8(TCCR, 10)
-  FIELD(TCCR, CKS,   0, 3)
-  FIELD(TCCR, CSS,   3, 2)
-  FIELD(TCCR, TMRIS, 7, 1)
-
-#define CSS_EXTERNAL  0x00
-#define CSS_INTERNAL  0x01
-#define CSS_INVALID   0x02
-#define CSS_CASCADING 0x03
-#define CCLR_A    0x01
-#define CCLR_B    0x02
-
-static const int clkdiv[] = {0, 1, 2, 8, 32, 64, 1024, 8192};
-
-static uint8_t concat_reg(uint8_t *reg)
-{
-    return (reg[0] << 8) | reg[1];
-}
-
-static void update_events(RTMRState *tmr, int ch)
-{
-    uint16_t diff[TMR_NR_EVENTS], min;
-    int64_t next_time;
-    int i, event;
-
-    if (tmr->tccr[ch] == 0) {
-        return ;
-    }
-    if (FIELD_EX8(tmr->tccr[ch], TCCR, CSS) == 0) {
-        /* external clock mode */
-        /* event not happened */
-        return ;
-    }
-    if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) == CSS_CASCADING) {
-        /* cascading mode */
-        if (ch == 1) {
-            tmr->next[ch] = none;
-            return ;
-        }
-        diff[cmia] = concat_reg(tmr->tcora) - concat_reg(tmr->tcnt);
-        diff[cmib] = concat_reg(tmr->tcorb) - concat_reg(tmr->tcnt);
-        diff[ovi] = 0x10000 - concat_reg(tmr->tcnt);
-    } else {
-        /* separate mode */
-        diff[cmia] = tmr->tcora[ch] - tmr->tcnt[ch];
-        diff[cmib] = tmr->tcorb[ch] - tmr->tcnt[ch];
-        diff[ovi] = 0x100 - tmr->tcnt[ch];
-    }
-    /* Search for the most recently occurring event. */
-    for (event = 0, min = diff[0], i = 1; i < none; i++) {
-        if (min > diff[i]) {
-            event = i;
-            min = diff[i];
-        }
-    }
-    tmr->next[ch] = event;
-    next_time = diff[event];
-    next_time *= clkdiv[FIELD_EX8(tmr->tccr[ch], TCCR, CKS)];
-    next_time *= NANOSECONDS_PER_SECOND;
-    next_time /= tmr->input_freq;
-    next_time += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-    timer_mod(&tmr->timer[ch], next_time);
-}
-
-static int elapsed_time(RTMRState *tmr, int ch, int64_t delta)
-{
-    int divrate = clkdiv[FIELD_EX8(tmr->tccr[ch], TCCR, CKS)];
-    int et;
-
-    tmr->div_round[ch] += delta;
-    if (divrate > 0) {
-        et = tmr->div_round[ch] / divrate;
-        tmr->div_round[ch] %= divrate;
-    } else {
-        /* disble clock. so no update */
-        et = 0;
-    }
-    return et;
-}
-
-static uint16_t read_tcnt(RTMRState *tmr, unsigned size, int ch)
-{
-    int64_t delta, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-    int elapsed, ovf = 0;
-    uint16_t tcnt[2];
-    uint32_t ret;
-
-    delta = (now - tmr->tick) * NANOSECONDS_PER_SECOND / tmr->input_freq;
-    if (delta > 0) {
-        tmr->tick = now;
-
-        switch (FIELD_EX8(tmr->tccr[1], TCCR, CSS)) {
-        case CSS_INTERNAL:
-            /* timer1 count update */
-            elapsed = elapsed_time(tmr, 1, delta);
-            if (elapsed >= 0x100) {
-                ovf = elapsed >> 8;
-            }
-            tcnt[1] = tmr->tcnt[1] + (elapsed & 0xff);
-            break;
-        case CSS_INVALID: /* guest error to have set this */
-        case CSS_EXTERNAL: /* QEMU doesn't implement these */
-        case CSS_CASCADING:
-            tcnt[1] = tmr->tcnt[1];
-            break;
-        default:
-            g_assert_not_reached();
-        }
-        switch (FIELD_EX8(tmr->tccr[0], TCCR, CSS)) {
-        case CSS_INTERNAL:
-            elapsed = elapsed_time(tmr, 0, delta);
-            tcnt[0] = tmr->tcnt[0] + elapsed;
-            break;
-        case CSS_CASCADING:
-            tcnt[0] = tmr->tcnt[0] + ovf;
-            break;
-        case CSS_INVALID: /* guest error to have set this */
-        case CSS_EXTERNAL: /* QEMU doesn't implement this */
-            tcnt[0] = tmr->tcnt[0];
-            break;
-        default:
-            g_assert_not_reached();
-        }
-    } else {
-        tcnt[0] = tmr->tcnt[0];
-        tcnt[1] = tmr->tcnt[1];
-    }
-    if (size == 1) {
-        return tcnt[ch];
-    } else {
-        ret = 0;
-        ret = deposit32(ret, 0, 8, tcnt[1]);
-        ret = deposit32(ret, 8, 8, tcnt[0]);
-        return ret;
-    }
-}
-
-static uint8_t read_tccr(uint8_t r)
-{
-    uint8_t tccr = 0;
-    tccr = FIELD_DP8(tccr, TCCR, TMRIS,
-                     FIELD_EX8(r, TCCR, TMRIS));
-    tccr = FIELD_DP8(tccr, TCCR, CSS,
-                     FIELD_EX8(r, TCCR, CSS));
-    tccr = FIELD_DP8(tccr, TCCR, CKS,
-                     FIELD_EX8(r, TCCR, CKS));
-    return tccr;
-}
-
-static uint64_t tmr_read(void *opaque, hwaddr addr, unsigned size)
-{
-    RTMRState *tmr = opaque;
-    int ch = addr & 1;
-    uint64_t ret;
-
-    if (size == 2 && (ch != 0 || addr == A_TCR || addr == A_TCSR)) {
-        qemu_log_mask(LOG_GUEST_ERROR, "renesas_tmr: Invalid read size 0x%"
-                                       HWADDR_PRIX "\n",
-                      addr);
-        return UINT64_MAX;
-    }
-    switch (addr & 0x0e) {
-    case A_TCR:
-        ret = 0;
-        ret = FIELD_DP8(ret, TCR, CCLR,
-                        FIELD_EX8(tmr->tcr[ch], TCR, CCLR));
-        ret = FIELD_DP8(ret, TCR, OVIE,
-                        FIELD_EX8(tmr->tcr[ch], TCR, OVIE));
-        ret = FIELD_DP8(ret, TCR, CMIEA,
-                        FIELD_EX8(tmr->tcr[ch], TCR, CMIEA));
-        ret = FIELD_DP8(ret, TCR, CMIEB,
-                        FIELD_EX8(tmr->tcr[ch], TCR, CMIEB));
-        return ret;
-    case A_TCSR:
-        ret = 0;
-        ret = FIELD_DP8(ret, TCSR, OSA,
-                        FIELD_EX8(tmr->tcsr[ch], TCSR, OSA));
-        ret = FIELD_DP8(ret, TCSR, OSB,
-                        FIELD_EX8(tmr->tcsr[ch], TCSR, OSB));
-        switch (ch) {
-        case 0:
-            ret = FIELD_DP8(ret, TCSR, ADTE,
-                            FIELD_EX8(tmr->tcsr[ch], TCSR, ADTE));
-            break;
-        case 1: /* CH1 ADTE unimplement always 1 */
-            ret = FIELD_DP8(ret, TCSR, ADTE, 1);
-            break;
-        }
-        return ret;
-    case A_TCORA:
-        if (size == 1) {
-            return tmr->tcora[ch];
-        } else if (ch == 0) {
-            return concat_reg(tmr->tcora);
-        }
-        /* fall through */
-    case A_TCORB:
-        if (size == 1) {
-            return tmr->tcorb[ch];
-        } else {
-            return concat_reg(tmr->tcorb);
-        }
-    case A_TCNT:
-        return read_tcnt(tmr, size, ch);
-    case A_TCCR:
-        if (size == 1) {
-            return read_tccr(tmr->tccr[ch]);
-        } else {
-            return read_tccr(tmr->tccr[0]) << 8 | read_tccr(tmr->tccr[1]);
-        }
-    default:
-        qemu_log_mask(LOG_UNIMP, "renesas_tmr: Register 0x%" HWADDR_PRIX
-                                 " not implemented\n",
-                      addr);
-        break;
-    }
-    return UINT64_MAX;
-}
-
-static void tmr_write_count(RTMRState *tmr, int ch, unsigned size,
-                            uint8_t *reg, uint64_t val)
-{
-    if (size == 1) {
-        reg[ch] = val;
-        update_events(tmr, ch);
-    } else {
-        reg[0] = extract32(val, 8, 8);
-        reg[1] = extract32(val, 0, 8);
-        update_events(tmr, 0);
-        update_events(tmr, 1);
-    }
-}
-
-static void tmr_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
-{
-    RTMRState *tmr = opaque;
-    int ch = addr & 1;
-
-    if (size == 2 && (ch != 0 || addr == A_TCR || addr == A_TCSR)) {
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "renesas_tmr: Invalid write size 0x%" HWADDR_PRIX "\n",
-                      addr);
-        return;
-    }
-    switch (addr & 0x0e) {
-    case A_TCR:
-        tmr->tcr[ch] = val;
-        break;
-    case A_TCSR:
-        tmr->tcsr[ch] = val;
-        break;
-    case A_TCORA:
-        tmr_write_count(tmr, ch, size, tmr->tcora, val);
-        break;
-    case A_TCORB:
-        tmr_write_count(tmr, ch, size, tmr->tcorb, val);
-        break;
-    case A_TCNT:
-        tmr_write_count(tmr, ch, size, tmr->tcnt, val);
-        break;
-    case A_TCCR:
-        tmr_write_count(tmr, ch, size, tmr->tccr, val);
-        break;
-    default:
-        qemu_log_mask(LOG_UNIMP, "renesas_tmr: Register 0x%" HWADDR_PRIX
-                                 " not implemented\n",
-                      addr);
-        break;
-    }
-}
-
-static const MemoryRegionOps tmr_ops = {
-    .write = tmr_write,
-    .read  = tmr_read,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 2,
-    },
-    .valid = {
-        .min_access_size = 1,
-        .max_access_size = 2,
-    },
-};
-
-static void timer_events(RTMRState *tmr, int ch);
-
-static uint16_t issue_event(RTMRState *tmr, int ch, int sz,
-                        uint16_t tcnt, uint16_t tcora, uint16_t tcorb)
-{
-    uint16_t ret = tcnt;
-
-    switch (tmr->next[ch]) {
-    case none:
-        break;
-    case cmia:
-        if (tcnt >= tcora) {
-            if (FIELD_EX8(tmr->tcr[ch], TCR, CCLR) == CCLR_A) {
-                ret = tcnt - tcora;
-            }
-            if (FIELD_EX8(tmr->tcr[ch], TCR, CMIEA)) {
-                qemu_irq_pulse(tmr->cmia[ch]);
-            }
-            if (sz == 8 && ch == 0 &&
-                FIELD_EX8(tmr->tccr[1], TCCR, CSS) == CSS_CASCADING) {
-                tmr->tcnt[1]++;
-                timer_events(tmr, 1);
-            }
-        }
-        break;
-    case cmib:
-        if (tcnt >= tcorb) {
-            if (FIELD_EX8(tmr->tcr[ch], TCR, CCLR) == CCLR_B) {
-                ret = tcnt - tcorb;
-            }
-            if (FIELD_EX8(tmr->tcr[ch], TCR, CMIEB)) {
-                qemu_irq_pulse(tmr->cmib[ch]);
-            }
-        }
-        break;
-    case ovi:
-        if ((tcnt >= (1 << sz)) && FIELD_EX8(tmr->tcr[ch], TCR, OVIE)) {
-            qemu_irq_pulse(tmr->ovi[ch]);
-        }
-        break;
-    default:
-        g_assert_not_reached();
-    }
-    return ret;
-}
-
-static void timer_events(RTMRState *tmr, int ch)
-{
-    uint16_t tcnt;
-
-    tmr->tcnt[ch] = read_tcnt(tmr, 1, ch);
-    if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) != CSS_CASCADING) {
-        tmr->tcnt[ch] = issue_event(tmr, ch, 8,
-                                    tmr->tcnt[ch],
-                                    tmr->tcora[ch],
-                                    tmr->tcorb[ch]) & 0xff;
-    } else {
-        if (ch == 1) {
-            return ;
-        }
-        tcnt = issue_event(tmr, ch, 16,
-                           concat_reg(tmr->tcnt),
-                           concat_reg(tmr->tcora),
-                           concat_reg(tmr->tcorb));
-        tmr->tcnt[0] = (tcnt >> 8) & 0xff;
-        tmr->tcnt[1] = tcnt & 0xff;
-    }
-    update_events(tmr, ch);
-}
-
-static void timer_event0(void *opaque)
-{
-    RTMRState *tmr = opaque;
-
-    timer_events(tmr, 0);
-}
-
-static void timer_event1(void *opaque)
-{
-    RTMRState *tmr = opaque;
-
-    timer_events(tmr, 1);
-}
-
-static void rtmr_reset(DeviceState *dev)
-{
-    RTMRState *tmr = RTMR(dev);
-    tmr->tcr[0]   = tmr->tcr[1]   = 0x00;
-    tmr->tcsr[0]  = 0x00;
-    tmr->tcsr[1]  = 0x10;
-    tmr->tcnt[0]  = tmr->tcnt[1]  = 0x00;
-    tmr->tcora[0] = tmr->tcora[1] = 0xff;
-    tmr->tcorb[0] = tmr->tcorb[1] = 0xff;
-    tmr->tccr[0]  = tmr->tccr[1]  = 0x00;
-    tmr->next[0]  = tmr->next[1]  = none;
-    tmr->tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-}
-
-static void rtmr_init(Object *obj)
-{
-    SysBusDevice *d = SYS_BUS_DEVICE(obj);
-    RTMRState *tmr = RTMR(obj);
-    int i;
-
-    memory_region_init_io(&tmr->memory, OBJECT(tmr), &tmr_ops,
-                          tmr, "renesas-tmr", 0x10);
-    sysbus_init_mmio(d, &tmr->memory);
-
-    for (i = 0; i < ARRAY_SIZE(tmr->ovi); i++) {
-        sysbus_init_irq(d, &tmr->cmia[i]);
-        sysbus_init_irq(d, &tmr->cmib[i]);
-        sysbus_init_irq(d, &tmr->ovi[i]);
-    }
-    timer_init_ns(&tmr->timer[0], QEMU_CLOCK_VIRTUAL, timer_event0, tmr);
-    timer_init_ns(&tmr->timer[1], QEMU_CLOCK_VIRTUAL, timer_event1, tmr);
-}
-
-static const VMStateDescription vmstate_rtmr = {
-    .name = "rx-tmr",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_INT64(tick, RTMRState),
-        VMSTATE_UINT8_ARRAY(tcnt, RTMRState, TMR_CH),
-        VMSTATE_UINT8_ARRAY(tcora, RTMRState, TMR_CH),
-        VMSTATE_UINT8_ARRAY(tcorb, RTMRState, TMR_CH),
-        VMSTATE_UINT8_ARRAY(tcr, RTMRState, TMR_CH),
-        VMSTATE_UINT8_ARRAY(tccr, RTMRState, TMR_CH),
-        VMSTATE_UINT8_ARRAY(tcor, RTMRState, TMR_CH),
-        VMSTATE_UINT8_ARRAY(tcsr, RTMRState, TMR_CH),
-        VMSTATE_INT64_ARRAY(div_round, RTMRState, TMR_CH),
-        VMSTATE_UINT8_ARRAY(next, RTMRState, TMR_CH),
-        VMSTATE_TIMER_ARRAY(timer, RTMRState, TMR_CH),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static Property rtmr_properties[] = {
-    DEFINE_PROP_UINT64("input-freq", RTMRState, input_freq, 0),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void rtmr_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-
-    dc->vmsd = &vmstate_rtmr;
-    dc->reset = rtmr_reset;
-    device_class_set_props(dc, rtmr_properties);
-}
-
-static const TypeInfo rtmr_info = {
-    .name = TYPE_RENESAS_TMR,
-    .parent = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(RTMRState),
-    .instance_init = rtmr_init,
-    .class_init = rtmr_class_init,
-};
-
-static void rtmr_register_types(void)
-{
-    type_register_static(&rtmr_info);
-}
-
-type_init(rtmr_register_types)
diff --git a/hw/timer/renesas_tmr8.c b/hw/timer/renesas_tmr8.c
new file mode 100644
index 0000000000..ba1d2faa07
--- /dev/null
+++ b/hw/timer/renesas_tmr8.c
@@ -0,0 +1,540 @@
+/*
+ * Renesas 8bit timer
+ *
+ * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
+ *            (Rev.1.40 R01UH0033EJ0140)
+ *
+ * Copyright (c) 2020 Yoshinori Sato
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * 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 "qemu/bitops.h"
+#include "hw/hw.h"
+#include "hw/irq.h"
+#include "hw/sysbus.h"
+#include "hw/registerfields.h"
+#include "hw/qdev-properties.h"
+#include "hw/qdev-clock.h"
+#include "hw/timer/renesas_tmr8.h"
+#include "migration/vmstate.h"
+#include "qemu/error-report.h"
+
+REG8(TCR, 0)
+  FIELD(TCR, CCLR, 3, 2)
+  FIELD(TCR, OVIE, 5, 1)
+  FIELD(TCR, CMIEA, 6, 1)
+  FIELD(TCR, CMIEB, 7, 1)
+  FIELD(TCR, CMIE, 6, 2)
+  FIELD(TCR, ALLIE, 5, 3)
+REG8(TCSR, 2)
+  FIELD(TCSR, OSA, 0, 2)
+  FIELD(TCSR, OSB, 2, 2)
+  FIELD(TCSR, ADTE, 4, 1)
+REG8(TCORA, 4)
+REG8(TCORB, 6)
+REG8(TCNT, 8)
+REG8(TCCR, 10)
+  FIELD(TCCR, CKS, 0, 3)
+  FIELD(TCCR, CSS, 3, 2)
+  FIELD(TCCR, TMRIS, 7, 1)
+
+#define CLK_EVT -1
+
+enum CSS {
+    CSS_EXT = 0,        /* extarnal clock */
+    CSS_INT = 1,        /* internal clock */
+    CSS_UND = 2,        /* undefined */
+    CSS_EVT = 3,        /* event count */
+};
+
+static void update_clk(RenesasTMR8State *tmr, int ch)
+{
+    int64_t t;
+    static const int divlist[] = {1, 2, 8, 32, 64, 1024, 8192, 0};
+    switch (FIELD_EX8(tmr->ch[ch].tccr, TCCR, CSS)) {
+    case CSS_EXT:
+        qemu_log_mask(LOG_UNIMP,
+                      "renesas_tmr8: External clock not implemented.\n");
+        tmr->ch[ch].clk = 0;
+        break;
+    case CSS_INT:
+        t = divlist[FIELD_EX8(tmr->ch[ch].tccr, TCCR, CKS)];
+        if (t > 0 && clock_is_enabled(tmr->pck)) {
+            t = tmr->input_freq / t;
+            tmr->ch[ch].clk = NANOSECONDS_PER_SECOND / t;
+        } else {
+            tmr->ch[ch].clk = 0;
+        }
+        break;
+    case CSS_UND:
+        qemu_log_mask(LOG_UNIMP,
+                      "renesas_8timer: CSS undefined.");
+        tmr->ch[ch].clk = 0;
+        break;
+    case CSS_EVT:
+        tmr->ch[ch].clk = CLK_EVT;
+        break;
+    }
+}
+
+static uint16_t catreg(uint8_t hi, uint8_t lo)
+{
+    uint16_t ret = 0;
+    ret = deposit32(ret, 8, 8, hi);
+    ret = deposit32(ret, 0, 8, lo);
+    return ret;
+}
+
+static bool is_clr_count(uint8_t tcr, enum timer_event event)
+{
+    switch (event) {
+    case EVT_CMIA:
+    case EVT_CMIB:
+        return FIELD_EX8(tcr, TCR, CCLR) == event;
+    case EVT_OVI:
+        return true;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static bool is_irq_enabled(uint8_t tcr, enum timer_event event)
+{
+    switch (event) {
+    case EVT_CMIA:
+        return FIELD_EX8(tcr, TCR, CMIEA);
+    case EVT_CMIB:
+        return FIELD_EX8(tcr, TCR, CMIEB);
+    case EVT_OVI:
+        return FIELD_EX8(tcr, TCR, OVIE);
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static bool event_enabled(uint8_t tcr, enum timer_event event)
+{
+    return is_clr_count(tcr, event) || is_irq_enabled(tcr, event);
+}
+
+static int event_cor(struct tmr8_ch *ch, enum timer_event event)
+{
+    switch (event) {
+    case EVT_CMIA:
+        return ch->cor[REG_A];
+    case EVT_CMIB:
+        return ch->cor[REG_B];
+    default:
+        return 0xff;
+    }
+}
+
+static bool is_word_mode(RenesasTMR8State *tmr)
+{
+    /*
+     * If the following conditions are met, it is treated as a 16-bit counter.
+     * ch0 - free running and no compare match event
+     * ch1 - free running no event
+     */
+    return tmr->ch[0].clk == CLK_EVT &&
+        tmr->ch[1].clk > 0 &&
+        FIELD_EX8(tmr->ch[0].tcr, TCR, CCLR) == 0 &&
+        FIELD_EX8(tmr->ch[0].tcr, TCR, CMIE) == 0 &&
+        FIELD_EX8(tmr->ch[0].tccr, TCCR, CSS) == CSS_EVT &&
+        FIELD_EX8(tmr->ch[1].tcr, TCR, CCLR) == 0 &&
+        FIELD_EX8(tmr->ch[1].tcr, TCR, ALLIE) == 0;
+}
+
+static void set_next_event(RenesasTMR8State *tmr, int ch)
+{
+    int64_t next = 0;
+    enum timer_event evt;
+    int cor;
+    int min;
+    if (ch == 1 && is_word_mode(tmr)) {
+        /* 16bit count mode */
+        next = 0x10000 - catreg(tmr->ch[0].cnt, tmr->ch[1].cnt);
+        next *= tmr->ch[1].clk;
+        tmr->ch[0].event = tmr->ch[1].event = EVT_WOVI;
+    } else if (tmr->ch[ch].clk > 0) {
+        /* Find the next event. */
+        min = 0x100 + 1;
+        for (evt = EVT_CMIA; evt < EVT_WOVI; evt++) {
+            cor = event_cor(&tmr->ch[ch], evt);
+            /* event happen in next count up */
+            cor++;
+            if (tmr->ch[ch].cnt < cor && min > cor &&
+                event_enabled(tmr->ch[ch].tcr, evt)) {
+                    min = cor;
+                    next = cor - tmr->ch[ch].cnt;
+                    next *= tmr->ch[ch].clk;
+                    tmr->ch[ch].event = evt;
+            }
+        }
+    }
+    if (next > 0) {
+        tmr->ch[ch].base = tmr->ch[ch].next;
+        tmr->ch[ch].next += next;
+        printf("%s %ld\n", __func__, next);
+        timer_mod(tmr->ch[ch].timer, tmr->ch[ch].next);
+    } else {
+        timer_del(tmr->ch[ch].timer);
+    }
+}
+
+static void sent_irq(struct tmr8_ch *ch, enum timer_event evt)
+{
+    if (is_irq_enabled(ch->tcr, evt)) {
+        qemu_irq_pulse(ch->irq[evt - 1]);
+    }
+}
+
+static void event_countup(struct tmr8_ch *ch)
+{
+    enum timer_event evt;
+    int cor;
+
+    ch->cnt++;
+    for (evt = EVT_CMIA; evt < EVT_WOVI; evt++) {
+        cor = event_cor(ch, evt) + 1;
+        if (ch->cnt == cor) {
+            if (is_clr_count(ch->tcr, evt)) {
+                ch->cnt = 0;
+            }
+            sent_irq(ch, evt);
+        }
+    }
+}
+
+static void timer_event(void *opaque)
+{
+    struct tmr8_ch *ch = opaque;
+    RenesasTMR8State *tmr = ch->tmrp;
+
+    switch (ch->event) {
+    case EVT_CMIA:
+        if (ch->id == 0 && tmr->ch[1].clk == CLK_EVT) {
+            /* CH1 event count */
+            event_countup(&tmr->ch[1]);
+        }
+        /* Falls through. */
+    case EVT_CMIB:
+        if (FIELD_EX8(ch->tcr, TCR, CCLR) == ch->event) {
+            ch->cnt = 0;
+        } else {
+        /* update current value */
+            ch->cnt = ch->cor[ch->event] + 1;
+        }
+        sent_irq(ch, ch->event);
+        break;
+    case EVT_OVI:
+        ch->cnt = 0;
+        sent_irq(ch, EVT_OVI);
+        if (ch->id == 1 && tmr->ch[0].clk == CLK_EVT) {
+            /* CH0 event count */
+            event_countup(&tmr->ch[0]);
+        }
+        break;
+    case EVT_WOVI:
+        tmr->ch[0].cnt = tmr->ch[1].cnt = 0;
+        sent_irq(ch, EVT_OVI);
+        break;
+    default:
+        g_assert_not_reached();
+    }
+    set_next_event(tmr, ch->id);
+}
+
+static uint16_t read_tcnt(RenesasTMR8State *tmr, unsigned int size, int ch)
+{
+    int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+    int64_t delta;
+    uint8_t ret[2];
+    int i;
+
+    switch (size) {
+    case 1:
+        if (tmr->ch[ch].clk > 0) {
+            delta = now - tmr->ch[ch].base;
+            delta /= tmr->ch[ch].clk;
+        } else {
+            delta = 0;
+        }
+        return tmr->ch[ch].cnt + delta;
+    case 2:
+        if (is_word_mode(tmr)) {
+            /* 16bit count mode */
+            delta = now - tmr->ch[1].base;
+            delta /= tmr->ch[1].clk;
+            return catreg(tmr->ch[0].cnt, tmr->ch[1].cnt) + delta;
+        } else {
+            for (i = 0; i < TMR_CH; i++) {
+                if (tmr->ch[i].clk > 0) {
+                    delta = now - tmr->ch[i].base;
+                    delta /= tmr->ch[i].clk;
+                } else {
+                    delta = 0;
+                }
+                ret[i] = tmr->ch[i].cnt + delta;
+            }
+            return catreg(ret[0], ret[1]);
+        }
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static void tmr_pck_update(void *opaque, ClockEvent evt)
+{
+    RenesasTMR8State *tmr = RENESAS_TMR8(opaque);
+    int i;
+    uint16_t tcnt = read_tcnt(tmr, 2, 0);
+
+    tmr->ch[0].cnt = extract16(tcnt, 8, 8);
+    tmr->ch[1].cnt = extract16(tcnt, 0, 8);
+
+    tmr->input_freq = clock_get_hz(tmr->pck);
+    for (i = 0; i < TMR_CH; i++) {
+        if (clock_is_enabled(tmr->pck)) {
+            update_clk(tmr, i);
+            set_next_event(tmr, i);
+        } else {
+            if (tmr->ch[i].timer) {
+                timer_del(tmr->ch[i].timer);
+            }
+        }
+    }
+}
+
+static int validate_access(hwaddr addr, unsigned int size)
+{
+    /* Byte access always OK */
+    if (size == 1) {
+        return 1;
+    }
+    /* word access allowed TCNT / TCOR / TCCR */
+    return ((addr & 1) == 0 && addr >= A_TCORA);
+}
+
+static uint64_t tmr8_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    RenesasTMR8State *tmr = RENESAS_TMR8(opaque);
+    int ch = addr & 1;
+    int cor;
+
+    if (!validate_access(addr, size)) {
+        qemu_log_mask(LOG_GUEST_ERROR, "renesas_tmr8: Invalid read size 0x%"
+                      HWADDR_PRIX "\n", addr);
+        return UINT64_MAX;
+    }
+    if (!clock_is_enabled(tmr->pck)) {
+        qemu_log_mask(LOG_GUEST_ERROR, "renesas_tmr8: Unit %d is stopped.\n",
+                      tmr->unit);
+        return UINT64_MAX;
+    }
+    switch (addr & ~1) {
+    case A_TCR:
+        return tmr->ch[ch].tcr;
+    case A_TCSR:
+        return tmr->ch[ch].tcsr;
+    case A_TCORA:
+    case A_TCORB:
+        cor = extract32(addr, 1, 1);
+        if (size == 1) {
+            /* 8bit read - single register */
+            return tmr->ch[ch].cor[cor];
+        } else {
+            /* 16bit read - high byte ch0 reg, low byte ch1 reg */
+            return catreg(tmr->ch[0].cor[cor], tmr->ch[1].cor[cor]);
+        }
+    case A_TCNT:
+        return read_tcnt(tmr, size, ch);
+    case A_TCCR:
+        if (size == 1) {
+            return tmr->ch[ch].tccr;
+        } else {
+            return catreg(tmr->ch[0].tccr, tmr->ch[1].tccr);
+        }
+    default:
+        qemu_log_mask(LOG_UNIMP, "renesas_tmr8: Register 0x%" HWADDR_PRIX
+                      " not implemented\n", addr);
+        break;
+    }
+    return UINT64_MAX;
+}
+
+static void tmr8_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    RenesasTMR8State *tmr = RENESAS_TMR8(opaque);
+    int ch = addr & 1;
+    int cor;
+    int64_t now;
+
+    if (!validate_access(addr, size)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "renesas_tmr: Invalid write size 0x%" HWADDR_PRIX
+                      "\n", addr);
+        return;
+    }
+    if (!clock_is_enabled(tmr->pck)) {
+        qemu_log_mask(LOG_GUEST_ERROR, "renesas_tmr8: Unit %d is stopped.\n",
+                      tmr->unit);
+        return;
+    }
+    switch (addr & ~1) {
+    case A_TCR:
+        tmr->ch[ch].tcr = val;
+        break;
+    case A_TCSR:
+        if (ch == 1) {
+            /* CH1 ADTR always 1 */
+            val = FIELD_DP8(val, TCSR, ADTE, 1);
+        }
+        tmr->ch[ch].tcsr = val;
+        break;
+    case A_TCORA:
+    case A_TCORB:
+        cor = extract32(addr, 1, 1);
+        if (size == 1) {
+            tmr->ch[ch].cor[cor] = val;
+        } else {
+            tmr->ch[0].cor[cor] = extract32(val, 0, 8);
+            tmr->ch[1].cor[cor] = extract32(val, 8, 8);
+        }
+        break;
+    case A_TCNT:
+        now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+        if (size == 1) {
+            tmr->ch[ch].base = now;
+            tmr->ch[ch].cnt = val;
+        } else {
+            tmr->ch[0].base = tmr->ch[1].base = now;
+            tmr->ch[0].cnt = extract32(val, 0, 8);
+            tmr->ch[1].cnt = extract32(val, 8, 8);
+        }
+        break;
+    case A_TCCR:
+        val &= ~0x6060;
+        if (size == 1) {
+            tmr->ch[ch].tccr = val;
+            update_clk(tmr, ch);
+        } else {
+            tmr->ch[0].tccr = extract32(val, 0, 8);
+            tmr->ch[1].tccr = extract32(val, 8, 8);
+            update_clk(tmr, 0);
+            update_clk(tmr, 1);
+        }
+        break;
+    default:
+        qemu_log_mask(LOG_UNIMP, "renesas_tmr: Register 0x%" HWADDR_PRIX
+                      " not implemented\n", addr);
+        return;
+    }
+    if (size == 1) {
+        set_next_event(tmr, ch);
+    } else {
+        set_next_event(tmr, 0);
+        set_next_event(tmr, 1);
+    }
+}
+
+static const MemoryRegionOps tmr_ops = {
+    .write = tmr8_write,
+    .read  = tmr8_read,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 2,
+    },
+};
+
+static void tmr8_realize(DeviceState *dev, Error **errp)
+{
+    RenesasTMR8State *tmr = RENESAS_TMR8(dev);
+    int i;
+
+    for (i = 0; i < TMR_CH; i++) {
+        tmr->ch[i].id = i;
+        tmr->ch[i].timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+                                        timer_event, &tmr->ch[i]);
+        tmr->ch[i].tmrp = tmr;
+        tmr->ch[i].tcr = 0x00;
+        tmr->ch[i].tcsr = (i == 0) ? 0x00 : 0x10;
+        tmr->ch[i].cnt = 0x00;
+        tmr->ch[i].cor[0] = 0xff;
+        tmr->ch[i].cor[1] = 0xff;
+        tmr->ch[i].tccr = 0x00;
+    }
+}
+
+static void tmr8_init(Object *obj)
+{
+    RenesasTMR8State *tmr = RENESAS_TMR8(obj);
+    SysBusDevice *d = SYS_BUS_DEVICE(obj);
+    int i;
+
+    memory_region_init_io(&tmr->memory, obj, &tmr_ops,
+                          tmr, "renesas-tmr8", 0x10);
+    sysbus_init_mmio(d, &tmr->memory);
+
+    for (i = 0; i < TMR_CH; i++) {
+        sysbus_init_irq(d, &tmr->ch[i].irq[IRQ_CMIA]);
+        sysbus_init_irq(d, &tmr->ch[i].irq[IRQ_CMIB]);
+        sysbus_init_irq(d, &tmr->ch[i].irq[IRQ_OVI]);
+    }
+    tmr->pck = qdev_init_clock_in(DEVICE(d), "pck",
+                                  tmr_pck_update, tmr, ClockUpdate);
+}
+
+static const VMStateDescription vmstate_rtmr = {
+    .name = "renesas-8tmr",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property tmr8_properties[] = {
+    DEFINE_PROP_UINT32("unit", RenesasTMR8State, unit, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void tmr8_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd = &vmstate_rtmr;
+    dc->realize = tmr8_realize;
+    device_class_set_props(dc, tmr8_properties);
+}
+
+static const TypeInfo tmr8_info[] = {
+    {
+        .name       = TYPE_RENESAS_TMR8,
+        .parent     = TYPE_SYS_BUS_DEVICE,
+        .instance_size = sizeof(RenesasTMR8State),
+        .instance_init = tmr8_init,
+        .class_init = tmr8_class_init,
+    }
+};
+
+DEFINE_TYPES(tmr8_info)
diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig
index ec19708a4d..6aee6f8a13 100644
--- a/hw/timer/Kconfig
+++ b/hw/timer/Kconfig
@@ -36,10 +36,7 @@ config CMSDK_APB_DUALTIMER
     bool
     select PTIMER
 
-config RENESAS_TMR
-    bool
-
-config RENESAS_CMT
+config RENESAS_TMR8
     bool
 
 config SSE_COUNTER
diff --git a/hw/timer/meson.build b/hw/timer/meson.build
index 03b40cfbee..0b4e8850ed 100644
--- a/hw/timer/meson.build
+++ b/hw/timer/meson.build
@@ -8,7 +8,7 @@ softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_timer.c'))
 softmmu_ss.add(when: 'CONFIG_CADENCE', if_true: files('cadence_ttc.c'))
 softmmu_ss.add(when: 'CONFIG_CMSDK_APB_DUALTIMER', if_true: files('cmsdk-apb-dualtimer.c'))
 softmmu_ss.add(when: 'CONFIG_CMSDK_APB_TIMER', if_true: files('cmsdk-apb-timer.c'))
-softmmu_ss.add(when: 'CONFIG_RENESAS_TMR', if_true: files('renesas_tmr.c'))
+softmmu_ss.add(when: 'CONFIG_RENESAS_TMR8', if_true: files('renesas_tmr8.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



^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 08/11] hw/rx: rx62n use new hw modules.
  2021-05-27  5:21 [PATCH 00/11] Unified peripheral emulation for Renesas chips Yoshinori Sato
                   ` (6 preceding siblings ...)
  2021-05-27  5:21 ` [PATCH 07/11] hw/timer: Renesas 8bit timer Yoshinori Sato
@ 2021-05-27  5:21 ` Yoshinori Sato
  2021-05-27  5:21 ` [PATCH 09/11] hw/sh4: sh7750 Add CPG Yoshinori Sato
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 21+ messages in thread
From: Yoshinori Sato @ 2021-05-27  5:21 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 include/hw/rx/rx62n.h | 10 +++++-----
 hw/rx/rx62n.c         | 18 ++++++++++++------
 hw/rx/Kconfig         |  4 ++--
 3 files changed, 19 insertions(+), 13 deletions(-)

diff --git a/include/hw/rx/rx62n.h b/include/hw/rx/rx62n.h
index 44f5fcc74d..942ed0639f 100644
--- a/include/hw/rx/rx62n.h
+++ b/include/hw/rx/rx62n.h
@@ -26,8 +26,8 @@
 
 #include "target/rx/cpu.h"
 #include "hw/intc/rx_icu.h"
-#include "hw/timer/renesas_tmr.h"
-#include "hw/timer/renesas_cmt.h"
+#include "hw/timer/renesas_tmr8.h"
+#include "hw/timer/renesas_timer.h"
 #include "hw/char/renesas_sci.h"
 #include "hw/rx/rx62n-cpg.h"
 #include "qemu/units.h"
@@ -56,9 +56,9 @@ struct RX62NState {
 
     RXCPU cpu;
     RXICUState icu;
-    RTMRState tmr[RX62N_NR_TMR];
-    RCMTState cmt[RX62N_NR_CMT];
-    RSCIState sci[RX62N_NR_SCI];
+    RenesasTMR8State tmr[RX62N_NR_TMR];
+    RenesasCMTState cmt[RX62N_NR_CMT];
+    RenesasSCIState sci[RX62N_NR_SCI];
     RX62NCPGState cpg;
 
     MemoryRegion *sysmem;
diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c
index cfd41930bf..58eff0b4a3 100644
--- a/hw/rx/rx62n.c
+++ b/hw/rx/rx62n.c
@@ -164,7 +164,7 @@ static void register_tmr(RX62NState *s, int unit)
     char ckname[16];
 
     object_initialize_child(OBJECT(s), "tmr[*]",
-                            &s->tmr[unit], TYPE_RENESAS_TMR);
+                            &s->tmr[unit], TYPE_RENESAS_TMR8);
     tmr = SYS_BUS_DEVICE(&s->tmr[unit]);
 
     irqbase = RX62N_TMR_IRQ + TMR_NR_IRQ * unit;
@@ -174,10 +174,10 @@ static void register_tmr(RX62NState *s, int unit)
     sysbus_mmio_map(tmr, 0, RX62N_TMR_BASE + unit * 0x10);
 
     qdev_prop_set_uint32(DEVICE(tmr), "unit", unit);
-    sysbus_realize(tmr, &error_abort);
     snprintf(ckname, sizeof(ckname), "pck_tmr8-%d", unit);
     qdev_connect_clock_in(DEVICE(tmr), "pck",
                           qdev_get_clock_out(DEVICE(&s->cpg), ckname));
+    sysbus_realize(tmr, &error_abort);
 }
 
 static void register_cmt(RX62NState *s, int unit)
@@ -190,6 +190,9 @@ static void register_cmt(RX62NState *s, int unit)
                             &s->cmt[unit], TYPE_RENESAS_CMT);
     cmt = SYS_BUS_DEVICE(&s->cmt[unit]);
     qdev_prop_set_uint32(DEVICE(cmt), "unit", unit);
+    snprintf(ckname, sizeof(ckname), "pck_cmt-%d", unit);
+    qdev_connect_clock_in(DEVICE(cmt), "pck",
+                          qdev_get_clock_out(DEVICE(&s->cpg), ckname));
 
     irqbase = RX62N_CMT_IRQ + CMT_NR_IRQ * unit;
     for (i = 0; i < CMT_NR_IRQ; i++) {
@@ -197,20 +200,23 @@ static void register_cmt(RX62NState *s, int unit)
     }
     sysbus_mmio_map(cmt, 0, RX62N_CMT_BASE + unit * 0x10);
     sysbus_realize(cmt, &error_abort);
-    snprintf(ckname, sizeof(ckname), "pck_cmt-%d", unit);
-    qdev_connect_clock_in(DEVICE(cmt), "pck",
-                          qdev_get_clock_out(DEVICE(&s->cpg), ckname));
 }
 
 static void register_sci(RX62NState *s, int unit)
 {
     SysBusDevice *sci;
     int i, irqbase;
+    char ckname[16];
 
     object_initialize_child(OBJECT(s), "sci[*]",
-                            &s->sci[unit], TYPE_RENESAS_SCI);
+                            &s->sci[unit], TYPE_RENESAS_SCIA);
     sci = SYS_BUS_DEVICE(&s->sci[unit]);
     qdev_prop_set_chr(DEVICE(sci), "chardev", serial_hd(unit));
+    qdev_prop_set_uint32(DEVICE(sci), "unit", unit);
+    qdev_prop_set_uint32(DEVICE(sci), "register-size", SCI_REGSIZE_8);
+    snprintf(ckname, sizeof(ckname), "pck_sci-%d", unit);
+    qdev_connect_clock_in(DEVICE(sci), "pck",
+                          qdev_get_clock_out(DEVICE(&s->cpg), ckname));
     sysbus_realize(sci, &error_abort);
 
     irqbase = RX62N_SCI_IRQ + SCI_NR_IRQ * unit;
diff --git a/hw/rx/Kconfig b/hw/rx/Kconfig
index 2b297c5a6a..9cd1082623 100644
--- a/hw/rx/Kconfig
+++ b/hw/rx/Kconfig
@@ -1,8 +1,8 @@
 config RX62N_MCU
     bool
     select RX_ICU
-    select RENESAS_TMR
-    select RENESAS_CMT
+    select RENESAS_TMR8
+    select RENESAS_TIMER
     select RENESAS_SCI
 
 config RX_GDBSIM
-- 
2.20.1



^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 09/11] hw/sh4: sh7750 Add CPG.
  2021-05-27  5:21 [PATCH 00/11] Unified peripheral emulation for Renesas chips Yoshinori Sato
                   ` (7 preceding siblings ...)
  2021-05-27  5:21 ` [PATCH 08/11] hw/rx: rx62n use new hw modules Yoshinori Sato
@ 2021-05-27  5:21 ` Yoshinori Sato
  2021-05-27  5:21 ` [PATCH 10/11] hw/sh4: sh7750 use new hw modules Yoshinori Sato
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 21+ messages in thread
From: Yoshinori Sato @ 2021-05-27  5:21 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

CPG required new hw modules.

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 include/hw/sh4/sh7751-cpg.h |  94 ++++++++
 hw/sh4/sh7750.c             |  25 ++
 hw/sh4/sh7751-cpg.c         | 457 ++++++++++++++++++++++++++++++++++++
 hw/sh4/meson.build          |   1 +
 4 files changed, 577 insertions(+)
 create mode 100644 include/hw/sh4/sh7751-cpg.h
 create mode 100644 hw/sh4/sh7751-cpg.c

diff --git a/include/hw/sh4/sh7751-cpg.h b/include/hw/sh4/sh7751-cpg.h
new file mode 100644
index 0000000000..79f9abe210
--- /dev/null
+++ b/include/hw/sh4/sh7751-cpg.h
@@ -0,0 +1,94 @@
+/*
+ * SH7751(R) Clock generator circuit
+ *
+ * Datasheet: SH7751 Group, SH7751R Group User's Manual: Hardware
+ *            (Rev.4.01 R01UH0457EJ0401)
+ *
+ * Copyright (c) 2020 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/>.
+ */
+
+#ifndef HW_SH4_SH7751_CPG_H
+#define HW_SH4_SH7751_CPG_H
+
+#include "hw/sysbus.h"
+#include "hw/qdev-clock.h"
+
+#define TYPE_SH7751_CPG_BASE "sh7751-cpg-base"
+#define SH7751CPGBase(obj) \
+    OBJECT_CHECK(SH7751CPGBaseState, (obj), TYPE_SH7751_CPG_BASE)
+#define TYPE_SH7751_CPG "sh7751-cpg"
+#define SH7751CPG(obj) OBJECT_CHECK(SH7751CPGState, (obj), TYPE_SH7751_CPG)
+#define TYPE_SH7751R_CPG "sh7751r-cpg"
+#define SH7751RCPG(obj) OBJECT_CHECK(SH7751RCPGState, (obj), TYPE_SH7751R_CPG)
+#define SH7751CPG_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(SH7751CPGBaseClass, obj, TYPE_SH7751_CPG_BASE)
+#define SH7751CPGBaseClass(klass) \
+    OBJECT_CLASS_CHECK(SH7751CPGBaseClass, klass, TYPE_SH7751_CPG_BASE)
+
+enum {
+    CK_DMAC,
+    CK_SCIF,
+    CK_TMU_0,
+    CK_RTC,
+    CK_SCI,
+    CK_SQ,
+    CK_UBC,
+    CK_PCIC,
+    CK_TMU_1,
+    CK_INTC,
+    NUM_SUBCLOCK,
+};
+
+typedef struct SH7751CPGBaseState {
+    SysBusDevice parent_obj;
+    uint8_t stbcr[2];
+    uint32_t clkstp00;
+    uint16_t freqcr;
+
+    uint32_t clock_mode;
+    int ick;
+    Clock *clk_ick;
+    int bck;
+    Clock *clk_bck;
+    int pck;
+    Clock *clk_pck;
+    Clock *dev_clocks[NUM_SUBCLOCK];
+    uint32_t xtal_freq_hz;
+    MemoryRegion memory[3 * 2];
+} SH7751CPGBaseState;
+
+typedef struct {
+    SH7751CPGBaseState parent_obj;
+} SH7751CPGState;
+
+typedef struct {
+    SH7751CPGBaseState parent_obj;
+} SH7751RCPGState;
+
+typedef struct {
+    SysBusDeviceClass parent;
+    int (*pll1mul)(int mode, uint16_t freqcr);
+    uint16_t *initfreqcr;
+} SH7751CPGBaseClass;
+
+typedef struct {
+    SH7751CPGBaseClass parent;
+} SH7751CPGClass;
+
+typedef struct {
+    SH7751CPGBaseClass parent;
+} SH7751RCPGClass;
+
+#endif
diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c
index d53a436d8c..2f6c382aa6 100644
--- a/hw/sh4/sh7750.c
+++ b/hw/sh4/sh7750.c
@@ -24,6 +24,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "qapi/error.h"
 #include "hw/irq.h"
 #include "hw/sh4/sh.h"
 #include "sysemu/sysemu.h"
@@ -32,6 +33,8 @@
 #include "hw/sh4/sh_intc.h"
 #include "hw/timer/tmu012.h"
 #include "exec/exec-all.h"
+#include "hw/sh4/sh7751-cpg.h"
+#include "hw/qdev-properties.h"
 
 #define NB_DEVICES 4
 
@@ -752,9 +755,29 @@ static const MemoryRegionOps sh7750_mmct_ops = {
     .endianness = DEVICE_NATIVE_ENDIAN,
 };
 
+static SH7751CPGBaseState *sh_cpg_init(MemoryRegion *sysmem,
+                                      int cputype)
+{
+    const char *cpgtype;
+    SH7751CPGBaseState *cpg;
+    if (cputype & (SH_CPU_SH7750R | SH_CPU_SH7751R)) {
+        cpgtype = TYPE_SH7751R_CPG;
+    } else {
+        cpgtype = TYPE_SH7751_CPG;
+    }
+    cpg = SH7751CPGBase(qdev_new(cpgtype));
+    qdev_prop_set_uint32(DEVICE(cpg), "xtal-frequency-hz", 20 * 1000 * 1000);
+    qdev_prop_set_uint32(DEVICE(cpg), "clock-mode", 5);
+    sysbus_mmio_map(SYS_BUS_DEVICE(cpg), 0, 0x1fc00000);
+    sysbus_mmio_map(SYS_BUS_DEVICE(cpg), 1, P4ADDR(0x1fc00000));
+    sysbus_mmio_map(SYS_BUS_DEVICE(cpg), 2, A7ADDR(0x1fc00000));
+    return cpg;
+}
+
 SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem)
 {
     SH7750State *s;
+    SH7751CPGBaseState *cpg;
 
     s = g_malloc0(sizeof(SH7750State));
     s->cpu = cpu;
@@ -800,6 +823,7 @@ SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem)
 
     cpu->env.intc_handle = &s->intc;
 
+    cpg = sh_cpg_init(sysmem, cpu->env.id);
     sh_serial_init(sysmem, 0x1fe00000,
                    0, s->periph_freq, serial_hd(0),
                    s->intc.irqs[SCI1_ERI],
@@ -824,6 +848,7 @@ SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem)
 		s->intc.irqs[TMU2_TUNI],
 		s->intc.irqs[TMU2_TICPI]);
 
+    sysbus_realize(SYS_BUS_DEVICE(cpg), &error_abort);
     if (cpu->env.id & (SH_CPU_SH7750 | SH_CPU_SH7750S | SH_CPU_SH7751)) {
         sh_intc_register_sources(&s->intc,
 				 _INTC_ARRAY(vectors_dma4),
diff --git a/hw/sh4/sh7751-cpg.c b/hw/sh4/sh7751-cpg.c
new file mode 100644
index 0000000000..4e057908ff
--- /dev/null
+++ b/hw/sh4/sh7751-cpg.c
@@ -0,0 +1,457 @@
+/*
+ * SH7750 / SH7751 Clock Generation Circuit
+ *
+ * Datasheet: SH7751 Group, SH7751R Group User's Manual: Hardware
+ *            (Rev.4.01 R01UH0457EJ0401)
+ *
+ * Copyright (c) 2020 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 "qapi/error.h"
+#include "qemu/log.h"
+#include "hw/hw.h"
+#include "hw/sh4/sh7751-cpg.h"
+#include "hw/sysbus.h"
+#include "hw/qdev-properties.h"
+#include "hw/registerfields.h"
+#include "hw/qdev-properties.h"
+#include "hw/clock.h"
+#include "migration/vmstate.h"
+
+#define SH7751_XTAL_MIN_HZ  (1 * 1000 * 1000)
+#define SH7751_XTAL_MAX_HZ (34 * 1000 * 1000)
+
+REG16(FREQCR, 0)
+  FIELD(FREQCR, PFC, 0, 3)
+  FIELD(FREQCR, BFC, 3, 3)
+  FIELD(FREQCR, IFC, 6, 3)
+  FIELD(FREQCR, PLL2EN, 9, 1)
+  FIELD(FREQCR, PLL1EN, 10, 1)
+  FIELD(FREQCR, CKOEN, 11, 1)
+REG8(STBCR, 4)
+REG8(STBCR2, 16)
+
+REG32(CLKSTP00, 0)
+REG32(CLKSTPCLR00, 8)
+
+typedef struct {
+    const char *name;
+    int devnum;
+    int reg;
+    int offset;
+} dev_clock_t;
+
+static const dev_clock_t dev_clock_list[] = {
+    { .name = "pck_sci", .devnum = CK_SCI, .reg = 0, .offset = 0},
+    { .name = "pck_rtc", .devnum = CK_RTC, .reg = 0, .offset = 1},
+    { .name = "pck_tmu-0", .devnum = CK_TMU_0, .reg = 0, .offset = 2},
+    { .name = "pck_scif",  .devnum = CK_SCIF, .reg = 0, .offset = 3},
+    { .name = "pck_dmac", .devnum = CK_DMAC, .reg = 0, .offset = 4},
+    { .name = "pck_ubc", .devnum = CK_UBC, .reg = 1, .offset = 0},
+    { .name = "pck_sq", .devnum = CK_SQ, .reg = 1, .offset = 1},
+    { .name = "pck_intc", .devnum = CK_INTC, .reg = 2, .offset = 0},
+    { .name = "pck_tmu-1", .devnum = CK_TMU_1, .reg = 2, .offset = 1},
+    { .name = "pck_pcic", .devnum = CK_PCIC, .reg = 2, .offset = 2},
+    { },
+};
+
+static void set_clock_in(SH7751CPGBaseState *cpg, const dev_clock_t *ck)
+{
+    Clock *out;
+    uint64_t period;
+
+    out = qdev_get_clock_out(DEVICE(cpg), ck->name);
+    g_assert(out);
+    period = 0;
+    switch (ck->reg) {
+    case 0:
+    case 1:
+        if (extract8(cpg->stbcr[ck->reg], ck->offset, 1) == 0) {
+            period = clock_get(cpg->clk_ick);
+        }
+        break;
+    case 2:
+        if (extract32(cpg->clkstp00, ck->offset, 1) == 0) {
+            period = clock_get(cpg->clk_ick);
+        }
+        break;
+    }
+    if (clock_get(out) != period) {
+        clock_update(out, period);
+    }
+}
+
+static void update_divrate(SH7751CPGBaseState *cpg)
+{
+    SH7751CPGBaseClass *k = SH7751CPG_GET_CLASS(cpg);
+    int ick = FIELD_EX32(cpg->freqcr, FREQCR, IFC);
+    int bck = FIELD_EX32(cpg->freqcr, FREQCR, BFC);
+    int pck = FIELD_EX32(cpg->freqcr, FREQCR, PFC);
+    const dev_clock_t *p = dev_clock_list;
+    uint32_t divinput;
+
+    divinput = cpg->xtal_freq_hz * k->pll1mul(cpg->clock_mode, cpg->freqcr);
+
+    ick = ick < 4 ? ick + 1 : (ick - 1) * 2;
+    clock_update_hz(cpg->clk_ick, divinput / ick);
+    bck = bck < 4 ? bck + 1 : (bck - 1) * 2;
+    clock_update_hz(cpg->clk_bck, divinput / bck);
+    pck = pck < 3 ? pck + 2 : pck * 2;
+    clock_update_hz(cpg->clk_pck, divinput / pck);
+
+     while (p->name) {
+        set_clock_in(cpg, p);
+        p++;
+    }
+}
+
+static const dev_clock_t *find_clock_list(int crno, int bit)
+{
+    const dev_clock_t *ret = dev_clock_list;
+    while (ret->name) {
+        if (ret->reg == crno && ret->offset == bit) {
+            return ret;
+        }
+        ret++;
+    }
+    return NULL;
+}
+
+static void update_stbcr(SH7751CPGBaseState *cpg, int no, uint32_t diff)
+{
+    int bit = 0;
+    const dev_clock_t *p;
+    static const char *reg[] = {"STBCR", "STBCR2", "CLKSTP00"};
+
+    while (diff) {
+        if (diff & 1) {
+            p = find_clock_list(no, bit);
+            if (p) {
+                set_clock_in(cpg, p);
+            } else {
+                qemu_log_mask(LOG_UNIMP, "sh7751-cpg: %s "
+                              " bit %d is not implement.\n", reg[no], bit);
+            }
+        }
+        bit++;
+        diff >>= 1;
+    }
+}
+
+static uint64_t cpg_read(void *opaque, hwaddr addr, unsigned size)
+{
+    SH7751CPGBaseState *cpg = SH7751CPGBase(opaque);
+    int reg;
+
+    switch (addr) {
+    case A_FREQCR:
+        if (size != 2) {
+            qemu_log_mask(LOG_GUEST_ERROR, "sh7751-cpg: Register 0x%"
+                          HWADDR_PRIX " Invalid access size.\n", addr);
+            return UINT64_MAX;
+        }
+        return cpg->freqcr;
+    case A_STBCR:
+    case A_STBCR2:
+        if (size != 1) {
+            qemu_log_mask(LOG_GUEST_ERROR, "sh7751-cpg: Register 0x%"
+                          HWADDR_PRIX " Invalid access size.\n", addr);
+            return UINT64_MAX;
+        }
+        reg = extract32(addr, 4, 1); /* STBCR -> 0x04 / STBCR2 -> 0x10 */
+        return cpg->stbcr[reg];
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "sh7751-cpg: Register 0x%"
+                      HWADDR_PRIX " Invalid address.\n", addr);
+        return UINT64_MAX;
+    }
+
+}
+
+static void cpg_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    SH7751CPGBaseState *cpg = SH7751CPGBase(opaque);
+    uint32_t old_stbcr;
+    int reg;
+
+    switch (addr) {
+    case A_FREQCR:
+        if (size != 2) {
+            qemu_log_mask(LOG_GUEST_ERROR, "sh7751-cpg: Register 0x%"
+                          HWADDR_PRIX " Invalid access size.\n", addr);
+            return;
+        }
+        if ((cpg->freqcr ^ val) & 0x0600) {
+            qemu_log_mask(LOG_UNIMP,
+                          "sh7751-cpg: PLL operation not supported.\n");
+        }
+        cpg->freqcr = val;
+        update_divrate(cpg);
+        break;
+    case A_STBCR:
+    case A_STBCR2:
+        if (size != 1) {
+            qemu_log_mask(LOG_GUEST_ERROR, "sh7751-cpg: Register 0x%"
+                          HWADDR_PRIX " Invalid access size.\n", addr);
+            return;
+        }
+        reg = extract32(addr, 4, 1); /* STBCR -> 0x04 / STBCR2 -> 0x10 */
+        old_stbcr = cpg->stbcr[reg];
+        old_stbcr ^= val;
+        cpg->stbcr[reg] = val;
+        update_stbcr(cpg, reg, old_stbcr);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "sh7751-cpg: Register 0x%"
+                      HWADDR_PRIX " Invalid address.\n", addr);
+    }
+}
+
+static uint64_t stp_read(void *opaque, hwaddr addr, unsigned size)
+{
+    SH7751CPGBaseState *cpg = SH7751CPGBase(opaque);
+
+    switch (addr) {
+    case A_CLKSTP00:
+        return cpg->clkstp00;
+    case A_CLKSTPCLR00:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "sh7751-cpg: CLKSTPCLR00 is write only.\n");
+        return UINT64_MAX;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "sh7751-cpg: Register 0x%"
+                      HWADDR_PRIX " Invalid address.\n", addr);
+        return UINT64_MAX;
+    }
+}
+
+static void stp_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    SH7751CPGBaseState *cpg = SH7751CPGBase(opaque);
+
+    val &= 0x7;
+    switch (addr) {
+    case A_CLKSTP00:
+        cpg->clkstp00 |= val;
+        update_stbcr(cpg, 2, val);
+        break;
+    case A_CLKSTPCLR00:
+        cpg->clkstp00 &= ~val;
+        update_stbcr(cpg, 2, val);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "sh7751-cpg: Register 0x%"
+                      HWADDR_PRIX " Invalid address.\n", addr);
+    }
+}
+
+static int sh7751_pll1mul(int mode, uint16_t freqcr)
+{
+    int div1;
+    int pll1;
+    switch (mode) {
+    case 3:
+    case 5:
+    case 6:
+        div1 = 2;
+        break;
+    default:
+        div1 = 1;
+    }
+    if (FIELD_EX16(freqcr, FREQCR, PLL1EN)) {
+        pll1 = 6;
+    } else {
+        pll1 = 1;
+    }
+    return pll1 / div1;
+}
+
+static int sh7751r_pll1mul(int mode, uint16_t freqcr)
+{
+    int pll1;
+    switch (mode) {
+    case 0:
+    case 1:
+    case 3:
+    case 5:
+        pll1 = 12;
+        break;
+    case 2:
+    case 4:
+    case 6:
+        pll1 = 6;
+        break;
+    default:
+        g_assert_not_reached();
+    }
+    if (!FIELD_EX16(freqcr, FREQCR, PLL1EN)) {
+        pll1 = 1;
+    }
+    return pll1;
+}
+
+static const MemoryRegionOps cpg_ops = {
+    .write = cpg_write,
+    .read  = cpg_read,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+};
+
+static const MemoryRegionOps stp_ops = {
+    .write = stp_write,
+    .read  = stp_read,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static const ClockPortInitArray sh7751_cpg_clocks = {
+    QDEV_CLOCK_OUT(SH7751CPGBaseState, clk_ick),
+    QDEV_CLOCK_OUT(SH7751CPGBaseState, clk_bck),
+    QDEV_CLOCK_OUT(SH7751CPGBaseState, clk_pck),
+    QDEV_CLOCK_END
+};
+
+static void sh7751cpg_realize(DeviceState *dev, Error **errp)
+{
+    SH7751CPGBaseState *cpg = SH7751CPGBase(dev);
+    SH7751CPGBaseClass *k = SH7751CPG_GET_CLASS(cpg);
+
+    if (cpg->xtal_freq_hz == 0) {
+        error_setg(errp, "\"xtal-frequency-hz\" property must be provided.");
+        return;
+    }
+    /* XTAL range: 1-34 MHz */
+    if (cpg->xtal_freq_hz < SH7751_XTAL_MIN_HZ ||
+        cpg->xtal_freq_hz > SH7751_XTAL_MAX_HZ) {
+        error_setg(errp, "\"xtal-frequency-hz\" property in incorrect range.");
+        return;
+    }
+    /* Clock mode: 0 - 6 */
+    if (cpg->clock_mode > 6) {
+        error_setg(errp, "\"clock-mode\" property in incorrect range.");
+        return;
+    }
+
+    cpg->freqcr = k->initfreqcr[cpg->clock_mode];
+    update_divrate(cpg);
+}
+
+static void sh7751_cpg_init(Object *obj)
+{
+    SH7751CPGBaseState *cpg = SH7751CPGBase(obj);
+    const dev_clock_t *p = dev_clock_list;
+    qdev_init_clocks(DEVICE(obj), sh7751_cpg_clocks);
+    /* connect parent clock */
+    while (p->name) {
+        cpg->dev_clocks[p->devnum] = qdev_init_clock_out(DEVICE(obj),
+                                                         p->name);
+        p++;
+    }
+
+    memory_region_init_io(&cpg->memory[0], OBJECT(cpg), &cpg_ops,
+                          cpg, "sh7751-cpg", 0x14);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &cpg->memory[0]);
+    memory_region_init_alias(&cpg->memory[1], NULL,
+                             "sh7751-cpg-a4", &cpg->memory[0], 0, 0x14);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &cpg->memory[1]);
+    memory_region_init_alias(&cpg->memory[2], NULL,
+                             "sh7751-cpg-p7", &cpg->memory[0], 0, 0x14);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &cpg->memory[2]);
+    memory_region_init_io(&cpg->memory[3], OBJECT(cpg), &stp_ops,
+                          cpg, "sh7751-stp", 0x10);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &cpg->memory[3]);
+    memory_region_init_alias(&cpg->memory[4], NULL,
+                             "sh7751-stp-a4", &cpg->memory[3], 0, 0x10);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &cpg->memory[4]);
+    memory_region_init_alias(&cpg->memory[5], NULL,
+                             "sh7751-stp-p7", &cpg->memory[3], 0, 0x10u);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &cpg->memory[5]);
+}
+
+static Property sh7751_cpg_properties[] = {
+    DEFINE_PROP_UINT32("xtal-frequency-hz",
+                       SH7751CPGBaseState, xtal_freq_hz, 0),
+    DEFINE_PROP_UINT32("clock-mode",
+                       SH7751CPGBaseState, clock_mode, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void sh7751cpg_base_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    device_class_set_props(dc, sh7751_cpg_properties);
+}
+
+static void sh7751cpg_class_init(ObjectClass *klass, void *data)
+{
+    SH7751CPGBaseClass *base = SH7751CPGBaseClass(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    static uint16_t initfreqcr[] = {0x0e1a, 0x0e23, 0x0e13, 0x0e13,
+                                   0x0e0a, 0x0e0a, 0x0808};
+
+    base->pll1mul = sh7751_pll1mul;
+    base->initfreqcr = initfreqcr;
+    dc->realize = sh7751cpg_realize;
+}
+
+static void sh7751rcpg_class_init(ObjectClass *klass, void *data)
+{
+    SH7751CPGBaseClass *base = SH7751CPGBaseClass(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    static uint16_t initfreqcr[] = {0x0e1a, 0x0e2c, 0x0e13, 0x0e13,
+                                   0x0e0a, 0x0e0a, 0x0808};
+
+    base->pll1mul = sh7751r_pll1mul;
+    base->initfreqcr = initfreqcr;
+    dc->realize = sh7751cpg_realize;
+}
+
+static const TypeInfo sh7751cpg_info[] = {
+    {
+        .name       = TYPE_SH7751_CPG_BASE,
+        .parent     = TYPE_SYS_BUS_DEVICE,
+        .instance_size = sizeof(SH7751CPGBaseState),
+        .class_init = sh7751cpg_base_class_init,
+        .class_size = sizeof(SH7751CPGBaseClass),
+        .abstract = true,
+    },
+    {
+        .name       = TYPE_SH7751_CPG,
+        .parent     = TYPE_SH7751_CPG_BASE,
+        .instance_size = sizeof(SH7751CPGState),
+        .instance_init = sh7751_cpg_init,
+        .class_init = sh7751cpg_class_init,
+        .class_size = sizeof(SH7751CPGClass),
+    },
+    {
+        .name       = TYPE_SH7751R_CPG,
+        .parent     = TYPE_SH7751_CPG_BASE,
+        .instance_size = sizeof(SH7751RCPGState),
+        .instance_init = sh7751_cpg_init,
+        .class_init = sh7751rcpg_class_init,
+        .class_size = sizeof(SH7751RCPGClass),
+    },
+};
+
+DEFINE_TYPES(sh7751cpg_info)
diff --git a/hw/sh4/meson.build b/hw/sh4/meson.build
index 424d5674de..7ed8246152 100644
--- a/hw/sh4/meson.build
+++ b/hw/sh4/meson.build
@@ -2,6 +2,7 @@ sh4_ss = ss.source_set()
 sh4_ss.add(files(
   'sh7750.c',
   'sh7750_regnames.c',
+  'sh7751-cpg.c',
 ))
 sh4_ss.add(when: 'CONFIG_R2D', if_true: files('r2d.c'))
 sh4_ss.add(when: 'CONFIG_SHIX', if_true: files('shix.c'))
-- 
2.20.1



^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 10/11] hw/sh4: sh7750 use new hw modules.
  2021-05-27  5:21 [PATCH 00/11] Unified peripheral emulation for Renesas chips Yoshinori Sato
                   ` (8 preceding siblings ...)
  2021-05-27  5:21 ` [PATCH 09/11] hw/sh4: sh7750 Add CPG Yoshinori Sato
@ 2021-05-27  5:21 ` Yoshinori Sato
  2021-05-27  5:21 ` [PATCH 11/11] hw/rx: rx-gdbsim Add bootstrup for linux Yoshinori Sato
  2021-05-27  5:33 ` [PATCH 00/11] Unified peripheral emulation for Renesas chips no-reply
  11 siblings, 0 replies; 21+ messages in thread
From: Yoshinori Sato @ 2021-05-27  5:21 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 include/hw/sh4/sh.h |  8 -----
 hw/sh4/sh7750.c     | 87 +++++++++++++++++++++++++++++++++++++++------
 hw/sh4/Kconfig      |  4 +--
 3 files changed, 79 insertions(+), 20 deletions(-)

diff --git a/include/hw/sh4/sh.h b/include/hw/sh4/sh.h
index becb596979..74e1ba59a8 100644
--- a/include/hw/sh4/sh.h
+++ b/include/hw/sh4/sh.h
@@ -55,14 +55,6 @@ int sh7750_register_io_device(struct SH7750State *s,
 
 /* sh_serial.c */
 #define SH_SERIAL_FEAT_SCIF (1 << 0)
-void sh_serial_init(MemoryRegion *sysmem,
-                    hwaddr base, int feat,
-                    uint32_t freq, Chardev *chr,
-		     qemu_irq eri_source,
-		     qemu_irq rxi_source,
-		     qemu_irq txi_source,
-		     qemu_irq tei_source,
-		     qemu_irq bri_source);
 
 /* sh7750.c */
 qemu_irq sh7750_irl(struct SH7750State *s);
diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c
index 2f6c382aa6..b2f6ebe936 100644
--- a/hw/sh4/sh7750.c
+++ b/hw/sh4/sh7750.c
@@ -31,9 +31,10 @@
 #include "sh7750_regs.h"
 #include "sh7750_regnames.h"
 #include "hw/sh4/sh_intc.h"
-#include "hw/timer/tmu012.h"
+#include "hw/timer/renesas_timer.h"
 #include "exec/exec-all.h"
 #include "hw/sh4/sh7751-cpg.h"
+#include "hw/char/renesas_sci.h"
 #include "hw/qdev-properties.h"
 
 #define NB_DEVICES 4
@@ -774,6 +775,74 @@ static SH7751CPGBaseState *sh_cpg_init(MemoryRegion *sysmem,
     return cpg;
 }
 
+static void tmu012_init(MemoryRegion *sysmem, hwaddr base,
+                        int unit,
+                        qemu_irq ch0_irq, qemu_irq ch1_irq,
+                        qemu_irq ch2_irq0, qemu_irq ch2_irq1,
+                        SH7751CPGBaseState *cpg)
+{
+    RenesasTMUState *tmu;
+    char ckname[16];
+
+    tmu = RENESAS_TMU(qdev_new(TYPE_RENESAS_TMU));
+    qdev_prop_set_uint32(DEVICE(tmu), "unit", unit);
+    snprintf(ckname, sizeof(ckname), "pck_tmu-%d", unit);
+    qdev_connect_clock_in(DEVICE(tmu), "pck",
+                          qdev_get_clock_out(DEVICE(cpg), "pck_tmu-0"));
+
+    sysbus_realize(SYS_BUS_DEVICE(tmu), &error_abort);
+    sysbus_connect_irq(SYS_BUS_DEVICE(tmu), 0, ch0_irq);
+    sysbus_connect_irq(SYS_BUS_DEVICE(tmu), 1, ch1_irq);
+    if (unit == 0) {
+        sysbus_connect_irq(SYS_BUS_DEVICE(tmu), 2, ch2_irq0);
+    }
+    /* ch2_irq1 is not used. */
+
+    sysbus_mmio_map(SYS_BUS_DEVICE(tmu), 0, base);
+    sysbus_mmio_map(SYS_BUS_DEVICE(tmu), 1, P4ADDR(base));
+    sysbus_mmio_map(SYS_BUS_DEVICE(tmu), 2, A7ADDR(base));
+}
+
+static void sh_serial_init(MemoryRegion *sysmem,
+                           hwaddr base, int feat,
+                           uint32_t freq, Chardev *chr,
+                           qemu_irq eri_source,
+                           qemu_irq rxi_source,
+                           qemu_irq txi_source,
+                           qemu_irq tei_source,
+                           qemu_irq bri_source,
+                           SH7751CPGBaseState *cpg)
+{
+    RenesasSCIBaseState *sci;
+    char ckname[16];
+
+    switch(feat) {
+    case 0: /* SCI */
+        sci = RENESAS_SCI_BASE(qdev_new(TYPE_RENESAS_SCI));
+        snprintf(ckname, sizeof(ckname), "pck_sci");
+        break;
+    case SH_SERIAL_FEAT_SCIF:
+        sci = RENESAS_SCI_BASE(qdev_new(TYPE_RENESAS_SCIF));
+        snprintf(ckname, sizeof(ckname), "pck_scif");
+        break;
+    }
+    qdev_prop_set_chr(DEVICE(sci), "chardev", chr);
+    qdev_prop_set_uint32(DEVICE(sci), "register-size", SCI_REGSIZE_32);
+    qdev_connect_clock_in(DEVICE(sci), "pck",
+                          qdev_get_clock_out(DEVICE(cpg), ckname));
+    sysbus_connect_irq(SYS_BUS_DEVICE(sci), 0, eri_source);
+    sysbus_connect_irq(SYS_BUS_DEVICE(sci), 1, rxi_source);
+    sysbus_connect_irq(SYS_BUS_DEVICE(sci), 2, txi_source);
+    if (tei_source)
+        sysbus_connect_irq(SYS_BUS_DEVICE(sci), 3, tei_source);
+    if (bri_source)
+        sysbus_connect_irq(SYS_BUS_DEVICE(sci), 3, bri_source);
+    sysbus_realize(SYS_BUS_DEVICE(sci), &error_abort);
+    sysbus_mmio_map(SYS_BUS_DEVICE(sci), 0, base);
+    sysbus_mmio_map(SYS_BUS_DEVICE(sci), 1, P4ADDR(base));
+    sysbus_mmio_map(SYS_BUS_DEVICE(sci), 2, A7ADDR(base));
+}
+
 SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem)
 {
     SH7750State *s;
@@ -830,7 +899,7 @@ SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem)
                    s->intc.irqs[SCI1_RXI],
                    s->intc.irqs[SCI1_TXI],
                    s->intc.irqs[SCI1_TEI],
-                   NULL);
+                   NULL, cpg);
     sh_serial_init(sysmem, 0x1fe80000,
                    SH_SERIAL_FEAT_SCIF,
                    s->periph_freq, serial_hd(1),
@@ -838,17 +907,14 @@ SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem)
                    s->intc.irqs[SCIF_RXI],
                    s->intc.irqs[SCIF_TXI],
                    NULL,
-                   s->intc.irqs[SCIF_BRI]);
+                   s->intc.irqs[SCIF_BRI], cpg);
 
-    tmu012_init(sysmem, 0x1fd80000,
-		TMU012_FEAT_TOCR | TMU012_FEAT_3CHAN | TMU012_FEAT_EXTCLK,
-		s->periph_freq,
+    tmu012_init(sysmem, 0x1fd80000, 0,
 		s->intc.irqs[TMU0],
 		s->intc.irqs[TMU1],
 		s->intc.irqs[TMU2_TUNI],
-		s->intc.irqs[TMU2_TICPI]);
+		s->intc.irqs[TMU2_TICPI], cpg);
 
-    sysbus_realize(SYS_BUS_DEVICE(cpg), &error_abort);
     if (cpu->env.id & (SH_CPU_SH7750 | SH_CPU_SH7750S | SH_CPU_SH7751)) {
         sh_intc_register_sources(&s->intc,
 				 _INTC_ARRAY(vectors_dma4),
@@ -865,10 +931,10 @@ SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem)
         sh_intc_register_sources(&s->intc,
 				 _INTC_ARRAY(vectors_tmu34),
 				 NULL, 0);
-        tmu012_init(sysmem, 0x1e100000, 0, s->periph_freq,
+        tmu012_init(sysmem, 0x1e100000, 1,
 		    s->intc.irqs[TMU3],
 		    s->intc.irqs[TMU4],
-		    NULL, NULL);
+		    NULL, NULL, cpg);
     }
 
     if (cpu->env.id & (SH_CPU_SH7751_ALL)) {
@@ -886,6 +952,7 @@ SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem)
     sh_intc_register_sources(&s->intc,
 				_INTC_ARRAY(vectors_irl),
 				_INTC_ARRAY(groups_irl));
+    sysbus_realize(SYS_BUS_DEVICE(cpg), &error_abort);
     return s;
 }
 
diff --git a/hw/sh4/Kconfig b/hw/sh4/Kconfig
index ab733a3f76..b2da634d22 100644
--- a/hw/sh4/Kconfig
+++ b/hw/sh4/Kconfig
@@ -20,5 +20,5 @@ config SHIX
 config SH7750
     bool
     select SH_INTC
-    select SH_SCI
-    select SH_TIMER
+    select RENESAS_TIMER
+    select RENESAS_SCI
-- 
2.20.1



^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 11/11] hw/rx: rx-gdbsim Add bootstrup for linux
  2021-05-27  5:21 [PATCH 00/11] Unified peripheral emulation for Renesas chips Yoshinori Sato
                   ` (9 preceding siblings ...)
  2021-05-27  5:21 ` [PATCH 10/11] hw/sh4: sh7750 use new hw modules Yoshinori Sato
@ 2021-05-27  5:21 ` Yoshinori Sato
  2021-05-27  5:33 ` [PATCH 00/11] Unified peripheral emulation for Renesas chips no-reply
  11 siblings, 0 replies; 21+ messages in thread
From: Yoshinori Sato @ 2021-05-27  5:21 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

linux kernel require initializing some peripherals.

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 include/hw/rx/rx62n.h | 16 ++++----
 hw/rx/rx-gdbsim.c     | 89 +++++++++++++++++++++++++------------------
 hw/rx/rx62n.c         | 15 --------
 3 files changed, 59 insertions(+), 61 deletions(-)

diff --git a/include/hw/rx/rx62n.h b/include/hw/rx/rx62n.h
index 942ed0639f..3bbeb5da52 100644
--- a/include/hw/rx/rx62n.h
+++ b/include/hw/rx/rx62n.h
@@ -33,14 +33,6 @@
 #include "qemu/units.h"
 #include "qom/object.h"
 
-#define TYPE_RX62N_MCU "rx62n-mcu"
-typedef struct RX62NState RX62NState;
-DECLARE_INSTANCE_CHECKER(RX62NState, RX62N_MCU,
-                         TYPE_RX62N_MCU)
-
-#define TYPE_R5F562N7_MCU "r5f562n7-mcu"
-#define TYPE_R5F562N8_MCU "r5f562n8-mcu"
-
 #define EXT_CS_BASE         0x01000000
 #define VECTOR_TABLE_BASE   0xffffff80
 #define RX62N_CFLASH_BASE   0xfff80000
@@ -49,7 +41,7 @@ DECLARE_INSTANCE_CHECKER(RX62NState, RX62N_MCU,
 #define RX62N_NR_CMT    2
 #define RX62N_NR_SCI    6
 
-struct RX62NState {
+typedef struct RX62NState {
     /*< private >*/
     DeviceState parent_obj;
     /*< public >*/
@@ -75,5 +67,11 @@ struct RX62NState {
     uint32_t xtal_freq_hz;
 } RX62NState;
 
+#define TYPE_RX62N_MCU "rx62n-mcu"
+
+#define TYPE_R5F562N7_MCU "r5f562n7-mcu"
+#define TYPE_R5F562N8_MCU "r5f562n8-mcu"
+DECLARE_INSTANCE_CHECKER(RX62NState, RX62N_MCU,
+                         TYPE_RX62N_MCU)
 
 #endif
diff --git a/hw/rx/rx-gdbsim.c b/hw/rx/rx-gdbsim.c
index 75d1fec6ca..34705d953b 100644
--- a/hw/rx/rx-gdbsim.c
+++ b/hw/rx/rx-gdbsim.c
@@ -31,14 +31,16 @@
 /* Same address of GDB integrated simulator */
 #define SDRAM_BASE  EXT_CS_BASE
 
+typedef struct RxGdbSimMachineClass RxGdbSimMachineClass;
+
 struct RxGdbSimMachineClass {
     /*< private >*/
     MachineClass parent_class;
     /*< public >*/
     const char *mcu_name;
     uint32_t xtal_freq_hz;
+    size_t romsize;
 };
-typedef struct RxGdbSimMachineClass RxGdbSimMachineClass;
 
 struct RxGdbSimMachineState {
     /*< private >*/
@@ -54,26 +56,50 @@ DECLARE_OBJ_CHECKERS(RxGdbSimMachineState, RxGdbSimMachineClass,
                      RX_GDBSIM_MACHINE, TYPE_RX_GDBSIM_MACHINE)
 
 
-static void rx_load_image(RXCPU *cpu, const char *filename,
-                          uint32_t start, uint32_t size)
+#define TINYBOOT_TOP (0xffffff00)
+
+static void set_bootstrap(hwaddr entry, hwaddr dtb)
 {
-    static uint32_t extable[32];
-    long kernel_size;
+    /* Minimal hardware initialize for kernel requirement */
+    /* linux kernel only works little-endian mode */
+    static uint8_t tinyboot[256] = {
+        0xfb, 0x2e, 0x20, 0x00, 0x08,       /* mov.l #0x80020, r2 */
+        0xf8, 0x2e, 0x00, 0x01, 0x01,       /* mov.l #0x00010100, [r2] */
+        0xfb, 0x2e, 0x10, 0x00, 0x08,       /* mov.l #0x80010, r2 */
+        0xf8, 0x22, 0xdf, 0x7d, 0xff, 0xff, /* mov.l #0xffff7ddf, [r2] */
+        0x62, 0x42,                         /* add #4, r2 */
+        0xf8, 0x22, 0xff, 0x7f, 0xff, 0x7f, /* mov.l #0x7fff7fff, [r2] */
+        0xfb, 0x2e, 0x40, 0x82, 0x08,       /* mov.l #0x88240, r2 */
+        0x3c, 0x22, 0x00,                   /* mov.b #0, 2[r2] */
+        0x3c, 0x21, 0x4e,                   /* mov.b #78, 1[r2] */
+        0xfb, 0x22, 0x70, 0xff, 0xff, 0xff, /* mov.l #0xffffff70, r2 */
+        0xec, 0x21,                         /* mov.l [r2], r1 */
+        0xfb, 0x22, 0x74, 0xff, 0xff, 0xff, /* mov.l #0xffffff74, r2 */
+        0xec, 0x22,                         /* mov.l [r2], r2 */
+        0x7f, 0x02,                         /* jmp r2 */
+    };
     int i;
 
+    *((uint32_t *)&tinyboot[0x70]) = cpu_to_le32(dtb);
+    *((uint32_t *)&tinyboot[0x74]) = cpu_to_le32(entry);
+
+    /* setup exception trap trampoline */
+    for (i = 0; i < 31; i++) {
+        *((uint32_t *)&tinyboot[0x80 + i * 4]) = cpu_to_le32(0x10 + i * 4);
+    }
+    *((uint32_t *)&tinyboot[0xfc]) = cpu_to_le32(TINYBOOT_TOP);
+    rom_add_blob_fixed("tinyboot", tinyboot, sizeof(tinyboot), TINYBOOT_TOP);
+}
+
+static void load_kernel(const char *filename, uint32_t start, uint32_t size)
+{
+    long kernel_size;
+
     kernel_size = load_image_targphys(filename, start, size);
     if (kernel_size < 0) {
         fprintf(stderr, "qemu: could not load kernel '%s'\n", filename);
         exit(1);
     }
-    cpu->env.pc = start;
-
-    /* setup exception trap trampoline */
-    /* linux kernel only works little-endian mode */
-    for (i = 0; i < ARRAY_SIZE(extable); i++) {
-        extable[i] = cpu_to_le32(0x10 + i * 4);
-    }
-    rom_add_blob_fixed("extable", extable, sizeof(extable), VECTOR_TABLE_BASE);
 }
 
 static void rx_gdbsim_init(MachineState *machine)
@@ -101,33 +127,15 @@ static void rx_gdbsim_init(MachineState *machine)
                              &error_abort);
     object_property_set_uint(OBJECT(&s->mcu), "xtal-frequency-hz",
                              rxc->xtal_freq_hz, &error_abort);
-    object_property_set_bool(OBJECT(&s->mcu), "load-kernel",
-                             kernel_filename != NULL, &error_abort);
-
-    if (!kernel_filename) {
-        if (machine->firmware) {
-            rom_add_file_fixed(machine->firmware, RX62N_CFLASH_BASE, 0);
-        } else if (!qtest_enabled()) {
-            error_report("No bios or kernel specified");
-            exit(1);
-        }
-    }
-
-    qdev_realize(DEVICE(&s->mcu), NULL, &error_abort);
-
     /* Load kernel and dtb */
     if (kernel_filename) {
         ram_addr_t kernel_offset;
-
-        /*
-         * The kernel image is loaded into
-         * the latter half of the SDRAM space.
-         */
+        ram_addr_t dtb_offset = 0;
         kernel_offset = machine->ram_size / 2;
-        rx_load_image(RX_CPU(first_cpu), kernel_filename,
-                      SDRAM_BASE + kernel_offset, kernel_offset);
+
+        load_kernel(machine->kernel_filename,
+                    SDRAM_BASE + kernel_offset, kernel_offset);
         if (dtb_filename) {
-            ram_addr_t dtb_offset;
             int dtb_size;
             g_autofree void *dtb = load_device_tree(dtb_filename, &dtb_size);
 
@@ -145,10 +153,17 @@ static void rx_gdbsim_init(MachineState *machine)
             dtb_offset = machine->ram_size - dtb_size;
             rom_add_blob_fixed("dtb", dtb, dtb_size,
                                SDRAM_BASE + dtb_offset);
-            /* Set dtb address to R1 */
-            RX_CPU(first_cpu)->env.regs[1] = SDRAM_BASE + dtb_offset;
+        }
+        set_bootstrap(SDRAM_BASE + kernel_offset, SDRAM_BASE + dtb_offset);
+    } else {
+        if (machine->firmware) {
+            rom_add_file_fixed(machine->firmware, RX62N_CFLASH_BASE, 0);
+        } else if (!qtest_enabled()) {
+            error_report("No bios or kernel specified");
+            exit(1);
         }
     }
+    qdev_realize(DEVICE(&s->mcu), NULL, &error_abort);
 }
 
 static void rx_gdbsim_class_init(ObjectClass *oc, void *data)
diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c
index 58eff0b4a3..d84ffa148c 100644
--- a/hw/rx/rx62n.c
+++ b/hw/rx/rx62n.c
@@ -58,20 +58,6 @@
 #define RX62N_XTAL_MIN_HZ  (8 * 1000 * 1000)
 #define RX62N_XTAL_MAX_HZ (14 * 1000 * 1000)
 
-struct RX62NClass {
-    /*< private >*/
-    DeviceClass parent_class;
-    /*< public >*/
-    const char *name;
-    uint64_t ram_size;
-    uint64_t rom_flash_size;
-    uint64_t data_flash_size;
-};
-typedef struct RX62NClass RX62NClass;
-
-DECLARE_CLASS_CHECKERS(RX62NClass, RX62N_MCU,
-                       TYPE_RX62N_MCU)
-
 /*
  * IRQ -> IPR mapping table
  * 0x00 - 0x91: IPR no (IPR00 to IPR91)
@@ -281,7 +267,6 @@ static void rx62n_realize(DeviceState *dev, Error **errp)
 static Property rx62n_properties[] = {
     DEFINE_PROP_LINK("main-bus", RX62NState, sysmem, TYPE_MEMORY_REGION,
                      MemoryRegion *),
-    DEFINE_PROP_BOOL("load-kernel", RX62NState, kernel, false),
     DEFINE_PROP_UINT32("xtal-frequency-hz", RX62NState, xtal_freq_hz, 0),
     DEFINE_PROP_END_OF_LIST(),
 };
-- 
2.20.1



^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH 00/11] Unified peripheral emulation for Renesas chips
  2021-05-27  5:21 [PATCH 00/11] Unified peripheral emulation for Renesas chips Yoshinori Sato
                   ` (10 preceding siblings ...)
  2021-05-27  5:21 ` [PATCH 11/11] hw/rx: rx-gdbsim Add bootstrup for linux Yoshinori Sato
@ 2021-05-27  5:33 ` no-reply
  11 siblings, 0 replies; 21+ messages in thread
From: no-reply @ 2021-05-27  5:33 UTC (permalink / raw)
  To: ysato; +Cc: qemu-devel, ysato

Patchew URL: https://patchew.org/QEMU/20210527052122.97103-1-ysato@users.sourceforge.jp/



Hi,

This series seems to have some coding style problems. See output below for
more information:

Type: series
Message-id: 20210527052122.97103-1-ysato@users.sourceforge.jp
Subject: [PATCH 00/11] Unified peripheral emulation for Renesas chips

=== TEST SCRIPT BEGIN ===
#!/bin/bash
git rev-parse base > /dev/null || exit 0
git config --local diff.renamelimit 0
git config --local diff.renames True
git config --local diff.algorithm histogram
./scripts/checkpatch.pl --mailback base..
=== TEST SCRIPT END ===

Updating 3c8cf5a9c21ff8782164d1def7f44bd888713384
From https://github.com/patchew-project/qemu
 * [new tag]         patchew/20210527052122.97103-1-ysato@users.sourceforge.jp -> patchew/20210527052122.97103-1-ysato@users.sourceforge.jp
Switched to a new branch 'test'
0d61629 hw/rx: rx-gdbsim Add bootstrup for linux
5595fd4 hw/sh4: sh7750 use new hw modules.
f85d3d2 hw/sh4: sh7750 Add CPG.
9422b37 hw/rx: rx62n use new hw modules.
88d7ca2 hw/timer: Renesas 8bit timer.
afe51ff hw/rx: Add RX62N Clock generator
87c0b3e hw/timer: Remove renesas_cmt.
e0a4816 hw/timer: Remove sh_timer.
0f983ff hw/timer: Renesas TMU/CMT module.
a5ba8bb hw/char: remove sh_serial.
872e5de hw/char: Renesas SCI module.

=== OUTPUT BEGIN ===
1/11 Checking commit 872e5de1daf1 (hw/char: Renesas SCI module.)
2/11 Checking commit a5ba8bb0a18d (hw/char: remove sh_serial.)
3/11 Checking commit 0f983ff94436 (hw/timer: Renesas TMU/CMT module.)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#44: 
new file mode 100644

total: 0 errors, 1 warnings, 752 lines checked

Patch 3/11 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
4/11 Checking commit e0a48164d14f (hw/timer: Remove sh_timer.)
5/11 Checking commit 87c0b3e06524 (hw/timer: Remove renesas_cmt.)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#30: 
deleted file mode 100644

total: 0 errors, 1 warnings, 7 lines checked

Patch 5/11 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
6/11 Checking commit afe51ff3750d (hw/rx: Add RX62N Clock generator)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#31: 
new file mode 100644

total: 0 errors, 1 warnings, 561 lines checked

Patch 6/11 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
7/11 Checking commit 88d7ca270dfb (hw/timer: Renesas 8bit timer.)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#50: 
deleted file mode 100644

total: 0 errors, 1 warnings, 626 lines checked

Patch 7/11 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
8/11 Checking commit 9422b37a5c14 (hw/rx: rx62n use new hw modules.)
9/11 Checking commit f85d3d235566 (hw/sh4: sh7750 Add CPG.)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#98: 
new file mode 100644

total: 0 errors, 1 warnings, 616 lines checked

Patch 9/11 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
10/11 Checking commit 5595fd490cbc (hw/sh4: sh7750 use new hw modules.)
ERROR: space required before the open parenthesis '('
#88: FILE: hw/sh4/sh7750.c:819:
+    switch(feat) {

ERROR: braces {} are necessary for all arms of this statement
#105: FILE: hw/sh4/sh7750.c:836:
+    if (tei_source)
[...]

ERROR: braces {} are necessary for all arms of this statement
#107: FILE: hw/sh4/sh7750.c:838:
+    if (bri_source)
[...]

ERROR: code indent should never use tabs
#142: FILE: hw/sh4/sh7750.c:916:
+^I^Is->intc.irqs[TMU2_TICPI], cpg);$

ERROR: code indent should never use tabs
#157: FILE: hw/sh4/sh7750.c:937:
+^I^I    NULL, NULL, cpg);$

total: 5 errors, 0 warnings, 153 lines checked

Patch 10/11 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

11/11 Checking commit 0d616299f2f2 (hw/rx: rx-gdbsim Add bootstrup for linux)
=== OUTPUT END ===

Test command exited with code: 1


The full log is available at
http://patchew.org/logs/20210527052122.97103-1-ysato@users.sourceforge.jp/testing.checkpatch/?type=message.
---
Email generated automatically by Patchew [https://patchew.org/].
Please send your feedback to patchew-devel@redhat.com

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 01/11] hw/char: Renesas SCI module.
  2021-05-27  5:21 ` [PATCH 01/11] hw/char: Renesas SCI module Yoshinori Sato
@ 2021-06-04  9:09   ` Peter Maydell
  2021-06-06 14:31     ` Yoshinori Sato
  2021-06-06 14:36     ` Yoshinori Sato
  0 siblings, 2 replies; 21+ messages in thread
From: Peter Maydell @ 2021-06-04  9:09 UTC (permalink / raw)
  To: Yoshinori Sato; +Cc: QEMU Developers

On Thu, 27 May 2021 at 06:30, Yoshinori Sato <ysato@users.sourceforge.jp> wrote:
>
> This module supported SCI / SCIa / SCIF.
>
> Hardware manual.
> SCI / SCIF
> https://www.renesas.com/us/en/doc/products/mpumcu/001/r01uh0457ej0401_sh7751.pdf
> SCIa
> https://www.renesas.com/us/en/doc/products/mpumcu/doc/rx_family/r01uh0033ej0140_rx62n.pdf
>
> Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
> ---
>  include/hw/char/renesas_sci.h |  129 +++-
>  hw/char/renesas_sci.c         | 1039 +++++++++++++++++++++++++++------
>  2 files changed, 966 insertions(+), 202 deletions(-)

This patch is much too large to review. Could you split it up into
multiple logical pieces, please?

>
> diff --git a/include/hw/char/renesas_sci.h b/include/hw/char/renesas_sci.h
> index a4764e3eee..ae9554db60 100644
> --- a/include/hw/char/renesas_sci.h
> +++ b/include/hw/char/renesas_sci.h
> @@ -1,54 +1,137 @@
>  /*
>   * Renesas Serial Communication Interface
>   *
> - * Copyright (c) 2018 Yoshinori Sato
> + * Copyright (c) 2020 Yoshinori Sato
> + *
> + * This code is licensed under the GPL version 2 or later.
>   *
> - * SPDX-License-Identifier: GPL-2.0-or-later

Did you have a reason for deleting the SPDX line ?

>   */
>
> -#ifndef HW_CHAR_RENESAS_SCI_H
> -#define HW_CHAR_RENESAS_SCI_H
> -

Why have you deleted the multiple-include guard?

thanks
-- PMM


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 02/11] hw/char: remove sh_serial.
  2021-05-27  5:21 ` [PATCH 02/11] hw/char: remove sh_serial Yoshinori Sato
@ 2021-06-04 10:08   ` Peter Maydell
  2021-06-06 14:33     ` Yoshinori Sato
                       ` (2 more replies)
  0 siblings, 3 replies; 21+ messages in thread
From: Peter Maydell @ 2021-06-04 10:08 UTC (permalink / raw)
  To: Yoshinori Sato; +Cc: QEMU Developers

On Thu, 27 May 2021 at 06:24, Yoshinori Sato <ysato@users.sourceforge.jp> wrote:
>
> Migrate to renesas_sci.
>
> Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
> ---
>  hw/char/sh_serial.c | 431 --------------------------------------------
>  MAINTAINERS         |   4 +-
>  hw/char/Kconfig     |   3 -
>  hw/char/meson.build |   1 -
>  4 files changed, 2 insertions(+), 437 deletions(-)
>  delete mode 100644 hw/char/sh_serial.c

This won't compile, because there are still boards which use
the old code you are removing here. The patchset has to be able
to compile and work at every step, so you need to first add new
devices, then convert the boards to use them, and then as the
last step you can remove the old implementations.

Checking, I find that patch 1 doesn't compile either.

thanks
-- PMM


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 07/11] hw/timer: Renesas 8bit timer.
  2021-05-27  5:21 ` [PATCH 07/11] hw/timer: Renesas 8bit timer Yoshinori Sato
@ 2021-06-04 10:12   ` Peter Maydell
  0 siblings, 0 replies; 21+ messages in thread
From: Peter Maydell @ 2021-06-04 10:12 UTC (permalink / raw)
  To: Yoshinori Sato; +Cc: QEMU Developers

On Thu, 27 May 2021 at 06:30, Yoshinori Sato <ysato@users.sourceforge.jp> wrote:
>
> Rewrite timer api.
>
> Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
> ---
>  include/hw/timer/renesas_tmr.h  |  58 ----
>  include/hw/timer/renesas_tmr8.h |  67 ++++
>  hw/timer/renesas_tmr.c          | 493 -----------------------------
>  hw/timer/renesas_tmr8.c         | 540 ++++++++++++++++++++++++++++++++
>  hw/timer/Kconfig                |   5 +-
>  hw/timer/meson.build            |   2 +-
>  6 files changed, 609 insertions(+), 556 deletions(-)
>  delete mode 100644 include/hw/timer/renesas_tmr.h
>  create mode 100644 include/hw/timer/renesas_tmr8.h
>  delete mode 100644 hw/timer/renesas_tmr.c
>  create mode 100644 hw/timer/renesas_tmr8.c

> +static const VMStateDescription vmstate_rtmr = {
> +    .name = "renesas-8tmr",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_END_OF_LIST()
> +    }
> +};

This seems to be missing the fields for the actual state ?

More generally, complete rewrites of devices are a pain
to code review. Could this be structured as incremental
improvements to the existing code ?


thanks
-- PMM


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 01/11] hw/char: Renesas SCI module.
  2021-06-04  9:09   ` Peter Maydell
@ 2021-06-06 14:31     ` Yoshinori Sato
  2021-06-06 14:36     ` Yoshinori Sato
  1 sibling, 0 replies; 21+ messages in thread
From: Yoshinori Sato @ 2021-06-06 14:31 UTC (permalink / raw)
  To: Peter Maydell; +Cc: QEMU Developers

On Fri, 04 Jun 2021 18:09:44 +0900,
Peter Maydell wrote:
> 
> On Thu, 27 May 2021 at 06:30, Yoshinori Sato <ysato@users.sourceforge.jp> wrote:
> >
> > This module supported SCI / SCIa / SCIF.
> >
> > Hardware manual.
> > SCI / SCIF
> > https://www.renesas.com/us/en/doc/products/mpumcu/001/r01uh0457ej0401_sh7751.pdf
> > SCIa
> > https://www.renesas.com/us/en/doc/products/mpumcu/doc/rx_family/r01uh0033ej0140_rx62n.pdf
> >
> > Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
> > ---
> >  include/hw/char/renesas_sci.h |  129 +++-
> >  hw/char/renesas_sci.c         | 1039 +++++++++++++++++++++++++++------
> >  2 files changed, 966 insertions(+), 202 deletions(-)
> 
> This patch is much too large to review. Could you split it up into
> multiple logical pieces, please?

OK. I will snt v2.

> 
> >
> > diff --git a/include/hw/char/renesas_sci.h b/include/hw/char/renesas_sci.h
> > index a4764e3eee..ae9554db60 100644
> > --- a/include/hw/char/renesas_sci.h
> > +++ b/include/hw/char/renesas_sci.h
> > @@ -1,54 +1,137 @@
> >  /*
> >   * Renesas Serial Communication Interface
> >   *
> > - * Copyright (c) 2018 Yoshinori Sato
> > + * Copyright (c) 2020 Yoshinori Sato
> > + *
> > + * This code is licensed under the GPL version 2 or later.
> >   *
> > - * SPDX-License-Identifier: GPL-2.0-or-later
> 
> Did you have a reason for deleting the SPDX line ?
> 
> >   */
> >
> > -#ifndef HW_CHAR_RENESAS_SCI_H
> > -#define HW_CHAR_RENESAS_SCI_H
> > -
> 
> Why have you deleted the multiple-include guard?
> 
> thanks
> -- PMM

-- 
Yoshinori Sato


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 02/11] hw/char: remove sh_serial.
  2021-06-04 10:08   ` Peter Maydell
@ 2021-06-06 14:33     ` Yoshinori Sato
  2021-06-06 14:33     ` Yoshinori Sato
  2021-06-06 14:37     ` Yoshinori Sato
  2 siblings, 0 replies; 21+ messages in thread
From: Yoshinori Sato @ 2021-06-06 14:33 UTC (permalink / raw)
  To: Peter Maydell; +Cc: QEMU Developers

On Fri, 04 Jun 2021 19:08:07 +0900,
Peter Maydell wrote:
> 
> On Thu, 27 May 2021 at 06:24, Yoshinori Sato <ysato@users.sourceforge.jp> wrote:
> >
> > Migrate to renesas_sci.
> >
> > Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
> > ---
> >  hw/char/sh_serial.c | 431 --------------------------------------------
> >  MAINTAINERS         |   4 +-
> >  hw/char/Kconfig     |   3 -
> >  hw/char/meson.build |   1 -
> >  4 files changed, 2 insertions(+), 437 deletions(-)
> >  delete mode 100644 hw/char/sh_serial.c
> 
> This won't compile, because there are still boards which use
> the old code you are removing here. The patchset has to be able
> to compile and work at every step, so you need to first add new
> devices, then convert the boards to use them, and then as the
> last step you can remove the old implementations.
> 
> Checking, I find that patch 1 doesn't compile either.
> 
> thanks
> -- PMM

OK. I will sent v2.

-- 
Yoshinori Sato


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 02/11] hw/char: remove sh_serial.
  2021-06-04 10:08   ` Peter Maydell
  2021-06-06 14:33     ` Yoshinori Sato
@ 2021-06-06 14:33     ` Yoshinori Sato
  2021-06-06 14:37     ` Yoshinori Sato
  2 siblings, 0 replies; 21+ messages in thread
From: Yoshinori Sato @ 2021-06-06 14:33 UTC (permalink / raw)
  To: Peter Maydell; +Cc: QEMU Developers

On Fri, 04 Jun 2021 19:08:07 +0900,
Peter Maydell wrote:
> 
> On Thu, 27 May 2021 at 06:24, Yoshinori Sato <ysato@users.sourceforge.jp> wrote:
> >
> > Migrate to renesas_sci.
> >
> > Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
> > ---
> >  hw/char/sh_serial.c | 431 --------------------------------------------
> >  MAINTAINERS         |   4 +-
> >  hw/char/Kconfig     |   3 -
> >  hw/char/meson.build |   1 -
> >  4 files changed, 2 insertions(+), 437 deletions(-)
> >  delete mode 100644 hw/char/sh_serial.c
> 
> This won't compile, because there are still boards which use
> the old code you are removing here. The patchset has to be able
> to compile and work at every step, so you need to first add new
> devices, then convert the boards to use them, and then as the
> last step you can remove the old implementations.
> 
> Checking, I find that patch 1 doesn't compile either.
> 
> thanks
> -- PMM

OK. I will sent v2.

-- 
Yoshinori Sato


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 01/11] hw/char: Renesas SCI module.
  2021-06-04  9:09   ` Peter Maydell
  2021-06-06 14:31     ` Yoshinori Sato
@ 2021-06-06 14:36     ` Yoshinori Sato
  1 sibling, 0 replies; 21+ messages in thread
From: Yoshinori Sato @ 2021-06-06 14:36 UTC (permalink / raw)
  To: Peter Maydell; +Cc: QEMU Developers

On Fri, 04 Jun 2021 18:09:44 +0900,
Peter Maydell wrote:
> 
> On Thu, 27 May 2021 at 06:30, Yoshinori Sato <ysato@users.sourceforge.jp> wrote:
> >
> > This module supported SCI / SCIa / SCIF.
> >
> > Hardware manual.
> > SCI / SCIF
> > https://www.renesas.com/us/en/doc/products/mpumcu/001/r01uh0457ej0401_sh7751.pdf
> > SCIa
> > https://www.renesas.com/us/en/doc/products/mpumcu/doc/rx_family/r01uh0033ej0140_rx62n.pdf
> >
> > Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
> > ---
> >  include/hw/char/renesas_sci.h |  129 +++-
> >  hw/char/renesas_sci.c         | 1039 +++++++++++++++++++++++++++------
> >  2 files changed, 966 insertions(+), 202 deletions(-)
> 
> This patch is much too large to review. Could you split it up into
> multiple logical pieces, please?

OK. I will snt v2.

> 
> >
> > diff --git a/include/hw/char/renesas_sci.h b/include/hw/char/renesas_sci.h
> > index a4764e3eee..ae9554db60 100644
> > --- a/include/hw/char/renesas_sci.h
> > +++ b/include/hw/char/renesas_sci.h
> > @@ -1,54 +1,137 @@
> >  /*
> >   * Renesas Serial Communication Interface
> >   *
> > - * Copyright (c) 2018 Yoshinori Sato
> > + * Copyright (c) 2020 Yoshinori Sato
> > + *
> > + * This code is licensed under the GPL version 2 or later.
> >   *
> > - * SPDX-License-Identifier: GPL-2.0-or-later
> 
> Did you have a reason for deleting the SPDX line ?
> 
> >   */
> >
> > -#ifndef HW_CHAR_RENESAS_SCI_H
> > -#define HW_CHAR_RENESAS_SCI_H
> > -
> 
> Why have you deleted the multiple-include guard?
> 
> thanks
> -- PMM

-- 
Yoshinori Sato


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 02/11] hw/char: remove sh_serial.
  2021-06-04 10:08   ` Peter Maydell
  2021-06-06 14:33     ` Yoshinori Sato
  2021-06-06 14:33     ` Yoshinori Sato
@ 2021-06-06 14:37     ` Yoshinori Sato
  2 siblings, 0 replies; 21+ messages in thread
From: Yoshinori Sato @ 2021-06-06 14:37 UTC (permalink / raw)
  To: Peter Maydell; +Cc: QEMU Developers

On Fri, 04 Jun 2021 19:08:07 +0900,
Peter Maydell wrote:
> 
> On Thu, 27 May 2021 at 06:24, Yoshinori Sato <ysato@users.sourceforge.jp> wrote:
> >
> > Migrate to renesas_sci.
> >
> > Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
> > ---
> >  hw/char/sh_serial.c | 431 --------------------------------------------
> >  MAINTAINERS         |   4 +-
> >  hw/char/Kconfig     |   3 -
> >  hw/char/meson.build |   1 -
> >  4 files changed, 2 insertions(+), 437 deletions(-)
> >  delete mode 100644 hw/char/sh_serial.c
> 
> This won't compile, because there are still boards which use
> the old code you are removing here. The patchset has to be able
> to compile and work at every step, so you need to first add new
> devices, then convert the boards to use them, and then as the
> last step you can remove the old implementations.
> 
> Checking, I find that patch 1 doesn't compile either.
> 
> thanks
> -- PMM

OK. I will sent v2.

-- 
Yoshinori Sato


^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2021-06-06 14:39 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-27  5:21 [PATCH 00/11] Unified peripheral emulation for Renesas chips Yoshinori Sato
2021-05-27  5:21 ` [PATCH 01/11] hw/char: Renesas SCI module Yoshinori Sato
2021-06-04  9:09   ` Peter Maydell
2021-06-06 14:31     ` Yoshinori Sato
2021-06-06 14:36     ` Yoshinori Sato
2021-05-27  5:21 ` [PATCH 02/11] hw/char: remove sh_serial Yoshinori Sato
2021-06-04 10:08   ` Peter Maydell
2021-06-06 14:33     ` Yoshinori Sato
2021-06-06 14:33     ` Yoshinori Sato
2021-06-06 14:37     ` Yoshinori Sato
2021-05-27  5:21 ` [PATCH 03/11] hw/timer: Renesas TMU/CMT module Yoshinori Sato
2021-05-27  5:21 ` [PATCH 04/11] hw/timer: Remove sh_timer Yoshinori Sato
2021-05-27  5:21 ` [PATCH 05/11] hw/timer: Remove renesas_cmt Yoshinori Sato
2021-05-27  5:21 ` [PATCH 06/11] hw/rx: Add RX62N Clock generator Yoshinori Sato
2021-05-27  5:21 ` [PATCH 07/11] hw/timer: Renesas 8bit timer Yoshinori Sato
2021-06-04 10:12   ` Peter Maydell
2021-05-27  5:21 ` [PATCH 08/11] hw/rx: rx62n use new hw modules Yoshinori Sato
2021-05-27  5:21 ` [PATCH 09/11] hw/sh4: sh7750 Add CPG Yoshinori Sato
2021-05-27  5:21 ` [PATCH 10/11] hw/sh4: sh7750 use new hw modules Yoshinori Sato
2021-05-27  5:21 ` [PATCH 11/11] hw/rx: rx-gdbsim Add bootstrup for linux Yoshinori Sato
2021-05-27  5:33 ` [PATCH 00/11] Unified peripheral emulation for Renesas chips no-reply

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.