All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/20] RX target update
@ 2020-08-27 12:38 Yoshinori Sato
  2020-08-27 12:38 ` [PATCH 01/20] loader.c: Add support Motrola S-record format Yoshinori Sato
                   ` (20 more replies)
  0 siblings, 21 replies; 40+ messages in thread
From: Yoshinori Sato @ 2020-08-27 12:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

Hello.
This series Renesas RX updates.

It consists of the following contents.
* Update firmware loader.
* Rewrite peripheal modules (Timer and SCI).
  - Unified SH4 module.
  - Using clock API
* New peripheal modules.
  - On-chip clock generator.
  - Multi-function timer.
  - Ethernet MAC.
* New real hardware target.
  - TokushudenshiKairo TKDN-RX62N-BRD.
  - CQ publishing CQ-FRK-RX62N

Yoshinori Sato (20):
  loader.c: Add support Motrola S-record format.
  include/elf.h: Add EM_RX.
  hw/rx: Firmware and kernel loader.
  hw/rx: New firmware loader.
  hw/rx: Add RX62N Clock generator
  hw/timer: Renesas 8bit timer emulation.
  hw/rx: RX62N convert new 8bit timer.
  hw/timer: Renesas TMU/CMT module.
  hw/timer: Remove renesas_cmt.
  hw/rx: Convert to renesas_timer
  hw/char: Renesas SCI module.
  hw/rx/rx62n: Use New SCI module.
  hw/timer: Add Renesas MTU2
  hw/rx/rx62n: RX62N Add MTU module
  hw/net: Add generic Bit-bang MDIO PHY.
  hw/net: Add Renesas On-chip Ethernet MAC
  hw/rx/rx62n: Add Ethernet support.
  hw/rx: Add Tokudenkairo TKDN-RX62N-BRD
  hw/rx: Add CQ-FRK-RX62N target
  MAINTAINERS: Update RX entry

 default-configs/rx-softmmu.mak   |    2 +
 include/elf.h                    |    2 +
 include/hw/char/renesas_sci.h    |  129 ++-
 include/hw/loader.h              |   14 +
 include/hw/net/mdio.h            |  126 +++
 include/hw/net/renesas_eth.h     |   57 ++
 include/hw/rx/loader.h           |   35 +
 include/hw/rx/rx62n-cpg.h        |   72 ++
 include/hw/rx/rx62n.h            |   36 +-
 include/hw/timer/renesas_cmt.h   |   40 -
 include/hw/timer/renesas_mtu.h   |   90 ++
 include/hw/timer/renesas_timer.h |  103 +++
 include/hw/timer/renesas_tmr.h   |   55 --
 include/hw/timer/renesas_tmr8.h  |   67 ++
 hw/char/renesas_sci.c            | 1040 ++++++++++++++++++-----
 hw/core/loader.c                 |  208 +++++
 hw/net/mdio.c                    |  264 ++++++
 hw/net/renesas_eth.c             |  875 ++++++++++++++++++++
 hw/rx/cq-frk-rx62n.c             |   94 +++
 hw/rx/loader.c                   |  182 +++++
 hw/rx/rx-gdbsim.c                |   98 +--
 hw/rx/rx62n-cpg.c                |  344 ++++++++
 hw/rx/rx62n.c                    |  140 ++--
 hw/rx/tkdn-rx62n.c               |  192 +++++
 hw/timer/renesas_cmt.c           |  283 -------
 hw/timer/renesas_mtu.c           | 1312 ++++++++++++++++++++++++++++++
 hw/timer/renesas_timer.c         |  639 +++++++++++++++
 hw/timer/renesas_tmr.c           |  477 -----------
 hw/timer/renesas_tmr8.c          |  540 ++++++++++++
 MAINTAINERS                      |    2 +
 hw/net/Kconfig                   |    8 +
 hw/net/meson.build               |    3 +
 hw/rx/Kconfig                    |   16 +-
 hw/rx/meson.build                |    5 +-
 hw/timer/Kconfig                 |    9 +-
 hw/timer/meson.build             |    5 +-
 36 files changed, 6391 insertions(+), 1173 deletions(-)
 create mode 100644 include/hw/net/mdio.h
 create mode 100644 include/hw/net/renesas_eth.h
 create mode 100644 include/hw/rx/loader.h
 create mode 100644 include/hw/rx/rx62n-cpg.h
 delete mode 100644 include/hw/timer/renesas_cmt.h
 create mode 100644 include/hw/timer/renesas_mtu.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
 create mode 100644 hw/net/mdio.c
 create mode 100644 hw/net/renesas_eth.c
 create mode 100644 hw/rx/cq-frk-rx62n.c
 create mode 100644 hw/rx/loader.c
 create mode 100644 hw/rx/rx62n-cpg.c
 create mode 100644 hw/rx/tkdn-rx62n.c
 delete mode 100644 hw/timer/renesas_cmt.c
 create mode 100644 hw/timer/renesas_mtu.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

-- 
2.20.1



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

* [PATCH 01/20] loader.c: Add support Motrola S-record format.
  2020-08-27 12:38 [PATCH 00/20] RX target update Yoshinori Sato
@ 2020-08-27 12:38 ` Yoshinori Sato
  2020-09-08 20:44   ` Philippe Mathieu-Daudé
                     ` (2 more replies)
  2020-08-27 12:38 ` [PATCH 02/20] include/elf.h: Add EM_RX Yoshinori Sato
                   ` (19 subsequent siblings)
  20 siblings, 3 replies; 40+ messages in thread
From: Yoshinori Sato @ 2020-08-27 12:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 include/hw/loader.h |  14 +++
 hw/core/loader.c    | 208 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 222 insertions(+)

diff --git a/include/hw/loader.h b/include/hw/loader.h
index a9eeea3952..6f1fb62ded 100644
--- a/include/hw/loader.h
+++ b/include/hw/loader.h
@@ -55,6 +55,20 @@ int load_image_targphys_as(const char *filename,
  */
 int load_targphys_hex_as(const char *filename, hwaddr *entry, AddressSpace *as);
 
+/*
+ * load_targphys_srec_as:
+ * @filename: Path to the .hex file
+ * @entry: Store the entry point given by the .hex file
+ * @as: The AddressSpace to load the .hex file to. The value of
+ *      address_space_memory is used if nothing is supplied here.
+ *
+ * Load a fixed .srec file into memory.
+ *
+ * Returns the size of the loaded .hex file on success, -1 otherwise.
+ */
+int load_targphys_srec_as(const char *filename,
+                          hwaddr *entry, AddressSpace *as);
+
 /** load_image_targphys:
  * Same as load_image_targphys_as(), but doesn't allow the caller to specify
  * an AddressSpace.
diff --git a/hw/core/loader.c b/hw/core/loader.c
index 8bbb1797a4..6964b04ec7 100644
--- a/hw/core/loader.c
+++ b/hw/core/loader.c
@@ -1618,3 +1618,211 @@ int load_targphys_hex_as(const char *filename, hwaddr *entry, AddressSpace *as)
     g_free(hex_blob);
     return total_size;
 }
+
+typedef enum {
+    SREC_SOH,
+    SREC_TYPE,
+    SREC_LEN,
+    SREC_ADDR,
+    SREC_DATA,
+    SREC_SKIP,
+    SREC_SUM,
+} srec_state;
+
+typedef struct {
+    srec_state state;
+    int nibble;
+    int total_size;
+    uint32_t address;
+    uint32_t topaddr;
+    uint32_t bufremain;
+    int length;
+    int addr_len;
+    int record_type;
+    uint8_t byte;
+    uint8_t data[DATA_FIELD_MAX_LEN];
+    uint8_t *datap;
+    uint8_t *bufptr;
+    uint8_t sum;
+} SrecLine;
+
+static bool parse_srec_line(SrecLine *line, char c)
+{
+    if (!g_ascii_isxdigit(c)) {
+        return false;
+    }
+    line->byte <<= 4;
+    line->byte |= g_ascii_xdigit_value(c);
+    line->nibble++;
+    if (line->nibble == 2) {
+        line->nibble = 0;
+        line->length--;
+        line->sum += line->byte;
+        switch (line->state) {
+        case SREC_SOH:
+        case SREC_TYPE:
+            /* first 2chars ignore parse */
+            break;
+        case SREC_LEN:
+            line->sum = line->length = line->byte;
+            if (line->addr_len > 0) {
+                line->state = SREC_ADDR;
+                line->address = 0;
+            } else {
+                line->state = SREC_SKIP;
+            }
+            break;
+        case SREC_ADDR:
+            line->address <<= 8;
+            line->address |= line->byte;
+            if (--line->addr_len == 0) {
+                if (line->length > 1) {
+                    if (line->record_type != 0) {
+                        line->state = SREC_DATA;
+                    } else {
+                        line->state = SREC_SKIP;
+                    }
+                    line->datap = line->data;
+                } else {
+                    line->state = SREC_SUM;
+                }
+            }
+            break;
+        case SREC_DATA:
+            *line->datap++ = line->byte;
+            /* fail through */
+        case SREC_SKIP:
+            if (line->length == 1) {
+                line->state = SREC_SUM;
+            }
+            break;
+        case SREC_SUM:
+            if ((line->sum & 0xff) != 0xff) {
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+#define SRECBUFSIZE 0x40000
+
+/* return size or -1 if error */
+static int parse_srec_blob(const char *filename, hwaddr *addr,
+                           uint8_t *hex_blob, size_t hex_blob_size,
+                           AddressSpace *as)
+{
+    SrecLine line;
+    size_t len;
+    int total_len = 0;
+    uint8_t *end = hex_blob + hex_blob_size;
+    rom_transaction_begin();
+    line.state = SREC_SOH;
+    line.bufptr = g_malloc(SRECBUFSIZE);
+    line.bufremain = SRECBUFSIZE;
+    line.topaddr = UINT32_MAX;
+    for (; hex_blob < end; ++hex_blob) {
+        switch (*hex_blob) {
+        case '\r':
+        case '\n':
+            if (line.state == SREC_SUM) {
+                switch (line.record_type) {
+                case 1:
+                case 2:
+                case 3:
+                    len = line.datap - line.data;
+                    if (line.topaddr == UINT32_MAX) {
+                        line.topaddr = line.address;
+                    }
+                    if (line.bufremain < len || line.address < line.topaddr) {
+                        rom_add_blob_fixed_as(filename, line.bufptr,
+                                              SRECBUFSIZE - line.bufremain,
+                                              line.topaddr, as);
+                        line.topaddr = line.address;
+                        line.bufremain = SRECBUFSIZE;
+                    }
+                    memcpy(line.bufptr + (line.address  - line.topaddr),
+                           line.data, len);
+                    line.bufremain -= len;
+                    total_len += len;
+                    break;
+                case 7:
+                case 8:
+                case 9:
+                    *addr = line.address;
+                    break;
+                }
+                line.state = SREC_SOH;
+            }
+            break;
+        /* start of a new record. */
+        case 'S':
+            if (line.state != SREC_SOH) {
+                total_len = -1;
+                goto out;
+            }
+            line.state = SREC_TYPE;
+            break;
+        /* decoding lines */
+        default:
+            if (line.state == SREC_TYPE) {
+                if (g_ascii_isdigit(*hex_blob)) {
+                    line.record_type = g_ascii_digit_value(*hex_blob);
+                    switch (line.record_type) {
+                    case 1:
+                    case 2:
+                    case 3:
+                        line.addr_len = 1 + line.record_type;
+                        break;
+                    case 0:
+                    case 5:
+                        line.addr_len = 2;
+                        break;
+                    case 7:
+                    case 8:
+                    case 9:
+                        line.addr_len = 11 - line.record_type;
+                        break;
+                    default:
+                        line.addr_len = 0;
+                    }
+                }
+                line.state = SREC_LEN;
+                line.nibble = 0;
+            } else {
+                if (!parse_srec_line(&line, *hex_blob)) {
+                    total_len = -1;
+                    goto out;
+                }
+            }
+            break;
+        }
+    }
+    if (line.bufremain < SRECBUFSIZE) {
+        rom_add_blob_fixed_as(filename, line.bufptr,
+                              SRECBUFSIZE - line.bufremain,
+                              line.topaddr, as);
+    }
+out:
+    rom_transaction_end(total_len != -1);
+    g_free(line.bufptr);
+    return total_len;
+}
+
+/* return size or -1 if error */
+int load_targphys_srec_as(const char *filename, hwaddr *entry, AddressSpace *as)
+{
+    gsize hex_blob_size;
+    gchar *hex_blob;
+    int total_size = 0;
+
+    if (!g_file_get_contents(filename, &hex_blob, &hex_blob_size, NULL)) {
+        return -1;
+    }
+
+    total_size = parse_srec_blob(filename, entry, (uint8_t *)hex_blob,
+                                 hex_blob_size, as);
+
+    g_free(hex_blob);
+    return total_size;
+}
-- 
2.20.1



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

* [PATCH 02/20] include/elf.h: Add EM_RX.
  2020-08-27 12:38 [PATCH 00/20] RX target update Yoshinori Sato
  2020-08-27 12:38 ` [PATCH 01/20] loader.c: Add support Motrola S-record format Yoshinori Sato
@ 2020-08-27 12:38 ` Yoshinori Sato
  2020-08-27 12:38 ` [PATCH 03/20] hw/rx: Firmware and kernel loader Yoshinori Sato
                   ` (18 subsequent siblings)
  20 siblings, 0 replies; 40+ messages in thread
From: Yoshinori Sato @ 2020-08-27 12:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 include/elf.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/include/elf.h b/include/elf.h
index c117a4d1ab..d9bf4a95d8 100644
--- a/include/elf.h
+++ b/include/elf.h
@@ -172,6 +172,8 @@ typedef struct mips_elf_abiflags_v0 {
 
 #define EM_UNICORE32    110     /* UniCore32 */
 
+#define EM_RX           173     /* Renesas RX family */
+
 #define EM_RISCV        243     /* RISC-V */
 
 #define EM_NANOMIPS     249     /* Wave Computing nanoMIPS */
-- 
2.20.1



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

* [PATCH 03/20] hw/rx: Firmware and kernel loader.
  2020-08-27 12:38 [PATCH 00/20] RX target update Yoshinori Sato
  2020-08-27 12:38 ` [PATCH 01/20] loader.c: Add support Motrola S-record format Yoshinori Sato
  2020-08-27 12:38 ` [PATCH 02/20] include/elf.h: Add EM_RX Yoshinori Sato
@ 2020-08-27 12:38 ` Yoshinori Sato
  2020-09-08 20:47   ` Philippe Mathieu-Daudé
  2020-08-27 12:38 ` [PATCH 04/20] hw/rx: New firmware loader Yoshinori Sato
                   ` (17 subsequent siblings)
  20 siblings, 1 reply; 40+ messages in thread
From: Yoshinori Sato @ 2020-08-27 12:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

Suppoerted format.
ELF, HEX, SREC and Raw firmware.
fit and Raw kernel image.

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 include/hw/rx/loader.h |  35 ++++++++
 hw/rx/loader.c         | 182 +++++++++++++++++++++++++++++++++++++++++
 hw/rx/Kconfig          |   1 +
 hw/rx/meson.build      |   1 +
 4 files changed, 219 insertions(+)
 create mode 100644 include/hw/rx/loader.h
 create mode 100644 hw/rx/loader.c

diff --git a/include/hw/rx/loader.h b/include/hw/rx/loader.h
new file mode 100644
index 0000000000..71f3bd2bb3
--- /dev/null
+++ b/include/hw/rx/loader.h
@@ -0,0 +1,35 @@
+/*
+ * RX QEMU frimware loader
+ *
+ * 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 "qapi/error.h"
+#include "qemu/error-report.h"
+
+typedef struct {
+    hwaddr ram_start;
+    size_t ram_size;
+    hwaddr entry;
+    hwaddr kernel_entry;
+    hwaddr dtb_address;
+    const char *filename;
+    const char *dtbname;
+    const char *cmdline;
+} rx_kernel_info_t;
+
+bool load_bios(const char *filename, int rom_size, Error **errp);
+
+bool load_kernel(rx_kernel_info_t *info);
diff --git a/hw/rx/loader.c b/hw/rx/loader.c
new file mode 100644
index 0000000000..c262f3ef86
--- /dev/null
+++ b/hw/rx/loader.c
@@ -0,0 +1,182 @@
+/*
+ * RX QEMU frimware loader
+ *
+ * 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 "elf.h"
+#include "hw/loader.h"
+#include "hw/loader-fit.h"
+#include "hw/rx/loader.h"
+#include "sysemu/device_tree.h"
+#include "exec/cpu-defs.h"
+#include <libfdt.h>
+
+#define RX_RESET_VEC 0xfffffffc
+#define ADDRESS_TOP ((1LL << TARGET_PHYS_ADDR_SPACE_BITS) - 1)
+
+bool load_bios(const char *filename, int rom_size, Error **errp)
+{
+    int size;
+    uint64_t entry64 = UINT64_MAX;
+    uint32_t entry;
+
+    size = load_elf(filename, NULL, NULL, NULL, &entry64,
+                    NULL, NULL, NULL, 0, EM_RX, 0, 0);
+    if (size > 0) {
+        goto load_ok;
+    }
+    size = load_targphys_hex_as(filename, &entry64, NULL);
+    if (size > 0) {
+        goto load_ok;
+    }
+    size = load_targphys_srec_as(filename, &entry64, NULL);
+    if (size > 0) {
+        goto load_ok;
+    }
+    size = get_image_size(filename);
+    if (size < 0) {
+        error_setg(errp, "\"%s\" is open failed.", filename);
+        return false;
+    }
+    if (size > rom_size) {
+        error_setg(errp, "\"%s\" is too large for ROM area.", filename);
+        return false;
+    }
+
+    /*
+     * The RX CPU reset vector is at the top of the ROM,
+     * so the raw binary is loaded there.
+     */
+    rom_add_file_fixed(filename, -size, 0);
+ load_ok:
+    if (rom_ptr(RX_RESET_VEC, 4) == NULL) {
+        if (entry64 <= ADDRESS_TOP) {
+            entry = cpu_to_le32(entry64);
+            rom_add_blob_fixed("entry", &entry, 4, RX_RESET_VEC);
+        } else {
+            error_setg(errp, "Reset vector is not set");
+            return false;
+        }
+    }
+    return true;
+}
+
+static hwaddr rx_addr_to_phys(void *opaque, uint64_t addr)
+{
+    /* No address translation */
+    return addr;
+}
+
+static bool setup_commandline(void *dtb, rx_kernel_info_t *info)
+{
+    if (info->cmdline &&
+        qemu_fdt_setprop_string(dtb, "/chosen", "bootargs",
+                                info->cmdline) < 0) {
+        return false;
+    }
+    return true;
+}
+
+
+static const void *rx_fdt_filter(void *opaque, const void *fdt_orig,
+                                 const void *match_data, hwaddr *load_addr)
+{
+    rx_kernel_info_t *info = opaque;
+    void *fdt;
+    size_t fdt_sz;
+    int err;
+
+    fdt_sz = fdt_totalsize(fdt_orig) + 0x1000;
+    fdt = g_malloc0(fdt_sz);
+
+    err = fdt_open_into(fdt_orig, fdt, fdt_sz);
+    if (err) {
+        error_report("couldn't open dtb");
+        return NULL;
+    }
+
+    if (!setup_commandline(fdt, info)) {
+        error_report("couldn't set /chosen/bootargs");
+        return NULL;
+    }
+    fdt_sz = fdt_totalsize(fdt);
+    fdt = g_realloc(fdt, fdt_totalsize(fdt));
+    info->dtb_address = info->ram_start + info->ram_size - fdt_sz;
+    *load_addr = info->dtb_address;
+
+    return fdt;
+}
+
+static const void *rx_kernel_filter(void *opaque, const void *kernel,
+                                        hwaddr *load_addr, hwaddr *entry_addr)
+{
+    rx_kernel_info_t *info = opaque;
+
+    info->kernel_entry = *entry_addr;
+
+    return kernel;
+}
+
+static const struct fit_loader rx_fit_loader = {
+    .addr_to_phys = rx_addr_to_phys,
+    .fdt_filter = rx_fdt_filter,
+    .kernel_filter = rx_kernel_filter,
+};
+
+bool load_kernel(rx_kernel_info_t *info)
+{
+    ram_addr_t kernel_offset;
+    size_t kernel_size;
+
+    if (load_fit(&rx_fit_loader, info->filename, info) == 0) {
+        return true;
+    }
+
+    /*
+     * The kernel image is loaded into
+     * the latter half of the SDRAM space.
+     */
+    kernel_offset = info->ram_size / 2;
+
+    info->entry = info->ram_start + kernel_offset;
+    kernel_size = load_image_targphys(info->filename,
+                                      info->entry, info->ram_size / 2);
+    if (kernel_size == -1) {
+        return false;
+    }
+    if (info->dtbname) {
+        ram_addr_t dtb_offset;
+        int dtb_size;
+        void *dtb;
+
+        dtb = load_device_tree(info->dtbname, &dtb_size);
+        if (dtb == NULL) {
+            error_report("Couldn't open dtb file %s", info->dtbname);
+            return false;
+        }
+        if (!setup_commandline(dtb, info)) {
+            error_report("Couldn't set /chosen/bootargs");
+            return false;
+        }
+        /* DTB is located at the end of SDRAM space. */
+        dtb_size = fdt_totalsize(dtb);
+        dtb_offset = info->ram_size - dtb_size;
+        info->dtb_address = info->ram_start + dtb_offset;
+        rom_add_blob_fixed("dtb", dtb, dtb_size, info->dtb_address);
+    }
+    return true;
+}
diff --git a/hw/rx/Kconfig b/hw/rx/Kconfig
index 2b297c5a6a..a63e4a5520 100644
--- a/hw/rx/Kconfig
+++ b/hw/rx/Kconfig
@@ -8,3 +8,4 @@ config RX62N_MCU
 config RX_GDBSIM
     bool
     select RX62N_MCU
+    select FITLOADER
diff --git a/hw/rx/meson.build b/hw/rx/meson.build
index d223512a78..e73850f303 100644
--- a/hw/rx/meson.build
+++ b/hw/rx/meson.build
@@ -1,4 +1,5 @@
 rx_ss = ss.source_set()
+rx_ss.add(files('loader.c'))
 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'))
 
-- 
2.20.1



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

* [PATCH 04/20] hw/rx: New firmware loader.
  2020-08-27 12:38 [PATCH 00/20] RX target update Yoshinori Sato
                   ` (2 preceding siblings ...)
  2020-08-27 12:38 ` [PATCH 03/20] hw/rx: Firmware and kernel loader Yoshinori Sato
@ 2020-08-27 12:38 ` Yoshinori Sato
  2020-08-27 12:38 ` [PATCH 05/20] hw/rx: Add RX62N Clock generator Yoshinori Sato
                   ` (16 subsequent siblings)
  20 siblings, 0 replies; 40+ messages in thread
From: Yoshinori Sato @ 2020-08-27 12:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

Separate the loading of the firmware from the target definition
 as it is an obstacle to adding more targets.

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

diff --git a/include/hw/rx/rx62n.h b/include/hw/rx/rx62n.h
index aa94758c27..32e460bbad 100644
--- a/include/hw/rx/rx62n.h
+++ b/include/hw/rx/rx62n.h
@@ -45,6 +45,21 @@
 #define RX62N_NR_CMT    2
 #define RX62N_NR_SCI    6
 
+typedef struct RX62NClass {
+    /*< private >*/
+    DeviceClass parent_class;
+    /*< public >*/
+    const char *name;
+    uint64_t ram_size;
+    uint64_t rom_flash_size;
+    uint64_t data_flash_size;
+} RX62NClass;
+
+#define RX62N_MCU_CLASS(klass) \
+    OBJECT_CLASS_CHECK(RX62NClass, (klass), TYPE_RX62N_MCU)
+#define RX62N_MCU_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(RX62NClass, (obj), TYPE_RX62N_MCU)
+
 typedef struct RX62NState {
     /*< private >*/
     DeviceState parent_obj;
diff --git a/hw/rx/rx-gdbsim.c b/hw/rx/rx-gdbsim.c
index 54992ebe57..02e03c797c 100644
--- a/hw/rx/rx-gdbsim.c
+++ b/hw/rx/rx-gdbsim.c
@@ -25,6 +25,7 @@
 #include "hw/hw.h"
 #include "hw/sysbus.h"
 #include "hw/loader.h"
+#include "hw/rx/loader.h"
 #include "hw/rx/rx62n.h"
 #include "sysemu/sysemu.h"
 #include "sysemu/qtest.h"
@@ -40,6 +41,7 @@ typedef struct RxGdbSimMachineClass {
     /*< public >*/
     const char *mcu_name;
     uint32_t xtal_freq_hz;
+    size_t romsize;
 } RxGdbSimMachineClass;
 
 typedef struct RxGdbSimMachineState {
@@ -59,26 +61,39 @@ typedef struct RxGdbSimMachineState {
 #define RX_GDBSIM_MACHINE_GET_CLASS(obj) \
     OBJECT_GET_CLASS(RxGdbSimMachineClass, (obj), 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;
 
-    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;
+    *((uint32_t *)&tinyboot[0x70]) = cpu_to_le32(dtb);
+    *((uint32_t *)&tinyboot[0x74]) = cpu_to_le32(entry);
 
     /* 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);
+    for (i = 0; i < 31; i++) {
+        *((uint32_t *)&tinyboot[0x40 + i * 4]) = cpu_to_le32(0x10 + i * 4);
     }
-    rom_add_blob_fixed("extable", extable, sizeof(extable), VECTOR_TABLE_BASE);
+    *((uint32_t *)&tinyboot[0xfc - 0x40]) = cpu_to_le32(TINYBOOT_TOP);
+    rom_add_blob_fixed("tinyboot", tinyboot, sizeof(tinyboot), TINYBOOT_TOP);
 }
 
 static void rx_gdbsim_init(MachineState *machine)
@@ -86,10 +101,11 @@ static void rx_gdbsim_init(MachineState *machine)
     MachineClass *mc = MACHINE_GET_CLASS(machine);
     RxGdbSimMachineState *s = RX_GDBSIM_MACHINE(machine);
     RxGdbSimMachineClass *rxc = RX_GDBSIM_MACHINE_GET_CLASS(machine);
+    RX62NClass *rx62nc;
     MemoryRegion *sysmem = get_system_memory();
     const char *kernel_filename = machine->kernel_filename;
     const char *dtb_filename = machine->dtb;
-
+    rx_kernel_info_t kernel_info;
     if (machine->ram_size < mc->default_ram_size) {
         char *sz = size_to_str(mc->default_ram_size);
         error_report("Invalid RAM size, should be more than %s", sz);
@@ -101,49 +117,33 @@ static void rx_gdbsim_init(MachineState *machine)
 
     /* Initialize MCU */
     object_initialize_child(OBJECT(machine), "mcu", &s->mcu, rxc->mcu_name);
+    rx62nc = RX62N_MCU_GET_CLASS(&s->mcu);
     object_property_set_link(OBJECT(&s->mcu), "main-bus", OBJECT(sysmem),
                              &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);
-    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.
-         */
-        kernel_offset = machine->ram_size / 2;
-        rx_load_image(RXCPU(first_cpu), kernel_filename,
-                      SDRAM_BASE + kernel_offset, kernel_offset);
-        if (dtb_filename) {
-            ram_addr_t dtb_offset;
-            int dtb_size;
-            void *dtb;
-
-            dtb = load_device_tree(dtb_filename, &dtb_size);
-            if (dtb == NULL) {
-                error_report("Couldn't open dtb file %s", dtb_filename);
-                exit(1);
-            }
-            if (machine->kernel_cmdline &&
-                qemu_fdt_setprop_string(dtb, "/chosen", "bootargs",
-                                        machine->kernel_cmdline) < 0) {
-                error_report("Couldn't set /chosen/bootargs");
-                exit(1);
+        kernel_info.ram_start = SDRAM_BASE;
+        kernel_info.ram_size = machine->ram_size;
+        kernel_info.filename = kernel_filename;
+        kernel_info.dtbname = dtb_filename;
+        kernel_info.cmdline = machine->kernel_cmdline;
+        if (!load_kernel(&kernel_info)) {
+            exit(1);
+        }
+        set_bootstrap(kernel_info.entry, kernel_info.dtb_address);
+    } else {
+        if (bios_name) {
+            if (!load_bios(bios_name, rx62nc->rom_flash_size, &error_abort)) {
+                exit(0);
             }
-            /* DTB is located at the end of SDRAM space. */
-            dtb_offset = machine->ram_size - dtb_size;
-            rom_add_blob_fixed("dtb", dtb, dtb_size,
-                               SDRAM_BASE + dtb_offset);
-            /* Set dtb address to R1 */
-            RXCPU(first_cpu)->env.regs[1] = SDRAM_BASE + dtb_offset;
+        } 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 b9c217ebfa..4b5c3c1079 100644
--- a/hw/rx/rx62n.c
+++ b/hw/rx/rx62n.c
@@ -60,21 +60,6 @@
 #define RX62N_XTAL_MAX_HZ (14 * 1000 * 1000)
 #define RX62N_PCLK_MAX_HZ (50 * 1000 * 1000)
 
-typedef struct RX62NClass {
-    /*< private >*/
-    DeviceClass parent_class;
-    /*< public >*/
-    const char *name;
-    uint64_t ram_size;
-    uint64_t rom_flash_size;
-    uint64_t data_flash_size;
-} RX62NClass;
-
-#define RX62N_MCU_CLASS(klass) \
-    OBJECT_CLASS_CHECK(RX62NClass, (klass), TYPE_RX62N_MCU)
-#define RX62N_MCU_GET_CLASS(obj) \
-    OBJECT_GET_CLASS(RX62NClass, (obj), TYPE_RX62N_MCU)
-
 /*
  * IRQ -> IPR mapping table
  * 0x00 - 0x91: IPR no (IPR00 to IPR91)
@@ -245,15 +230,6 @@ static void rx62n_realize(DeviceState *dev, Error **errp)
                            rxc->rom_flash_size, &error_abort);
     memory_region_add_subregion(s->sysmem, RX62N_CFLASH_BASE, &s->c_flash);
 
-    if (!s->kernel) {
-        if (bios_name) {
-            rom_add_file_fixed(bios_name, RX62N_CFLASH_BASE, 0);
-        }  else if (!qtest_enabled()) {
-            error_report("No bios or kernel specified");
-            exit(1);
-        }
-    }
-
     /* Initialize CPU */
     object_initialize_child(OBJECT(s), "cpu", &s->cpu, TYPE_RX62N_CPU);
     qdev_realize(DEVICE(&s->cpu), NULL, &error_abort);
@@ -270,7 +246,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] 40+ messages in thread

* [PATCH 05/20] hw/rx: Add RX62N Clock generator
  2020-08-27 12:38 [PATCH 00/20] RX target update Yoshinori Sato
                   ` (3 preceding siblings ...)
  2020-08-27 12:38 ` [PATCH 04/20] hw/rx: New firmware loader Yoshinori Sato
@ 2020-08-27 12:38 ` Yoshinori Sato
  2020-09-08 21:11   ` Philippe Mathieu-Daudé
  2020-10-24 21:56   ` Philippe Mathieu-Daudé
  2020-08-27 12:38 ` [PATCH 06/20] hw/timer: Renesas 8bit timer emulation Yoshinori Sato
                   ` (15 subsequent siblings)
  20 siblings, 2 replies; 40+ messages in thread
From: Yoshinori Sato @ 2020-08-27 12:38 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     |   5 +-
 hw/rx/rx62n-cpg.c         | 344 ++++++++++++++++++++++++++++++++++++++
 hw/rx/rx62n.c             |  52 +++---
 hw/rx/meson.build         |   2 +-
 5 files changed, 447 insertions(+), 28 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 32e460bbad..e0ca1cfc33 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"
 
 #define TYPE_RX62N_MCU "rx62n-mcu"
@@ -70,9 +71,9 @@ typedef 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;
@@ -84,8 +85,6 @@ typedef 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 4b5c3c1079..ec63fa5db1 100644
--- a/hw/rx/rx62n.c
+++ b/hw/rx/rx62n.c
@@ -47,6 +47,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,10 +57,6 @@
 #define RX62N_CMT_IRQ   28
 #define RX62N_SCI_IRQ   214
 
-#define RX62N_XTAL_MIN_HZ  (8 * 1000 * 1000)
-#define RX62N_XTAL_MAX_HZ (14 * 1000 * 1000)
-#define RX62N_PCLK_MAX_HZ (50 * 1000 * 1000)
-
 /*
  * IRQ -> IPR mapping table
  * 0x00 - 0x91: IPR no (IPR00 to IPR91)
@@ -149,36 +146,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)
@@ -190,7 +196,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;
@@ -200,26 +205,23 @@ 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);
     RX62NClass *rxc = RX62N_MCU_GET_CLASS(dev);
 
-    if (s->xtal_freq_hz == 0) {
-        error_setg(errp, "\"xtal-frequency-hz\" property must be provided.");
-        return;
-    }
-    /* XTAL range: 8-14 MHz */
-    if (s->xtal_freq_hz < RX62N_XTAL_MIN_HZ
-            || s->xtal_freq_hz > RX62N_XTAL_MAX_HZ) {
-        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);
@@ -236,11 +238,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 e73850f303..3a81d85a53 100644
--- a/hw/rx/meson.build
+++ b/hw/rx/meson.build
@@ -1,6 +1,6 @@
 rx_ss = ss.source_set()
 rx_ss.add(files('loader.c'))
 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] 40+ messages in thread

* [PATCH 06/20] hw/timer: Renesas 8bit timer emulation.
  2020-08-27 12:38 [PATCH 00/20] RX target update Yoshinori Sato
                   ` (4 preceding siblings ...)
  2020-08-27 12:38 ` [PATCH 05/20] hw/rx: Add RX62N Clock generator Yoshinori Sato
@ 2020-08-27 12:38 ` Yoshinori Sato
  2020-10-24 21:27   ` Philippe Mathieu-Daudé
  2020-08-27 12:38 ` [PATCH 07/20] hw/rx: RX62N convert new 8bit timer Yoshinori Sato
                   ` (14 subsequent siblings)
  20 siblings, 1 reply; 40+ messages in thread
From: Yoshinori Sato @ 2020-08-27 12:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

Rewrite for clock API.

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 include/hw/timer/renesas_tmr.h  |  55 ----
 include/hw/timer/renesas_tmr8.h |  67 ++++
 hw/timer/renesas_tmr.c          | 477 ----------------------------
 hw/timer/renesas_tmr8.c         | 540 ++++++++++++++++++++++++++++++++
 hw/timer/Kconfig                |   3 +-
 hw/timer/meson.build            |   2 +-
 6 files changed, 610 insertions(+), 534 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 cf3baa7a28..0000000000
--- a/include/hw/timer/renesas_tmr.h
+++ /dev/null
@@ -1,55 +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"
-
-#define TYPE_RENESAS_TMR "renesas-tmr"
-#define RTMR(obj) OBJECT_CHECK(RTMRState, (obj), 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
-};
-
-typedef 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];
-} RTMRState;
-
-#endif
diff --git a/include/hw/timer/renesas_tmr8.h b/include/hw/timer/renesas_tmr8.h
new file mode 100644
index 0000000000..23935bd4a3
--- /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"
+#define RenesasTMR8(obj) \
+    OBJECT_CHECK(RenesasTMR8State, (obj), TYPE_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 446f2eacdd..0000000000
--- a/hw/timer/renesas_tmr.c
+++ /dev/null
@@ -1,477 +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 INTERNAL  0x01
-#define 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) == 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;
-
-        if (FIELD_EX8(tmr->tccr[1], TCCR, CSS) == INTERNAL) {
-            /* timer1 count update */
-            elapsed = elapsed_time(tmr, 1, delta);
-            if (elapsed >= 0x100) {
-                ovf = elapsed >> 8;
-            }
-            tcnt[1] = tmr->tcnt[1] + (elapsed & 0xff);
-        }
-        switch (FIELD_EX8(tmr->tccr[0], TCCR, CSS)) {
-        case INTERNAL:
-            elapsed = elapsed_time(tmr, 0, delta);
-            tcnt[0] = tmr->tcnt[0] + elapsed;
-            break;
-        case CASCADING:
-            if (ovf > 0) {
-                tcnt[0] = tmr->tcnt[0] + ovf;
-            }
-            break;
-        }
-    } 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);
-        }
-    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) == 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) != 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..39deabce47
--- /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
+ *
+ * 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;
+        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)
+{
+    RenesasTMR8State *tmr = 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 = 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 = 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 = RenesasTMR8(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 = RenesasTMR8(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);
+}
+
+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,
+};
+
+static void tmr8_register_types(void)
+{
+    type_register_static(&tmr8_info);
+}
+
+type_init(tmr8_register_types)
diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig
index 8749edfb6a..5288660cda 100644
--- a/hw/timer/Kconfig
+++ b/hw/timer/Kconfig
@@ -36,7 +36,7 @@ config CMSDK_APB_DUALTIMER
     bool
     select PTIMER
 
-config RENESAS_TMR
+config RENESAS_TMR8
     bool
 
 config RENESAS_CMT
@@ -44,3 +44,4 @@ config RENESAS_CMT
 
 config AVR_TIMER16
     bool
+
diff --git a/hw/timer/meson.build b/hw/timer/meson.build
index 9f0a267c83..a02e45fdbd 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_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'))
-- 
2.20.1



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

* [PATCH 07/20] hw/rx: RX62N convert new 8bit timer.
  2020-08-27 12:38 [PATCH 00/20] RX target update Yoshinori Sato
                   ` (5 preceding siblings ...)
  2020-08-27 12:38 ` [PATCH 06/20] hw/timer: Renesas 8bit timer emulation Yoshinori Sato
@ 2020-08-27 12:38 ` Yoshinori Sato
  2020-08-27 12:38 ` [PATCH 08/20] hw/timer: Renesas TMU/CMT module Yoshinori Sato
                   ` (13 subsequent siblings)
  20 siblings, 0 replies; 40+ messages in thread
From: Yoshinori Sato @ 2020-08-27 12:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

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

diff --git a/include/hw/rx/rx62n.h b/include/hw/rx/rx62n.h
index e0ca1cfc33..75945bae50 100644
--- a/include/hw/rx/rx62n.h
+++ b/include/hw/rx/rx62n.h
@@ -26,7 +26,7 @@
 
 #include "target/rx/cpu.h"
 #include "hw/intc/rx_icu.h"
-#include "hw/timer/renesas_tmr.h"
+#include "hw/timer/renesas_tmr8.h"
 #include "hw/timer/renesas_cmt.h"
 #include "hw/char/renesas_sci.h"
 #include "hw/rx/rx62n-cpg.h"
@@ -68,7 +68,7 @@ typedef struct RX62NState {
 
     RXCPU cpu;
     RXICUState icu;
-    RTMRState tmr[RX62N_NR_TMR];
+    RenesasTMR8State tmr[RX62N_NR_TMR];
     RCMTState cmt[RX62N_NR_CMT];
     RSCIState sci[RX62N_NR_SCI];
     RX62NCPGState cpg;
diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c
index ec63fa5db1..0223396110 100644
--- a/hw/rx/rx62n.c
+++ b/hw/rx/rx62n.c
@@ -149,7 +149,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;
diff --git a/hw/rx/Kconfig b/hw/rx/Kconfig
index a63e4a5520..0406f27bee 100644
--- a/hw/rx/Kconfig
+++ b/hw/rx/Kconfig
@@ -1,7 +1,7 @@
 config RX62N_MCU
     bool
     select RX_ICU
-    select RENESAS_TMR
+    select RENESAS_TMR8
     select RENESAS_CMT
     select RENESAS_SCI
 
-- 
2.20.1



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

* [PATCH 08/20] hw/timer: Renesas TMU/CMT module.
  2020-08-27 12:38 [PATCH 00/20] RX target update Yoshinori Sato
                   ` (6 preceding siblings ...)
  2020-08-27 12:38 ` [PATCH 07/20] hw/rx: RX62N convert new 8bit timer Yoshinori Sato
@ 2020-08-27 12:38 ` Yoshinori Sato
  2020-10-24 22:47   ` Philippe Mathieu-Daudé
  2020-08-27 12:38 ` [PATCH 09/20] hw/timer: Remove renesas_cmt Yoshinori Sato
                   ` (12 subsequent siblings)
  20 siblings, 1 reply; 40+ messages in thread
From: Yoshinori Sato @ 2020-08-27 12:38 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 | 103 +++++
 hw/timer/renesas_timer.c         | 639 +++++++++++++++++++++++++++++++
 hw/timer/Kconfig                 |   4 +-
 hw/timer/meson.build             |   2 +-
 4 files changed, 745 insertions(+), 3 deletions(-)
 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..27300ae574
--- /dev/null
+++ b/include/hw/timer/renesas_timer.h
@@ -0,0 +1,103 @@
+/*
+ * 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"
+#define RenesasTimerBase(obj) \
+    OBJECT_CHECK(RenesasTimerBaseState, (obj), TYPE_RENESAS_TIMER_BASE)
+#define TYPE_RENESAS_CMT "renesas-cmt"
+#define RenesasCMT(obj) OBJECT_CHECK(RenesasCMTState, (obj), TYPE_RENESAS_CMT)
+#define TYPE_RENESAS_TMU "renesas-tmu"
+#define RenesasTMU(obj) OBJECT_CHECK(RenesasTMUState, (obj), TYPE_RENESAS_TMU)
+
+#define RenesasTimer_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(RenesasTimerBaseClass, obj, TYPE_RENESAS_TIMER_BASE)
+#define TimerBaseClass(klass) \
+    OBJECT_CLASS_CHECK(RenesasTimerBaseClass, klass, TYPE_RENESAS_TIMER_BASE)
+#define CMTClass(klass) \
+    OBJECT_CLASS_CHECK(RenesasCMTClass, klass, TYPE_RENESAS_CMT)
+#define TMUClass(klass) \
+    OBJECT_CLASS_CHECK(RenesasTMUClass, klass, TYPE_RENESAS_TMU)
+
+enum {
+    TIMER_CH_CMT = 2,
+    TIMER_CH_TMU = 3,
+};
+
+enum {
+    CMT_NR_IRQ = 1 * TIMER_CH_CMT,
+};
+
+struct RTIMERState;
+
+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 RTIMERState *tmrp;
+};
+
+typedef struct RenesasTimerBaseState {
+    SysBusDevice parent_obj;
+
+    uint64_t input_freq;
+    MemoryRegion memory;
+    MemoryRegion memory_p4;
+    MemoryRegion memory_a7;
+    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;
+} 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..e1da328d1b
--- /dev/null
+++ b/hw/timer/renesas_timer.c
@@ -0,0 +1,639 @@
+/*
+ * Renesas 16bit Compare-match timer
+ *
+ * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
+ * (Rev.1.40 R01UH0033EJ0140)
+ *
+ * Copyright (c) 2019 Yoshinori Sato
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "qemu/timer.h"
+#include "hw/hw.h"
+#include "hw/irq.h"
+#include "hw/sysbus.h"
+#include "hw/registerfields.h"
+#include "hw/qdev-properties.h"
+#include "hw/timer/renesas_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 = RenesasTimer_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 = RenesasTimer_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 = RenesasTimer_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)
+{
+    RenesasTimerBaseState *tmr = RenesasTimerBase(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 = RenesasCMT(opaque);
+    RenesasTimerBaseState *tmr = RenesasTimerBase(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(RenesasTimerBase(cmt));
+    } else {
+        ch = addr / 4;
+        if (addr < 4) {
+            /* skip CMSTR */
+            addr--;
+        }
+        reg = 2 - (addr % 4);
+        return channel_read(RenesasTimerBase(cmt), ch, reg);
+    }
+}
+
+static uint64_t tmu_read(void *opaque, hwaddr addr, unsigned size)
+{
+    RenesasTMUState *tmu = RenesasTMU(opaque);
+    RenesasTimerBaseState *tmr = RenesasTimerBase(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(RenesasTimerBase(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(RenesasTimerBase(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 = RenesasTimer_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 = RenesasTimerBase(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(RenesasTimerBase(tmr), ch, val, 0x0043);
+        } else {
+            channel_write(RenesasTimerBase(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 = RenesasTMU(opaque);
+    RenesasTimerBaseState *tmr = RenesasTimerBase(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 = RenesasCMT(dev);
+    RenesasTimerBaseState *tmr = RenesasTimerBase(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 = RenesasCMT(obj);
+    RenesasTimerBaseState *tmr = RenesasTimerBase(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);
+}
+
+static void tmu_realize(DeviceState *dev, Error **errp)
+{
+    SysBusDevice *d = SYS_BUS_DEVICE(dev);
+    RenesasTMUState *tmu = RenesasTMU(dev);
+    RenesasTimerBaseState *tmr = RenesasTimerBase(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 = RenesasTimerBase(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(&tmr->memory_p4, NULL, "renesas-tmu-p4",
+                             &tmr->memory, 0, 0x30);
+    sysbus_init_mmio(d, &tmr->memory_p4);
+    memory_region_init_alias(&tmr->memory_a7, NULL, "renesas-tmu-a7",
+                             &tmr->memory, 0, 0x30);
+    sysbus_init_mmio(d, &tmr->memory_a7);
+    tmr->pck = qdev_init_clock_in(DEVICE(obj), "pck",
+                                  tmr_pck_update, tmr);
+}
+
+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 = TimerBaseClass(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 = TimerBaseClass(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 = TimerBaseClass(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 5288660cda..4d21b50ab0 100644
--- a/hw/timer/Kconfig
+++ b/hw/timer/Kconfig
@@ -39,9 +39,9 @@ config CMSDK_APB_DUALTIMER
 config RENESAS_TMR8
     bool
 
-config RENESAS_CMT
+config AVR_TIMER16
     bool
 
-config AVR_TIMER16
+config RENESAS_TIMER
     bool
 
diff --git a/hw/timer/meson.build b/hw/timer/meson.build
index a02e45fdbd..6aed6d1e5f 100644
--- a/hw/timer/meson.build
+++ b/hw/timer/meson.build
@@ -9,7 +9,7 @@ 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_TMR8', if_true: files('renesas_tmr8.c'))
-softmmu_ss.add(when: 'CONFIG_RENESAS_CMT', if_true: files('renesas_cmt.c'))
+softmmu_ss.add(when: 'CONFIG_RENESAS_TIMER', if_true: files('renesas_timer.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] 40+ messages in thread

* [PATCH 09/20] hw/timer: Remove renesas_cmt.
  2020-08-27 12:38 [PATCH 00/20] RX target update Yoshinori Sato
                   ` (7 preceding siblings ...)
  2020-08-27 12:38 ` [PATCH 08/20] hw/timer: Renesas TMU/CMT module Yoshinori Sato
@ 2020-08-27 12:38 ` Yoshinori Sato
  2020-08-27 12:38 ` [PATCH 10/20] hw/rx: Convert to renesas_timer Yoshinori Sato
                   ` (11 subsequent siblings)
  20 siblings, 0 replies; 40+ messages in thread
From: Yoshinori Sato @ 2020-08-27 12:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

This module replaced to unified renesas_timer.

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 include/hw/timer/renesas_cmt.h |  40 -----
 hw/timer/renesas_cmt.c         | 283 ---------------------------------
 2 files changed, 323 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 e28a15cb38..0000000000
--- a/include/hw/timer/renesas_cmt.h
+++ /dev/null
@@ -1,40 +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"
-
-#define TYPE_RENESAS_CMT "renesas-cmt"
-#define RCMT(obj) OBJECT_CHECK(RCMTState, (obj), TYPE_RENESAS_CMT)
-
-enum {
-    CMT_CH = 2,
-    CMT_NR_IRQ = 1 * CMT_CH
-};
-
-typedef 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];
-} RCMTState;
-
-#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)
-- 
2.20.1



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

* [PATCH 10/20] hw/rx: Convert to renesas_timer
  2020-08-27 12:38 [PATCH 00/20] RX target update Yoshinori Sato
                   ` (8 preceding siblings ...)
  2020-08-27 12:38 ` [PATCH 09/20] hw/timer: Remove renesas_cmt Yoshinori Sato
@ 2020-08-27 12:38 ` Yoshinori Sato
  2020-08-27 12:38 ` [PATCH 11/20] hw/char: Renesas SCI module Yoshinori Sato
                   ` (10 subsequent siblings)
  20 siblings, 0 replies; 40+ messages in thread
From: Yoshinori Sato @ 2020-08-27 12:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

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

diff --git a/include/hw/rx/rx62n.h b/include/hw/rx/rx62n.h
index 75945bae50..1182ca24de 100644
--- a/include/hw/rx/rx62n.h
+++ b/include/hw/rx/rx62n.h
@@ -27,7 +27,7 @@
 #include "target/rx/cpu.h"
 #include "hw/intc/rx_icu.h"
 #include "hw/timer/renesas_tmr8.h"
-#include "hw/timer/renesas_cmt.h"
+#include "hw/timer/renesas_timer.h"
 #include "hw/char/renesas_sci.h"
 #include "hw/rx/rx62n-cpg.h"
 #include "qemu/units.h"
@@ -69,7 +69,7 @@ typedef struct RX62NState {
     RXCPU cpu;
     RXICUState icu;
     RenesasTMR8State tmr[RX62N_NR_TMR];
-    RCMTState cmt[RX62N_NR_CMT];
+    RenesasCMTState cmt[RX62N_NR_CMT];
     RSCIState sci[RX62N_NR_SCI];
     RX62NCPGState cpg;
 
diff --git a/hw/rx/Kconfig b/hw/rx/Kconfig
index 0406f27bee..d1812870ea 100644
--- a/hw/rx/Kconfig
+++ b/hw/rx/Kconfig
@@ -2,7 +2,7 @@ config RX62N_MCU
     bool
     select RX_ICU
     select RENESAS_TMR8
-    select RENESAS_CMT
+    select RENESAS_TIMER
     select RENESAS_SCI
 
 config RX_GDBSIM
-- 
2.20.1



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

* [PATCH 11/20] hw/char: Renesas SCI module.
  2020-08-27 12:38 [PATCH 00/20] RX target update Yoshinori Sato
                   ` (9 preceding siblings ...)
  2020-08-27 12:38 ` [PATCH 10/20] hw/rx: Convert to renesas_timer Yoshinori Sato
@ 2020-08-27 12:38 ` Yoshinori Sato
  2020-10-24 21:40   ` Philippe Mathieu-Daudé
  2020-08-27 12:38 ` [PATCH 12/20] hw/rx/rx62n: Use New " Yoshinori Sato
                   ` (9 subsequent siblings)
  20 siblings, 1 reply; 40+ messages in thread
From: Yoshinori Sato @ 2020-08-27 12:38 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         | 1040 +++++++++++++++++++++++++++------
 2 files changed, 967 insertions(+), 202 deletions(-)

diff --git a/include/hw/char/renesas_sci.h b/include/hw/char/renesas_sci.h
index efdebc620a..07140e8aad 100644
--- a/include/hw/char/renesas_sci.h
+++ b/include/hw/char/renesas_sci.h
@@ -1,51 +1,138 @@
 /*
  * 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 "hw/clock.h"
 
+#define TYPE_RENESAS_SCI_COMMON "renesas-sci-common"
+#define RSCICommon(obj) OBJECT_CHECK(RSCICommonState, (obj), \
+                                     TYPE_RENESAS_SCI_COMMON)
 #define TYPE_RENESAS_SCI "renesas-sci"
 #define RSCI(obj) OBJECT_CHECK(RSCIState, (obj), TYPE_RENESAS_SCI)
+#define TYPE_RENESAS_SCIA "renesas-scia"
+#define RSCIA(obj) OBJECT_CHECK(RSCIAState, (obj), TYPE_RENESAS_SCIA)
+#define TYPE_RENESAS_SCIF "renesas-scif"
+#define RSCIF(obj) OBJECT_CHECK(RSCIFState, (obj), TYPE_RENESAS_SCIF)
+
+#define SCI_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(RenesasSCICommonClass, obj, TYPE_RENESAS_SCI_COMMON)
+#define SCI_COMMON_CLASS(klass) \
+    OBJECT_CLASS_CHECK(RenesasSCICommonClass, klass, TYPE_RENESAS_SCI_COMMON)
+#define SCI_CLASS(klass) \
+    OBJECT_CLASS_CHECK(RenesasSCIClass, klass, TYPE_RENESAS_SCI)
+#define SCIA_CLASS(klass) \
+    OBJECT_CLASS_CHECK(RenesasSCIAClass, klass, TYPE_RENESAS_SCIA)
+#define SCIF_CLASS(klass) \
+    OBJECT_CLASS_CHECK(RenesasSCIFClass, klass, TYPE_RENESAS_SCIF)
 
 enum {
     ERI = 0,
     RXI = 1,
     TXI = 2,
-    TEI = 3,
-    SCI_NR_IRQ = 4
+    BRI_TEI = 3,
+    SCI_NR_IRQ = 4,
 };
 
-typedef struct {
-    /*< private >*/
-    SysBusDevice parent_obj;
-    /*< public >*/
+enum {
+    RXTOUT,
+    RXNEXT,
+    TXEMPTY,
+    TXEND,
+    NR_SCI_EVENT,
+};
 
+typedef struct RSCICommonState {
+    SysBusDevice parent_obj;
     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 RSCICommonState *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;
+} RSCICommonState;
+
+typedef struct {
+    RSCICommonState parent_obj;
+
+    /* SCI specific register */
+    uint8_t sptr;
 } RSCIState;
 
-#endif
+typedef struct {
+    RSCICommonState parent_obj;
+
+    /* SCIa specific register */
+    uint8_t scmr;
+    uint8_t semr;
+} RSCIAState;
+
+typedef struct {
+    RSCICommonState parent_obj;
+
+    /* SCIF specific register */
+    uint16_t fcr;
+    uint16_t sptr;
+    uint16_t lsr;
+
+    /* internal use */
+    uint16_t read_lsr;
+    int tdcnt;
+} RSCIFState;
+
+typedef struct RenesasSCICommonClass {
+    SysBusDeviceClass parent;
+
+    const struct MemoryRegionOps *ops;
+    void (*irq_fn)(RSCICommonState *sci, int request);
+    int (*divrate)(RSCICommonState *sci);
+} RenesasSCICommonClass;
+
+typedef struct RenesasSCIClass {
+    RenesasSCICommonClass parent;
+
+    void (*p_irq_fn)(RSCICommonState *sci, int request);
+} RenesasSCIClass;
+
+typedef struct RenesasSCIAClass {
+    RenesasSCICommonClass parent;
+
+    void (*p_irq_fn)(RSCICommonState *sci, int request);
+    int (*p_divrate)(RSCICommonState *sci);
+} RenesasSCIAClass;
+
+typedef struct RenesasSCIFClass {
+    RenesasSCICommonClass parent;
+
+    void (*p_irq_fn)(RSCICommonState *sci, int request);
+} RenesasSCIFClass;
diff --git a/hw/char/renesas_sci.c b/hw/char/renesas_sci.c
index 5d7c6e6523..24c23709ee 100644
--- a/hw/char/renesas_sci.c
+++ b/hw/char/renesas_sci.c
@@ -1,12 +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)
+ * (Rev.1.40 R01UH0033EJ0140)
+ * And SH7751 Group, SH7751R Group User's Manual: Hardware
+ * (Rev.4.01 R01UH0457EJ0401)
  *
- * Copyright (c) 2019 Yoshinori Sato
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
+ * 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,
@@ -23,14 +23,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-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.
+ */
+REG16(SMR, 0) /* 8bit */
   FIELD(SMR, CKS,  0, 2)
   FIELD(SMR, MP,   2, 1)
   FIELD(SMR, STOP, 3, 1)
@@ -38,263 +49,839 @@ 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)
+REG16(BRR, 2) /* 8bit */
+REG16(SCR, 4)
+  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)
+REG16(TDR, 6) /* 8bit */
+REG16(SSR, 8) /* 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)
+REG16(FSR, 8)
+  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)
+REG16(RDR, 10) /* 8bit */
+REG16(SCMR, 12) /* 8bit */
   FIELD(SCMR, SMIF, 0, 1)
   FIELD(SCMR, SINV, 2, 1)
   FIELD(SCMR, SDIR, 3, 1)
   FIELD(SCMR, BCP2, 7, 1)
-REG8(SEMR, 7)
+REG16(FCR, 12)
+  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)
+REG16(SEMR, 14) /* 8bit */
   FIELD(SEMR, ACS0, 0, 1)
   FIELD(SEMR, ABCS, 4, 1)
+REG16(FDR, 14)
+  FIELD(FDR, Rn, 0, 4)
+  FIELD(FDR, Tn, 8, 4)
+REG16(SPTR, 16)
+  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)
+REG16(LSR, 18)
+  FIELD(LSR, ORER, 0, 1)
+
+#define SCIF_FIFO_DEPTH 16
+
+static const int sci_rtrg[] = {1, 4, 8, 14};
 
-static int can_receive(void *opaque)
+static void update_event_time(RSCICommonState *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(RSCICommonState *sci_common, int req)
+{
+    int irq = 0;
+    int rie;
+    int tie;
+    RSCIState *sci = RSCI(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(RSCICommonState *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(RSCICommonState *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 void send_byte(RSCIState *sci)
+static int sci_can_receive(void *opaque)
+{
+    RSCICommonState *sci = RSCICommon(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)
+{
+    RSCICommonState *sci = RSCICommon(opaque);
+    RenesasSCICommonClass *rc = SCI_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)
+{
+    RSCIFState *scif = RSCIF(opaque);
+    RSCICommonState *sci = RSCICommon(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)
+{
+    RSCIFState *scif = RSCIF(opaque);
+    RSCICommonState *sci = RSCICommon(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 sci_send_byte(RSCICommonState *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(RSCIFState *scif)
+{
+    RSCICommonState *sci = RSCICommon(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(RSCICommonState *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(RSCICommonState *sci)
+{
+    int64_t next_event = 0;
+    RenesasSCICommonClass *rc = SCI_GET_CLASS(sci);
+    if (!fifo8_is_empty(&sci->rxfifo)) {
+        if (FIELD_EX16(sci->Xsr, SSR, RDRF)) {
+            /* Receiver overrun */
+            sci->Xsr = FIELD_DP16(sci->Xsr, SSR, ORER, 1);
+            rc->irq_fn(sci, ERI);
+        } else {
+            /* Trigger next event */
+            sci->Xsr = FIELD_DP16(sci->Xsr, SSR, RDRF, 1);
+            rc->irq_fn(sci, RXI);
+            next_event = sci->trtime;
+        }
+    }
+    return next_event;
+}
+
+static int64_t sci_tx_empty(RSCICommonState *sci)
+{
+    int64_t ret = 0;
+    RenesasSCICommonClass *rc = SCI_GET_CLASS(sci);
+    if (!FIELD_EX16(sci->Xsr, SSR, TDRE)) {
+        sci_send_byte(sci);
+        ret = sci->trtime;
+        rc->irq_fn(sci, TXI);
     } else {
-        sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 1);
-        if (FIELD_EX8(sci->scr, SCR, TEIE)) {
-            qemu_set_irq(sci->irq[TEI], 1);
+        sci->Xsr = FIELD_DP16(sci->Xsr, SSR, TEND, 1);
+        rc->irq_fn(sci, BRI_TEI);
+    }
+    return ret;
+}
+
+static int64_t scif_tx_empty(RSCICommonState *sci)
+{
+    RSCIFState *scif = RSCIF(sci);
+    scif->tdcnt -= transmit_byte(scif);
+    sci->Xsr = FIELD_DP16(sci->Xsr, FSR, TDFE, 1);
+    scif_irq(sci, TXI);
+    return 0;
+}
+
+static int64_t scif_tx_end(RSCICommonState *sci)
+{
+    RSCIFState *scif = RSCIF(sci);
+    scif->tdcnt = 0;
+    sci->Xsr = FIELD_DP16(sci->Xsr, FSR, TEND, 1);
+    return 0;
+}
+
+static void sci_timer_event(void *opaque)
+{
+    RSCICommonState *sci = RSCICommon(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;
+        }
+        if (sci->event[i].time > 0) {
+            next = MIN(next, sci->event[i].time);
         }
     }
+    if (next < INT64_MAX) {
+        timer_mod(sci->event_timer, next);
+    } else {
+        timer_del(sci->event_timer);
+    }
 }
 
-static void update_trtime(RSCIState *sci)
+static int static_divrate(RSCICommonState *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;
+    /* SCI / SCIF have static divide rate */
+    return 32;
 }
 
-static bool sci_is_tr_enabled(RSCIState *sci)
+static int scia_divrate(RSCICommonState *sci)
 {
-    return FIELD_EX8(sci->scr, SCR, TE) || FIELD_EX8(sci->scr, SCR, RE);
+    /*
+     * SEMR.ABCS = 0 -> 32
+     * SEMR.ABCS = 1 -> 16
+     */
+    RSCIAState *scia = RSCIA(sci);
+    return 16 * (2 - FIELD_EX8(scia->semr, SEMR, ABCS));
 }
 
-static void sci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
+static void update_trtime(RSCICommonState *sci)
 {
-    RSCIState *sci = RSCI(opaque);
+    RenesasSCICommonClass *rc = SCI_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;
 
-    switch (offset) {
-    case A_SMR:
-        if (!sci_is_tr_enabled(sci)) {
-            sci->smr = val;
-            update_trtime(sci);
+        /* 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)
+{
+    RSCICommonState *sci = RSCICommon(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(RSCICommonState *sci, hwaddr addr)
+{
+    return (addr << 1) >> sci->regshift;
+}
+
+static void sci_common_write(void *opaque, hwaddr addr,
+                             uint64_t val, unsigned size)
+{
+    RSCICommonState *sci = RSCICommon(opaque);
+    RenesasSCICommonClass *rc = SCI_GET_CLASS(opaque);
+    switch (addr) {
+    case A_SCR:
+        sci->scr = val;
+        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:
-        if (!sci_is_tr_enabled(sci)) {
-            sci->brr = val;
-            update_trtime(sci);
-        }
+        sci->brr = val;
+        update_trtime(sci);
         break;
-    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]);
-            }
+    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)
+{
+    RSCICommonState *sci = RSCICommon(opaque);
+    RenesasSCICommonClass *rc = SCI_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(RSCICommon(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:
+        RSCI(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)
+{
+    RSCICommonState *sci = RSCICommon(opaque);
+    RSCIAState *scia = RSCIA(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);
+    RSCICommonState *sci = RSCICommon(opaque);
+    RSCIFState *scif = RSCIF(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)
+{
+    RSCICommonState *sci = RSCICommon(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;
+        return fifo8_pop(&sci->rxfifo);
     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)
+{
+    RSCICommonState *sci = RSCICommon(opaque);
+    addr = map_address(sci, addr);
+
+    if (clock_is_enabled(sci->pck)) {
+        switch (addr) {
+        case A_TDR:
+            return sci->tdr;
+        case A_SPTR:
+            return RSCI(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);
+    RSCICommonState *sci = RSCICommon(opaque);
+    RSCIAState *scia = RSCIA(opaque);
+    uint64_t ret;
+
+    if (clock_is_enabled(sci->pck)) {
+        addr = map_address(sci, addr);
+        switch (addr) {
+        case A_TDR:
+            return sci->tdr;
+        case A_RDR:
+            ret = fifo8_pop(&sci->rxfifo);
+            sci->Xsr = FIELD_DP16(sci->Xsr, SSR, RDRF, 0);
+            return ret;
+        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)
+{
+    RSCIFState *scif = RSCIF(opaque);
+    RSCICommonState *sci = RSCICommon(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)
+{
+    RSCICommonState *sci = RSCICommon(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);
 }
 
 static void sci_event(void *opaque, QEMUChrEvent event)
 {
-    RSCIState *sci = RSCI(opaque);
+    RSCICommonState *sci = RSCICommon(opaque);
+    RenesasSCICommonClass *rc = SCI_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)
+{
+    RSCICommonState *sci = RSCICommon(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);
+    RSCICommonState *sci = RSCICommon(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->regshift = sci->regshift / 8 - 1;
+    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(RSCICommonState *sci, int size)
 {
-    SysBusDevice *d = SYS_BUS_DEVICE(obj);
-    RSCIState *sci = RSCI(obj);
-    int i;
+    SysBusDevice *d = SYS_BUS_DEVICE(sci);
+    RenesasSCICommonClass *rc = SCI_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)
+{
+    RSCIState *sci = RSCI(dev);
+    RSCICommonState *common = RSCICommon(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)
+{
+    RSCIAState *sci = RSCIA(dev);
+    RSCICommonState *common = RSCICommon(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)
+{
+    RSCIFState *sci = RSCIF(dev);
+    RSCICommonState *common = RSCICommon(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 = {
@@ -302,49 +889,140 @@ 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", RSCICommonState, regshift, 8),
+    DEFINE_PROP_UINT32("unit", RSCICommonState, unit, 0),
+    DEFINE_PROP_CHR("chardev", RSCICommonState, chr),
     DEFINE_PROP_END_OF_LIST(),
 };
 
-static void rsci_class_init(ObjectClass *klass, void *data)
+static void rsci_init(Object *obj)
 {
+    RSCICommonState *sci = RSCICommon(obj);
+    sci->event[RXNEXT].handler = sci_rx_next;
+    sci->event[TXEMPTY].handler = sci_tx_empty;
+}
+
+static void rscif_init(Object *obj)
+{
+    RSCICommonState *sci = RSCICommon(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)
+{
+    RenesasSCICommonClass *rc = SCI_COMMON_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);
+    RenesasSCICommonClass *comm_rc = SCI_COMMON_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)
+{
+    RenesasSCICommonClass *comm_rc = SCI_COMMON_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)
+{
+    RenesasSCICommonClass *comm_rc = SCI_COMMON_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_COMMON,
+        .parent     = TYPE_SYS_BUS_DEVICE,
+        .instance_size = sizeof(RSCICommonState),
+        .instance_init = rsci_common_init,
+        .class_init = rsci_common_class_init,
+        .class_size = sizeof(RenesasSCICommonClass),
+        .abstract = true,
+    },
+    {
+        .name       = TYPE_RENESAS_SCI,
+        .parent     = TYPE_RENESAS_SCI_COMMON,
+        .instance_size = sizeof(RSCIState),
+        .instance_init = rsci_init,
+        .class_init = rsci_class_init,
+        .class_size = sizeof(RenesasSCIClass),
+    },
+    {
+        .name       = TYPE_RENESAS_SCIA,
+        .parent     = TYPE_RENESAS_SCI_COMMON,
+        .instance_size = sizeof(RSCIAState),
+        /* 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_COMMON,
+        .instance_size = sizeof(RSCIFState),
+        .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] 40+ messages in thread

* [PATCH 12/20] hw/rx/rx62n: Use New SCI module.
  2020-08-27 12:38 [PATCH 00/20] RX target update Yoshinori Sato
                   ` (10 preceding siblings ...)
  2020-08-27 12:38 ` [PATCH 11/20] hw/char: Renesas SCI module Yoshinori Sato
@ 2020-08-27 12:38 ` Yoshinori Sato
  2020-10-25  0:33   ` Philippe Mathieu-Daudé
  2020-08-27 12:38 ` [PATCH 13/20] hw/timer: Add Renesas MTU2 Yoshinori Sato
                   ` (8 subsequent siblings)
  20 siblings, 1 reply; 40+ messages in thread
From: Yoshinori Sato @ 2020-08-27 12:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 include/hw/rx/rx62n.h | 2 +-
 hw/rx/rx62n.c         | 7 ++++++-
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/include/hw/rx/rx62n.h b/include/hw/rx/rx62n.h
index 1182ca24de..f463148799 100644
--- a/include/hw/rx/rx62n.h
+++ b/include/hw/rx/rx62n.h
@@ -70,7 +70,7 @@ typedef struct RX62NState {
     RXICUState icu;
     RenesasTMR8State tmr[RX62N_NR_TMR];
     RenesasCMTState cmt[RX62N_NR_CMT];
-    RSCIState sci[RX62N_NR_SCI];
+    RSCIAState sci[RX62N_NR_SCI];
     RX62NCPGState cpg;
 
     MemoryRegion *sysmem;
diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c
index 0223396110..f61383a4c2 100644
--- a/hw/rx/rx62n.c
+++ b/hw/rx/rx62n.c
@@ -191,11 +191,13 @@ 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);
     sysbus_realize(sci, &error_abort);
 
     irqbase = RX62N_SCI_IRQ + SCI_NR_IRQ * unit;
@@ -203,6 +205,9 @@ static void register_sci(RX62NState *s, int unit)
         sysbus_connect_irq(sci, i, s->irq[irqbase + i]);
     }
     sysbus_mmio_map(sci, 0, RX62N_SCI_BASE + unit * 0x08);
+    snprintf(ckname, sizeof(ckname), "pck_sci-%d", unit);
+    qdev_connect_clock_in(DEVICE(sci), "pck",
+                          qdev_get_clock_out(DEVICE(&s->cpg), ckname));
 }
 
 static void register_cpg(RX62NState *s)
-- 
2.20.1



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

* [PATCH 13/20] hw/timer: Add Renesas MTU2
  2020-08-27 12:38 [PATCH 00/20] RX target update Yoshinori Sato
                   ` (11 preceding siblings ...)
  2020-08-27 12:38 ` [PATCH 12/20] hw/rx/rx62n: Use New " Yoshinori Sato
@ 2020-08-27 12:38 ` Yoshinori Sato
  2020-08-27 12:38 ` [PATCH 14/20] hw/rx/rx62n: RX62N Add MTU module Yoshinori Sato
                   ` (7 subsequent siblings)
  20 siblings, 0 replies; 40+ messages in thread
From: Yoshinori Sato @ 2020-08-27 12:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 include/hw/timer/renesas_mtu.h |   90 +++
 hw/timer/renesas_mtu.c         | 1312 ++++++++++++++++++++++++++++++++
 hw/timer/Kconfig               |    2 +
 hw/timer/meson.build           |    1 +
 4 files changed, 1405 insertions(+)
 create mode 100644 include/hw/timer/renesas_mtu.h
 create mode 100644 hw/timer/renesas_mtu.c

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



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

* [PATCH 14/20] hw/rx/rx62n: RX62N Add MTU module
  2020-08-27 12:38 [PATCH 00/20] RX target update Yoshinori Sato
                   ` (12 preceding siblings ...)
  2020-08-27 12:38 ` [PATCH 13/20] hw/timer: Add Renesas MTU2 Yoshinori Sato
@ 2020-08-27 12:38 ` Yoshinori Sato
  2020-09-08 21:12   ` Philippe Mathieu-Daudé
  2020-08-27 12:38 ` [PATCH 15/20] hw/net: Add generic Bit-bang MDIO PHY Yoshinori Sato
                   ` (6 subsequent siblings)
  20 siblings, 1 reply; 40+ messages in thread
From: Yoshinori Sato @ 2020-08-27 12:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 include/hw/rx/rx62n.h |  3 +++
 hw/rx/rx62n.c         | 28 ++++++++++++++++++++++++++++
 hw/rx/Kconfig         |  1 +
 3 files changed, 32 insertions(+)

diff --git a/include/hw/rx/rx62n.h b/include/hw/rx/rx62n.h
index f463148799..170c8cb6fc 100644
--- a/include/hw/rx/rx62n.h
+++ b/include/hw/rx/rx62n.h
@@ -28,6 +28,7 @@
 #include "hw/intc/rx_icu.h"
 #include "hw/timer/renesas_tmr8.h"
 #include "hw/timer/renesas_timer.h"
+#include "hw/timer/renesas_mtu.h"
 #include "hw/char/renesas_sci.h"
 #include "hw/rx/rx62n-cpg.h"
 #include "qemu/units.h"
@@ -45,6 +46,7 @@
 #define RX62N_NR_TMR    2
 #define RX62N_NR_CMT    2
 #define RX62N_NR_SCI    6
+#define RX62N_NR_MTU    2
 
 typedef struct RX62NClass {
     /*< private >*/
@@ -70,6 +72,7 @@ typedef struct RX62NState {
     RXICUState icu;
     RenesasTMR8State tmr[RX62N_NR_TMR];
     RenesasCMTState cmt[RX62N_NR_CMT];
+    RenesasMTU2State mtu[RX62N_NR_MTU];
     RSCIAState sci[RX62N_NR_SCI];
     RX62NCPGState cpg;
 
diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c
index f61383a4c2..344be846bc 100644
--- a/hw/rx/rx62n.c
+++ b/hw/rx/rx62n.c
@@ -46,6 +46,7 @@
 #define RX62N_ICU_BASE  0x00087000
 #define RX62N_TMR_BASE  0x00088200
 #define RX62N_CMT_BASE  0x00088000
+#define RX62N_MTU_BASE  0x00088600
 #define RX62N_SCI_BASE  0x00088240
 #define RX62N_CPG_BASE  0x00080010
 
@@ -55,6 +56,7 @@
  */
 #define RX62N_TMR_IRQ   174
 #define RX62N_CMT_IRQ   28
+#define RX62N_MTU_IRQ   114
 #define RX62N_SCI_IRQ   214
 
 /*
@@ -187,6 +189,30 @@ static void register_cmt(RX62NState *s, int unit)
                           qdev_get_clock_out(DEVICE(&s->cpg), ckname));
 }
 
+static void register_mtu(RX62NState *s, int unit)
+{
+    SysBusDevice *mtu;
+    int i, irqbase;
+    char ckname[16];
+
+    object_initialize_child(OBJECT(s), "mtu[*]", &s->mtu[unit],
+                            TYPE_RENESAS_MTU2);
+    mtu = SYS_BUS_DEVICE(&s->mtu[unit]);
+    qdev_prop_set_uint32(DEVICE(mtu), "unit", unit);
+
+    sysbus_mmio_map(mtu, 0, RX62N_MTU_BASE + 0x100 + unit * 0x400);
+    sysbus_mmio_map(mtu, 1, RX62N_MTU_BASE + unit * 0x400);
+    sysbus_mmio_map(mtu, 2, RX62N_MTU_BASE + 0x280 + unit * 0x400);
+    irqbase = RX62N_MTU_IRQ + MTU_NR_IRQ * unit;
+    for (i = 0; i < MTU_NR_IRQ; i++) {
+        sysbus_connect_irq(mtu, i, s->irq[irqbase + i]);
+    }
+    sysbus_realize(mtu, &error_abort);
+    snprintf(ckname, sizeof(ckname), "pck_mtu-%d", unit);
+    qdev_connect_clock_in(DEVICE(mtu), "pck",
+                          qdev_get_clock_out(DEVICE(&s->cpg), ckname));
+}
+
 static void register_sci(RX62NState *s, int unit)
 {
     SysBusDevice *sci;
@@ -248,6 +274,8 @@ static void rx62n_realize(DeviceState *dev, Error **errp)
     register_tmr(s, 1);
     register_cmt(s, 0);
     register_cmt(s, 1);
+    register_mtu(s, 0);
+    register_mtu(s, 1);
     register_sci(s, 0);
     sysbus_realize(SYS_BUS_DEVICE(&s->cpg), &error_abort);
 }
diff --git a/hw/rx/Kconfig b/hw/rx/Kconfig
index d1812870ea..887a5782bb 100644
--- a/hw/rx/Kconfig
+++ b/hw/rx/Kconfig
@@ -4,6 +4,7 @@ config RX62N_MCU
     select RENESAS_TMR8
     select RENESAS_TIMER
     select RENESAS_SCI
+    select RENESAS_MTU
 
 config RX_GDBSIM
     bool
-- 
2.20.1



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

* [PATCH 15/20] hw/net: Add generic Bit-bang MDIO PHY.
  2020-08-27 12:38 [PATCH 00/20] RX target update Yoshinori Sato
                   ` (13 preceding siblings ...)
  2020-08-27 12:38 ` [PATCH 14/20] hw/rx/rx62n: RX62N Add MTU module Yoshinori Sato
@ 2020-08-27 12:38 ` Yoshinori Sato
  2020-08-27 12:38 ` [PATCH 16/20] hw/net: Add Renesas On-chip Ethernet MAC Yoshinori Sato
                   ` (5 subsequent siblings)
  20 siblings, 0 replies; 40+ messages in thread
From: Yoshinori Sato @ 2020-08-27 12:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

Only supported link status.

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 include/hw/net/mdio.h | 126 ++++++++++++++++++++
 hw/net/mdio.c         | 264 ++++++++++++++++++++++++++++++++++++++++++
 hw/net/Kconfig        |   3 +
 hw/net/meson.build    |   2 +
 4 files changed, 395 insertions(+)
 create mode 100644 include/hw/net/mdio.h
 create mode 100644 hw/net/mdio.c

diff --git a/include/hw/net/mdio.h b/include/hw/net/mdio.h
new file mode 100644
index 0000000000..55a7170e67
--- /dev/null
+++ b/include/hw/net/mdio.h
@@ -0,0 +1,126 @@
+/*
+ *  MDIO PHY emulation
+ *
+ *  Copyright 2020 Yoshinori Sato <ysato@users.sourceforge.jp>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; under version 2 of the License.
+ *
+ *  This program is distributed in the hope that 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/>.
+ *
+ *  Contributions after 2012-01-13 are licensed under the terms of the
+ *  GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#ifndef MDIO_H
+#define MDIO_H
+
+#include "hw/qdev-core.h"
+#include "hw/net/mii.h"
+
+typedef enum mdio_pin {
+    mdio_z = -1,
+    mdio_l = 0,
+    mdio_h = 1,
+} MDIOPin;
+
+#define TYPE_ETHER_PHY "ether-phy"
+#define TYPE_ETHER_PHY_CLASS(obj) \
+    OBJECT_GET_CLASS(EtherPHYClass, (obj), TYPE_ETHER_PHY)
+#define EtherPHYClass(klass) \
+    OBJECT_CHECK_CLASS(EtherPHYClass, (klass), TYPE_ETHER_PHY)
+#define EtherPHY(obj) \
+    OBJECT_CHECK(PHYState, (obj), TYPE_ETHER_PHY)
+
+#define TYPE_ETHER_MDIO_BB "ether-mdio-bb"
+#define TYPE_ETHER_MDIO_BB_CLASS(obj)                           \
+    OBJECT_GET_CLASS(MDIO_BBClass, (obj), TYPE_ETHER_MDIO_BB)
+#define MDIO_BBClass(klass) \
+    OBJECT_CHECK_CLASS(MDIO_BBClass, (klass), TYPE_ETHER_MDIO_BB)
+#define MDIO_BB(obj) \
+    OBJECT_CHECK(MDIOState, (obj), TYPE_ETHER_MDIO_BB)
+
+typedef enum {
+    phy_out_p = 0,    /* Link up is 'H' */
+    phy_out_n = 1,    /* Link up is 'L' */
+} phy_output_polarity;
+
+typedef struct {
+    DeviceState parent;
+
+    uint16_t regs[32];
+    uint32_t identifier;
+    bool link_ok;
+    phy_output_polarity link_out_pol;
+    uint16_t bmsr;
+    uint16_t anlpar;
+} PHYState;
+
+#define MDIO_ANLPAR_LINK \
+    (MII_ANLPAR_TXFD | MII_ANLPAR_TX | MII_ANLPAR_10FD | MII_ANLPAR_10 | \
+     MII_ANLPAR_CSMACD)
+
+typedef enum {
+    BB_PRE,
+    BB_ST,
+    BB_CMD,
+    BB_TA_R,
+    BB_TA_W,
+    BB_DATA_R,
+    BB_DATA_W,
+    BB_INH,
+} mdio_bb_state;
+
+typedef struct {
+    DeviceState parent;
+
+    PHYState *phy;
+    mdio_bb_state bb_state;
+    int pclk;
+    int bits;
+    int cmd;
+    int phyad;
+    int selphy;
+    int regad;
+    int data;
+    int mdi_pin;
+    int mdo_pin;
+} MDIOState;
+
+#define mdio_get_phy(s) (s->phy)
+
+typedef struct {
+    DeviceClass parent;
+} EtherPHYClass;
+
+typedef struct {
+    DeviceClass parent;
+} MDIO_BBClass;
+
+/* Generic PHY interface */
+void mdio_phy_set_link(PHYState *s, bool ok);
+int mdio_phy_linksta(PHYState *s);
+uint16_t mdio_phy_read(PHYState *s, int addr);
+void mdio_phy_write(PHYState *s, int addr, uint16_t val);
+
+/* Bit-bang MDIO operation */
+static inline MDIOPin mdio_read_mdi_pin(MDIOState *s)
+{
+    return s->mdi_pin;
+}
+
+static inline void mdio_set_mdo_pin(MDIOState *s, MDIOPin mdo)
+{
+    s->mdo_pin = mdo;
+}
+
+void mdio_set_mdc_pin(MDIOState *s, int clk);
+
+#endif
diff --git a/hw/net/mdio.c b/hw/net/mdio.c
new file mode 100644
index 0000000000..39670e70c6
--- /dev/null
+++ b/hw/net/mdio.c
@@ -0,0 +1,264 @@
+/*
+ *  Bit-bang MII emulation
+ *
+ *  Copyright 2020 Yoshinori Sato <ysato@users.sourceforge.jp>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; under version 2 of the License.
+ *
+ *  This program is distributed in the hope that 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/>.
+ *
+ *  Contributions after 2012-01-13 are licensed under the terms of the
+ *  GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/bitops.h"
+#include "qemu/log.h"
+#include "hw/qdev-properties.h"
+#include "hw/net/mdio.h"
+
+void mdio_phy_set_link(PHYState *s, bool ok)
+{
+    if (ok) {
+        s->regs[MII_BMSR] |= MII_BMSR_LINK_ST;
+        s->regs[MII_ANLPAR] |= MDIO_ANLPAR_LINK;
+    } else {
+        s->regs[MII_BMSR] &= ~(MII_BMSR_LINK_ST | MII_BMSR_AUTONEG);
+        s->regs[MII_ANLPAR] &= MDIO_ANLPAR_LINK;
+    }
+    s->link_ok = ok;
+}
+
+static void mdio_phy_reset(PHYState *s)
+{
+    memset(s->regs, 0, sizeof(s->regs));
+    s->regs[MII_BMSR] = s->bmsr;
+    s->regs[MII_ANLPAR] = s->anlpar;
+    s->regs[MII_PHYID1] = extract32(s->identifier, 16, 16);
+    s->regs[MII_PHYID2] = extract32(s->identifier, 0, 16);
+    mdio_phy_set_link(s, s->link_ok);
+}
+
+uint16_t mdio_phy_read(PHYState *s, int addr)
+{
+    if (addr >= 0 && addr < 32) {
+        return s->regs[addr];
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "mdio: Register %04x invalid address.\n", addr);
+        return 0;
+    }
+}
+
+int mdio_phy_linksta(PHYState *s)
+{
+    return s->link_ok ^ s->link_out_pol;
+}
+
+void mdio_phy_write(PHYState *s, int addr, uint16_t val)
+{
+    switch (addr) {
+    case MII_BMCR:
+        s->regs[MII_BMCR] = val & 0xfd80;
+        if (val & MII_BMCR_RESET) {
+            mdio_phy_reset(s);
+        }
+        break;
+    case MII_BMSR:
+    case MII_ANLPAR:
+        /* Read only */
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "mdio: Register %04x is read only register.\n", addr);
+        break;
+    case MII_PHYID1:
+    case MII_PHYID2:
+        s->regs[addr] = val;
+        break;
+    case MII_ANAR:
+        s->regs[addr] = val & 0x2dff;
+        break;
+    default:
+        qemu_log_mask(LOG_UNIMP,
+                      "mdio: Register %04x not implemented\n", addr);
+        break;
+    }
+}
+
+static Property phy_properties[] = {
+    DEFINE_PROP_UINT32("phy-id", PHYState, identifier, 0),
+    DEFINE_PROP_UINT32("link-out-pol", PHYState, link_out_pol, 0),
+    DEFINE_PROP_UINT16("bmsr", PHYState, bmsr, 0),
+    DEFINE_PROP_UINT16("anlpar", PHYState, anlpar, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void phy_realize(DeviceState *dev, Error **errp)
+{
+    PHYState *s = EtherPHY(dev);
+    mdio_phy_reset(s);
+}
+
+static void phy_class_init(ObjectClass *klass, void *class_data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    device_class_set_props(dc, phy_properties);
+    dc->realize = phy_realize;
+}
+
+/* shift in MDO */
+static void read_mdo(MDIOState *s)
+{
+    int op;
+    s->bits++;
+    switch (s->bb_state) {
+    case BB_PRE: /* preamble */
+        if (s->mdo_pin == 0) {
+            /* ST 1st bit found */
+            s->bb_state = BB_ST;
+        }
+        break;
+    case BB_ST: /* ST 2nd bit */
+        if (s->mdo_pin == 0) {
+            s->bb_state = BB_CMD;
+            s->cmd = 0;
+            s->bits = 2;
+            s->selphy = -1;
+            s->regad = -1;
+        } else {
+            s->bb_state = BB_PRE;
+        }
+        break;
+    case BB_CMD:
+        s->cmd <<= 1;
+        s->cmd |= (s->mdo_pin & 1);
+        if (s->bits == 14) {
+            op = extract32(s->cmd, 10, 2);
+            s->selphy = extract32(s->cmd, 5, 5);
+            s->regad = extract32(s->cmd, 0, 5);
+            switch (op) {
+            case 0x02: /* READ */
+                s->bb_state = BB_TA_R;
+                break;
+            case 0x01: /* WRITE */
+                s->bb_state = BB_TA_W;
+                break;
+            default:
+                s->bb_state = BB_INH;
+                break;
+            }
+        }
+        break;
+    case BB_TA_R:
+        s->mdi_pin = 0;
+        if (s->bits == 16) {
+            if (s->phyad == s->selphy) {
+                s->data = mdio_phy_read(s->phy, s->regad);
+                s->bb_state = BB_DATA_R;
+            } else {
+                s->bb_state = BB_INH;
+            }
+        }
+        break;
+    case BB_TA_W:
+        if (s->bits == 16) {
+            s->bb_state = BB_DATA_W;
+        }
+        break;
+    case BB_DATA_W:
+        s->data <<= 1;
+        s->data |= (s->mdo_pin & 1);
+        if (s->bits == 32) {
+            if (s->phyad == s->selphy) {
+                mdio_phy_write(s->phy, s->regad, s->data);
+            }
+            s->bb_state = BB_PRE;
+        }
+        break;
+    case BB_INH:
+    case BB_DATA_R:
+        if (s->bits == 32) {
+            s->bb_state = BB_PRE;
+        }
+        break;
+    }
+}
+
+/* shift out MDI */
+static void write_mdi(MDIOState *s)
+{
+    switch (s->bb_state) {
+    case BB_DATA_R:
+        s->mdi_pin = (s->data >> 15) & 1;
+        s->data <<= 1;
+        break;
+    case BB_TA_R:
+        s->mdi_pin = 0;
+        break;
+    default:
+        s->mdi_pin = mdio_z;
+        break;
+    }
+}
+
+/* MDIO pin operation */
+void mdio_set_mdc_pin(MDIOState *s, int clk)
+{
+    if (s->pclk ^ (clk & 1)) {
+        s->pclk = (clk & 1);
+        if (s->pclk == 1) {
+            /* rising edge */
+            read_mdo(s);
+        } else {
+            /* faling edge */
+            write_mdi(s);
+        }
+    }
+}
+
+static Property bb_properties[] = {
+    DEFINE_PROP_INT32("address", MDIOState, phyad, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void bb_init(Object *obj)
+{
+    MDIOState *s = MDIO_BB(obj);
+
+    object_property_add_link(obj, "phy",
+                             TYPE_ETHER_PHY,
+                             (Object **)&s->phy,
+                             qdev_prop_allow_set_link_before_realize,
+                             OBJ_PROP_LINK_STRONG);
+}
+
+static void bb_class_init(ObjectClass *klass, void *class_data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    device_class_set_props(dc, bb_properties);
+}
+
+static const TypeInfo phy_types_info[] = {
+    {
+        .name = TYPE_ETHER_PHY,
+        .parent = TYPE_DEVICE,
+        .class_init = phy_class_init,
+        .instance_size = sizeof(PHYState),
+    },
+    {
+        .name = TYPE_ETHER_MDIO_BB,
+        .parent = TYPE_DEVICE,
+        .class_init = bb_class_init,
+        .instance_size = sizeof(MDIOState),
+        .instance_init = bb_init,
+    },
+};
+
+DEFINE_TYPES(phy_types_info);
diff --git a/hw/net/Kconfig b/hw/net/Kconfig
index e43c96dae0..e6a32a2ab0 100644
--- a/hw/net/Kconfig
+++ b/hw/net/Kconfig
@@ -143,3 +143,6 @@ config CAN_SJA1000
     default y if PCI_DEVICES
     depends on PCI
     select CAN_BUS
+
+config MDIO_PHY
+    bool
diff --git a/hw/net/meson.build b/hw/net/meson.build
index 4a7051b54a..faa4e3d2c0 100644
--- a/hw/net/meson.build
+++ b/hw/net/meson.build
@@ -64,4 +64,6 @@ softmmu_ss.add(when: 'CONFIG_ROCKER', if_true: files(
 ), if_false: files('rocker/qmp-norocker.c'))
 softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('rocker/qmp-norocker.c'))
 
+softmmu_ss.add(when: 'CONFIG_MDIO_PHY', if_true: files('mdio.c'))
+
 subdir('can')
-- 
2.20.1



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

* [PATCH 16/20] hw/net: Add Renesas On-chip Ethernet MAC
  2020-08-27 12:38 [PATCH 00/20] RX target update Yoshinori Sato
                   ` (14 preceding siblings ...)
  2020-08-27 12:38 ` [PATCH 15/20] hw/net: Add generic Bit-bang MDIO PHY Yoshinori Sato
@ 2020-08-27 12:38 ` Yoshinori Sato
  2020-10-24 21:37   ` Philippe Mathieu-Daudé
  2020-08-27 12:38 ` [PATCH 17/20] hw/rx/rx62n: Add Ethernet support Yoshinori Sato
                   ` (4 subsequent siblings)
  20 siblings, 1 reply; 40+ messages in thread
From: Yoshinori Sato @ 2020-08-27 12:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 include/hw/net/renesas_eth.h |  57 +++
 hw/net/renesas_eth.c         | 875 +++++++++++++++++++++++++++++++++++
 hw/net/Kconfig               |   5 +
 hw/net/meson.build           |   1 +
 4 files changed, 938 insertions(+)
 create mode 100644 include/hw/net/renesas_eth.h
 create mode 100644 hw/net/renesas_eth.c

diff --git a/include/hw/net/renesas_eth.h b/include/hw/net/renesas_eth.h
new file mode 100644
index 0000000000..e0026c6434
--- /dev/null
+++ b/include/hw/net/renesas_eth.h
@@ -0,0 +1,57 @@
+/*
+ *  Renesas ETHERC / EDMAC
+ *
+ *  Copyright 2019 Yoshinori Sato <ysato@users.sourceforge.jp>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; under version 2 of the License.
+ *
+ *  This program is distributed in the hope that 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/>.
+ *
+ *  Contributions after 2012-01-13 are licensed under the terms of the
+ *  GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "hw/sysbus.h"
+#include "net/net.h"
+#include "hw/net/mdio.h"
+#include "hw/register.h"
+#include "hw/clock.h"
+
+#define TYPE_RENESAS_ETH "renesas_eth"
+#define RenesasEth(obj) OBJECT_CHECK(RenesasEthState, (obj), TYPE_RENESAS_ETH)
+
+#define RENESAS_ETHERC_R_MAX (0x100 / 4)
+#define RENESAS_EDMAC_R_MAX  (0x100 / 4)
+
+typedef struct RenesasEthState {
+    SysBusDevice parent_obj;
+
+    NICState *nic;
+    NICConf conf;
+    MemoryRegion etherc_mem;
+    MemoryRegion edmac_mem;
+    qemu_irq irq;
+
+    /* ETHERC registers */
+    RegisterInfo etherc_regs_info[RENESAS_ETHERC_R_MAX];
+    uint32_t etherc_regs[RENESAS_ETHERC_R_MAX];
+
+    /* EDMAC register */
+    RegisterInfo edmac_regs_info[RENESAS_EDMAC_R_MAX];
+    uint32_t edmac_regs[RENESAS_EDMAC_R_MAX];
+
+    int descsize;
+    int rcv_bcast;
+    uint8_t macadr[6];
+    int link_sta;
+    MDIOState *mdiodev;
+    Clock *ick;
+} RenesasEthState;
diff --git a/hw/net/renesas_eth.c b/hw/net/renesas_eth.c
new file mode 100644
index 0000000000..d5fc2bb30c
--- /dev/null
+++ b/hw/net/renesas_eth.c
@@ -0,0 +1,875 @@
+/*
+ *  Renesas ETHERC / EDMAC
+ *
+ *  Copyright 2019 Yoshinori Sato <ysato@users.sourceforge.jp>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; under version 2 of the License.
+ *
+ *  This program is distributed in the hope that 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/>.
+ *
+ *  Contributions after 2012-01-13 are licensed under the terms of the
+ *  GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "qemu/osdep.h"
+
+#include "hw/hw.h"
+#include "sysemu/dma.h"
+#include "qemu/log.h"
+#include "hw/qdev-properties.h"
+#include "hw/qdev-clock.h"
+#include "net/net.h"
+#include "sysemu/sysemu.h"
+#include "hw/irq.h"
+#include "hw/net/renesas_eth.h"
+
+/* ETHERC Registers */
+REG32(ECMR, 0x00)
+  FIELD(ECMR, PRM, 0, 1)
+  FIELD(ECMR, DM, 1, 1)
+  FIELD(ECMR, RTM, 2, 1)
+  FIELD(ECMR, ILB, 3, 1)
+  FIELD(ECMR, TE, 5, 1)
+  FIELD(ECMR, RE, 6, 1)
+  FIELD(ECMR, MPDE, 9, 1)
+  FIELD(ECMR, PRCREF, 12, 1)
+  FIELD(ECMR, TXF, 16, 1)
+  FIELD(ECMR, RXF, 17, 1)
+  FIELD(ECMR, PFR, 18, 1)
+  FIELD(ECMR, ZPF, 19, 1)
+  FIELD(ECMR, TPC, 20, 1)
+REG32(RFLR, 0x08)
+  FIELD(RFLR, RFL, 0, 12)
+REG32(ECSR, 0x10)
+  FIELD(ECSR, ICD, 0, 1)
+  FIELD(ECSR, MPD, 1, 1)
+  FIELD(ECSR, LCHNG, 2, 1)
+  FIELD(ECSR, PSRTO, 4, 1)
+  FIELD(ECSR, BFR, 5, 1)
+REG32(ECSIPR, 0x18)
+  FIELD(ECSIPR, ICDIP, 0, 1)
+  FIELD(ECSIPR, MPDIP, 1, 1)
+  FIELD(ECSIPR, LCHNGIP, 2, 1)
+  FIELD(ECSIPR, PSRTOIP, 4, 1)
+  FIELD(ECSIPR, BFSIPR, 5, 1)
+REG32(PIR, 0x20)
+  FIELD(PIR, MDC, 0, 1)
+  FIELD(PIR, MMD, 1, 1)
+  FIELD(PIR, MDO, 2, 1)
+  FIELD(PIR, MDI, 3, 1)
+REG32(PSR, 0x28)
+  FIELD(PSR, LMON, 0, 1)
+REG32(RDMLR, 0x40)
+  FIELD(RDMLR, RMD, 0, 20)
+REG32(IPGR, 0x50)
+  FIELD(IPGR, IPG, 0, 5)
+REG32(APR, 0x54)
+  FIELD(APR, AP, 0, 16)
+REG32(MPR, 0x58)
+  FIELD(MPR, MP, 0, 16)
+REG32(RFCF, 0x60)
+  FIELD(RFCF, RPAUSE, 0, 8)
+REG32(TPAUSER, 0x64)
+REG32(TPAUSECR, 0x68)
+  FIELD(TPAUSECR, TXP, 0, 8)
+  FIELD(TPAUSER, TPAUSE, 0, 16)
+REG32(BCFRR, 0x6c)
+  FIELD(BCFRR, BCF, 0, 16)
+REG32(MAHR, 0xc0)
+  FIELD(MAHR, MA, 0, 32)
+REG32(MALR, 0xc8)
+  FIELD(MALR, MA, 0, 16)
+REG32(TROCR, 0xd0)
+REG32(CDCR, 0xd4)
+REG32(LCCR, 0xd8)
+REG32(CNDCR, 0xdc)
+REG32(CEFCR, 0xe4)
+REG32(FRECR, 0xe8)
+REG32(TSFRCR, 0xec)
+REG32(TLFRCR, 0xf0)
+REG32(RFCR, 0xf4)
+REG32(MAFCR, 0xf8)
+
+/* EDMAC register */
+REG32(EDMR, 0x00)
+  FIELD(EDMR, SWR, 0, 1)
+  FIELD(EDMR, DL, 4, 2)
+  FIELD(EDMR, DE, 6, 1)
+REG32(EDTRR, 0x08)
+  FIELD(EDTRR, TR, 0, 1)
+REG32(EDRRR, 0x10)
+  FIELD(EDRRR, RR, 0, 1)
+REG32(TDLAR, 0x18)
+REG32(RDLAR, 0x20)
+REG32(EESR, 0x28)
+  FIELD(EESR, CERF, 0, 1)
+  FIELD(EESR, PRE,  1, 1)
+  FIELD(EESR, RTSF, 2, 1)
+  FIELD(EESR, RTLF, 3, 1)
+  FIELD(EESR, RRF,  4, 1)
+  FIELD(EESR, RMAF, 7, 1)
+  FIELD(EESR, TRO,  8, 1)
+  FIELD(EESR, CD,   9, 1)
+  FIELD(EESR, RDESC, 0, 10)
+  FIELD(EESR, DLC,  10, 1)
+  FIELD(EESR, CND,  11, 1)
+  FIELD(EESR, RDOF, 16, 1)
+  FIELD(EESR, RDE,  17, 1)
+  FIELD(EESR, FR,   18, 1)
+  FIELD(EESR, TFUF, 19, 1)
+  FIELD(EESR, TDE,  20, 1)
+  FIELD(EESR, TC,   21, 1)
+  FIELD(EESR, ECI,  22, 1)
+  FIELD(EESR, ADE,  23, 1)
+  FIELD(EESR, RFCOF, 24, 1)
+  FIELD(EESR, RABT, 25, 1)
+  FIELD(EESR, TABT, 26, 1)
+  FIELD(EESR, TWB,  30, 1)
+REG32(EESIPR, 0x30)
+  FIELD(EESIPR, CERFIP, 0, 1)
+  FIELD(EESIPR, PREIP,  1, 1)
+  FIELD(EESIPR, RTSFIP, 2, 1)
+  FIELD(EESIPR, RTLFIP, 3, 1)
+  FIELD(EESIPR, RRFIP,  4, 1)
+  FIELD(EESIPR, RMAFIP, 7, 1)
+  FIELD(EESIPR, TROIP,  8, 1)
+  FIELD(EESIPR, CDIP,   9, 1)
+  FIELD(EESIPR, DLCIP,  10, 1)
+  FIELD(EESIPR, CNDIP,  11, 1)
+  FIELD(EESIPR, RDOFIP, 16, 1)
+  FIELD(EESIPR, RDEIP,  17, 1)
+  FIELD(EESIPR, FRIP,   18, 1)
+  FIELD(EESIPR, TFUFIP, 19, 1)
+  FIELD(EESIPR, TDEIP,  20, 1)
+  FIELD(EESIPR, TCIP,   21, 1)
+  FIELD(EESIPR, ECIIP,  22, 1)
+  FIELD(EESIPR, ADEIP,  23, 1)
+  FIELD(EESIPR, RFCOFIP, 24, 1)
+  FIELD(EESIPR, RABTIP, 25, 1)
+  FIELD(EESIPR, TABTIP, 26, 1)
+  FIELD(EESIPR, TWBIP,  30, 1)
+REG32(TRSCER, 0x38)
+  FIELD(TRSCER, RRFCE, 4, 1)
+  FIELD(TRSCER, RMAFCE, 7, 1)
+REG32(RMFCR, 0x40)
+  FIELD(RMFCR, MFC, 0, 16)
+REG32(TFTR, 0x48)
+  FIELD(TFTR, TFT, 0, 11)
+REG32(FDR, 0x50)
+  FIELD(FDR, RFD, 0, 5)
+  FIELD(FDR, TFD, 8, 5)
+REG32(RMCR, 0x58)
+  FIELD(RMCR, RNR, 0, 1)
+  FIELD(RMCR, RNC, 1, 1)
+REG32(TFUCR, 0x64)
+  FIELD(TFUCR, UNDER, 0, 16)
+REG32(RFOCR, 0x68)
+  FIELD(RFOCR, OVER, 0, 16)
+REG32(IOSR, 0x6c)
+  FIELD(IOSR, ELB, 0, 1);
+REG32(FCFTR, 0x70)
+  FIELD(FCFTR, RFDO, 0, 3)
+  FIELD(FCFTR, RFFO, 16, 3)
+REG32(RPADIR, 0x78)
+  FIELD(RPADIR, PADR, 0, 6)
+  FIELD(RPADIR, PADS, 16, 2)
+REG32(TRIMD, 0x7c)
+  FIELD(TRIMD, TIS, 0, 1)
+  FIELD(TRIMD, TIM, 4, 1)
+REG32(RBWAR, 0xc8)
+REG32(RDFAR, 0xcc)
+REG32(TBRAR, 0xd4)
+REG32(TDFAR, 0xd8)
+
+/* Transmit Descriptor */
+REG32(TD0, 0x0000)
+  FIELD(TD0, TFS0, 0, 1)
+  FIELD(TD0, TFS1, 1, 1)
+  FIELD(TD0, TFS2, 2, 1)
+  FIELD(TD0, TFS3, 3, 1)
+  FIELD(TD0, TFS8, 8, 1)
+  FIELD(TD0, TWBI, 26, 1)
+  FIELD(TD0, TFE,  27, 1)
+  FIELD(TD0, TFP,  28, 2)
+  FIELD(TD0, TDLE, 30, 1)
+  FIELD(TD0, TACT, 31, 1)
+REG32(TD1, 0x0004)
+  FIELD(TD1, TBL, 16, 16)
+REG32(TD2, 0x0008)
+  FIELD(TD2, TBA, 0, 32)
+
+/* Receive Descriptor */
+REG32(RD0, 0x0000)
+  FIELD(RD0, RFS,  0, 10)
+    FIELD(RD0, RFS0, 0, 1)
+    FIELD(RD0, RFS1, 1, 1)
+    FIELD(RD0, RFS2, 2, 1)
+    FIELD(RD0, RFS3, 3, 1)
+    FIELD(RD0, RFS4, 4, 1)
+    FIELD(RD0, RFS7, 7, 1)
+    FIELD(RD0, RFS8, 8, 1)
+    FIELD(RD0, RFS9, 9, 1)
+  FIELD(RD0, RFE,  27, 1)
+  FIELD(RD0, RFP,  28, 2)
+  FIELD(RD0, RFP0, 28, 1)
+  FIELD(RD0, RDLE, 30, 1)
+  FIELD(RD0, RACT, 31, 1)
+REG32(RD1, 0x0004)
+  FIELD(RD1, RFL, 0, 16)
+  FIELD(RD1, RBL, 16, 16)
+REG32(RD2, 0x0008)
+  FIELD(RD2, RBA, 0, 32)
+
+static void renesas_eth_set_irq(RenesasEthState *s)
+{
+    if (s->edmac_regs[R_EESR] & s->edmac_regs[R_EESIPR]) {
+        qemu_set_irq(s->irq, 1);
+    } else {
+        qemu_set_irq(s->irq, 0);
+    }
+}
+
+static bool renesas_eth_can_receive(NetClientState *nc)
+{
+    RenesasEthState *s = RenesasEth(qemu_get_nic_opaque(nc));
+
+    return FIELD_EX32(s->edmac_regs[R_EDRRR], EDRRR, RR);
+}
+
+static void set_ecsr(RenesasEthState *s, int bit)
+{
+    s->etherc_regs[R_ECSR] = deposit32(s->etherc_regs[R_ECSR], bit, 1, 1);
+    if (s->etherc_regs[R_ECSR] & s->etherc_regs[R_ECSIPR]) {
+        s->edmac_regs[R_EESR] = FIELD_DP32(s->edmac_regs[R_EESR],
+                                           EESR, ECI, 1);
+    }
+    renesas_eth_set_irq(s);
+}
+
+static void renesas_eth_set_link_status(NetClientState *nc)
+{
+    RenesasEthState *s = RenesasEth(qemu_get_nic_opaque(nc));
+    int old_lmon, new_lmon;
+    if (s->mdiodev) {
+        old_lmon = mdio_phy_linksta(mdio_get_phy(s->mdiodev));
+        mdio_phy_set_link(mdio_get_phy(s->mdiodev), !nc->link_down);
+        new_lmon = mdio_phy_linksta(mdio_get_phy(s->mdiodev));
+        if (old_lmon ^ new_lmon) {
+            set_ecsr(s, R_ECSR_LCHNG_SHIFT);
+        }
+    }
+}
+
+static void edmac_write(RenesasEthState *s, const uint8_t *buf,
+                        size_t size, int pad)
+{
+    uint32_t rdesc[3];
+    uint32_t eesr;
+    int state = 0;
+
+    while (size > 0) {
+        size_t wsize;
+        /* RDESC read */
+        dma_memory_read(&address_space_memory,
+                        s->edmac_regs[R_RDFAR], rdesc, sizeof(rdesc));
+        if (FIELD_EX32(rdesc[0], RD0, RACT)) {
+            if (state == 0) {
+                /* Fist block */
+                rdesc[0] = FIELD_DP32(rdesc[0], RD0, RFP, 2);
+            }
+            state++;
+            s->edmac_regs[R_RBWAR] = rdesc[2];
+            wsize = MIN(FIELD_EX32(rdesc[1], RD1, RBL), size);
+            /* Write receive data */
+            dma_memory_write(&address_space_memory,
+                             s->edmac_regs[R_RBWAR], buf, wsize);
+            buf += wsize;
+            size -= wsize;
+            rdesc[1] = FIELD_DP32(rdesc[1], RD1, RFL, wsize);
+            if (size == 0) {
+                /* Last descriptor */
+                rdesc[0] = FIELD_DP32(rdesc[0], RD0, RFP0, 1);
+                if (FIELD_EX32(s->edmac_regs[R_RMCR], RMCR, RNR) == 0) {
+                    s->edmac_regs[R_EDRRR] = FIELD_DP32(s->edmac_regs[R_EDRRR],
+                                                  EDRRR, RR, 0);
+                }
+                s->edmac_regs[R_EESR] = FIELD_DP32(s->edmac_regs[R_EESR],
+                                                   EESR, FR, 1);
+                renesas_eth_set_irq(s);
+            }
+            eesr = FIELD_EX32(s->edmac_regs[R_EESR], EESR, RDESC);
+            rdesc[0] = FIELD_DP32(rdesc[0], RD0, RFS,
+                                  eesr & ~(s->edmac_regs[R_TRSCER]));
+            rdesc[0] = FIELD_DP32(rdesc[0], RD0, RFE, eesr != 0);
+            rdesc[0] = FIELD_DP32(rdesc[0], RD0, RACT, 0);
+            /* RDESC write back */
+            dma_memory_write(&address_space_memory,
+                             s->edmac_regs[R_RDFAR], rdesc, sizeof(rdesc));
+            if (FIELD_EX32(rdesc[0], RD0, RDLE)) {
+                s->edmac_regs[R_RDFAR] = s->edmac_regs[R_RDLAR];
+            } else {
+                s->edmac_regs[R_RDFAR] += s->descsize;
+            }
+            s->edmac_regs[R_EESR] = FIELD_DP32(s->edmac_regs[R_EESR],
+                                               EESR, FR, 1);
+        } else {
+            /* no active RDESC */
+            if (FIELD_EX32(s->edmac_regs[R_RMCR], RMCR, RNC) == 0) {
+                s->edmac_regs[R_EDRRR] = FIELD_DP32(s->edmac_regs[R_EDRRR],
+                                                    EDRRR, RR, 0);
+            }
+            s->edmac_regs[R_EESR] = FIELD_DP32(s->edmac_regs[R_EESR],
+                                               EESR, RDE, 1);
+            break;
+        }
+    }
+    renesas_eth_set_irq(s);
+}
+
+static inline void update_count(uint32_t *cnt)
+{
+    if (*cnt < UINT32_MAX) {
+        /* Satulate on 32bit value */
+        (*cnt)++;
+    }
+}
+
+#define MIN_BUF_SIZE 60
+static ssize_t renesas_eth_receive(NetClientState *nc,
+                            const uint8_t *buf, size_t size)
+{
+    RenesasEthState *s = RenesasEth(qemu_get_nic_opaque(nc));
+    static const uint8_t bcast_addr[] = {
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+    };
+    static const uint8_t pad[3] = { 0 };
+    uint8_t buf1[MIN_BUF_SIZE];
+    bool receive = false;
+    size_t pads;
+    uint32_t rflr;
+
+    if (size >= 6) {
+        if (memcmp(buf, bcast_addr, sizeof(bcast_addr)) == 0) {
+            /* broadcast */
+            if (s->etherc_regs[R_BCFRR] == 0 ||
+                s->etherc_regs[R_BCFRR] < s->rcv_bcast) {
+                s->rcv_bcast++;
+                receive = true;
+            }
+        } else if (buf[0] & 0x1) {
+            /* multicast */
+            receive = true;
+            s->edmac_regs[R_EESR] = FIELD_DP32(s->edmac_regs[R_EESR],
+                                               EESR, RMAF, 1);
+            update_count(&s->edmac_regs[R_MAFCR]);
+        } else if (FIELD_EX32(s->edmac_regs[R_ECMR], ECMR, PRM)) {
+            /* promiscas */
+            receive = true;
+        } else if (memcmp(buf, s->macadr, sizeof(s->macadr)) == 0) {
+            /* normal */
+            receive = true;
+        }
+    }
+    if (!receive) {
+        return size;
+    }
+    /* if too small buffer, then expand it */
+    if (size < MIN_BUF_SIZE) {
+        memcpy(buf1, buf, size);
+        memset(buf1 + size, 0, MIN_BUF_SIZE - size);
+        buf = buf1;
+        size = MIN_BUF_SIZE;
+    }
+
+    rflr = FIELD_EX32(s->etherc_regs[R_RFLR], RFLR, RFL);
+    rflr = MAX(rflr, 1518);
+    if (size > rflr) {
+        update_count(&s->etherc_regs[R_TLFRCR]);
+        s->edmac_regs[R_EESR] = FIELD_DP32(s->edmac_regs[R_EESR],
+                                           EESR, RTLF, 1);
+    }
+    pads = FIELD_EX32(s->edmac_regs[R_RPADIR], RPADIR, PADS);
+    if (pads > 0) {
+        int pos = FIELD_EX32(s->edmac_regs[R_RPADIR], RPADIR, PADR);
+        uint8_t *padbuf = g_new(uint8_t, size + pads);
+        if (size > pos) {
+            if (pos > 0) {
+                memcpy(padbuf, buf, pos);
+            }
+            memcpy(padbuf + pos, pad, pads);
+            memcpy(padbuf + pos + pads, buf + pos, size - pos);
+        } else {
+            pads = 0;
+        }
+        edmac_write(s, padbuf, size + pads, pads);
+        g_free(padbuf);
+    } else {
+        edmac_write(s, buf, size, 0);
+    }
+    return size;
+}
+
+static size_t edmac_read(RenesasEthState *s, uint8_t **buf)
+{
+    uint32_t tdesc[3];
+    uint32_t size = 0;
+
+    *buf = NULL;
+    for (;;) {
+        size_t rsize;
+        dma_memory_read(&address_space_memory,
+                        s->edmac_regs[R_TDFAR], tdesc, sizeof(tdesc));
+        if (FIELD_EX32(tdesc[0], TD0, TACT)) {
+            s->edmac_regs[R_TBRAR] = tdesc[2];
+            rsize = FIELD_EX32(tdesc[1], TD1, TBL);
+            *buf = g_realloc(*buf, size + rsize);
+            dma_memory_read(&address_space_memory,
+                            s->edmac_regs[R_TBRAR], *buf + size, rsize);
+            tdesc[0] = FIELD_DP32(tdesc[0], TD0, TACT, 0);
+            dma_memory_write(&address_space_memory,
+                            s->edmac_regs[R_TDFAR], tdesc, sizeof(tdesc));
+            size += rsize;
+            if (FIELD_EX32(tdesc[0], TD0, TDLE)) {
+                s->edmac_regs[R_TDFAR] = s->edmac_regs[R_TDLAR];
+            } else {
+                s->edmac_regs[R_TDFAR] += s->descsize;
+            }
+            if (FIELD_EX32(tdesc[0], TD0, TFP) & 1) {
+                break;
+            }
+        } else {
+            s->edmac_regs[R_EESR] = FIELD_DP32(s->edmac_regs[R_EESR],
+                                               EESR, TDE, 1);
+            renesas_eth_set_irq(s);
+            break;
+        }
+    }
+    return size;
+}
+
+static void renesas_eth_start_xmit(RenesasEthState *s)
+{
+    uint8_t *txbuf;
+    size_t size;
+
+    size = edmac_read(s, &txbuf);
+    qemu_send_packet(qemu_get_queue(s->nic), txbuf, size);
+    g_free(txbuf);
+    s->edmac_regs[R_EESR] = FIELD_DP32(s->edmac_regs[R_EESR], EESR, TWB, 1);
+    s->edmac_regs[R_EDTRR] = FIELD_DP32(s->edmac_regs[R_EDTRR], EDTRR, TR, 0);
+    renesas_eth_set_irq(s);
+}
+
+static void renesas_eth_reset(RenesasEthState *s)
+{
+    int i;
+
+    for (i = 0; i < RENESAS_ETHERC_R_MAX; i++) {
+        register_reset(&s->etherc_regs_info[i]);
+    }
+    for (i = 0; i < RENESAS_EDMAC_R_MAX; i++) {
+        register_reset(&s->edmac_regs_info[i]);
+    }
+}
+
+static uint64_t ecsr_pre_write(RegisterInfo *reg, uint64_t val)
+{
+    RenesasEthState *s = RenesasEth(reg->opaque);
+    uint32_t old_val = s->etherc_regs[R_ECSR];
+
+    val ^= old_val;
+    val &= old_val;
+    return val;
+}
+
+static void ecsr_post_write(RegisterInfo *reg, uint64_t val)
+{
+    RenesasEthState *s = RenesasEth(reg->opaque);
+
+    if (s->etherc_regs[R_ECSR] & s->etherc_regs[R_ECSIPR]) {
+        s->edmac_regs[R_EESR] = FIELD_DP32(s->edmac_regs[R_EESR],
+                                           EESR, ECI, 1);
+    } else {
+        s->edmac_regs[R_EESR] = FIELD_DP32(s->edmac_regs[R_EESR],
+                                           EESR, ECI, 0);
+    }
+    renesas_eth_set_irq(s);
+}
+
+static void pir_post_write(RegisterInfo *reg, uint64_t val)
+{
+    RenesasEthState *s = RenesasEth(reg->opaque);
+    if (s->mdiodev) {
+        mdio_set_mdc_pin(s->mdiodev, FIELD_EX32(val, PIR, MDC));
+        if (FIELD_EX32(val, PIR, MMD)) {
+            mdio_set_mdo_pin(s->mdiodev, FIELD_EX32(val, PIR, MDO));
+        }
+    }
+}
+
+static uint64_t pir_post_read(RegisterInfo *reg, uint64_t val)
+{
+    RenesasEthState *s = RenesasEth(reg->opaque);
+    if (s->mdiodev) {
+        val = FIELD_DP64(val, PIR, MDI, mdio_read_mdi_pin(s->mdiodev));
+    }
+    return val;
+}
+
+static uint64_t mar_pre_write(RegisterInfo *reg, uint64_t val)
+{
+    RenesasEthState *s = RenesasEth(reg->opaque);
+    if (FIELD_EX32(s->edmac_regs[R_EDTRR], EDTRR, TR) ||
+        FIELD_EX32(s->edmac_regs[R_EDRRR], EDRRR, RR)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "renesas_eth: Tx/Rx enabled in MAR write.\n");
+    }
+    return val;
+}
+
+static void mar_post_write(RegisterInfo *reg, uint64_t val)
+{
+    int i;
+    RenesasEthState *s = RenesasEth(reg->opaque);
+    for (i = 0; i < 4; i++) {
+        s->macadr[i] = extract32(s->etherc_regs[R_MAHR], 8 * (3 - i), 8);
+    }
+    for (i = 0; i < 2; i++) {
+        s->macadr[i + 4] = extract32(s->etherc_regs[R_MALR], 8 * (1 - i), 8);
+    }
+}
+
+static uint64_t etherc_counter_write(RegisterInfo *reg, uint64_t val)
+{
+    /* Counter register clear in any write operation */
+    return 0;
+}
+
+static void edmr_post_write(RegisterInfo *reg, uint64_t val)
+{
+    RenesasEthState *s = RenesasEth(reg->opaque);
+    uint32_t TDLAR, RMFCR, TFUCR, RFOCR;
+    int dl;
+
+    if (FIELD_EX32(val, EDMR, SWR)) {
+        /* Following register keep for SWR */
+        TDLAR = s->edmac_regs[R_TDLAR];
+        RMFCR = s->edmac_regs[R_RMFCR];
+        TFUCR = s->edmac_regs[R_TFUCR];
+        RFOCR = s->edmac_regs[R_RFOCR];
+        renesas_eth_reset(s);
+        s->edmac_regs[R_TDLAR] = TDLAR;
+        s->edmac_regs[R_RMFCR] = RMFCR;
+        s->edmac_regs[R_TFUCR] = TFUCR;
+        s->edmac_regs[R_RFOCR] = RFOCR;
+    }
+    dl = FIELD_EX32(val, EDMR, DL) % 3;
+    s->descsize = 16 << dl;
+}
+
+static void edtrr_post_write(RegisterInfo *reg, uint64_t val)
+{
+    RenesasEthState *s = RenesasEth(reg->opaque);
+    if (FIELD_EX32(val, EDTRR, TR)) {
+        renesas_eth_start_xmit(s);
+    }
+}
+
+static uint64_t eesr_pre_write(RegisterInfo *reg, uint64_t val)
+{
+    uint32_t eesr;
+    RenesasEthState *s = RenesasEth(reg->opaque);
+    /* flag clear for write 1 */
+    eesr = s->edmac_regs[R_EESR];
+    val = FIELD_DP64(val, EESR, ECI, 0); /* Keep ECI value */
+    eesr &= ~val;
+    return eesr;
+}
+
+static void eesr_post_write(RegisterInfo *reg, uint64_t val)
+{
+    RenesasEthState *s = RenesasEth(reg->opaque);
+    renesas_eth_set_irq(s);
+}
+
+static void tdlar_post_write(RegisterInfo *reg, uint64_t val)
+{
+    RenesasEthState *s = RenesasEth(reg->opaque);
+    s->edmac_regs[R_TDFAR] = s->edmac_regs[R_TDLAR];
+}
+
+static void rdlar_post_write(RegisterInfo *reg, uint64_t val)
+{
+    RenesasEthState *s = RenesasEth(reg->opaque);
+    s->edmac_regs[R_RDFAR] = s->edmac_regs[R_RDLAR];
+}
+
+static uint64_t fdr_pre_write(RegisterInfo *reg, uint64_t val)
+{
+    RenesasEthState *s = RenesasEth(reg->opaque);
+    if (FIELD_EX32(val, FDR, TFD) != 7 || FIELD_EX32(val, FDR, RFD) != 7) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "renesas_eth: invalid FDR setting %"
+                      HWADDR_PRIX ".\n", val);
+    }
+    if (FIELD_EX32(s->edmac_regs[R_EDTRR], EDTRR, TR) ||
+        FIELD_EX32(s->edmac_regs[R_EDRRR], EDRRR, RR)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "renesas_eth: Tx/Rx enabled in FDR write.\n");
+    }
+    return val;
+}
+
+static uint64_t edmac_reg_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    RegisterInfoArray *ra = opaque;
+    RenesasEthState *s = RenesasEth(ra->r[0]->opaque);
+    if (clock_is_enabled(s->ick)) {
+        return register_read_memory(ra, addr, size);
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "renesas_eth: EDMAC module stopped.\n");
+        return UINT64_MAX;
+    }
+}
+
+static void edmac_reg_write(void *opaque, hwaddr addr,
+                        uint64_t value, unsigned int size)
+{
+    RegisterInfoArray *ra = opaque;
+    RenesasEthState *s = RenesasEth(ra->r[0]->opaque);
+    if (clock_is_enabled(s->ick)) {
+        register_write_memory(ra, addr, value, size);
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "renesas_eth: EDMAC module stopped.\n");
+    }
+}
+
+static const MemoryRegionOps renesas_etherc_ops = {
+    .read = register_read_memory,
+    .write = register_write_memory,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static const MemoryRegionOps renesas_edmac_ops = {
+    .read = edmac_reg_read,
+    .write = edmac_reg_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static NetClientInfo net_renesas_eth_info = {
+    .type = NET_CLIENT_DRIVER_NIC,
+    .size = sizeof(NICState),
+    .can_receive = renesas_eth_can_receive,
+    .receive = renesas_eth_receive,
+    .link_status_changed = renesas_eth_set_link_status,
+};
+
+static const RegisterAccessInfo renesas_etherc_regs_info[] = {
+    { .name = "ECMR", .addr = A_ECMR,
+      .rsvd = 0xffe0ed90, },
+    { .name = "RFLR", .addr = A_RFLR,
+      .rsvd = 0xfffff000, },
+    { .name = "ECSR", .addr = A_ECSR,
+      .rsvd = 0xffffffc8,
+      .pre_write = ecsr_pre_write,
+      .post_write = ecsr_post_write, },
+    { .name = "ECSIPR", .addr = A_ECSIPR,
+      .rsvd = 0xffffffc8,
+      .post_write = ecsr_post_write, },
+    { .name = "PIR", .addr = A_PIR,
+      .rsvd = 0xfffffff0,
+      .post_write = pir_post_write,
+      .post_read = pir_post_read, },
+    { .name = "PSR", .addr = A_PSR,
+      .rsvd = 0xfffffffe, },
+    { .name = "RDMLR", .addr = A_RDMLR,
+      .rsvd = 0xfff00000, },
+    { .name = "IPGR", .addr = A_IPGR,
+      .rsvd = 0xffffffe0, .reset = 0x00000014, },
+    { .name = "APR", .addr = A_APR,
+      .rsvd = 0xffff0000, },
+    { .name = "MPR", .addr = A_MPR,
+      .rsvd = 0xffff0000, },
+    { .name = "RFCF", .addr = A_RFCF,
+      .rsvd = 0xffffff00, },
+    { .name = "TPAUSER", .addr = A_TPAUSER,
+      .rsvd = 0xffff0000, },
+    { .name = "TPAUSECR", .addr = A_TPAUSECR,
+      .rsvd = 0xffffff00, },
+    { .name = "BCFRR", .addr = A_BCFRR,
+      .rsvd = 0xffff0000, },
+    { .name = "MAHR", .addr = A_MAHR,
+      .pre_write = mar_pre_write,
+      .post_write = mar_post_write, },
+    { .name = "MALR", .addr = A_MALR,
+      .rsvd = 0xffff0000,
+      .pre_write = mar_pre_write,
+      .post_write = mar_post_write, },
+    { .name = "TROCR", .addr = A_TROCR,
+      .pre_write = etherc_counter_write, },
+    { .name = "CDCR", .addr = A_CDCR,
+      .pre_write = etherc_counter_write, },
+    { .name = "LCCR", .addr = A_LCCR,
+      .pre_write = etherc_counter_write, },
+    { .name = "CNDCR", .addr = A_CNDCR,
+      .pre_write = etherc_counter_write, },
+    { .name = "CEFCR", .addr = A_CEFCR,
+      .pre_write = etherc_counter_write, },
+    { .name = "FRECR", .addr = A_FRECR,
+      .pre_write = etherc_counter_write, },
+    { .name = "TSFRCR", .addr = A_TSFRCR,
+      .pre_write = etherc_counter_write, },
+    { .name = "TLFRCR", .addr = A_TLFRCR,
+      .pre_write = etherc_counter_write, },
+    { .name = "RFCR", .addr = A_RFCR,
+      .pre_write = etherc_counter_write, },
+    { .name = "MAFCR", .addr = A_MAFCR,
+      .pre_write = etherc_counter_write, },
+};
+
+static const RegisterAccessInfo renesas_edmac_regs_info[] = {
+    { .name = "EDMR", .addr = A_EDMR,
+      .rsvd = 0xfffffff8e,
+      .post_write = edmr_post_write, },
+    { .name = "EDTRR", .addr = A_EDTRR,
+      .rsvd = 0xffffffffe,
+      .post_write = edtrr_post_write, },
+    { .name = "EDRRR", .addr = A_EDRRR,
+      .rsvd = 0xffffffffe, },
+    { .name = "TDLAR", .addr = A_TDLAR,
+      .post_write = tdlar_post_write, },
+    { .name = "RDLAR", .addr = A_RDLAR,
+      .post_write = rdlar_post_write, },
+    { .name = "EESR", .addr = A_EESR,
+      .rsvd = 0xb800f0c0, .ro = 0x00400000,
+      .pre_write = eesr_pre_write,
+      .post_write = eesr_post_write, },
+    { .name = "EESIPR", .addr = A_EESIPR,
+      .rsvd = 0xb800f060,
+      .post_write = eesr_post_write, },
+    { .name = "TRSCER", .addr = A_TRSCER,
+      .rsvd = 0xfffffd6f, },
+    { .name = "RMFCR", .addr = A_RMFCR,
+      .rsvd = 0xffff0000, },
+    { .name = "TFTR", .addr = A_TFTR,
+      .rsvd = 0xfffff800, },
+    { .name = "FDR", .addr = A_FDR,
+      .rsvd = 0xffffe0e0,
+      .pre_write = fdr_pre_write, },
+    { .name = "RMCR", .addr = A_RMCR,
+      .rsvd = 0xfffffffc, },
+    { .name = "TFUCR", .addr = A_TFUCR,
+      .rsvd = 0xffff0000,
+      .pre_write = etherc_counter_write, },
+    { .name = "RFOCR", .addr = A_RFOCR,
+      .rsvd = 0xffff0000,
+      .pre_write = etherc_counter_write, },
+    { .name = "RBWAR", .addr = A_RBWAR,
+      .ro = 0xffffffff, .rsvd = 0xffff0000, },
+    { .name = "RDFAR", .addr = A_RDFAR,
+      .ro = 0xffffffff, .rsvd = 0xffff0000, },
+    { .name = "TBRAR", .addr = A_TBRAR,
+      .ro = 0xffffffff, .rsvd = 0xffff0000, },
+    { .name = "TDFAR", .addr = A_TDFAR,
+      .ro = 0xffffffff, .rsvd = 0xffff0000, },
+    { .name = "FCFTR", .addr = A_FCFTR,
+      .rsvd = 0xfff8fff8, },
+    { .name = "RPADIR", .addr = A_RPADIR,
+      .rsvd = 0xfffcffc0, },
+    { .name = "TRIMD", .addr = A_TRIMD,
+      .rsvd = 0xffffffee, },
+    { .name = "IOSR", .addr = A_IOSR,
+      .rsvd = 0xfffffffe, },
+};
+
+static void renesas_eth_realize(DeviceState *dev, Error **errp)
+{
+    RenesasEthState *s = RenesasEth(dev);
+
+    s->nic = qemu_new_nic(&net_renesas_eth_info, &s->conf,
+                          object_get_typename(OBJECT(s)), dev->id, s);
+
+    renesas_eth_reset(s);
+    if (s->mdiodev) {
+        mdio_phy_set_link(mdio_get_phy(s->mdiodev),
+                          !qemu_get_queue(s->nic)->link_down);
+    }
+}
+
+static Property renesas_eth_properties[] = {
+    DEFINE_NIC_PROPERTIES(RenesasEthState, conf),
+    DEFINE_PROP_LINK("mdio", RenesasEthState, mdiodev, TYPE_ETHER_MDIO_BB,
+                     MDIOState *),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void renesas_eth_init(Object *obj)
+{
+    SysBusDevice *d = SYS_BUS_DEVICE(obj);
+    RenesasEthState *s = RenesasEth(obj);
+    RegisterInfoArray *ra_etherc;
+    RegisterInfoArray *ra_edmac;
+
+    memory_region_init(&s->etherc_mem, obj, "renesas-etherc", 0x100);
+    ra_etherc = register_init_block32(DEVICE(d), renesas_etherc_regs_info,
+                                      ARRAY_SIZE(renesas_etherc_regs_info),
+                                      s->etherc_regs_info, s->etherc_regs,
+                                      &renesas_etherc_ops,
+                                      false, 0x100);
+    memory_region_add_subregion(&s->etherc_mem, 0x00, &ra_etherc->mem);
+    sysbus_init_mmio(d, &s->etherc_mem);
+
+    memory_region_init(&s->edmac_mem, obj, "renesas-edmac", 0x100);
+    ra_edmac = register_init_block32(DEVICE(d), renesas_edmac_regs_info,
+                                     ARRAY_SIZE(renesas_edmac_regs_info),
+                                     s->edmac_regs_info, s->edmac_regs,
+                                     &renesas_edmac_ops,
+                                     false, 0x100);
+    memory_region_add_subregion(&s->edmac_mem, 0x00, &ra_edmac->mem);
+    sysbus_init_mmio(d, &s->edmac_mem);
+
+    sysbus_init_irq(d, &s->irq);
+    s->ick =  qdev_init_clock_in(DEVICE(d), "ick", NULL, NULL);
+}
+
+static void renesas_eth_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
+    device_class_set_props(dc, renesas_eth_properties);
+    dc->realize = renesas_eth_realize;
+}
+
+static const TypeInfo renesas_eth_info = {
+    .name          = TYPE_RENESAS_ETH,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(RenesasEthState),
+    .instance_init = renesas_eth_init,
+    .class_init    = renesas_eth_class_init,
+};
+
+static void renesas_eth_register_types(void)
+{
+    type_register_static(&renesas_eth_info);
+}
+
+type_init(renesas_eth_register_types)
diff --git a/hw/net/Kconfig b/hw/net/Kconfig
index e6a32a2ab0..7cb3aeadeb 100644
--- a/hw/net/Kconfig
+++ b/hw/net/Kconfig
@@ -146,3 +146,8 @@ config CAN_SJA1000
 
 config MDIO_PHY
     bool
+
+config RENESAS_ETH
+    bool
+    select MDIO_PHY
+    select REGISTER
diff --git a/hw/net/meson.build b/hw/net/meson.build
index faa4e3d2c0..0f64af7b8f 100644
--- a/hw/net/meson.build
+++ b/hw/net/meson.build
@@ -65,5 +65,6 @@ softmmu_ss.add(when: 'CONFIG_ROCKER', if_true: files(
 softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('rocker/qmp-norocker.c'))
 
 softmmu_ss.add(when: 'CONFIG_MDIO_PHY', if_true: files('mdio.c'))
+softmmu_ss.add(when: 'CONFIG_RENESAS_ETH', if_true: files('renesas_eth.c'))
 
 subdir('can')
-- 
2.20.1



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

* [PATCH 17/20] hw/rx/rx62n: Add Ethernet support.
  2020-08-27 12:38 [PATCH 00/20] RX target update Yoshinori Sato
                   ` (15 preceding siblings ...)
  2020-08-27 12:38 ` [PATCH 16/20] hw/net: Add Renesas On-chip Ethernet MAC Yoshinori Sato
@ 2020-08-27 12:38 ` Yoshinori Sato
  2020-08-27 12:38 ` [PATCH 18/20] hw/rx: Add Tokudenkairo TKDN-RX62N-BRD Yoshinori Sato
                   ` (3 subsequent siblings)
  20 siblings, 0 replies; 40+ messages in thread
From: Yoshinori Sato @ 2020-08-27 12:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 include/hw/rx/rx62n.h |  3 +++
 hw/rx/rx62n.c         | 26 ++++++++++++++++++++++++++
 hw/rx/Kconfig         |  3 ++-
 3 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/include/hw/rx/rx62n.h b/include/hw/rx/rx62n.h
index 170c8cb6fc..4f11ca3fd9 100644
--- a/include/hw/rx/rx62n.h
+++ b/include/hw/rx/rx62n.h
@@ -30,6 +30,7 @@
 #include "hw/timer/renesas_timer.h"
 #include "hw/timer/renesas_mtu.h"
 #include "hw/char/renesas_sci.h"
+#include "hw/net/renesas_eth.h"
 #include "hw/rx/rx62n-cpg.h"
 #include "qemu/units.h"
 
@@ -74,6 +75,8 @@ typedef struct RX62NState {
     RenesasCMTState cmt[RX62N_NR_CMT];
     RenesasMTU2State mtu[RX62N_NR_MTU];
     RSCIAState sci[RX62N_NR_SCI];
+    RenesasEthState ether;
+    MDIOState *mdio;
     RX62NCPGState cpg;
 
     MemoryRegion *sysmem;
diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c
index 344be846bc..8b41cdf90c 100644
--- a/hw/rx/rx62n.c
+++ b/hw/rx/rx62n.c
@@ -28,6 +28,7 @@
 #include "hw/loader.h"
 #include "hw/sysbus.h"
 #include "hw/qdev-properties.h"
+#include "hw/net/mdio.h"
 #include "sysemu/sysemu.h"
 #include "sysemu/qtest.h"
 #include "cpu.h"
@@ -48,6 +49,8 @@
 #define RX62N_CMT_BASE  0x00088000
 #define RX62N_MTU_BASE  0x00088600
 #define RX62N_SCI_BASE  0x00088240
+#define RX62N_EDMAC_BASE 0x000c0000
+#define RX62N_ETHER_BASE 0x000c0100
 #define RX62N_CPG_BASE  0x00080010
 
 /*
@@ -58,6 +61,7 @@
 #define RX62N_CMT_IRQ   28
 #define RX62N_MTU_IRQ   114
 #define RX62N_SCI_IRQ   214
+#define RX62N_EDMAC_IRQ 32
 
 /*
  * IRQ -> IPR mapping table
@@ -236,6 +240,25 @@ static void register_sci(RX62NState *s, int unit)
                           qdev_get_clock_out(DEVICE(&s->cpg), ckname));
 }
 
+static void register_eth(RX62NState *s, NICInfo *nd)
+{
+    SysBusDevice *etherc;
+
+    qemu_check_nic_model(nd, TYPE_RENESAS_ETH);
+    object_initialize_child(OBJECT(s), "ether",
+                            &s->ether, TYPE_RENESAS_ETH);
+    etherc = SYS_BUS_DEVICE(&s->ether);
+    qdev_set_nic_properties(DEVICE(etherc), nd);
+    object_property_set_link(OBJECT(etherc), "mdio",
+                             OBJECT(s->mdio), &error_abort);
+    sysbus_realize(etherc, &error_abort);
+    sysbus_connect_irq(etherc, 0, s->irq[RX62N_EDMAC_IRQ]);
+    sysbus_mmio_map(etherc, 0, RX62N_ETHER_BASE);
+    sysbus_mmio_map(etherc, 1, RX62N_EDMAC_BASE);
+    qdev_connect_clock_in(DEVICE(etherc), "ick",
+                          qdev_get_clock_out(DEVICE(&s->cpg), "ick_edmac"));
+}
+
 static void register_cpg(RX62NState *s)
 {
     SysBusDevice *cpg;
@@ -277,6 +300,7 @@ static void rx62n_realize(DeviceState *dev, Error **errp)
     register_mtu(s, 0);
     register_mtu(s, 1);
     register_sci(s, 0);
+    register_eth(s, nd_table);
     sysbus_realize(SYS_BUS_DEVICE(&s->cpg), &error_abort);
 }
 
@@ -284,6 +308,8 @@ static Property rx62n_properties[] = {
     DEFINE_PROP_LINK("main-bus", RX62NState, sysmem, TYPE_MEMORY_REGION,
                      MemoryRegion *),
     DEFINE_PROP_UINT32("xtal-frequency-hz", RX62NState, xtal_freq_hz, 0),
+    DEFINE_PROP_LINK("mdiodev", RX62NState, mdio, TYPE_ETHER_MDIO_BB,
+                     MDIOState *),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/rx/Kconfig b/hw/rx/Kconfig
index 887a5782bb..f20ea63fd9 100644
--- a/hw/rx/Kconfig
+++ b/hw/rx/Kconfig
@@ -3,8 +3,9 @@ config RX62N_MCU
     select RX_ICU
     select RENESAS_TMR8
     select RENESAS_TIMER
-    select RENESAS_SCI
     select RENESAS_MTU
+    select RENESAS_SCI
+    select RENESAS_ETH
 
 config RX_GDBSIM
     bool
-- 
2.20.1



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

* [PATCH 18/20] hw/rx: Add Tokudenkairo TKDN-RX62N-BRD
  2020-08-27 12:38 [PATCH 00/20] RX target update Yoshinori Sato
                   ` (16 preceding siblings ...)
  2020-08-27 12:38 ` [PATCH 17/20] hw/rx/rx62n: Add Ethernet support Yoshinori Sato
@ 2020-08-27 12:38 ` Yoshinori Sato
  2020-09-08 21:18   ` Philippe Mathieu-Daudé
  2020-08-27 12:38 ` [PATCH 19/20] hw/rx: Add CQ-FRK-RX62N target Yoshinori Sato
                   ` (2 subsequent siblings)
  20 siblings, 1 reply; 40+ messages in thread
From: Yoshinori Sato @ 2020-08-27 12:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

Hardware information.
http://www.tokudenkairo.co.jp/rx62n/
(Japanese)

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 default-configs/rx-softmmu.mak |   1 +
 hw/rx/tkdn-rx62n.c             | 192 +++++++++++++++++++++++++++++++++
 hw/rx/Kconfig                  |   6 ++
 hw/rx/meson.build              |   1 +
 4 files changed, 200 insertions(+)
 create mode 100644 hw/rx/tkdn-rx62n.c

diff --git a/default-configs/rx-softmmu.mak b/default-configs/rx-softmmu.mak
index df2b4e4f42..ea8731d67b 100644
--- a/default-configs/rx-softmmu.mak
+++ b/default-configs/rx-softmmu.mak
@@ -1,3 +1,4 @@
 # Default configuration for rx-softmmu
 
 CONFIG_RX_GDBSIM=y
+CONFIG_TKDN_RX62N=y
diff --git a/hw/rx/tkdn-rx62n.c b/hw/rx/tkdn-rx62n.c
new file mode 100644
index 0000000000..3db0fc8294
--- /dev/null
+++ b/hw/rx/tkdn-rx62n.c
@@ -0,0 +1,192 @@
+/*
+ * Tokushudenshikairo TKDN-RX62N-BRD
+ *
+ * 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 "qemu/cutils.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/loader.h"
+#include "hw/rx/loader.h"
+#include "hw/qdev-properties.h"
+#include "hw/rx/rx62n.h"
+#include "net/net.h"
+#include "hw/net/mii.h"
+#include "hw/boards.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/qtest.h"
+#include "sysemu/device_tree.h"
+
+typedef struct {
+    /*< private >*/
+    MachineState parent_obj;
+    /*< public >*/
+    RX62NState mcu;
+    PHYState phy;
+    MDIOState mdio;
+} TKDN_RX62NMachineState;
+
+#define TYPE_TKDN_RX62N_MACHINE MACHINE_TYPE_NAME("tkdn-rx62n-brd")
+
+#define TKDN_RX62N_MACHINE(obj) \
+    OBJECT_CHECK(TKDN_RX62NMachineState, (obj), TYPE_TKDN_RX62N_MACHINE)
+
+#define TINYBOOT_TOP (0xffffff00)
+
+static void set_bootstrap(hwaddr entry, hwaddr dtb)
+{
+    /* 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);
+}
+
+/* Link 100BaseTX-FD */
+#define BMSR (MII_BMSR_100TX_FD | MII_BMSR_100TX_HD |                   \
+              MII_BMSR_10T_FD | MII_BMSR_10T_HD | MII_BMSR_MFPS |       \
+              MII_BMSR_AN_COMP | MII_BMSR_AUTONEG)
+#define ANLPAR (MII_ANLPAR_TXFD | MII_ANAR_CSMACD)
+
+static void tkdn_rx62n_net_init(MachineState *m, RX62NState *s,
+                                      NICInfo *nd)
+{
+    TKDN_RX62NMachineState *t = TKDN_RX62N_MACHINE(m);
+    object_initialize_child(OBJECT(t), "ether-phy",
+                            &t->phy, TYPE_ETHER_PHY);
+    qdev_prop_set_uint32(DEVICE(&t->phy), "phy-id", 0x0007c0f0); /* LAN8720A */
+    qdev_prop_set_uint32(DEVICE(&t->phy), "link-out-pol", phy_out_p);
+    qdev_prop_set_uint16(DEVICE(&t->phy), "bmsr", BMSR);
+    qdev_prop_set_uint16(DEVICE(&t->phy), "anlpar", ANLPAR);
+    qdev_realize(DEVICE(&t->phy), NULL, &error_abort);
+
+    object_initialize_child(OBJECT(t), "mdio-bb",
+                            &t->mdio, TYPE_ETHER_MDIO_BB);
+    object_property_set_link(OBJECT(&t->mdio), "phy",
+                             OBJECT(&t->phy), &error_abort);
+    qdev_prop_set_int32(DEVICE(&t->mdio), "address", 0);
+    qdev_realize(DEVICE(&t->mdio), NULL, &error_abort);
+}
+
+#define SDRAM_BASE 0x08000000
+
+static void tkdn_rx62n_init(MachineState *machine)
+{
+    MachineClass *mc = MACHINE_GET_CLASS(machine);
+    TKDN_RX62NMachineState *s = TKDN_RX62N_MACHINE(machine);
+    RX62NClass *rx62nc;
+    MemoryRegion *sysmem = get_system_memory();
+    const char *kernel_filename = machine->kernel_filename;
+    const char *dtb_filename = machine->dtb;
+    rx_kernel_info_t kernel_info;
+
+    if (machine->ram_size < mc->default_ram_size) {
+        char *sz = size_to_str(mc->default_ram_size);
+        error_report("Invalid RAM size, should be more than %s", sz);
+        g_free(sz);
+    }
+
+    /* Allocate memory space */
+    memory_region_add_subregion(sysmem, SDRAM_BASE, machine->ram);
+
+    /* Initialize MCU */
+    object_initialize_child(OBJECT(machine), "mcu",
+                            &s->mcu, TYPE_R5F562N8_MCU);
+    rx62nc = RX62N_MCU_GET_CLASS(&s->mcu);
+    object_property_set_link(OBJECT(&s->mcu), "main-bus", OBJECT(sysmem),
+                             &error_abort);
+    object_property_set_uint(OBJECT(&s->mcu), "xtal-frequency-hz",
+                             12 * 1000 * 1000, &error_abort);
+    tkdn_rx62n_net_init(machine, &s->mcu, nd_table);
+    object_property_set_link(OBJECT(&s->mcu), "mdiodev",
+                             OBJECT(&s->mdio), &error_abort);
+    /* Load kernel and dtb */
+    if (kernel_filename) {
+        kernel_info.ram_start = SDRAM_BASE;
+        kernel_info.ram_size = machine->ram_size;
+        kernel_info.filename = kernel_filename;
+        kernel_info.dtbname = dtb_filename;
+        kernel_info.cmdline = machine->kernel_cmdline;
+        if (!load_kernel(&kernel_info)) {
+            exit(1);
+        }
+        set_bootstrap(kernel_info.entry, kernel_info.dtb_address);
+    } else {
+        if (bios_name) {
+            if (!load_bios(bios_name, rx62nc->rom_flash_size, &error_abort)) {
+                exit(0);
+            }
+        } else if (!qtest_enabled()) {
+            error_report("No bios or kernel specified");
+            exit(1);
+        }
+    }
+    qdev_realize(DEVICE(&s->mcu), NULL, &error_abort);
+}
+
+static void tkdn_rx62n_class_init(ObjectClass *oc, void *data)
+{
+    MachineClass *mc = MACHINE_CLASS(oc);
+
+    mc->desc = "TokushuDenshiKairo Inc, TKDN-RX62N-BRD";
+    mc->init = tkdn_rx62n_init;
+    mc->is_default = 0;
+    mc->default_cpu_type = TYPE_RX62N_CPU;
+    mc->default_ram_size = 16 * MiB;
+    mc->default_ram_id = "ext-sdram";
+}
+
+static const TypeInfo tkdn_rx62n_type = {
+    .name = TYPE_TKDN_RX62N_MACHINE,
+    .parent = TYPE_MACHINE,
+    .instance_size  = sizeof(TKDN_RX62NMachineState),
+    .class_init = tkdn_rx62n_class_init,
+};
+
+static void tkdn_rx62n_machine_init(void)
+{
+    type_register_static(&tkdn_rx62n_type);
+}
+
+type_init(tkdn_rx62n_machine_init)
diff --git a/hw/rx/Kconfig b/hw/rx/Kconfig
index f20ea63fd9..0ef20d0c3c 100644
--- a/hw/rx/Kconfig
+++ b/hw/rx/Kconfig
@@ -11,3 +11,9 @@ config RX_GDBSIM
     bool
     select RX62N_MCU
     select FITLOADER
+
+config TKDN_RX62N
+    bool
+    select RX62N_MCU
+    select FITLOADER
+
diff --git a/hw/rx/meson.build b/hw/rx/meson.build
index 3a81d85a53..0a741e091c 100644
--- a/hw/rx/meson.build
+++ b/hw/rx/meson.build
@@ -1,6 +1,7 @@
 rx_ss = ss.source_set()
 rx_ss.add(files('loader.c'))
 rx_ss.add(when: 'CONFIG_RX_GDBSIM', if_true: files('rx-gdbsim.c'))
+rx_ss.add(when: 'CONFIG_TKDN_RX62N', if_true: files('tkdn-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] 40+ messages in thread

* [PATCH 19/20] hw/rx: Add CQ-FRK-RX62N target
  2020-08-27 12:38 [PATCH 00/20] RX target update Yoshinori Sato
                   ` (17 preceding siblings ...)
  2020-08-27 12:38 ` [PATCH 18/20] hw/rx: Add Tokudenkairo TKDN-RX62N-BRD Yoshinori Sato
@ 2020-08-27 12:38 ` Yoshinori Sato
  2020-09-08 21:20   ` Philippe Mathieu-Daudé
  2020-08-27 12:38 ` [PATCH 20/20] MAINTAINERS: Update RX entry Yoshinori Sato
  2020-08-31 20:38 ` [PATCH 00/20] RX target update Philippe Mathieu-Daudé
  20 siblings, 1 reply; 40+ messages in thread
From: Yoshinori Sato @ 2020-08-27 12:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

It most popular RX target board in Japan.

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 default-configs/rx-softmmu.mak |  1 +
 hw/rx/cq-frk-rx62n.c           | 94 ++++++++++++++++++++++++++++++++++
 hw/rx/Kconfig                  |  3 ++
 hw/rx/meson.build              |  1 +
 4 files changed, 99 insertions(+)
 create mode 100644 hw/rx/cq-frk-rx62n.c

diff --git a/default-configs/rx-softmmu.mak b/default-configs/rx-softmmu.mak
index ea8731d67b..dbbaee8809 100644
--- a/default-configs/rx-softmmu.mak
+++ b/default-configs/rx-softmmu.mak
@@ -2,3 +2,4 @@
 
 CONFIG_RX_GDBSIM=y
 CONFIG_TKDN_RX62N=y
+CONFIG_FRK_RX62N=y
diff --git a/hw/rx/cq-frk-rx62n.c b/hw/rx/cq-frk-rx62n.c
new file mode 100644
index 0000000000..a1cd9cb2ad
--- /dev/null
+++ b/hw/rx/cq-frk-rx62n.c
@@ -0,0 +1,94 @@
+/*
+ * CQ publishing CQ-FRK-RX62N
+ *
+ * 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 "qemu/cutils.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/loader.h"
+#include "hw/rx/loader.h"
+#include "hw/qdev-properties.h"
+#include "hw/rx/rx62n.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/qtest.h"
+#include "sysemu/device_tree.h"
+#include "hw/boards.h"
+
+typedef struct {
+    /*< private >*/
+    MachineState parent_obj;
+    /*< public >*/
+    RX62NState mcu;
+} FRK_RX62NMachineState;
+
+#define TYPE_FRK_RX62N_MACHINE MACHINE_TYPE_NAME("cq-frk-rx62n")
+
+#define FRK_RX62N_MACHINE(obj) \
+    OBJECT_CHECK(FRK_RX62NMachineState, (obj), TYPE_FRK_RX62N_MACHINE)
+
+static void frk_rx62n_init(MachineState *machine)
+{
+    FRK_RX62NMachineState *s = FRK_RX62N_MACHINE(machine);
+    RX62NClass *rx62nc;
+    MemoryRegion *sysmem = get_system_memory();
+
+    /* Initialize MCU */
+    object_initialize_child(OBJECT(machine), "mcu",
+                            &s->mcu, TYPE_R5F562N7_MCU);
+    rx62nc = RX62N_MCU_GET_CLASS(&s->mcu);
+    object_property_set_link(OBJECT(&s->mcu), "main-bus", OBJECT(sysmem),
+                             &error_abort);
+    object_property_set_uint(OBJECT(&s->mcu), "xtal-frequency-hz",
+                             12 * 1000 * 1000, &error_abort);
+    if (bios_name) {
+        if (!load_bios(bios_name, rx62nc->rom_flash_size, &error_abort)) {
+            exit(0);
+        }
+    } else if (!qtest_enabled()) {
+        error_report("No bios specified");
+        exit(1);
+    }
+    qdev_realize(DEVICE(&s->mcu), NULL, &error_abort);
+}
+
+static void frk_rx62n_class_init(ObjectClass *oc, void *data)
+{
+    MachineClass *mc = MACHINE_CLASS(oc);
+
+    mc->desc = "CQ publishing CQ-FRK-RX62N";
+    mc->init = frk_rx62n_init;
+    mc->is_default = 0;
+    mc->default_cpu_type = TYPE_RX62N_CPU;
+}
+
+static const TypeInfo frk_rx62n_type = {
+    .name = MACHINE_TYPE_NAME("cq-frk-rx62n"),
+    .parent = TYPE_MACHINE,
+    .instance_size  = sizeof(FRK_RX62NMachineState),
+    .class_init = frk_rx62n_class_init,
+};
+
+static void frk_rx62n_machine_init(void)
+{
+    type_register_static(&frk_rx62n_type);
+}
+
+type_init(frk_rx62n_machine_init)
diff --git a/hw/rx/Kconfig b/hw/rx/Kconfig
index 0ef20d0c3c..ab2c472510 100644
--- a/hw/rx/Kconfig
+++ b/hw/rx/Kconfig
@@ -17,3 +17,6 @@ config TKDN_RX62N
     select RX62N_MCU
     select FITLOADER
 
+config FRK_RX62N
+    bool
+    select RX62N_MCU
diff --git a/hw/rx/meson.build b/hw/rx/meson.build
index 0a741e091c..0f26f1fcb2 100644
--- a/hw/rx/meson.build
+++ b/hw/rx/meson.build
@@ -2,6 +2,7 @@ rx_ss = ss.source_set()
 rx_ss.add(files('loader.c'))
 rx_ss.add(when: 'CONFIG_RX_GDBSIM', if_true: files('rx-gdbsim.c'))
 rx_ss.add(when: 'CONFIG_TKDN_RX62N', if_true: files('tkdn-rx62n.c'))
+rx_ss.add(when: 'CONFIG_FRK_RX62N', if_true: files('cq-frk-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] 40+ messages in thread

* [PATCH 20/20] MAINTAINERS: Update RX entry
  2020-08-27 12:38 [PATCH 00/20] RX target update Yoshinori Sato
                   ` (18 preceding siblings ...)
  2020-08-27 12:38 ` [PATCH 19/20] hw/rx: Add CQ-FRK-RX62N target Yoshinori Sato
@ 2020-08-27 12:38 ` Yoshinori Sato
  2020-09-08 21:21   ` Philippe Mathieu-Daudé
  2020-08-31 20:38 ` [PATCH 00/20] RX target update Philippe Mathieu-Daudé
  20 siblings, 1 reply; 40+ messages in thread
From: Yoshinori Sato @ 2020-08-27 12:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 MAINTAINERS | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 5a22c8be42..cee8448a73 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2054,9 +2054,11 @@ F: hw/char/renesas_sci.c
 F: hw/char/sh_serial.c
 F: hw/timer/renesas_*.c
 F: hw/timer/sh_timer.c
+F: hw/net/renesas_eth.c
 F: include/hw/char/renesas_sci.h
 F: include/hw/sh4/sh.h
 F: include/hw/timer/renesas_*.h
+F: include/hw/net/renesas_eth.h
 
 Renesas RX peripherals
 M: Yoshinori Sato <ysato@users.sourceforge.jp>
-- 
2.20.1



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

* Re: [PATCH 00/20] RX target update
  2020-08-27 12:38 [PATCH 00/20] RX target update Yoshinori Sato
                   ` (19 preceding siblings ...)
  2020-08-27 12:38 ` [PATCH 20/20] MAINTAINERS: Update RX entry Yoshinori Sato
@ 2020-08-31 20:38 ` Philippe Mathieu-Daudé
  2020-09-10 16:06   ` Yoshinori Sato
  20 siblings, 1 reply; 40+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-08-31 20:38 UTC (permalink / raw)
  To: Yoshinori Sato; +Cc: qemu-devel@nongnu.org Developers

[-- Attachment #1: Type: text/plain, Size: 4226 bytes --]

Hello Yoshinori,

Le jeu. 27 août 2020 14:43, Yoshinori Sato <ysato@users.sourceforge.jp> a
écrit :

> Hello.
> This series Renesas RX updates.
>
> It consists of the following contents.
> * Update firmware loader.
> * Rewrite peripheal modules (Timer and SCI).
>   - Unified SH4 module.
>   - Using clock API
> * New peripheal modules.
>   - On-chip clock generator.
>   - Multi-function timer.
>   - Ethernet MAC.
> * New real hardware target.
>   - TokushudenshiKairo TKDN-RX62N-BRD.
>   - CQ publishing CQ-FRK-RX62N
>

How can we test them?


> Yoshinori Sato (20):
>   loader.c: Add support Motrola S-record format.
>   include/elf.h: Add EM_RX.
>   hw/rx: Firmware and kernel loader.
>   hw/rx: New firmware loader.
>   hw/rx: Add RX62N Clock generator
>   hw/timer: Renesas 8bit timer emulation.
>   hw/rx: RX62N convert new 8bit timer.
>   hw/timer: Renesas TMU/CMT module.
>   hw/timer: Remove renesas_cmt.
>   hw/rx: Convert to renesas_timer
>   hw/char: Renesas SCI module.
>   hw/rx/rx62n: Use New SCI module.
>   hw/timer: Add Renesas MTU2
>   hw/rx/rx62n: RX62N Add MTU module
>   hw/net: Add generic Bit-bang MDIO PHY.
>   hw/net: Add Renesas On-chip Ethernet MAC
>   hw/rx/rx62n: Add Ethernet support.
>   hw/rx: Add Tokudenkairo TKDN-RX62N-BRD
>   hw/rx: Add CQ-FRK-RX62N target
>   MAINTAINERS: Update RX entry
>
>  default-configs/rx-softmmu.mak   |    2 +
>  include/elf.h                    |    2 +
>  include/hw/char/renesas_sci.h    |  129 ++-
>  include/hw/loader.h              |   14 +
>  include/hw/net/mdio.h            |  126 +++
>  include/hw/net/renesas_eth.h     |   57 ++
>  include/hw/rx/loader.h           |   35 +
>  include/hw/rx/rx62n-cpg.h        |   72 ++
>  include/hw/rx/rx62n.h            |   36 +-
>  include/hw/timer/renesas_cmt.h   |   40 -
>  include/hw/timer/renesas_mtu.h   |   90 ++
>  include/hw/timer/renesas_timer.h |  103 +++
>  include/hw/timer/renesas_tmr.h   |   55 --
>  include/hw/timer/renesas_tmr8.h  |   67 ++
>  hw/char/renesas_sci.c            | 1040 ++++++++++++++++++-----
>  hw/core/loader.c                 |  208 +++++
>  hw/net/mdio.c                    |  264 ++++++
>  hw/net/renesas_eth.c             |  875 ++++++++++++++++++++
>  hw/rx/cq-frk-rx62n.c             |   94 +++
>  hw/rx/loader.c                   |  182 +++++
>  hw/rx/rx-gdbsim.c                |   98 +--
>  hw/rx/rx62n-cpg.c                |  344 ++++++++
>  hw/rx/rx62n.c                    |  140 ++--
>  hw/rx/tkdn-rx62n.c               |  192 +++++
>  hw/timer/renesas_cmt.c           |  283 -------
>  hw/timer/renesas_mtu.c           | 1312 ++++++++++++++++++++++++++++++
>  hw/timer/renesas_timer.c         |  639 +++++++++++++++
>  hw/timer/renesas_tmr.c           |  477 -----------
>  hw/timer/renesas_tmr8.c          |  540 ++++++++++++
>  MAINTAINERS                      |    2 +
>  hw/net/Kconfig                   |    8 +
>  hw/net/meson.build               |    3 +
>  hw/rx/Kconfig                    |   16 +-
>  hw/rx/meson.build                |    5 +-
>  hw/timer/Kconfig                 |    9 +-
>  hw/timer/meson.build             |    5 +-
>  36 files changed, 6391 insertions(+), 1173 deletions(-)
>  create mode 100644 include/hw/net/mdio.h
>  create mode 100644 include/hw/net/renesas_eth.h
>  create mode 100644 include/hw/rx/loader.h
>  create mode 100644 include/hw/rx/rx62n-cpg.h
>  delete mode 100644 include/hw/timer/renesas_cmt.h
>  create mode 100644 include/hw/timer/renesas_mtu.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
>  create mode 100644 hw/net/mdio.c
>  create mode 100644 hw/net/renesas_eth.c
>  create mode 100644 hw/rx/cq-frk-rx62n.c
>  create mode 100644 hw/rx/loader.c
>  create mode 100644 hw/rx/rx62n-cpg.c
>  create mode 100644 hw/rx/tkdn-rx62n.c
>  delete mode 100644 hw/timer/renesas_cmt.c
>  create mode 100644 hw/timer/renesas_mtu.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
>
> --
> 2.20.1
>
>
>

[-- Attachment #2: Type: text/html, Size: 5328 bytes --]

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

* Re: [PATCH 01/20] loader.c: Add support Motrola S-record format.
  2020-08-27 12:38 ` [PATCH 01/20] loader.c: Add support Motrola S-record format Yoshinori Sato
@ 2020-09-08 20:44   ` Philippe Mathieu-Daudé
  2020-10-25  0:43   ` Philippe Mathieu-Daudé
  2020-10-27 21:05   ` Alistair Francis
  2 siblings, 0 replies; 40+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-08 20:44 UTC (permalink / raw)
  To: Yoshinori Sato, qemu-devel, Thomas Huth

Hi Yoshinori,

On 8/27/20 2:38 PM, Yoshinori Sato wrote:
> Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
> ---
>  include/hw/loader.h |  14 +++
>  hw/core/loader.c    | 208 ++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 222 insertions(+)
> 
> diff --git a/include/hw/loader.h b/include/hw/loader.h
> index a9eeea3952..6f1fb62ded 100644
> --- a/include/hw/loader.h
> +++ b/include/hw/loader.h
> @@ -55,6 +55,20 @@ int load_image_targphys_as(const char *filename,
>   */
>  int load_targphys_hex_as(const char *filename, hwaddr *entry, AddressSpace *as);
>  
> +/*
> + * load_targphys_srec_as:
> + * @filename: Path to the .hex file
> + * @entry: Store the entry point given by the .hex file
> + * @as: The AddressSpace to load the .hex file to. The value of
> + *      address_space_memory is used if nothing is supplied here.
> + *
> + * Load a fixed .srec file into memory.
> + *
> + * Returns the size of the loaded .hex file on success, -1 otherwise.
> + */
> +int load_targphys_srec_as(const char *filename,
> +                          hwaddr *entry, AddressSpace *as);

Can you add a qtest for this format please?

See tests/qtest/hexloader-test.c added in commit 645d3cbebb1:
("Add QTest testcase for the Intel Hexadecimal"), it should
be pretty trivial.

Thanks,

Phil.


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

* Re: [PATCH 03/20] hw/rx: Firmware and kernel loader.
  2020-08-27 12:38 ` [PATCH 03/20] hw/rx: Firmware and kernel loader Yoshinori Sato
@ 2020-09-08 20:47   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 40+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-08 20:47 UTC (permalink / raw)
  To: Yoshinori Sato, qemu-devel

On 8/27/20 2:38 PM, Yoshinori Sato wrote:
> Suppoerted format.

Typo "Supported".

(Also, QEMU commits avoid to have a trailing dot '.'
 in their subject).

> ELF, HEX, SREC and Raw firmware.
> fit and Raw kernel image.
> 
> Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
> ---
>  include/hw/rx/loader.h |  35 ++++++++
>  hw/rx/loader.c         | 182 +++++++++++++++++++++++++++++++++++++++++
>  hw/rx/Kconfig          |   1 +
>  hw/rx/meson.build      |   1 +
>  4 files changed, 219 insertions(+)
>  create mode 100644 include/hw/rx/loader.h
>  create mode 100644 hw/rx/loader.c
> 
> diff --git a/include/hw/rx/loader.h b/include/hw/rx/loader.h
> new file mode 100644
> index 0000000000..71f3bd2bb3
> --- /dev/null
> +++ b/include/hw/rx/loader.h
> @@ -0,0 +1,35 @@
> +/*
> + * RX QEMU frimware loader
> + *
> + * 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 "qapi/error.h"
> +#include "qemu/error-report.h"
> +
> +typedef struct {
> +    hwaddr ram_start;
> +    size_t ram_size;
> +    hwaddr entry;
> +    hwaddr kernel_entry;
> +    hwaddr dtb_address;
> +    const char *filename;
> +    const char *dtbname;
> +    const char *cmdline;
> +} rx_kernel_info_t;
> +
> +bool load_bios(const char *filename, int rom_size, Error **errp);
> +
> +bool load_kernel(rx_kernel_info_t *info);
> diff --git a/hw/rx/loader.c b/hw/rx/loader.c
> new file mode 100644
> index 0000000000..c262f3ef86
> --- /dev/null
> +++ b/hw/rx/loader.c
> @@ -0,0 +1,182 @@
> +/*
> + * RX QEMU frimware loader

Typo "firmware".

[...]


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

* Re: [PATCH 05/20] hw/rx: Add RX62N Clock generator
  2020-08-27 12:38 ` [PATCH 05/20] hw/rx: Add RX62N Clock generator Yoshinori Sato
@ 2020-09-08 21:11   ` Philippe Mathieu-Daudé
  2020-10-24 21:56   ` Philippe Mathieu-Daudé
  1 sibling, 0 replies; 40+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-08 21:11 UTC (permalink / raw)
  To: Yoshinori Sato, qemu-devel

On 8/27/20 2:38 PM, Yoshinori Sato wrote:
> 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     |   5 +-
>  hw/rx/rx62n-cpg.c         | 344 ++++++++++++++++++++++++++++++++++++++
>  hw/rx/rx62n.c             |  52 +++---
>  hw/rx/meson.build         |   2 +-
>  5 files changed, 447 insertions(+), 28 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

Please move the key to rx62n-cpg.c.

> +
> +#endif
> diff --git a/include/hw/rx/rx62n.h b/include/hw/rx/rx62n.h
> index 32e460bbad..e0ca1cfc33 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"
>  
>  #define TYPE_RX62N_MCU "rx62n-mcu"
> @@ -70,9 +71,9 @@ typedef 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;
> @@ -84,8 +85,6 @@ typedef 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, },
> +    { },
> +};

Could simplify to remove offset field, and index by offset:

static const dev_clock_t dev_clock_list[32] = {
 [ 4] = { .name = "pck_tmr8-1", .devnum = CK_TMR8_1,
          .reg = 0, .offset = 4, .parentck = parent_pck, },
 ...

 [31] = { .name = "pck_sci-0", .devnum = CK_SCI0,
          .reg = 1, .parentck = parent_pck, }
};

And replace the while (p->name) by
for (... ARRAY_SIZE(dev_clock_list) ...).

> +
> +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;                              \
> +    }

Can you replace by a update_ck(clk, const char *name) function?
(validate_setting too).

> +
> +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 4b5c3c1079..ec63fa5db1 100644
> --- a/hw/rx/rx62n.c
> +++ b/hw/rx/rx62n.c
> @@ -47,6 +47,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,10 +57,6 @@
>  #define RX62N_CMT_IRQ   28
>  #define RX62N_SCI_IRQ   214
>  
> -#define RX62N_XTAL_MIN_HZ  (8 * 1000 * 1000)
> -#define RX62N_XTAL_MAX_HZ (14 * 1000 * 1000)
> -#define RX62N_PCLK_MAX_HZ (50 * 1000 * 1000)
> -
>  /*
>   * IRQ -> IPR mapping table
>   * 0x00 - 0x91: IPR no (IPR00 to IPR91)
> @@ -149,36 +146,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);

Please move here:

> +    qdev_prop_set_uint32(DEVICE(tmr), "unit", unit);
> +    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);

Note, you need to call sysbus_realize() *after* qdev_connect_clock_in().

>  
>      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));

Ditto move.

>  }
>  
>  static void register_sci(RX62NState *s, int unit)
> @@ -190,7 +196,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;
> @@ -200,26 +205,23 @@ 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);

Eventually you can alias the property from the CPG on the SOC
using object_property_add_alias(), and remove RX62NState::xtal_freq_hz.

> +
> +    sysbus_mmio_map(cpg, 0, RX62N_CPG_BASE);
> +}
> +
>  static void rx62n_realize(DeviceState *dev, Error **errp)
>  {
>      RX62NState *s = RX62N_MCU(dev);
>      RX62NClass *rxc = RX62N_MCU_GET_CLASS(dev);
>  
> -    if (s->xtal_freq_hz == 0) {
> -        error_setg(errp, "\"xtal-frequency-hz\" property must be provided.");
> -        return;
> -    }
> -    /* XTAL range: 8-14 MHz */
> -    if (s->xtal_freq_hz < RX62N_XTAL_MIN_HZ
> -            || s->xtal_freq_hz > RX62N_XTAL_MAX_HZ) {
> -        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);
> @@ -236,11 +238,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 e73850f303..3a81d85a53 100644
> --- a/hw/rx/meson.build
> +++ b/hw/rx/meson.build
> @@ -1,6 +1,6 @@
>  rx_ss = ss.source_set()
>  rx_ss.add(files('loader.c'))
>  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}
> 

Very good patch :)


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

* Re: [PATCH 14/20] hw/rx/rx62n: RX62N Add MTU module
  2020-08-27 12:38 ` [PATCH 14/20] hw/rx/rx62n: RX62N Add MTU module Yoshinori Sato
@ 2020-09-08 21:12   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 40+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-08 21:12 UTC (permalink / raw)
  To: Yoshinori Sato, qemu-devel

On 8/27/20 2:38 PM, Yoshinori Sato wrote:
> Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
> ---
>  include/hw/rx/rx62n.h |  3 +++
>  hw/rx/rx62n.c         | 28 ++++++++++++++++++++++++++++
>  hw/rx/Kconfig         |  1 +
>  3 files changed, 32 insertions(+)
> 
> diff --git a/include/hw/rx/rx62n.h b/include/hw/rx/rx62n.h
> index f463148799..170c8cb6fc 100644
> --- a/include/hw/rx/rx62n.h
> +++ b/include/hw/rx/rx62n.h
> @@ -28,6 +28,7 @@
>  #include "hw/intc/rx_icu.h"
>  #include "hw/timer/renesas_tmr8.h"
>  #include "hw/timer/renesas_timer.h"
> +#include "hw/timer/renesas_mtu.h"
>  #include "hw/char/renesas_sci.h"
>  #include "hw/rx/rx62n-cpg.h"
>  #include "qemu/units.h"
> @@ -45,6 +46,7 @@
>  #define RX62N_NR_TMR    2
>  #define RX62N_NR_CMT    2
>  #define RX62N_NR_SCI    6
> +#define RX62N_NR_MTU    2
>  
>  typedef struct RX62NClass {
>      /*< private >*/
> @@ -70,6 +72,7 @@ typedef struct RX62NState {
>      RXICUState icu;
>      RenesasTMR8State tmr[RX62N_NR_TMR];
>      RenesasCMTState cmt[RX62N_NR_CMT];
> +    RenesasMTU2State mtu[RX62N_NR_MTU];
>      RSCIAState sci[RX62N_NR_SCI];
>      RX62NCPGState cpg;
>  
> diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c
> index f61383a4c2..344be846bc 100644
> --- a/hw/rx/rx62n.c
> +++ b/hw/rx/rx62n.c
> @@ -46,6 +46,7 @@
>  #define RX62N_ICU_BASE  0x00087000
>  #define RX62N_TMR_BASE  0x00088200
>  #define RX62N_CMT_BASE  0x00088000
> +#define RX62N_MTU_BASE  0x00088600
>  #define RX62N_SCI_BASE  0x00088240
>  #define RX62N_CPG_BASE  0x00080010
>  
> @@ -55,6 +56,7 @@
>   */
>  #define RX62N_TMR_IRQ   174
>  #define RX62N_CMT_IRQ   28
> +#define RX62N_MTU_IRQ   114
>  #define RX62N_SCI_IRQ   214
>  
>  /*
> @@ -187,6 +189,30 @@ static void register_cmt(RX62NState *s, int unit)
>                            qdev_get_clock_out(DEVICE(&s->cpg), ckname));
>  }
>  
> +static void register_mtu(RX62NState *s, int unit)
> +{
> +    SysBusDevice *mtu;
> +    int i, irqbase;
> +    char ckname[16];
> +
> +    object_initialize_child(OBJECT(s), "mtu[*]", &s->mtu[unit],
> +                            TYPE_RENESAS_MTU2);
> +    mtu = SYS_BUS_DEVICE(&s->mtu[unit]);
> +    qdev_prop_set_uint32(DEVICE(mtu), "unit", unit);
> +
> +    sysbus_mmio_map(mtu, 0, RX62N_MTU_BASE + 0x100 + unit * 0x400);
> +    sysbus_mmio_map(mtu, 1, RX62N_MTU_BASE + unit * 0x400);
> +    sysbus_mmio_map(mtu, 2, RX62N_MTU_BASE + 0x280 + unit * 0x400);
> +    irqbase = RX62N_MTU_IRQ + MTU_NR_IRQ * unit;
> +    for (i = 0; i < MTU_NR_IRQ; i++) {
> +        sysbus_connect_irq(mtu, i, s->irq[irqbase + i]);
> +    }
> +    sysbus_realize(mtu, &error_abort);
> +    snprintf(ckname, sizeof(ckname), "pck_mtu-%d", unit);
> +    qdev_connect_clock_in(DEVICE(mtu), "pck",
> +                          qdev_get_clock_out(DEVICE(&s->cpg), ckname));

Moving sysbus_realize() after qdev_connect_clock_in():
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

> +}
> +
>  static void register_sci(RX62NState *s, int unit)
>  {
>      SysBusDevice *sci;
> @@ -248,6 +274,8 @@ static void rx62n_realize(DeviceState *dev, Error **errp)
>      register_tmr(s, 1);
>      register_cmt(s, 0);
>      register_cmt(s, 1);
> +    register_mtu(s, 0);
> +    register_mtu(s, 1);
>      register_sci(s, 0);
>      sysbus_realize(SYS_BUS_DEVICE(&s->cpg), &error_abort);
>  }
> diff --git a/hw/rx/Kconfig b/hw/rx/Kconfig
> index d1812870ea..887a5782bb 100644
> --- a/hw/rx/Kconfig
> +++ b/hw/rx/Kconfig
> @@ -4,6 +4,7 @@ config RX62N_MCU
>      select RENESAS_TMR8
>      select RENESAS_TIMER
>      select RENESAS_SCI
> +    select RENESAS_MTU
>  
>  config RX_GDBSIM
>      bool
> 



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

* Re: [PATCH 18/20] hw/rx: Add Tokudenkairo TKDN-RX62N-BRD
  2020-08-27 12:38 ` [PATCH 18/20] hw/rx: Add Tokudenkairo TKDN-RX62N-BRD Yoshinori Sato
@ 2020-09-08 21:18   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 40+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-08 21:18 UTC (permalink / raw)
  To: Yoshinori Sato, qemu-devel

On 8/27/20 2:38 PM, Yoshinori Sato wrote:
> Hardware information.
> http://www.tokudenkairo.co.jp/rx62n/
> (Japanese)
> 
> Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
> ---
>  default-configs/rx-softmmu.mak |   1 +
>  hw/rx/tkdn-rx62n.c             | 192 +++++++++++++++++++++++++++++++++
>  hw/rx/Kconfig                  |   6 ++
>  hw/rx/meson.build              |   1 +
>  4 files changed, 200 insertions(+)
>  create mode 100644 hw/rx/tkdn-rx62n.c
> 
> diff --git a/default-configs/rx-softmmu.mak b/default-configs/rx-softmmu.mak
> index df2b4e4f42..ea8731d67b 100644
> --- a/default-configs/rx-softmmu.mak
> +++ b/default-configs/rx-softmmu.mak
> @@ -1,3 +1,4 @@
>  # Default configuration for rx-softmmu
>  
>  CONFIG_RX_GDBSIM=y
> +CONFIG_TKDN_RX62N=y
> diff --git a/hw/rx/tkdn-rx62n.c b/hw/rx/tkdn-rx62n.c
> new file mode 100644
> index 0000000000..3db0fc8294
> --- /dev/null
> +++ b/hw/rx/tkdn-rx62n.c
> @@ -0,0 +1,192 @@
> +/*
> + * Tokushudenshikairo TKDN-RX62N-BRD
> + *
> + * 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 "qemu/cutils.h"
> +#include "qapi/error.h"
> +#include "qemu-common.h"
> +#include "cpu.h"
> +#include "hw/hw.h"
> +#include "hw/sysbus.h"
> +#include "hw/loader.h"
> +#include "hw/rx/loader.h"
> +#include "hw/qdev-properties.h"
> +#include "hw/rx/rx62n.h"
> +#include "net/net.h"
> +#include "hw/net/mii.h"
> +#include "hw/boards.h"
> +#include "sysemu/sysemu.h"
> +#include "sysemu/qtest.h"
> +#include "sysemu/device_tree.h"
> +
> +typedef struct {
> +    /*< private >*/
> +    MachineState parent_obj;
> +    /*< public >*/
> +    RX62NState mcu;
> +    PHYState phy;
> +    MDIOState mdio;
> +} TKDN_RX62NMachineState;
> +
> +#define TYPE_TKDN_RX62N_MACHINE MACHINE_TYPE_NAME("tkdn-rx62n-brd")
> +
> +#define TKDN_RX62N_MACHINE(obj) \
> +    OBJECT_CHECK(TKDN_RX62NMachineState, (obj), TYPE_TKDN_RX62N_MACHINE)
> +
> +#define TINYBOOT_TOP (0xffffff00)
> +
> +static void set_bootstrap(hwaddr entry, hwaddr dtb)
> +{
> +    /* 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);

Please use the ld/st API (docs/devel/loads-stores.rst) instead:

       stl_le_p(&tinyboot[0x70], 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);
> +}
> +
> +/* Link 100BaseTX-FD */
> +#define BMSR (MII_BMSR_100TX_FD | MII_BMSR_100TX_HD |                   \
> +              MII_BMSR_10T_FD | MII_BMSR_10T_HD | MII_BMSR_MFPS |       \
> +              MII_BMSR_AN_COMP | MII_BMSR_AUTONEG)
> +#define ANLPAR (MII_ANLPAR_TXFD | MII_ANAR_CSMACD)
> +
> +static void tkdn_rx62n_net_init(MachineState *m, RX62NState *s,
> +                                      NICInfo *nd)
> +{
> +    TKDN_RX62NMachineState *t = TKDN_RX62N_MACHINE(m);
> +    object_initialize_child(OBJECT(t), "ether-phy",
> +                            &t->phy, TYPE_ETHER_PHY);
> +    qdev_prop_set_uint32(DEVICE(&t->phy), "phy-id", 0x0007c0f0); /* LAN8720A */
> +    qdev_prop_set_uint32(DEVICE(&t->phy), "link-out-pol", phy_out_p);
> +    qdev_prop_set_uint16(DEVICE(&t->phy), "bmsr", BMSR);
> +    qdev_prop_set_uint16(DEVICE(&t->phy), "anlpar", ANLPAR);
> +    qdev_realize(DEVICE(&t->phy), NULL, &error_abort);
> +
> +    object_initialize_child(OBJECT(t), "mdio-bb",
> +                            &t->mdio, TYPE_ETHER_MDIO_BB);
> +    object_property_set_link(OBJECT(&t->mdio), "phy",
> +                             OBJECT(&t->phy), &error_abort);
> +    qdev_prop_set_int32(DEVICE(&t->mdio), "address", 0);
> +    qdev_realize(DEVICE(&t->mdio), NULL, &error_abort);
> +}
> +
> +#define SDRAM_BASE 0x08000000
> +
> +static void tkdn_rx62n_init(MachineState *machine)
> +{
> +    MachineClass *mc = MACHINE_GET_CLASS(machine);
> +    TKDN_RX62NMachineState *s = TKDN_RX62N_MACHINE(machine);
> +    RX62NClass *rx62nc;
> +    MemoryRegion *sysmem = get_system_memory();
> +    const char *kernel_filename = machine->kernel_filename;
> +    const char *dtb_filename = machine->dtb;
> +    rx_kernel_info_t kernel_info;
> +
> +    if (machine->ram_size < mc->default_ram_size) {
> +        char *sz = size_to_str(mc->default_ram_size);
> +        error_report("Invalid RAM size, should be more than %s", sz);
> +        g_free(sz);

Missing exit()?

Otherwise patch is good.

> +    }
> +
> +    /* Allocate memory space */
> +    memory_region_add_subregion(sysmem, SDRAM_BASE, machine->ram);
> +
> +    /* Initialize MCU */
> +    object_initialize_child(OBJECT(machine), "mcu",
> +                            &s->mcu, TYPE_R5F562N8_MCU);
> +    rx62nc = RX62N_MCU_GET_CLASS(&s->mcu);
> +    object_property_set_link(OBJECT(&s->mcu), "main-bus", OBJECT(sysmem),
> +                             &error_abort);
> +    object_property_set_uint(OBJECT(&s->mcu), "xtal-frequency-hz",
> +                             12 * 1000 * 1000, &error_abort);
> +    tkdn_rx62n_net_init(machine, &s->mcu, nd_table);
> +    object_property_set_link(OBJECT(&s->mcu), "mdiodev",
> +                             OBJECT(&s->mdio), &error_abort);
> +    /* Load kernel and dtb */
> +    if (kernel_filename) {
> +        kernel_info.ram_start = SDRAM_BASE;
> +        kernel_info.ram_size = machine->ram_size;
> +        kernel_info.filename = kernel_filename;
> +        kernel_info.dtbname = dtb_filename;
> +        kernel_info.cmdline = machine->kernel_cmdline;
> +        if (!load_kernel(&kernel_info)) {
> +            exit(1);
> +        }
> +        set_bootstrap(kernel_info.entry, kernel_info.dtb_address);
> +    } else {
> +        if (bios_name) {
> +            if (!load_bios(bios_name, rx62nc->rom_flash_size, &error_abort)) {
> +                exit(0);
> +            }
> +        } else if (!qtest_enabled()) {
> +            error_report("No bios or kernel specified");
> +            exit(1);
> +        }
> +    }
> +    qdev_realize(DEVICE(&s->mcu), NULL, &error_abort);
> +}
> +
> +static void tkdn_rx62n_class_init(ObjectClass *oc, void *data)
> +{
> +    MachineClass *mc = MACHINE_CLASS(oc);
> +
> +    mc->desc = "TokushuDenshiKairo Inc, TKDN-RX62N-BRD";
> +    mc->init = tkdn_rx62n_init;
> +    mc->is_default = 0;
> +    mc->default_cpu_type = TYPE_RX62N_CPU;
> +    mc->default_ram_size = 16 * MiB;
> +    mc->default_ram_id = "ext-sdram";
> +}
> +
> +static const TypeInfo tkdn_rx62n_type = {
> +    .name = TYPE_TKDN_RX62N_MACHINE,
> +    .parent = TYPE_MACHINE,
> +    .instance_size  = sizeof(TKDN_RX62NMachineState),
> +    .class_init = tkdn_rx62n_class_init,
> +};
> +
> +static void tkdn_rx62n_machine_init(void)
> +{
> +    type_register_static(&tkdn_rx62n_type);
> +}
> +
> +type_init(tkdn_rx62n_machine_init)
> diff --git a/hw/rx/Kconfig b/hw/rx/Kconfig
> index f20ea63fd9..0ef20d0c3c 100644
> --- a/hw/rx/Kconfig
> +++ b/hw/rx/Kconfig
> @@ -11,3 +11,9 @@ config RX_GDBSIM
>      bool
>      select RX62N_MCU
>      select FITLOADER
> +
> +config TKDN_RX62N
> +    bool
> +    select RX62N_MCU
> +    select FITLOADER
> +
> diff --git a/hw/rx/meson.build b/hw/rx/meson.build
> index 3a81d85a53..0a741e091c 100644
> --- a/hw/rx/meson.build
> +++ b/hw/rx/meson.build
> @@ -1,6 +1,7 @@
>  rx_ss = ss.source_set()
>  rx_ss.add(files('loader.c'))
>  rx_ss.add(when: 'CONFIG_RX_GDBSIM', if_true: files('rx-gdbsim.c'))
> +rx_ss.add(when: 'CONFIG_TKDN_RX62N', if_true: files('tkdn-rx62n.c'))
>  rx_ss.add(when: 'CONFIG_RX62N_MCU', if_true: files('rx62n.c', 'rx62n-cpg.c'))
>  
>  hw_arch += {'rx': rx_ss}
> 



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

* Re: [PATCH 19/20] hw/rx: Add CQ-FRK-RX62N target
  2020-08-27 12:38 ` [PATCH 19/20] hw/rx: Add CQ-FRK-RX62N target Yoshinori Sato
@ 2020-09-08 21:20   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 40+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-08 21:20 UTC (permalink / raw)
  To: Yoshinori Sato, qemu-devel

On 8/27/20 2:38 PM, Yoshinori Sato wrote:
> It most popular RX target board in Japan.
> 
> Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
> ---
>  default-configs/rx-softmmu.mak |  1 +
>  hw/rx/cq-frk-rx62n.c           | 94 ++++++++++++++++++++++++++++++++++
>  hw/rx/Kconfig                  |  3 ++
>  hw/rx/meson.build              |  1 +
>  4 files changed, 99 insertions(+)
>  create mode 100644 hw/rx/cq-frk-rx62n.c
> 
> diff --git a/default-configs/rx-softmmu.mak b/default-configs/rx-softmmu.mak
> index ea8731d67b..dbbaee8809 100644
> --- a/default-configs/rx-softmmu.mak
> +++ b/default-configs/rx-softmmu.mak
> @@ -2,3 +2,4 @@
>  
>  CONFIG_RX_GDBSIM=y
>  CONFIG_TKDN_RX62N=y
> +CONFIG_FRK_RX62N=y
> diff --git a/hw/rx/cq-frk-rx62n.c b/hw/rx/cq-frk-rx62n.c
> new file mode 100644
> index 0000000000..a1cd9cb2ad
> --- /dev/null
> +++ b/hw/rx/cq-frk-rx62n.c
> @@ -0,0 +1,94 @@
> +/*
> + * CQ publishing CQ-FRK-RX62N
> + *
> + * 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 "qemu/cutils.h"
> +#include "qapi/error.h"
> +#include "qemu-common.h"
> +#include "cpu.h"
> +#include "hw/hw.h"
> +#include "hw/sysbus.h"
> +#include "hw/loader.h"
> +#include "hw/rx/loader.h"
> +#include "hw/qdev-properties.h"
> +#include "hw/rx/rx62n.h"
> +#include "sysemu/sysemu.h"
> +#include "sysemu/qtest.h"
> +#include "sysemu/device_tree.h"
> +#include "hw/boards.h"
> +
> +typedef struct {
> +    /*< private >*/
> +    MachineState parent_obj;
> +    /*< public >*/
> +    RX62NState mcu;
> +} FRK_RX62NMachineState;

CODING_STYLE.rst suggests CamelCase: FrkRx62nMachineState.

Otherwise patch is good.

> +
> +#define TYPE_FRK_RX62N_MACHINE MACHINE_TYPE_NAME("cq-frk-rx62n")
> +
> +#define FRK_RX62N_MACHINE(obj) \
> +    OBJECT_CHECK(FRK_RX62NMachineState, (obj), TYPE_FRK_RX62N_MACHINE)
> +
> +static void frk_rx62n_init(MachineState *machine)
> +{
> +    FRK_RX62NMachineState *s = FRK_RX62N_MACHINE(machine);
> +    RX62NClass *rx62nc;
> +    MemoryRegion *sysmem = get_system_memory();
> +
> +    /* Initialize MCU */
> +    object_initialize_child(OBJECT(machine), "mcu",
> +                            &s->mcu, TYPE_R5F562N7_MCU);
> +    rx62nc = RX62N_MCU_GET_CLASS(&s->mcu);
> +    object_property_set_link(OBJECT(&s->mcu), "main-bus", OBJECT(sysmem),
> +                             &error_abort);
> +    object_property_set_uint(OBJECT(&s->mcu), "xtal-frequency-hz",
> +                             12 * 1000 * 1000, &error_abort);
> +    if (bios_name) {
> +        if (!load_bios(bios_name, rx62nc->rom_flash_size, &error_abort)) {
> +            exit(0);
> +        }
> +    } else if (!qtest_enabled()) {
> +        error_report("No bios specified");
> +        exit(1);
> +    }
> +    qdev_realize(DEVICE(&s->mcu), NULL, &error_abort);
> +}
> +
> +static void frk_rx62n_class_init(ObjectClass *oc, void *data)
> +{
> +    MachineClass *mc = MACHINE_CLASS(oc);
> +
> +    mc->desc = "CQ publishing CQ-FRK-RX62N";
> +    mc->init = frk_rx62n_init;
> +    mc->is_default = 0;
> +    mc->default_cpu_type = TYPE_RX62N_CPU;
> +}
> +
> +static const TypeInfo frk_rx62n_type = {
> +    .name = MACHINE_TYPE_NAME("cq-frk-rx62n"),
> +    .parent = TYPE_MACHINE,
> +    .instance_size  = sizeof(FRK_RX62NMachineState),
> +    .class_init = frk_rx62n_class_init,
> +};
> +
> +static void frk_rx62n_machine_init(void)
> +{
> +    type_register_static(&frk_rx62n_type);
> +}
> +
> +type_init(frk_rx62n_machine_init)
> diff --git a/hw/rx/Kconfig b/hw/rx/Kconfig
> index 0ef20d0c3c..ab2c472510 100644
> --- a/hw/rx/Kconfig
> +++ b/hw/rx/Kconfig
> @@ -17,3 +17,6 @@ config TKDN_RX62N
>      select RX62N_MCU
>      select FITLOADER
>  
> +config FRK_RX62N
> +    bool
> +    select RX62N_MCU
> diff --git a/hw/rx/meson.build b/hw/rx/meson.build
> index 0a741e091c..0f26f1fcb2 100644
> --- a/hw/rx/meson.build
> +++ b/hw/rx/meson.build
> @@ -2,6 +2,7 @@ rx_ss = ss.source_set()
>  rx_ss.add(files('loader.c'))
>  rx_ss.add(when: 'CONFIG_RX_GDBSIM', if_true: files('rx-gdbsim.c'))
>  rx_ss.add(when: 'CONFIG_TKDN_RX62N', if_true: files('tkdn-rx62n.c'))
> +rx_ss.add(when: 'CONFIG_FRK_RX62N', if_true: files('cq-frk-rx62n.c'))
>  rx_ss.add(when: 'CONFIG_RX62N_MCU', if_true: files('rx62n.c', 'rx62n-cpg.c'))
>  
>  hw_arch += {'rx': rx_ss}
> 



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

* Re: [PATCH 20/20] MAINTAINERS: Update RX entry
  2020-08-27 12:38 ` [PATCH 20/20] MAINTAINERS: Update RX entry Yoshinori Sato
@ 2020-09-08 21:21   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 40+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-08 21:21 UTC (permalink / raw)
  To: Yoshinori Sato, qemu-devel

On 8/27/20 2:38 PM, Yoshinori Sato wrote:
> Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
> ---
>  MAINTAINERS | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 5a22c8be42..cee8448a73 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -2054,9 +2054,11 @@ F: hw/char/renesas_sci.c
>  F: hw/char/sh_serial.c
>  F: hw/timer/renesas_*.c
>  F: hw/timer/sh_timer.c
> +F: hw/net/renesas_eth.c
>  F: include/hw/char/renesas_sci.h
>  F: include/hw/sh4/sh.h
>  F: include/hw/timer/renesas_*.h
> +F: include/hw/net/renesas_eth.h
>  
>  Renesas RX peripherals
>  M: Yoshinori Sato <ysato@users.sourceforge.jp>
> 

Please squash to patch 16 "Add Renesas On-chip Ethernet MAC".



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

* Re: [PATCH 00/20] RX target update
  2020-08-31 20:38 ` [PATCH 00/20] RX target update Philippe Mathieu-Daudé
@ 2020-09-10 16:06   ` Yoshinori Sato
  0 siblings, 0 replies; 40+ messages in thread
From: Yoshinori Sato @ 2020-09-10 16:06 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé; +Cc: qemu-devel@nongnu.org Developers

On Tue, 01 Sep 2020 05:38:37 +0900,
Philippe Mathieu-Daudé wrote:
> 
> [1  <text/plain; UTF-8 (quoted-printable)>]
> [2  <text/html; UTF-8 (quoted-printable)>]
> Hello Yoshinori,
> 
> Le jeu. 27 août 2020 14:43, Yoshinori Sato <ysato@users.sourceforge.jp> a
> écrit :
> 
>     Hello.
>     This series Renesas RX updates.
>    
>     It consists of the following contents.
>     * Update firmware loader.
>     * Rewrite peripheal modules (Timer and SCI).
>       - Unified SH4 module.
>       - Using clock API
>     * New peripheal modules.
>       - On-chip clock generator.
>       - Multi-function timer.
>       - Ethernet MAC.
>     * New real hardware target.
>       - TokushudenshiKairo TKDN-RX62N-BRD.
>       - CQ publishing CQ-FRK-RX62N
> 
> How can we test them? 

Sorry too late reply.
TKDN-RX62N-BRD can work u-boot and linux.
The configuration is different from gdbsim.
I'll upload what I'm using for testing later.

CQ-FRK-RX62N can work micropython.
I will upload this later as well.

>     Yoshinori Sato (20):
>       loader.c: Add support Motrola S-record format.
>       include/elf.h: Add EM_RX.
>       hw/rx: Firmware and kernel loader.
>       hw/rx: New firmware loader.
>       hw/rx: Add RX62N Clock generator
>       hw/timer: Renesas 8bit timer emulation.
>       hw/rx: RX62N convert new 8bit timer.
>       hw/timer: Renesas TMU/CMT module.
>       hw/timer: Remove renesas_cmt.
>       hw/rx: Convert to renesas_timer
>       hw/char: Renesas SCI module.
>       hw/rx/rx62n: Use New SCI module.
>       hw/timer: Add Renesas MTU2
>       hw/rx/rx62n: RX62N Add MTU module
>       hw/net: Add generic Bit-bang MDIO PHY.
>       hw/net: Add Renesas On-chip Ethernet MAC
>       hw/rx/rx62n: Add Ethernet support.
>       hw/rx: Add Tokudenkairo TKDN-RX62N-BRD
>       hw/rx: Add CQ-FRK-RX62N target
>       MAINTAINERS: Update RX entry
>    
>      default-configs/rx-softmmu.mak   |    2 +
>      include/elf.h                    |    2 +
>      include/hw/char/renesas_sci.h    |  129 ++-
>      include/hw/loader.h              |   14 +
>      include/hw/net/mdio.h            |  126 +++
>      include/hw/net/renesas_eth.h     |   57 ++
>      include/hw/rx/loader.h           |   35 +
>      include/hw/rx/rx62n-cpg.h        |   72 ++
>      include/hw/rx/rx62n.h            |   36 +-
>      include/hw/timer/renesas_cmt.h   |   40 -
>      include/hw/timer/renesas_mtu.h   |   90 ++
>      include/hw/timer/renesas_timer.h |  103 +++
>      include/hw/timer/renesas_tmr.h   |   55 --
>      include/hw/timer/renesas_tmr8.h  |   67 ++
>      hw/char/renesas_sci.c            | 1040 ++++++++++++++++++-----
>      hw/core/loader.c                 |  208 +++++
>      hw/net/mdio.c                    |  264 ++++++
>      hw/net/renesas_eth.c             |  875 ++++++++++++++++++++
>      hw/rx/cq-frk-rx62n.c             |   94 +++
>      hw/rx/loader.c                   |  182 +++++
>      hw/rx/rx-gdbsim.c                |   98 +--
>      hw/rx/rx62n-cpg.c                |  344 ++++++++
>      hw/rx/rx62n.c                    |  140 ++--
>      hw/rx/tkdn-rx62n.c               |  192 +++++
>      hw/timer/renesas_cmt.c           |  283 -------
>      hw/timer/renesas_mtu.c           | 1312 ++++++++++++++++++++++++++++++
>      hw/timer/renesas_timer.c         |  639 +++++++++++++++
>      hw/timer/renesas_tmr.c           |  477 -----------
>      hw/timer/renesas_tmr8.c          |  540 ++++++++++++
>      MAINTAINERS                      |    2 +
>      hw/net/Kconfig                   |    8 +
>      hw/net/meson.build               |    3 +
>      hw/rx/Kconfig                    |   16 +-
>      hw/rx/meson.build                |    5 +-
>      hw/timer/Kconfig                 |    9 +-
>      hw/timer/meson.build             |    5 +-
>      36 files changed, 6391 insertions(+), 1173 deletions(-)
>      create mode 100644 include/hw/net/mdio.h
>      create mode 100644 include/hw/net/renesas_eth.h
>      create mode 100644 include/hw/rx/loader.h
>      create mode 100644 include/hw/rx/rx62n-cpg.h
>      delete mode 100644 include/hw/timer/renesas_cmt.h
>      create mode 100644 include/hw/timer/renesas_mtu.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
>      create mode 100644 hw/net/mdio.c
>      create mode 100644 hw/net/renesas_eth.c
>      create mode 100644 hw/rx/cq-frk-rx62n.c
>      create mode 100644 hw/rx/loader.c
>      create mode 100644 hw/rx/rx62n-cpg.c
>      create mode 100644 hw/rx/tkdn-rx62n.c
>      delete mode 100644 hw/timer/renesas_cmt.c
>      create mode 100644 hw/timer/renesas_mtu.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
>    
>     --
>     2.20.1
> 
> 

-- 
Yosinori Sato


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

* Re: [PATCH 06/20] hw/timer: Renesas 8bit timer emulation.
  2020-08-27 12:38 ` [PATCH 06/20] hw/timer: Renesas 8bit timer emulation Yoshinori Sato
@ 2020-10-24 21:27   ` Philippe Mathieu-Daudé
  2020-10-24 21:29     ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 40+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-10-24 21:27 UTC (permalink / raw)
  To: Yoshinori Sato, qemu-devel

On 8/27/20 2:38 PM, Yoshinori Sato wrote:
> Rewrite for clock API.
> 
> Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
> ---
>   include/hw/timer/renesas_tmr.h  |  55 ----
>   include/hw/timer/renesas_tmr8.h |  67 ++++
>   hw/timer/renesas_tmr.c          | 477 ----------------------------
>   hw/timer/renesas_tmr8.c         | 540 ++++++++++++++++++++++++++++++++
>   hw/timer/Kconfig                |   3 +-
>   hw/timer/meson.build            |   2 +-
>   6 files changed, 610 insertions(+), 534 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/hw/timer/Kconfig b/hw/timer/Kconfig
> index 8749edfb6a..5288660cda 100644
> --- a/hw/timer/Kconfig
> +++ b/hw/timer/Kconfig
> @@ -36,7 +36,7 @@ config CMSDK_APB_DUALTIMER
>       bool
>       select PTIMER
>   
> -config RENESAS_TMR
> +config RENESAS_TMR8
>       bool

Build failure:

undefined symbol RENESAS_TMR

$ git grep RENESAS_TMR
hw/rx/Kconfig:4:    select RENESAS_TMR

>   
>   config RENESAS_CMT
> @@ -44,3 +44,4 @@ config RENESAS_CMT
>   
>   config AVR_TIMER16
>       bool
> +
> diff --git a/hw/timer/meson.build b/hw/timer/meson.build
> index 9f0a267c83..a02e45fdbd 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_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'))
> 



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

* Re: [PATCH 06/20] hw/timer: Renesas 8bit timer emulation.
  2020-10-24 21:27   ` Philippe Mathieu-Daudé
@ 2020-10-24 21:29     ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 40+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-10-24 21:29 UTC (permalink / raw)
  To: Yoshinori Sato, qemu-devel

On 10/24/20 11:27 PM, Philippe Mathieu-Daudé wrote:
> On 8/27/20 2:38 PM, Yoshinori Sato wrote:
>> Rewrite for clock API.
>>
>> Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
>> ---
>>   include/hw/timer/renesas_tmr.h  |  55 ----
>>   include/hw/timer/renesas_tmr8.h |  67 ++++
>>   hw/timer/renesas_tmr.c          | 477 ----------------------------
>>   hw/timer/renesas_tmr8.c         | 540 ++++++++++++++++++++++++++++++++
>>   hw/timer/Kconfig                |   3 +-
>>   hw/timer/meson.build            |   2 +-
>>   6 files changed, 610 insertions(+), 534 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/hw/timer/Kconfig b/hw/timer/Kconfig
>> index 8749edfb6a..5288660cda 100644
>> --- a/hw/timer/Kconfig
>> +++ b/hw/timer/Kconfig
>> @@ -36,7 +36,7 @@ config CMSDK_APB_DUALTIMER
>>       bool
>>       select PTIMER
>> -config RENESAS_TMR
>> +config RENESAS_TMR8
>>       bool
> 
> Build failure:
> 
> undefined symbol RENESAS_TMR

And:

In file included from hw/rx/rx62n.c:27:
include/hw/rx/rx62n.h:29:10: fatal error: hw/timer/renesas_tmr.h: No 
such file or directory
    29 | #include "hw/timer/renesas_tmr.h"
       |          ^~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.


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

* Re: [PATCH 16/20] hw/net: Add Renesas On-chip Ethernet MAC
  2020-08-27 12:38 ` [PATCH 16/20] hw/net: Add Renesas On-chip Ethernet MAC Yoshinori Sato
@ 2020-10-24 21:37   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 40+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-10-24 21:37 UTC (permalink / raw)
  To: Yoshinori Sato, qemu-devel; +Cc: Jason Wang

Cc'ing Jason Wang, maintainer of network devices.

On 8/27/20 2:38 PM, Yoshinori Sato wrote:
> Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
> ---
>   include/hw/net/renesas_eth.h |  57 +++
>   hw/net/renesas_eth.c         | 875 +++++++++++++++++++++++++++++++++++
>   hw/net/Kconfig               |   5 +
>   hw/net/meson.build           |   1 +
>   4 files changed, 938 insertions(+)
>   create mode 100644 include/hw/net/renesas_eth.h
>   create mode 100644 hw/net/renesas_eth.c
> 
> diff --git a/include/hw/net/renesas_eth.h b/include/hw/net/renesas_eth.h
> new file mode 100644
> index 0000000000..e0026c6434
> --- /dev/null
> +++ b/include/hw/net/renesas_eth.h
> @@ -0,0 +1,57 @@
> +/*
> + *  Renesas ETHERC / EDMAC
> + *
> + *  Copyright 2019 Yoshinori Sato <ysato@users.sourceforge.jp>
> + *
> + *  This program is free software; you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation; under version 2 of the License.
> + *
> + *  This program is distributed in the hope that 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/>.
> + *
> + *  Contributions after 2012-01-13 are licensed under the terms of the
> + *  GNU GPL, version 2 or (at your option) any later version.
> + */
> +
> +#include "hw/sysbus.h"
> +#include "net/net.h"
> +#include "hw/net/mdio.h"
> +#include "hw/register.h"
> +#include "hw/clock.h"
> +
> +#define TYPE_RENESAS_ETH "renesas_eth"
> +#define RenesasEth(obj) OBJECT_CHECK(RenesasEthState, (obj), TYPE_RENESAS_ETH)
> +
> +#define RENESAS_ETHERC_R_MAX (0x100 / 4)
> +#define RENESAS_EDMAC_R_MAX  (0x100 / 4)
> +
> +typedef struct RenesasEthState {
> +    SysBusDevice parent_obj;
> +
> +    NICState *nic;
> +    NICConf conf;
> +    MemoryRegion etherc_mem;
> +    MemoryRegion edmac_mem;
> +    qemu_irq irq;
> +
> +    /* ETHERC registers */
> +    RegisterInfo etherc_regs_info[RENESAS_ETHERC_R_MAX];
> +    uint32_t etherc_regs[RENESAS_ETHERC_R_MAX];
> +
> +    /* EDMAC register */
> +    RegisterInfo edmac_regs_info[RENESAS_EDMAC_R_MAX];
> +    uint32_t edmac_regs[RENESAS_EDMAC_R_MAX];
> +
> +    int descsize;
> +    int rcv_bcast;
> +    uint8_t macadr[6];
> +    int link_sta;
> +    MDIOState *mdiodev;
> +    Clock *ick;
> +} RenesasEthState;
> diff --git a/hw/net/renesas_eth.c b/hw/net/renesas_eth.c
> new file mode 100644
> index 0000000000..d5fc2bb30c
> --- /dev/null
> +++ b/hw/net/renesas_eth.c
> @@ -0,0 +1,875 @@
> +/*
> + *  Renesas ETHERC / EDMAC
> + *
> + *  Copyright 2019 Yoshinori Sato <ysato@users.sourceforge.jp>
> + *
> + *  This program is free software; you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation; under version 2 of the License.
> + *
> + *  This program is distributed in the hope that 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/>.
> + *
> + *  Contributions after 2012-01-13 are licensed under the terms of the
> + *  GNU GPL, version 2 or (at your option) any later version.
> + */
> +
> +#include "qemu/osdep.h"
> +
> +#include "hw/hw.h"
> +#include "sysemu/dma.h"
> +#include "qemu/log.h"
> +#include "hw/qdev-properties.h"
> +#include "hw/qdev-clock.h"
> +#include "net/net.h"
> +#include "sysemu/sysemu.h"
> +#include "hw/irq.h"
> +#include "hw/net/renesas_eth.h"
> +
> +/* ETHERC Registers */
> +REG32(ECMR, 0x00)
> +  FIELD(ECMR, PRM, 0, 1)
> +  FIELD(ECMR, DM, 1, 1)
> +  FIELD(ECMR, RTM, 2, 1)
> +  FIELD(ECMR, ILB, 3, 1)
> +  FIELD(ECMR, TE, 5, 1)
> +  FIELD(ECMR, RE, 6, 1)
> +  FIELD(ECMR, MPDE, 9, 1)
> +  FIELD(ECMR, PRCREF, 12, 1)
> +  FIELD(ECMR, TXF, 16, 1)
> +  FIELD(ECMR, RXF, 17, 1)
> +  FIELD(ECMR, PFR, 18, 1)
> +  FIELD(ECMR, ZPF, 19, 1)
> +  FIELD(ECMR, TPC, 20, 1)
> +REG32(RFLR, 0x08)
> +  FIELD(RFLR, RFL, 0, 12)
> +REG32(ECSR, 0x10)
> +  FIELD(ECSR, ICD, 0, 1)
> +  FIELD(ECSR, MPD, 1, 1)
> +  FIELD(ECSR, LCHNG, 2, 1)
> +  FIELD(ECSR, PSRTO, 4, 1)
> +  FIELD(ECSR, BFR, 5, 1)
> +REG32(ECSIPR, 0x18)
> +  FIELD(ECSIPR, ICDIP, 0, 1)
> +  FIELD(ECSIPR, MPDIP, 1, 1)
> +  FIELD(ECSIPR, LCHNGIP, 2, 1)
> +  FIELD(ECSIPR, PSRTOIP, 4, 1)
> +  FIELD(ECSIPR, BFSIPR, 5, 1)
> +REG32(PIR, 0x20)
> +  FIELD(PIR, MDC, 0, 1)
> +  FIELD(PIR, MMD, 1, 1)
> +  FIELD(PIR, MDO, 2, 1)
> +  FIELD(PIR, MDI, 3, 1)
> +REG32(PSR, 0x28)
> +  FIELD(PSR, LMON, 0, 1)
> +REG32(RDMLR, 0x40)
> +  FIELD(RDMLR, RMD, 0, 20)
> +REG32(IPGR, 0x50)
> +  FIELD(IPGR, IPG, 0, 5)
> +REG32(APR, 0x54)
> +  FIELD(APR, AP, 0, 16)
> +REG32(MPR, 0x58)
> +  FIELD(MPR, MP, 0, 16)
> +REG32(RFCF, 0x60)
> +  FIELD(RFCF, RPAUSE, 0, 8)
> +REG32(TPAUSER, 0x64)
> +REG32(TPAUSECR, 0x68)
> +  FIELD(TPAUSECR, TXP, 0, 8)
> +  FIELD(TPAUSER, TPAUSE, 0, 16)
> +REG32(BCFRR, 0x6c)
> +  FIELD(BCFRR, BCF, 0, 16)
> +REG32(MAHR, 0xc0)
> +  FIELD(MAHR, MA, 0, 32)
> +REG32(MALR, 0xc8)
> +  FIELD(MALR, MA, 0, 16)
> +REG32(TROCR, 0xd0)
> +REG32(CDCR, 0xd4)
> +REG32(LCCR, 0xd8)
> +REG32(CNDCR, 0xdc)
> +REG32(CEFCR, 0xe4)
> +REG32(FRECR, 0xe8)
> +REG32(TSFRCR, 0xec)
> +REG32(TLFRCR, 0xf0)
> +REG32(RFCR, 0xf4)
> +REG32(MAFCR, 0xf8)
> +
> +/* EDMAC register */
> +REG32(EDMR, 0x00)
> +  FIELD(EDMR, SWR, 0, 1)
> +  FIELD(EDMR, DL, 4, 2)
> +  FIELD(EDMR, DE, 6, 1)
> +REG32(EDTRR, 0x08)
> +  FIELD(EDTRR, TR, 0, 1)
> +REG32(EDRRR, 0x10)
> +  FIELD(EDRRR, RR, 0, 1)
> +REG32(TDLAR, 0x18)
> +REG32(RDLAR, 0x20)
> +REG32(EESR, 0x28)
> +  FIELD(EESR, CERF, 0, 1)
> +  FIELD(EESR, PRE,  1, 1)
> +  FIELD(EESR, RTSF, 2, 1)
> +  FIELD(EESR, RTLF, 3, 1)
> +  FIELD(EESR, RRF,  4, 1)
> +  FIELD(EESR, RMAF, 7, 1)
> +  FIELD(EESR, TRO,  8, 1)
> +  FIELD(EESR, CD,   9, 1)
> +  FIELD(EESR, RDESC, 0, 10)
> +  FIELD(EESR, DLC,  10, 1)
> +  FIELD(EESR, CND,  11, 1)
> +  FIELD(EESR, RDOF, 16, 1)
> +  FIELD(EESR, RDE,  17, 1)
> +  FIELD(EESR, FR,   18, 1)
> +  FIELD(EESR, TFUF, 19, 1)
> +  FIELD(EESR, TDE,  20, 1)
> +  FIELD(EESR, TC,   21, 1)
> +  FIELD(EESR, ECI,  22, 1)
> +  FIELD(EESR, ADE,  23, 1)
> +  FIELD(EESR, RFCOF, 24, 1)
> +  FIELD(EESR, RABT, 25, 1)
> +  FIELD(EESR, TABT, 26, 1)
> +  FIELD(EESR, TWB,  30, 1)
> +REG32(EESIPR, 0x30)
> +  FIELD(EESIPR, CERFIP, 0, 1)
> +  FIELD(EESIPR, PREIP,  1, 1)
> +  FIELD(EESIPR, RTSFIP, 2, 1)
> +  FIELD(EESIPR, RTLFIP, 3, 1)
> +  FIELD(EESIPR, RRFIP,  4, 1)
> +  FIELD(EESIPR, RMAFIP, 7, 1)
> +  FIELD(EESIPR, TROIP,  8, 1)
> +  FIELD(EESIPR, CDIP,   9, 1)
> +  FIELD(EESIPR, DLCIP,  10, 1)
> +  FIELD(EESIPR, CNDIP,  11, 1)
> +  FIELD(EESIPR, RDOFIP, 16, 1)
> +  FIELD(EESIPR, RDEIP,  17, 1)
> +  FIELD(EESIPR, FRIP,   18, 1)
> +  FIELD(EESIPR, TFUFIP, 19, 1)
> +  FIELD(EESIPR, TDEIP,  20, 1)
> +  FIELD(EESIPR, TCIP,   21, 1)
> +  FIELD(EESIPR, ECIIP,  22, 1)
> +  FIELD(EESIPR, ADEIP,  23, 1)
> +  FIELD(EESIPR, RFCOFIP, 24, 1)
> +  FIELD(EESIPR, RABTIP, 25, 1)
> +  FIELD(EESIPR, TABTIP, 26, 1)
> +  FIELD(EESIPR, TWBIP,  30, 1)
> +REG32(TRSCER, 0x38)
> +  FIELD(TRSCER, RRFCE, 4, 1)
> +  FIELD(TRSCER, RMAFCE, 7, 1)
> +REG32(RMFCR, 0x40)
> +  FIELD(RMFCR, MFC, 0, 16)
> +REG32(TFTR, 0x48)
> +  FIELD(TFTR, TFT, 0, 11)
> +REG32(FDR, 0x50)
> +  FIELD(FDR, RFD, 0, 5)
> +  FIELD(FDR, TFD, 8, 5)
> +REG32(RMCR, 0x58)
> +  FIELD(RMCR, RNR, 0, 1)
> +  FIELD(RMCR, RNC, 1, 1)
> +REG32(TFUCR, 0x64)
> +  FIELD(TFUCR, UNDER, 0, 16)
> +REG32(RFOCR, 0x68)
> +  FIELD(RFOCR, OVER, 0, 16)
> +REG32(IOSR, 0x6c)
> +  FIELD(IOSR, ELB, 0, 1);
> +REG32(FCFTR, 0x70)
> +  FIELD(FCFTR, RFDO, 0, 3)
> +  FIELD(FCFTR, RFFO, 16, 3)
> +REG32(RPADIR, 0x78)
> +  FIELD(RPADIR, PADR, 0, 6)
> +  FIELD(RPADIR, PADS, 16, 2)
> +REG32(TRIMD, 0x7c)
> +  FIELD(TRIMD, TIS, 0, 1)
> +  FIELD(TRIMD, TIM, 4, 1)
> +REG32(RBWAR, 0xc8)
> +REG32(RDFAR, 0xcc)
> +REG32(TBRAR, 0xd4)
> +REG32(TDFAR, 0xd8)
> +
> +/* Transmit Descriptor */
> +REG32(TD0, 0x0000)
> +  FIELD(TD0, TFS0, 0, 1)
> +  FIELD(TD0, TFS1, 1, 1)
> +  FIELD(TD0, TFS2, 2, 1)
> +  FIELD(TD0, TFS3, 3, 1)
> +  FIELD(TD0, TFS8, 8, 1)
> +  FIELD(TD0, TWBI, 26, 1)
> +  FIELD(TD0, TFE,  27, 1)
> +  FIELD(TD0, TFP,  28, 2)
> +  FIELD(TD0, TDLE, 30, 1)
> +  FIELD(TD0, TACT, 31, 1)
> +REG32(TD1, 0x0004)
> +  FIELD(TD1, TBL, 16, 16)
> +REG32(TD2, 0x0008)
> +  FIELD(TD2, TBA, 0, 32)
> +
> +/* Receive Descriptor */
> +REG32(RD0, 0x0000)
> +  FIELD(RD0, RFS,  0, 10)
> +    FIELD(RD0, RFS0, 0, 1)
> +    FIELD(RD0, RFS1, 1, 1)
> +    FIELD(RD0, RFS2, 2, 1)
> +    FIELD(RD0, RFS3, 3, 1)
> +    FIELD(RD0, RFS4, 4, 1)
> +    FIELD(RD0, RFS7, 7, 1)
> +    FIELD(RD0, RFS8, 8, 1)
> +    FIELD(RD0, RFS9, 9, 1)
> +  FIELD(RD0, RFE,  27, 1)
> +  FIELD(RD0, RFP,  28, 2)
> +  FIELD(RD0, RFP0, 28, 1)
> +  FIELD(RD0, RDLE, 30, 1)
> +  FIELD(RD0, RACT, 31, 1)
> +REG32(RD1, 0x0004)
> +  FIELD(RD1, RFL, 0, 16)
> +  FIELD(RD1, RBL, 16, 16)
> +REG32(RD2, 0x0008)
> +  FIELD(RD2, RBA, 0, 32)
> +
> +static void renesas_eth_set_irq(RenesasEthState *s)
> +{
> +    if (s->edmac_regs[R_EESR] & s->edmac_regs[R_EESIPR]) {
> +        qemu_set_irq(s->irq, 1);
> +    } else {
> +        qemu_set_irq(s->irq, 0);
> +    }
> +}
> +
> +static bool renesas_eth_can_receive(NetClientState *nc)
> +{
> +    RenesasEthState *s = RenesasEth(qemu_get_nic_opaque(nc));
> +
> +    return FIELD_EX32(s->edmac_regs[R_EDRRR], EDRRR, RR);
> +}
> +
> +static void set_ecsr(RenesasEthState *s, int bit)
> +{
> +    s->etherc_regs[R_ECSR] = deposit32(s->etherc_regs[R_ECSR], bit, 1, 1);
> +    if (s->etherc_regs[R_ECSR] & s->etherc_regs[R_ECSIPR]) {
> +        s->edmac_regs[R_EESR] = FIELD_DP32(s->edmac_regs[R_EESR],
> +                                           EESR, ECI, 1);
> +    }
> +    renesas_eth_set_irq(s);
> +}
> +
> +static void renesas_eth_set_link_status(NetClientState *nc)
> +{
> +    RenesasEthState *s = RenesasEth(qemu_get_nic_opaque(nc));
> +    int old_lmon, new_lmon;
> +    if (s->mdiodev) {
> +        old_lmon = mdio_phy_linksta(mdio_get_phy(s->mdiodev));
> +        mdio_phy_set_link(mdio_get_phy(s->mdiodev), !nc->link_down);
> +        new_lmon = mdio_phy_linksta(mdio_get_phy(s->mdiodev));
> +        if (old_lmon ^ new_lmon) {
> +            set_ecsr(s, R_ECSR_LCHNG_SHIFT);
> +        }
> +    }
> +}
> +
> +static void edmac_write(RenesasEthState *s, const uint8_t *buf,
> +                        size_t size, int pad)
> +{
> +    uint32_t rdesc[3];
> +    uint32_t eesr;
> +    int state = 0;
> +
> +    while (size > 0) {
> +        size_t wsize;
> +        /* RDESC read */
> +        dma_memory_read(&address_space_memory,
> +                        s->edmac_regs[R_RDFAR], rdesc, sizeof(rdesc));
> +        if (FIELD_EX32(rdesc[0], RD0, RACT)) {
> +            if (state == 0) {
> +                /* Fist block */
> +                rdesc[0] = FIELD_DP32(rdesc[0], RD0, RFP, 2);
> +            }
> +            state++;
> +            s->edmac_regs[R_RBWAR] = rdesc[2];
> +            wsize = MIN(FIELD_EX32(rdesc[1], RD1, RBL), size);
> +            /* Write receive data */
> +            dma_memory_write(&address_space_memory,
> +                             s->edmac_regs[R_RBWAR], buf, wsize);
> +            buf += wsize;
> +            size -= wsize;
> +            rdesc[1] = FIELD_DP32(rdesc[1], RD1, RFL, wsize);
> +            if (size == 0) {
> +                /* Last descriptor */
> +                rdesc[0] = FIELD_DP32(rdesc[0], RD0, RFP0, 1);
> +                if (FIELD_EX32(s->edmac_regs[R_RMCR], RMCR, RNR) == 0) {
> +                    s->edmac_regs[R_EDRRR] = FIELD_DP32(s->edmac_regs[R_EDRRR],
> +                                                  EDRRR, RR, 0);
> +                }
> +                s->edmac_regs[R_EESR] = FIELD_DP32(s->edmac_regs[R_EESR],
> +                                                   EESR, FR, 1);
> +                renesas_eth_set_irq(s);
> +            }
> +            eesr = FIELD_EX32(s->edmac_regs[R_EESR], EESR, RDESC);
> +            rdesc[0] = FIELD_DP32(rdesc[0], RD0, RFS,
> +                                  eesr & ~(s->edmac_regs[R_TRSCER]));
> +            rdesc[0] = FIELD_DP32(rdesc[0], RD0, RFE, eesr != 0);
> +            rdesc[0] = FIELD_DP32(rdesc[0], RD0, RACT, 0);
> +            /* RDESC write back */
> +            dma_memory_write(&address_space_memory,
> +                             s->edmac_regs[R_RDFAR], rdesc, sizeof(rdesc));
> +            if (FIELD_EX32(rdesc[0], RD0, RDLE)) {
> +                s->edmac_regs[R_RDFAR] = s->edmac_regs[R_RDLAR];
> +            } else {
> +                s->edmac_regs[R_RDFAR] += s->descsize;
> +            }
> +            s->edmac_regs[R_EESR] = FIELD_DP32(s->edmac_regs[R_EESR],
> +                                               EESR, FR, 1);
> +        } else {
> +            /* no active RDESC */
> +            if (FIELD_EX32(s->edmac_regs[R_RMCR], RMCR, RNC) == 0) {
> +                s->edmac_regs[R_EDRRR] = FIELD_DP32(s->edmac_regs[R_EDRRR],
> +                                                    EDRRR, RR, 0);
> +            }
> +            s->edmac_regs[R_EESR] = FIELD_DP32(s->edmac_regs[R_EESR],
> +                                               EESR, RDE, 1);
> +            break;
> +        }
> +    }
> +    renesas_eth_set_irq(s);
> +}
> +
> +static inline void update_count(uint32_t *cnt)
> +{
> +    if (*cnt < UINT32_MAX) {
> +        /* Satulate on 32bit value */
> +        (*cnt)++;
> +    }
> +}
> +
> +#define MIN_BUF_SIZE 60
> +static ssize_t renesas_eth_receive(NetClientState *nc,
> +                            const uint8_t *buf, size_t size)
> +{
> +    RenesasEthState *s = RenesasEth(qemu_get_nic_opaque(nc));
> +    static const uint8_t bcast_addr[] = {
> +        0xff, 0xff, 0xff, 0xff, 0xff, 0xff
> +    };
> +    static const uint8_t pad[3] = { 0 };
> +    uint8_t buf1[MIN_BUF_SIZE];
> +    bool receive = false;
> +    size_t pads;
> +    uint32_t rflr;
> +
> +    if (size >= 6) {
> +        if (memcmp(buf, bcast_addr, sizeof(bcast_addr)) == 0) {
> +            /* broadcast */
> +            if (s->etherc_regs[R_BCFRR] == 0 ||
> +                s->etherc_regs[R_BCFRR] < s->rcv_bcast) {
> +                s->rcv_bcast++;
> +                receive = true;
> +            }
> +        } else if (buf[0] & 0x1) {
> +            /* multicast */
> +            receive = true;
> +            s->edmac_regs[R_EESR] = FIELD_DP32(s->edmac_regs[R_EESR],
> +                                               EESR, RMAF, 1);
> +            update_count(&s->edmac_regs[R_MAFCR]);
> +        } else if (FIELD_EX32(s->edmac_regs[R_ECMR], ECMR, PRM)) {
> +            /* promiscas */
> +            receive = true;
> +        } else if (memcmp(buf, s->macadr, sizeof(s->macadr)) == 0) {
> +            /* normal */
> +            receive = true;
> +        }
> +    }
> +    if (!receive) {
> +        return size;
> +    }
> +    /* if too small buffer, then expand it */
> +    if (size < MIN_BUF_SIZE) {
> +        memcpy(buf1, buf, size);
> +        memset(buf1 + size, 0, MIN_BUF_SIZE - size);
> +        buf = buf1;
> +        size = MIN_BUF_SIZE;
> +    }
> +
> +    rflr = FIELD_EX32(s->etherc_regs[R_RFLR], RFLR, RFL);
> +    rflr = MAX(rflr, 1518);
> +    if (size > rflr) {
> +        update_count(&s->etherc_regs[R_TLFRCR]);
> +        s->edmac_regs[R_EESR] = FIELD_DP32(s->edmac_regs[R_EESR],
> +                                           EESR, RTLF, 1);
> +    }
> +    pads = FIELD_EX32(s->edmac_regs[R_RPADIR], RPADIR, PADS);
> +    if (pads > 0) {
> +        int pos = FIELD_EX32(s->edmac_regs[R_RPADIR], RPADIR, PADR);
> +        uint8_t *padbuf = g_new(uint8_t, size + pads);
> +        if (size > pos) {
> +            if (pos > 0) {
> +                memcpy(padbuf, buf, pos);
> +            }
> +            memcpy(padbuf + pos, pad, pads);
> +            memcpy(padbuf + pos + pads, buf + pos, size - pos);
> +        } else {
> +            pads = 0;
> +        }
> +        edmac_write(s, padbuf, size + pads, pads);
> +        g_free(padbuf);
> +    } else {
> +        edmac_write(s, buf, size, 0);
> +    }
> +    return size;
> +}
> +
> +static size_t edmac_read(RenesasEthState *s, uint8_t **buf)
> +{
> +    uint32_t tdesc[3];
> +    uint32_t size = 0;
> +
> +    *buf = NULL;
> +    for (;;) {
> +        size_t rsize;
> +        dma_memory_read(&address_space_memory,
> +                        s->edmac_regs[R_TDFAR], tdesc, sizeof(tdesc));
> +        if (FIELD_EX32(tdesc[0], TD0, TACT)) {
> +            s->edmac_regs[R_TBRAR] = tdesc[2];
> +            rsize = FIELD_EX32(tdesc[1], TD1, TBL);
> +            *buf = g_realloc(*buf, size + rsize);
> +            dma_memory_read(&address_space_memory,
> +                            s->edmac_regs[R_TBRAR], *buf + size, rsize);
> +            tdesc[0] = FIELD_DP32(tdesc[0], TD0, TACT, 0);
> +            dma_memory_write(&address_space_memory,
> +                            s->edmac_regs[R_TDFAR], tdesc, sizeof(tdesc));
> +            size += rsize;
> +            if (FIELD_EX32(tdesc[0], TD0, TDLE)) {
> +                s->edmac_regs[R_TDFAR] = s->edmac_regs[R_TDLAR];
> +            } else {
> +                s->edmac_regs[R_TDFAR] += s->descsize;
> +            }
> +            if (FIELD_EX32(tdesc[0], TD0, TFP) & 1) {
> +                break;
> +            }
> +        } else {
> +            s->edmac_regs[R_EESR] = FIELD_DP32(s->edmac_regs[R_EESR],
> +                                               EESR, TDE, 1);
> +            renesas_eth_set_irq(s);
> +            break;
> +        }
> +    }
> +    return size;
> +}
> +
> +static void renesas_eth_start_xmit(RenesasEthState *s)
> +{
> +    uint8_t *txbuf;
> +    size_t size;
> +
> +    size = edmac_read(s, &txbuf);
> +    qemu_send_packet(qemu_get_queue(s->nic), txbuf, size);
> +    g_free(txbuf);
> +    s->edmac_regs[R_EESR] = FIELD_DP32(s->edmac_regs[R_EESR], EESR, TWB, 1);
> +    s->edmac_regs[R_EDTRR] = FIELD_DP32(s->edmac_regs[R_EDTRR], EDTRR, TR, 0);
> +    renesas_eth_set_irq(s);
> +}
> +
> +static void renesas_eth_reset(RenesasEthState *s)
> +{
> +    int i;
> +
> +    for (i = 0; i < RENESAS_ETHERC_R_MAX; i++) {
> +        register_reset(&s->etherc_regs_info[i]);
> +    }
> +    for (i = 0; i < RENESAS_EDMAC_R_MAX; i++) {
> +        register_reset(&s->edmac_regs_info[i]);
> +    }
> +}
> +
> +static uint64_t ecsr_pre_write(RegisterInfo *reg, uint64_t val)
> +{
> +    RenesasEthState *s = RenesasEth(reg->opaque);
> +    uint32_t old_val = s->etherc_regs[R_ECSR];
> +
> +    val ^= old_val;
> +    val &= old_val;
> +    return val;
> +}
> +
> +static void ecsr_post_write(RegisterInfo *reg, uint64_t val)
> +{
> +    RenesasEthState *s = RenesasEth(reg->opaque);
> +
> +    if (s->etherc_regs[R_ECSR] & s->etherc_regs[R_ECSIPR]) {
> +        s->edmac_regs[R_EESR] = FIELD_DP32(s->edmac_regs[R_EESR],
> +                                           EESR, ECI, 1);
> +    } else {
> +        s->edmac_regs[R_EESR] = FIELD_DP32(s->edmac_regs[R_EESR],
> +                                           EESR, ECI, 0);
> +    }
> +    renesas_eth_set_irq(s);
> +}
> +
> +static void pir_post_write(RegisterInfo *reg, uint64_t val)
> +{
> +    RenesasEthState *s = RenesasEth(reg->opaque);
> +    if (s->mdiodev) {
> +        mdio_set_mdc_pin(s->mdiodev, FIELD_EX32(val, PIR, MDC));
> +        if (FIELD_EX32(val, PIR, MMD)) {
> +            mdio_set_mdo_pin(s->mdiodev, FIELD_EX32(val, PIR, MDO));
> +        }
> +    }
> +}
> +
> +static uint64_t pir_post_read(RegisterInfo *reg, uint64_t val)
> +{
> +    RenesasEthState *s = RenesasEth(reg->opaque);
> +    if (s->mdiodev) {
> +        val = FIELD_DP64(val, PIR, MDI, mdio_read_mdi_pin(s->mdiodev));
> +    }
> +    return val;
> +}
> +
> +static uint64_t mar_pre_write(RegisterInfo *reg, uint64_t val)
> +{
> +    RenesasEthState *s = RenesasEth(reg->opaque);
> +    if (FIELD_EX32(s->edmac_regs[R_EDTRR], EDTRR, TR) ||
> +        FIELD_EX32(s->edmac_regs[R_EDRRR], EDRRR, RR)) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "renesas_eth: Tx/Rx enabled in MAR write.\n");
> +    }
> +    return val;
> +}
> +
> +static void mar_post_write(RegisterInfo *reg, uint64_t val)
> +{
> +    int i;
> +    RenesasEthState *s = RenesasEth(reg->opaque);
> +    for (i = 0; i < 4; i++) {
> +        s->macadr[i] = extract32(s->etherc_regs[R_MAHR], 8 * (3 - i), 8);
> +    }
> +    for (i = 0; i < 2; i++) {
> +        s->macadr[i + 4] = extract32(s->etherc_regs[R_MALR], 8 * (1 - i), 8);
> +    }
> +}
> +
> +static uint64_t etherc_counter_write(RegisterInfo *reg, uint64_t val)
> +{
> +    /* Counter register clear in any write operation */
> +    return 0;
> +}
> +
> +static void edmr_post_write(RegisterInfo *reg, uint64_t val)
> +{
> +    RenesasEthState *s = RenesasEth(reg->opaque);
> +    uint32_t TDLAR, RMFCR, TFUCR, RFOCR;
> +    int dl;
> +
> +    if (FIELD_EX32(val, EDMR, SWR)) {
> +        /* Following register keep for SWR */
> +        TDLAR = s->edmac_regs[R_TDLAR];
> +        RMFCR = s->edmac_regs[R_RMFCR];
> +        TFUCR = s->edmac_regs[R_TFUCR];
> +        RFOCR = s->edmac_regs[R_RFOCR];
> +        renesas_eth_reset(s);
> +        s->edmac_regs[R_TDLAR] = TDLAR;
> +        s->edmac_regs[R_RMFCR] = RMFCR;
> +        s->edmac_regs[R_TFUCR] = TFUCR;
> +        s->edmac_regs[R_RFOCR] = RFOCR;
> +    }
> +    dl = FIELD_EX32(val, EDMR, DL) % 3;
> +    s->descsize = 16 << dl;
> +}
> +
> +static void edtrr_post_write(RegisterInfo *reg, uint64_t val)
> +{
> +    RenesasEthState *s = RenesasEth(reg->opaque);
> +    if (FIELD_EX32(val, EDTRR, TR)) {
> +        renesas_eth_start_xmit(s);
> +    }
> +}
> +
> +static uint64_t eesr_pre_write(RegisterInfo *reg, uint64_t val)
> +{
> +    uint32_t eesr;
> +    RenesasEthState *s = RenesasEth(reg->opaque);
> +    /* flag clear for write 1 */
> +    eesr = s->edmac_regs[R_EESR];
> +    val = FIELD_DP64(val, EESR, ECI, 0); /* Keep ECI value */
> +    eesr &= ~val;
> +    return eesr;
> +}
> +
> +static void eesr_post_write(RegisterInfo *reg, uint64_t val)
> +{
> +    RenesasEthState *s = RenesasEth(reg->opaque);
> +    renesas_eth_set_irq(s);
> +}
> +
> +static void tdlar_post_write(RegisterInfo *reg, uint64_t val)
> +{
> +    RenesasEthState *s = RenesasEth(reg->opaque);
> +    s->edmac_regs[R_TDFAR] = s->edmac_regs[R_TDLAR];
> +}
> +
> +static void rdlar_post_write(RegisterInfo *reg, uint64_t val)
> +{
> +    RenesasEthState *s = RenesasEth(reg->opaque);
> +    s->edmac_regs[R_RDFAR] = s->edmac_regs[R_RDLAR];
> +}
> +
> +static uint64_t fdr_pre_write(RegisterInfo *reg, uint64_t val)
> +{
> +    RenesasEthState *s = RenesasEth(reg->opaque);
> +    if (FIELD_EX32(val, FDR, TFD) != 7 || FIELD_EX32(val, FDR, RFD) != 7) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "renesas_eth: invalid FDR setting %"
> +                      HWADDR_PRIX ".\n", val);
> +    }
> +    if (FIELD_EX32(s->edmac_regs[R_EDTRR], EDTRR, TR) ||
> +        FIELD_EX32(s->edmac_regs[R_EDRRR], EDRRR, RR)) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "renesas_eth: Tx/Rx enabled in FDR write.\n");
> +    }
> +    return val;
> +}
> +
> +static uint64_t edmac_reg_read(void *opaque, hwaddr addr, unsigned int size)
> +{
> +    RegisterInfoArray *ra = opaque;
> +    RenesasEthState *s = RenesasEth(ra->r[0]->opaque);
> +    if (clock_is_enabled(s->ick)) {
> +        return register_read_memory(ra, addr, size);
> +    } else {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "renesas_eth: EDMAC module stopped.\n");
> +        return UINT64_MAX;
> +    }
> +}
> +
> +static void edmac_reg_write(void *opaque, hwaddr addr,
> +                        uint64_t value, unsigned int size)
> +{
> +    RegisterInfoArray *ra = opaque;
> +    RenesasEthState *s = RenesasEth(ra->r[0]->opaque);
> +    if (clock_is_enabled(s->ick)) {
> +        register_write_memory(ra, addr, value, size);
> +    } else {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "renesas_eth: EDMAC module stopped.\n");
> +    }
> +}
> +
> +static const MemoryRegionOps renesas_etherc_ops = {
> +    .read = register_read_memory,
> +    .write = register_write_memory,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +    .impl = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +};
> +
> +static const MemoryRegionOps renesas_edmac_ops = {
> +    .read = edmac_reg_read,
> +    .write = edmac_reg_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +    .impl = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +};
> +
> +static NetClientInfo net_renesas_eth_info = {
> +    .type = NET_CLIENT_DRIVER_NIC,
> +    .size = sizeof(NICState),
> +    .can_receive = renesas_eth_can_receive,
> +    .receive = renesas_eth_receive,
> +    .link_status_changed = renesas_eth_set_link_status,
> +};
> +
> +static const RegisterAccessInfo renesas_etherc_regs_info[] = {
> +    { .name = "ECMR", .addr = A_ECMR,
> +      .rsvd = 0xffe0ed90, },
> +    { .name = "RFLR", .addr = A_RFLR,
> +      .rsvd = 0xfffff000, },
> +    { .name = "ECSR", .addr = A_ECSR,
> +      .rsvd = 0xffffffc8,
> +      .pre_write = ecsr_pre_write,
> +      .post_write = ecsr_post_write, },
> +    { .name = "ECSIPR", .addr = A_ECSIPR,
> +      .rsvd = 0xffffffc8,
> +      .post_write = ecsr_post_write, },
> +    { .name = "PIR", .addr = A_PIR,
> +      .rsvd = 0xfffffff0,
> +      .post_write = pir_post_write,
> +      .post_read = pir_post_read, },
> +    { .name = "PSR", .addr = A_PSR,
> +      .rsvd = 0xfffffffe, },
> +    { .name = "RDMLR", .addr = A_RDMLR,
> +      .rsvd = 0xfff00000, },
> +    { .name = "IPGR", .addr = A_IPGR,
> +      .rsvd = 0xffffffe0, .reset = 0x00000014, },
> +    { .name = "APR", .addr = A_APR,
> +      .rsvd = 0xffff0000, },
> +    { .name = "MPR", .addr = A_MPR,
> +      .rsvd = 0xffff0000, },
> +    { .name = "RFCF", .addr = A_RFCF,
> +      .rsvd = 0xffffff00, },
> +    { .name = "TPAUSER", .addr = A_TPAUSER,
> +      .rsvd = 0xffff0000, },
> +    { .name = "TPAUSECR", .addr = A_TPAUSECR,
> +      .rsvd = 0xffffff00, },
> +    { .name = "BCFRR", .addr = A_BCFRR,
> +      .rsvd = 0xffff0000, },
> +    { .name = "MAHR", .addr = A_MAHR,
> +      .pre_write = mar_pre_write,
> +      .post_write = mar_post_write, },
> +    { .name = "MALR", .addr = A_MALR,
> +      .rsvd = 0xffff0000,
> +      .pre_write = mar_pre_write,
> +      .post_write = mar_post_write, },
> +    { .name = "TROCR", .addr = A_TROCR,
> +      .pre_write = etherc_counter_write, },
> +    { .name = "CDCR", .addr = A_CDCR,
> +      .pre_write = etherc_counter_write, },
> +    { .name = "LCCR", .addr = A_LCCR,
> +      .pre_write = etherc_counter_write, },
> +    { .name = "CNDCR", .addr = A_CNDCR,
> +      .pre_write = etherc_counter_write, },
> +    { .name = "CEFCR", .addr = A_CEFCR,
> +      .pre_write = etherc_counter_write, },
> +    { .name = "FRECR", .addr = A_FRECR,
> +      .pre_write = etherc_counter_write, },
> +    { .name = "TSFRCR", .addr = A_TSFRCR,
> +      .pre_write = etherc_counter_write, },
> +    { .name = "TLFRCR", .addr = A_TLFRCR,
> +      .pre_write = etherc_counter_write, },
> +    { .name = "RFCR", .addr = A_RFCR,
> +      .pre_write = etherc_counter_write, },
> +    { .name = "MAFCR", .addr = A_MAFCR,
> +      .pre_write = etherc_counter_write, },
> +};
> +
> +static const RegisterAccessInfo renesas_edmac_regs_info[] = {
> +    { .name = "EDMR", .addr = A_EDMR,
> +      .rsvd = 0xfffffff8e,
> +      .post_write = edmr_post_write, },
> +    { .name = "EDTRR", .addr = A_EDTRR,
> +      .rsvd = 0xffffffffe,
> +      .post_write = edtrr_post_write, },
> +    { .name = "EDRRR", .addr = A_EDRRR,
> +      .rsvd = 0xffffffffe, },
> +    { .name = "TDLAR", .addr = A_TDLAR,
> +      .post_write = tdlar_post_write, },
> +    { .name = "RDLAR", .addr = A_RDLAR,
> +      .post_write = rdlar_post_write, },
> +    { .name = "EESR", .addr = A_EESR,
> +      .rsvd = 0xb800f0c0, .ro = 0x00400000,
> +      .pre_write = eesr_pre_write,
> +      .post_write = eesr_post_write, },
> +    { .name = "EESIPR", .addr = A_EESIPR,
> +      .rsvd = 0xb800f060,
> +      .post_write = eesr_post_write, },
> +    { .name = "TRSCER", .addr = A_TRSCER,
> +      .rsvd = 0xfffffd6f, },
> +    { .name = "RMFCR", .addr = A_RMFCR,
> +      .rsvd = 0xffff0000, },
> +    { .name = "TFTR", .addr = A_TFTR,
> +      .rsvd = 0xfffff800, },
> +    { .name = "FDR", .addr = A_FDR,
> +      .rsvd = 0xffffe0e0,
> +      .pre_write = fdr_pre_write, },
> +    { .name = "RMCR", .addr = A_RMCR,
> +      .rsvd = 0xfffffffc, },
> +    { .name = "TFUCR", .addr = A_TFUCR,
> +      .rsvd = 0xffff0000,
> +      .pre_write = etherc_counter_write, },
> +    { .name = "RFOCR", .addr = A_RFOCR,
> +      .rsvd = 0xffff0000,
> +      .pre_write = etherc_counter_write, },
> +    { .name = "RBWAR", .addr = A_RBWAR,
> +      .ro = 0xffffffff, .rsvd = 0xffff0000, },
> +    { .name = "RDFAR", .addr = A_RDFAR,
> +      .ro = 0xffffffff, .rsvd = 0xffff0000, },
> +    { .name = "TBRAR", .addr = A_TBRAR,
> +      .ro = 0xffffffff, .rsvd = 0xffff0000, },
> +    { .name = "TDFAR", .addr = A_TDFAR,
> +      .ro = 0xffffffff, .rsvd = 0xffff0000, },
> +    { .name = "FCFTR", .addr = A_FCFTR,
> +      .rsvd = 0xfff8fff8, },
> +    { .name = "RPADIR", .addr = A_RPADIR,
> +      .rsvd = 0xfffcffc0, },
> +    { .name = "TRIMD", .addr = A_TRIMD,
> +      .rsvd = 0xffffffee, },
> +    { .name = "IOSR", .addr = A_IOSR,
> +      .rsvd = 0xfffffffe, },
> +};
> +
> +static void renesas_eth_realize(DeviceState *dev, Error **errp)
> +{
> +    RenesasEthState *s = RenesasEth(dev);
> +
> +    s->nic = qemu_new_nic(&net_renesas_eth_info, &s->conf,
> +                          object_get_typename(OBJECT(s)), dev->id, s);
> +
> +    renesas_eth_reset(s);
> +    if (s->mdiodev) {
> +        mdio_phy_set_link(mdio_get_phy(s->mdiodev),
> +                          !qemu_get_queue(s->nic)->link_down);
> +    }
> +}
> +
> +static Property renesas_eth_properties[] = {
> +    DEFINE_NIC_PROPERTIES(RenesasEthState, conf),
> +    DEFINE_PROP_LINK("mdio", RenesasEthState, mdiodev, TYPE_ETHER_MDIO_BB,
> +                     MDIOState *),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void renesas_eth_init(Object *obj)
> +{
> +    SysBusDevice *d = SYS_BUS_DEVICE(obj);
> +    RenesasEthState *s = RenesasEth(obj);
> +    RegisterInfoArray *ra_etherc;
> +    RegisterInfoArray *ra_edmac;
> +
> +    memory_region_init(&s->etherc_mem, obj, "renesas-etherc", 0x100);
> +    ra_etherc = register_init_block32(DEVICE(d), renesas_etherc_regs_info,
> +                                      ARRAY_SIZE(renesas_etherc_regs_info),
> +                                      s->etherc_regs_info, s->etherc_regs,
> +                                      &renesas_etherc_ops,
> +                                      false, 0x100);
> +    memory_region_add_subregion(&s->etherc_mem, 0x00, &ra_etherc->mem);
> +    sysbus_init_mmio(d, &s->etherc_mem);
> +
> +    memory_region_init(&s->edmac_mem, obj, "renesas-edmac", 0x100);
> +    ra_edmac = register_init_block32(DEVICE(d), renesas_edmac_regs_info,
> +                                     ARRAY_SIZE(renesas_edmac_regs_info),
> +                                     s->edmac_regs_info, s->edmac_regs,
> +                                     &renesas_edmac_ops,
> +                                     false, 0x100);
> +    memory_region_add_subregion(&s->edmac_mem, 0x00, &ra_edmac->mem);
> +    sysbus_init_mmio(d, &s->edmac_mem);
> +
> +    sysbus_init_irq(d, &s->irq);
> +    s->ick =  qdev_init_clock_in(DEVICE(d), "ick", NULL, NULL);
> +}
> +
> +static void renesas_eth_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
> +    device_class_set_props(dc, renesas_eth_properties);
> +    dc->realize = renesas_eth_realize;
> +}
> +
> +static const TypeInfo renesas_eth_info = {
> +    .name          = TYPE_RENESAS_ETH,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(RenesasEthState),
> +    .instance_init = renesas_eth_init,
> +    .class_init    = renesas_eth_class_init,
> +};
> +
> +static void renesas_eth_register_types(void)
> +{
> +    type_register_static(&renesas_eth_info);
> +}
> +
> +type_init(renesas_eth_register_types)
> diff --git a/hw/net/Kconfig b/hw/net/Kconfig
> index e6a32a2ab0..7cb3aeadeb 100644
> --- a/hw/net/Kconfig
> +++ b/hw/net/Kconfig
> @@ -146,3 +146,8 @@ config CAN_SJA1000
>   
>   config MDIO_PHY
>       bool
> +
> +config RENESAS_ETH
> +    bool
> +    select MDIO_PHY
> +    select REGISTER
> diff --git a/hw/net/meson.build b/hw/net/meson.build
> index faa4e3d2c0..0f64af7b8f 100644
> --- a/hw/net/meson.build
> +++ b/hw/net/meson.build
> @@ -65,5 +65,6 @@ softmmu_ss.add(when: 'CONFIG_ROCKER', if_true: files(
>   softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('rocker/qmp-norocker.c'))
>   
>   softmmu_ss.add(when: 'CONFIG_MDIO_PHY', if_true: files('mdio.c'))
> +softmmu_ss.add(when: 'CONFIG_RENESAS_ETH', if_true: files('renesas_eth.c'))
>   
>   subdir('can')
> 



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

* Re: [PATCH 11/20] hw/char: Renesas SCI module.
  2020-08-27 12:38 ` [PATCH 11/20] hw/char: Renesas SCI module Yoshinori Sato
@ 2020-10-24 21:40   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 40+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-10-24 21:40 UTC (permalink / raw)
  To: Yoshinori Sato, qemu-devel; +Cc: Marc-André Lureau

Cc'ing Marc-André, maintainer of character devices.

On 8/27/20 2:38 PM, Yoshinori Sato 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         | 1040 +++++++++++++++++++++++++++------
>   2 files changed, 967 insertions(+), 202 deletions(-)
> 
> diff --git a/include/hw/char/renesas_sci.h b/include/hw/char/renesas_sci.h
> index efdebc620a..07140e8aad 100644
> --- a/include/hw/char/renesas_sci.h
> +++ b/include/hw/char/renesas_sci.h
> @@ -1,51 +1,138 @@
>   /*
>    * 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 "hw/clock.h"
>   
> +#define TYPE_RENESAS_SCI_COMMON "renesas-sci-common"
> +#define RSCICommon(obj) OBJECT_CHECK(RSCICommonState, (obj), \
> +                                     TYPE_RENESAS_SCI_COMMON)
>   #define TYPE_RENESAS_SCI "renesas-sci"
>   #define RSCI(obj) OBJECT_CHECK(RSCIState, (obj), TYPE_RENESAS_SCI)
> +#define TYPE_RENESAS_SCIA "renesas-scia"
> +#define RSCIA(obj) OBJECT_CHECK(RSCIAState, (obj), TYPE_RENESAS_SCIA)
> +#define TYPE_RENESAS_SCIF "renesas-scif"
> +#define RSCIF(obj) OBJECT_CHECK(RSCIFState, (obj), TYPE_RENESAS_SCIF)
> +
> +#define SCI_GET_CLASS(obj) \
> +    OBJECT_GET_CLASS(RenesasSCICommonClass, obj, TYPE_RENESAS_SCI_COMMON)
> +#define SCI_COMMON_CLASS(klass) \
> +    OBJECT_CLASS_CHECK(RenesasSCICommonClass, klass, TYPE_RENESAS_SCI_COMMON)
> +#define SCI_CLASS(klass) \
> +    OBJECT_CLASS_CHECK(RenesasSCIClass, klass, TYPE_RENESAS_SCI)
> +#define SCIA_CLASS(klass) \
> +    OBJECT_CLASS_CHECK(RenesasSCIAClass, klass, TYPE_RENESAS_SCIA)
> +#define SCIF_CLASS(klass) \
> +    OBJECT_CLASS_CHECK(RenesasSCIFClass, klass, TYPE_RENESAS_SCIF)
>   
>   enum {
>       ERI = 0,
>       RXI = 1,
>       TXI = 2,
> -    TEI = 3,
> -    SCI_NR_IRQ = 4
> +    BRI_TEI = 3,
> +    SCI_NR_IRQ = 4,
>   };
>   
> -typedef struct {
> -    /*< private >*/
> -    SysBusDevice parent_obj;
> -    /*< public >*/
> +enum {
> +    RXTOUT,
> +    RXNEXT,
> +    TXEMPTY,
> +    TXEND,
> +    NR_SCI_EVENT,
> +};
>   
> +typedef struct RSCICommonState {
> +    SysBusDevice parent_obj;
>       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 RSCICommonState *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;
> +} RSCICommonState;
> +
> +typedef struct {
> +    RSCICommonState parent_obj;
> +
> +    /* SCI specific register */
> +    uint8_t sptr;
>   } RSCIState;
>   
> -#endif
> +typedef struct {
> +    RSCICommonState parent_obj;
> +
> +    /* SCIa specific register */
> +    uint8_t scmr;
> +    uint8_t semr;
> +} RSCIAState;
> +
> +typedef struct {
> +    RSCICommonState parent_obj;
> +
> +    /* SCIF specific register */
> +    uint16_t fcr;
> +    uint16_t sptr;
> +    uint16_t lsr;
> +
> +    /* internal use */
> +    uint16_t read_lsr;
> +    int tdcnt;
> +} RSCIFState;
> +
> +typedef struct RenesasSCICommonClass {
> +    SysBusDeviceClass parent;
> +
> +    const struct MemoryRegionOps *ops;
> +    void (*irq_fn)(RSCICommonState *sci, int request);
> +    int (*divrate)(RSCICommonState *sci);
> +} RenesasSCICommonClass;
> +
> +typedef struct RenesasSCIClass {
> +    RenesasSCICommonClass parent;
> +
> +    void (*p_irq_fn)(RSCICommonState *sci, int request);
> +} RenesasSCIClass;
> +
> +typedef struct RenesasSCIAClass {
> +    RenesasSCICommonClass parent;
> +
> +    void (*p_irq_fn)(RSCICommonState *sci, int request);
> +    int (*p_divrate)(RSCICommonState *sci);
> +} RenesasSCIAClass;
> +
> +typedef struct RenesasSCIFClass {
> +    RenesasSCICommonClass parent;
> +
> +    void (*p_irq_fn)(RSCICommonState *sci, int request);
> +} RenesasSCIFClass;
> diff --git a/hw/char/renesas_sci.c b/hw/char/renesas_sci.c
> index 5d7c6e6523..24c23709ee 100644
> --- a/hw/char/renesas_sci.c
> +++ b/hw/char/renesas_sci.c
> @@ -1,12 +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)
> + * (Rev.1.40 R01UH0033EJ0140)
> + * And SH7751 Group, SH7751R Group User's Manual: Hardware
> + * (Rev.4.01 R01UH0457EJ0401)
>    *
> - * Copyright (c) 2019 Yoshinori Sato
> - *
> - * SPDX-License-Identifier: GPL-2.0-or-later
> + * 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,
> @@ -23,14 +23,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-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.
> + */
> +REG16(SMR, 0) /* 8bit */
>     FIELD(SMR, CKS,  0, 2)
>     FIELD(SMR, MP,   2, 1)
>     FIELD(SMR, STOP, 3, 1)
> @@ -38,263 +49,839 @@ 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)
> +REG16(BRR, 2) /* 8bit */
> +REG16(SCR, 4)
> +  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)
> +REG16(TDR, 6) /* 8bit */
> +REG16(SSR, 8) /* 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)
> +REG16(FSR, 8)
> +  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)
> +REG16(RDR, 10) /* 8bit */
> +REG16(SCMR, 12) /* 8bit */
>     FIELD(SCMR, SMIF, 0, 1)
>     FIELD(SCMR, SINV, 2, 1)
>     FIELD(SCMR, SDIR, 3, 1)
>     FIELD(SCMR, BCP2, 7, 1)
> -REG8(SEMR, 7)
> +REG16(FCR, 12)
> +  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)
> +REG16(SEMR, 14) /* 8bit */
>     FIELD(SEMR, ACS0, 0, 1)
>     FIELD(SEMR, ABCS, 4, 1)
> +REG16(FDR, 14)
> +  FIELD(FDR, Rn, 0, 4)
> +  FIELD(FDR, Tn, 8, 4)
> +REG16(SPTR, 16)
> +  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)
> +REG16(LSR, 18)
> +  FIELD(LSR, ORER, 0, 1)
> +
> +#define SCIF_FIFO_DEPTH 16
> +
> +static const int sci_rtrg[] = {1, 4, 8, 14};
>   
> -static int can_receive(void *opaque)
> +static void update_event_time(RSCICommonState *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(RSCICommonState *sci_common, int req)
> +{
> +    int irq = 0;
> +    int rie;
> +    int tie;
> +    RSCIState *sci = RSCI(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(RSCICommonState *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(RSCICommonState *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 void send_byte(RSCIState *sci)
> +static int sci_can_receive(void *opaque)
> +{
> +    RSCICommonState *sci = RSCICommon(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)
> +{
> +    RSCICommonState *sci = RSCICommon(opaque);
> +    RenesasSCICommonClass *rc = SCI_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)
> +{
> +    RSCIFState *scif = RSCIF(opaque);
> +    RSCICommonState *sci = RSCICommon(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)
> +{
> +    RSCIFState *scif = RSCIF(opaque);
> +    RSCICommonState *sci = RSCICommon(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 sci_send_byte(RSCICommonState *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(RSCIFState *scif)
> +{
> +    RSCICommonState *sci = RSCICommon(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(RSCICommonState *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(RSCICommonState *sci)
> +{
> +    int64_t next_event = 0;
> +    RenesasSCICommonClass *rc = SCI_GET_CLASS(sci);
> +    if (!fifo8_is_empty(&sci->rxfifo)) {
> +        if (FIELD_EX16(sci->Xsr, SSR, RDRF)) {
> +            /* Receiver overrun */
> +            sci->Xsr = FIELD_DP16(sci->Xsr, SSR, ORER, 1);
> +            rc->irq_fn(sci, ERI);
> +        } else {
> +            /* Trigger next event */
> +            sci->Xsr = FIELD_DP16(sci->Xsr, SSR, RDRF, 1);
> +            rc->irq_fn(sci, RXI);
> +            next_event = sci->trtime;
> +        }
> +    }
> +    return next_event;
> +}
> +
> +static int64_t sci_tx_empty(RSCICommonState *sci)
> +{
> +    int64_t ret = 0;
> +    RenesasSCICommonClass *rc = SCI_GET_CLASS(sci);
> +    if (!FIELD_EX16(sci->Xsr, SSR, TDRE)) {
> +        sci_send_byte(sci);
> +        ret = sci->trtime;
> +        rc->irq_fn(sci, TXI);
>       } else {
> -        sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 1);
> -        if (FIELD_EX8(sci->scr, SCR, TEIE)) {
> -            qemu_set_irq(sci->irq[TEI], 1);
> +        sci->Xsr = FIELD_DP16(sci->Xsr, SSR, TEND, 1);
> +        rc->irq_fn(sci, BRI_TEI);
> +    }
> +    return ret;
> +}
> +
> +static int64_t scif_tx_empty(RSCICommonState *sci)
> +{
> +    RSCIFState *scif = RSCIF(sci);
> +    scif->tdcnt -= transmit_byte(scif);
> +    sci->Xsr = FIELD_DP16(sci->Xsr, FSR, TDFE, 1);
> +    scif_irq(sci, TXI);
> +    return 0;
> +}
> +
> +static int64_t scif_tx_end(RSCICommonState *sci)
> +{
> +    RSCIFState *scif = RSCIF(sci);
> +    scif->tdcnt = 0;
> +    sci->Xsr = FIELD_DP16(sci->Xsr, FSR, TEND, 1);
> +    return 0;
> +}
> +
> +static void sci_timer_event(void *opaque)
> +{
> +    RSCICommonState *sci = RSCICommon(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;
> +        }
> +        if (sci->event[i].time > 0) {
> +            next = MIN(next, sci->event[i].time);
>           }
>       }
> +    if (next < INT64_MAX) {
> +        timer_mod(sci->event_timer, next);
> +    } else {
> +        timer_del(sci->event_timer);
> +    }
>   }
>   
> -static void update_trtime(RSCIState *sci)
> +static int static_divrate(RSCICommonState *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;
> +    /* SCI / SCIF have static divide rate */
> +    return 32;
>   }
>   
> -static bool sci_is_tr_enabled(RSCIState *sci)
> +static int scia_divrate(RSCICommonState *sci)
>   {
> -    return FIELD_EX8(sci->scr, SCR, TE) || FIELD_EX8(sci->scr, SCR, RE);
> +    /*
> +     * SEMR.ABCS = 0 -> 32
> +     * SEMR.ABCS = 1 -> 16
> +     */
> +    RSCIAState *scia = RSCIA(sci);
> +    return 16 * (2 - FIELD_EX8(scia->semr, SEMR, ABCS));
>   }
>   
> -static void sci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
> +static void update_trtime(RSCICommonState *sci)
>   {
> -    RSCIState *sci = RSCI(opaque);
> +    RenesasSCICommonClass *rc = SCI_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;
>   
> -    switch (offset) {
> -    case A_SMR:
> -        if (!sci_is_tr_enabled(sci)) {
> -            sci->smr = val;
> -            update_trtime(sci);
> +        /* 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)
> +{
> +    RSCICommonState *sci = RSCICommon(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(RSCICommonState *sci, hwaddr addr)
> +{
> +    return (addr << 1) >> sci->regshift;
> +}
> +
> +static void sci_common_write(void *opaque, hwaddr addr,
> +                             uint64_t val, unsigned size)
> +{
> +    RSCICommonState *sci = RSCICommon(opaque);
> +    RenesasSCICommonClass *rc = SCI_GET_CLASS(opaque);
> +    switch (addr) {
> +    case A_SCR:
> +        sci->scr = val;
> +        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:
> -        if (!sci_is_tr_enabled(sci)) {
> -            sci->brr = val;
> -            update_trtime(sci);
> -        }
> +        sci->brr = val;
> +        update_trtime(sci);
>           break;
> -    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]);
> -            }
> +    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)
> +{
> +    RSCICommonState *sci = RSCICommon(opaque);
> +    RenesasSCICommonClass *rc = SCI_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(RSCICommon(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:
> +        RSCI(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)
> +{
> +    RSCICommonState *sci = RSCICommon(opaque);
> +    RSCIAState *scia = RSCIA(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);
> +    RSCICommonState *sci = RSCICommon(opaque);
> +    RSCIFState *scif = RSCIF(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)
> +{
> +    RSCICommonState *sci = RSCICommon(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;
> +        return fifo8_pop(&sci->rxfifo);
>       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)
> +{
> +    RSCICommonState *sci = RSCICommon(opaque);
> +    addr = map_address(sci, addr);
> +
> +    if (clock_is_enabled(sci->pck)) {
> +        switch (addr) {
> +        case A_TDR:
> +            return sci->tdr;
> +        case A_SPTR:
> +            return RSCI(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);
> +    RSCICommonState *sci = RSCICommon(opaque);
> +    RSCIAState *scia = RSCIA(opaque);
> +    uint64_t ret;
> +
> +    if (clock_is_enabled(sci->pck)) {
> +        addr = map_address(sci, addr);
> +        switch (addr) {
> +        case A_TDR:
> +            return sci->tdr;
> +        case A_RDR:
> +            ret = fifo8_pop(&sci->rxfifo);
> +            sci->Xsr = FIELD_DP16(sci->Xsr, SSR, RDRF, 0);
> +            return ret;
> +        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)
> +{
> +    RSCIFState *scif = RSCIF(opaque);
> +    RSCICommonState *sci = RSCICommon(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)
> +{
> +    RSCICommonState *sci = RSCICommon(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);
>   }
>   
>   static void sci_event(void *opaque, QEMUChrEvent event)
>   {
> -    RSCIState *sci = RSCI(opaque);
> +    RSCICommonState *sci = RSCICommon(opaque);
> +    RenesasSCICommonClass *rc = SCI_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)
> +{
> +    RSCICommonState *sci = RSCICommon(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);
> +    RSCICommonState *sci = RSCICommon(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->regshift = sci->regshift / 8 - 1;
> +    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(RSCICommonState *sci, int size)
>   {
> -    SysBusDevice *d = SYS_BUS_DEVICE(obj);
> -    RSCIState *sci = RSCI(obj);
> -    int i;
> +    SysBusDevice *d = SYS_BUS_DEVICE(sci);
> +    RenesasSCICommonClass *rc = SCI_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)
> +{
> +    RSCIState *sci = RSCI(dev);
> +    RSCICommonState *common = RSCICommon(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)
> +{
> +    RSCIAState *sci = RSCIA(dev);
> +    RSCICommonState *common = RSCICommon(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)
> +{
> +    RSCIFState *sci = RSCIF(dev);
> +    RSCICommonState *common = RSCICommon(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 = {
> @@ -302,49 +889,140 @@ 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", RSCICommonState, regshift, 8),
> +    DEFINE_PROP_UINT32("unit", RSCICommonState, unit, 0),
> +    DEFINE_PROP_CHR("chardev", RSCICommonState, chr),
>       DEFINE_PROP_END_OF_LIST(),
>   };
>   
> -static void rsci_class_init(ObjectClass *klass, void *data)
> +static void rsci_init(Object *obj)
>   {
> +    RSCICommonState *sci = RSCICommon(obj);
> +    sci->event[RXNEXT].handler = sci_rx_next;
> +    sci->event[TXEMPTY].handler = sci_tx_empty;
> +}
> +
> +static void rscif_init(Object *obj)
> +{
> +    RSCICommonState *sci = RSCICommon(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)
> +{
> +    RenesasSCICommonClass *rc = SCI_COMMON_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);
> +    RenesasSCICommonClass *comm_rc = SCI_COMMON_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)
> +{
> +    RenesasSCICommonClass *comm_rc = SCI_COMMON_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)
> +{
> +    RenesasSCICommonClass *comm_rc = SCI_COMMON_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_COMMON,
> +        .parent     = TYPE_SYS_BUS_DEVICE,
> +        .instance_size = sizeof(RSCICommonState),
> +        .instance_init = rsci_common_init,
> +        .class_init = rsci_common_class_init,
> +        .class_size = sizeof(RenesasSCICommonClass),
> +        .abstract = true,
> +    },
> +    {
> +        .name       = TYPE_RENESAS_SCI,
> +        .parent     = TYPE_RENESAS_SCI_COMMON,
> +        .instance_size = sizeof(RSCIState),
> +        .instance_init = rsci_init,
> +        .class_init = rsci_class_init,
> +        .class_size = sizeof(RenesasSCIClass),
> +    },
> +    {
> +        .name       = TYPE_RENESAS_SCIA,
> +        .parent     = TYPE_RENESAS_SCI_COMMON,
> +        .instance_size = sizeof(RSCIAState),
> +        /* 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_COMMON,
> +        .instance_size = sizeof(RSCIFState),
> +        .instance_init = rscif_init,
> +        .class_init = rscif_class_init,
> +        .class_size = sizeof(RenesasSCIFClass),
> +    },
> +};
> +
> +DEFINE_TYPES(renesas_sci_info)
> 



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

* Re: [PATCH 05/20] hw/rx: Add RX62N Clock generator
  2020-08-27 12:38 ` [PATCH 05/20] hw/rx: Add RX62N Clock generator Yoshinori Sato
  2020-09-08 21:11   ` Philippe Mathieu-Daudé
@ 2020-10-24 21:56   ` Philippe Mathieu-Daudé
  2020-10-24 21:58     ` Philippe Mathieu-Daudé
  1 sibling, 1 reply; 40+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-10-24 21:56 UTC (permalink / raw)
  To: Yoshinori Sato, qemu-devel

On 8/27/20 2:38 PM, Yoshinori Sato wrote:
> 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     |   5 +-
>   hw/rx/rx62n-cpg.c         | 344 ++++++++++++++++++++++++++++++++++++++
>   hw/rx/rx62n.c             |  52 +++---
>   hw/rx/meson.build         |   2 +-
>   5 files changed, 447 insertions(+), 28 deletions(-)
>   create mode 100644 include/hw/rx/rx62n-cpg.h
>   create mode 100644 hw/rx/rx62n-cpg.c
...

> diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c
> index 4b5c3c1079..ec63fa5db1 100644
> --- a/hw/rx/rx62n.c
> +++ b/hw/rx/rx62n.c
> @@ -47,6 +47,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,10 +57,6 @@
>   #define RX62N_CMT_IRQ   28
>   #define RX62N_SCI_IRQ   214
>   
> -#define RX62N_XTAL_MIN_HZ  (8 * 1000 * 1000)
> -#define RX62N_XTAL_MAX_HZ (14 * 1000 * 1000)
> -#define RX62N_PCLK_MAX_HZ (50 * 1000 * 1000)
> -
>   /*
>    * IRQ -> IPR mapping table
>    * 0x00 - 0x91: IPR no (IPR00 to IPR91)
> @@ -149,36 +146,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);

Runtime failure:

qemu-system-rx: Property 'renesas-tmr.unit' not found

> +    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));
>   }


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

* Re: [PATCH 05/20] hw/rx: Add RX62N Clock generator
  2020-10-24 21:56   ` Philippe Mathieu-Daudé
@ 2020-10-24 21:58     ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 40+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-10-24 21:58 UTC (permalink / raw)
  To: Yoshinori Sato, qemu-devel

On 10/24/20 11:56 PM, Philippe Mathieu-Daudé wrote:
> On 8/27/20 2:38 PM, Yoshinori Sato wrote:
>> 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     |   5 +-
>>   hw/rx/rx62n-cpg.c         | 344 ++++++++++++++++++++++++++++++++++++++
>>   hw/rx/rx62n.c             |  52 +++---
>>   hw/rx/meson.build         |   2 +-
>>   5 files changed, 447 insertions(+), 28 deletions(-)
>>   create mode 100644 include/hw/rx/rx62n-cpg.h
>>   create mode 100644 hw/rx/rx62n-cpg.c
> ...
> 
>> diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c
>> index 4b5c3c1079..ec63fa5db1 100644
>> --- a/hw/rx/rx62n.c
>> +++ b/hw/rx/rx62n.c
>> @@ -47,6 +47,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,10 +57,6 @@
>>   #define RX62N_CMT_IRQ   28
>>   #define RX62N_SCI_IRQ   214
>> -#define RX62N_XTAL_MIN_HZ  (8 * 1000 * 1000)
>> -#define RX62N_XTAL_MAX_HZ (14 * 1000 * 1000)
>> -#define RX62N_PCLK_MAX_HZ (50 * 1000 * 1000)
>> -
>>   /*
>>    * IRQ -> IPR mapping table
>>    * 0x00 - 0x91: IPR no (IPR00 to IPR91)
>> @@ -149,36 +146,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);
> 
> Runtime failure:
> 
> qemu-system-rx: Property 'renesas-tmr.unit' not found
> 
>> +    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));

qemu-system-rx: Can not find clock-in 'pck' for device type 'renesas-tmr'

>>   }
> 


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

* Re: [PATCH 08/20] hw/timer: Renesas TMU/CMT module.
  2020-08-27 12:38 ` [PATCH 08/20] hw/timer: Renesas TMU/CMT module Yoshinori Sato
@ 2020-10-24 22:47   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 40+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-10-24 22:47 UTC (permalink / raw)
  To: Yoshinori Sato, qemu-devel

On 8/27/20 2:38 PM, Yoshinori Sato wrote:
> 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 | 103 +++++
>   hw/timer/renesas_timer.c         | 639 +++++++++++++++++++++++++++++++
>   hw/timer/Kconfig                 |   4 +-
>   hw/timer/meson.build             |   2 +-
>   4 files changed, 745 insertions(+), 3 deletions(-)
>   create mode 100644 include/hw/timer/renesas_timer.h
>   create mode 100644 hw/timer/renesas_timer.c
...

> +static void update_clk(RenesasTimerBaseState *tmr, int ch)
> +{
> +    RenesasTimerBaseClass *tc = RenesasTimer_GET_CLASS(tmr);
> +    int t;
> +    t = tc->divrate(tmr, ch);
> +    if (t > 0) {
> +        t = tmr->input_freq / t;

If the clock is connected between INIT and REALIZE,
then t=0 and ...

> +        tmr->ch[ch].clk = NANOSECONDS_PER_SECOND / t;

... you get a DIV#0 here.

You can avoid it using clock_get_hz(tmr->pck) instead of
tmr->input_freq, which can be removed IMO.

> +    } else {
> +        tmr->ch[ch].clk = 0;
> +    }
> +}
...

> +static void tmr_pck_update(void *opaque)
> +{
> +    RenesasTimerBaseState *tmr = RenesasTimerBase(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 void cmt_init(Object *obj)
> +{
> +    SysBusDevice *d = SYS_BUS_DEVICE(obj);
> +    RenesasCMTState *cmt = RenesasCMT(obj);
> +    RenesasTimerBaseState *tmr = RenesasTimerBase(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);
> +}
...


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

* Re: [PATCH 12/20] hw/rx/rx62n: Use New SCI module.
  2020-08-27 12:38 ` [PATCH 12/20] hw/rx/rx62n: Use New " Yoshinori Sato
@ 2020-10-25  0:33   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 40+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-10-25  0:33 UTC (permalink / raw)
  To: Yoshinori Sato, qemu-devel

On 8/27/20 2:38 PM, Yoshinori Sato wrote:
> Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
> ---
>   include/hw/rx/rx62n.h | 2 +-
>   hw/rx/rx62n.c         | 7 ++++++-
>   2 files changed, 7 insertions(+), 2 deletions(-)
> 
> diff --git a/include/hw/rx/rx62n.h b/include/hw/rx/rx62n.h
> index 1182ca24de..f463148799 100644
> --- a/include/hw/rx/rx62n.h
> +++ b/include/hw/rx/rx62n.h
> @@ -70,7 +70,7 @@ typedef struct RX62NState {
>       RXICUState icu;
>       RenesasTMR8State tmr[RX62N_NR_TMR];
>       RenesasCMTState cmt[RX62N_NR_CMT];
> -    RSCIState sci[RX62N_NR_SCI];
> +    RSCIAState sci[RX62N_NR_SCI];
>       RX62NCPGState cpg;
>   
>       MemoryRegion *sysmem;
> diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c
> index 0223396110..f61383a4c2 100644
> --- a/hw/rx/rx62n.c
> +++ b/hw/rx/rx62n.c
> @@ -191,11 +191,13 @@ 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);
>       sysbus_realize(sci, &error_abort);
>   
>       irqbase = RX62N_SCI_IRQ + SCI_NR_IRQ * unit;
> @@ -203,6 +205,9 @@ static void register_sci(RX62NState *s, int unit)
>           sysbus_connect_irq(sci, i, s->irq[irqbase + i]);
>       }
>       sysbus_mmio_map(sci, 0, RX62N_SCI_BASE + unit * 0x08);
> +    snprintf(ckname, sizeof(ckname), "pck_sci-%d", unit);
> +    qdev_connect_clock_in(DEVICE(sci), "pck",
> +                          qdev_get_clock_out(DEVICE(&s->cpg), ckname));
>   }
>   
>   static void register_cpg(RX62NState *s)
> 

This makes the test_linux_sash test timeout:

$ avocado --show=app,console run -t arch:rx  tests/acceptance/
Fetching asset from 
tests/acceptance/machine_rx_gdbsim.py:RxGdbSimMachine.test_uboot
Fetching asset from 
tests/acceptance/machine_rx_gdbsim.py:RxGdbSimMachine.test_linux_sash
Fetching asset from 
tests/acceptance/machine_rx_gdbsim.py:RxGdbSimMachine.test_linux_sash
JOB ID     : 932d3a7a15be19e0d9772705d69d5b815793b1d3
JOB LOG    : 
/home/phil/avocado/job-results/job-2020-10-25T02.18-932d3a7/job.log
  (1/2) 
tests/acceptance/machine_rx_gdbsim.py:RxGdbSimMachine.test_uboot: 
console: U-Boot 2016.05-rc3-23705-ga1ef3c71cb-dirty (Feb 05 2019 - 
21:56:06 +0900)
PASS (0.11 s)
  (2/2) 
tests/acceptance/machine_rx_gdbsim.py:RxGdbSimMachine.test_linux_sash: 
console: Linux version 4.19.0+ (yo-satoh@yo-satoh-debian) (gcc version 
9.0.0 20181105 (experimental) (GCC)) #137 Wed Feb 20 23:20:02 JST 2019
console: Built 1 zonelists, mobility grouping on.  Total pages: 8128
console: Kernel command line:
console: Dentry cache hash table entries: 4096 (order: 2, 16384 bytes)
console: Inode-cache hash table entries: 2048 (order: 1, 8192 bytes)
console: Memory: 14648K/32768K available (871K kernel code, 95K rwdata, 
140K rodata, 96K init, 175K bss, 18120K reserved, 0K cma-reserved)
console: NR_IRQS: 256
console: rx-cmt: used for periodic clock events
console: clocksource: rx-tpu: mask: 0xffffffff max_cycles: 0xffffffff, 
max_idle_ns: 1274173631191 ns
console: 96.00 BogoMIPS (lpj=480000)
console: pid_max: default: 4096 minimum: 301
console: Mount-cache hash table entries: 1024 (order: 0, 4096 bytes)
console: Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes)
console: clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, 
max_idle_ns: 19112604462750000 ns
console: clocksource: Switched to clocksource rx-tpu
console: workingset: timestamp_bits=30 max_order=12 bucket_order=0
console: SuperH (H)SCI(F) driver initialized
console: 88240.serial: ttySC0 at MMIO 0x88240 (irq = 215, base_baud = 0) 
is a sci
console: console [ttySC0] enabled
console: 88248.serial: ttySC1 at MMIO 0x88248 (irq = 219, base_baud = 0) 
is a sci
console: random: get_random_bytes called from 0x01002e48 with crng_init=0
console: Freeing unused kernel memory: 96K
console: This architecture does not have kernel memory protection.
console: Run /sbin/init as init process
console: Run /etc/init as init process
console: Run /bin/init as init process
console: Run /bin/sh as init process
\console: random: fast init done
INTERRUPTED: Test interrupted by SIGTERM\nRunner error occurred: Timeout 
reached\nOriginal status: ERROR\n{'name': 
'2-tests/acceptance/machine_rx_gdbsim.py:RxGdbSimMachine.test_linux_sash', 
'logdir': 
'/home/phil/avocado/job-results/job-2020-10-25T02.18-932d3a7/test-resul... 
(30.23 s)
RESULTS    : PASS 1 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 1 | 
CANCEL 0
JOB TIME   : 30.73 s



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

* Re: [PATCH 01/20] loader.c: Add support Motrola S-record format.
  2020-08-27 12:38 ` [PATCH 01/20] loader.c: Add support Motrola S-record format Yoshinori Sato
  2020-09-08 20:44   ` Philippe Mathieu-Daudé
@ 2020-10-25  0:43   ` Philippe Mathieu-Daudé
  2020-10-27 21:05   ` Alistair Francis
  2 siblings, 0 replies; 40+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-10-25  0:43 UTC (permalink / raw)
  To: Yoshinori Sato, qemu-devel; +Cc: Alistair Francis, Su Hang

Cc'ing Alistair and Su Hang.

On 8/27/20 2:38 PM, Yoshinori Sato wrote:
> Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
> ---
>   include/hw/loader.h |  14 +++
>   hw/core/loader.c    | 208 ++++++++++++++++++++++++++++++++++++++++++++
>   2 files changed, 222 insertions(+)
> 
> diff --git a/include/hw/loader.h b/include/hw/loader.h
> index a9eeea3952..6f1fb62ded 100644
> --- a/include/hw/loader.h
> +++ b/include/hw/loader.h
> @@ -55,6 +55,20 @@ int load_image_targphys_as(const char *filename,
>    */
>   int load_targphys_hex_as(const char *filename, hwaddr *entry, AddressSpace *as);
>   
> +/*
> + * load_targphys_srec_as:
> + * @filename: Path to the .hex file
> + * @entry: Store the entry point given by the .hex file
> + * @as: The AddressSpace to load the .hex file to. The value of
> + *      address_space_memory is used if nothing is supplied here.
> + *
> + * Load a fixed .srec file into memory.
> + *
> + * Returns the size of the loaded .hex file on success, -1 otherwise.
> + */
> +int load_targphys_srec_as(const char *filename,
> +                          hwaddr *entry, AddressSpace *as);
> +
>   /** load_image_targphys:
>    * Same as load_image_targphys_as(), but doesn't allow the caller to specify
>    * an AddressSpace.
> diff --git a/hw/core/loader.c b/hw/core/loader.c
> index 8bbb1797a4..6964b04ec7 100644
> --- a/hw/core/loader.c
> +++ b/hw/core/loader.c
> @@ -1618,3 +1618,211 @@ int load_targphys_hex_as(const char *filename, hwaddr *entry, AddressSpace *as)
>       g_free(hex_blob);
>       return total_size;
>   }
> +
> +typedef enum {
> +    SREC_SOH,
> +    SREC_TYPE,
> +    SREC_LEN,
> +    SREC_ADDR,
> +    SREC_DATA,
> +    SREC_SKIP,
> +    SREC_SUM,
> +} srec_state;
> +
> +typedef struct {
> +    srec_state state;
> +    int nibble;
> +    int total_size;
> +    uint32_t address;
> +    uint32_t topaddr;
> +    uint32_t bufremain;
> +    int length;
> +    int addr_len;
> +    int record_type;
> +    uint8_t byte;
> +    uint8_t data[DATA_FIELD_MAX_LEN];
> +    uint8_t *datap;
> +    uint8_t *bufptr;
> +    uint8_t sum;
> +} SrecLine;
> +
> +static bool parse_srec_line(SrecLine *line, char c)
> +{
> +    if (!g_ascii_isxdigit(c)) {
> +        return false;
> +    }
> +    line->byte <<= 4;
> +    line->byte |= g_ascii_xdigit_value(c);
> +    line->nibble++;
> +    if (line->nibble == 2) {
> +        line->nibble = 0;
> +        line->length--;
> +        line->sum += line->byte;
> +        switch (line->state) {
> +        case SREC_SOH:
> +        case SREC_TYPE:
> +            /* first 2chars ignore parse */
> +            break;
> +        case SREC_LEN:
> +            line->sum = line->length = line->byte;
> +            if (line->addr_len > 0) {
> +                line->state = SREC_ADDR;
> +                line->address = 0;
> +            } else {
> +                line->state = SREC_SKIP;
> +            }
> +            break;
> +        case SREC_ADDR:
> +            line->address <<= 8;
> +            line->address |= line->byte;
> +            if (--line->addr_len == 0) {
> +                if (line->length > 1) {
> +                    if (line->record_type != 0) {
> +                        line->state = SREC_DATA;
> +                    } else {
> +                        line->state = SREC_SKIP;
> +                    }
> +                    line->datap = line->data;
> +                } else {
> +                    line->state = SREC_SUM;
> +                }
> +            }
> +            break;
> +        case SREC_DATA:
> +            *line->datap++ = line->byte;
> +            /* fail through */
> +        case SREC_SKIP:
> +            if (line->length == 1) {
> +                line->state = SREC_SUM;
> +            }
> +            break;
> +        case SREC_SUM:
> +            if ((line->sum & 0xff) != 0xff) {
> +                return false;
> +            }
> +        }
> +    }
> +    return true;
> +}
> +
> +#define SRECBUFSIZE 0x40000
> +
> +/* return size or -1 if error */
> +static int parse_srec_blob(const char *filename, hwaddr *addr,
> +                           uint8_t *hex_blob, size_t hex_blob_size,
> +                           AddressSpace *as)
> +{
> +    SrecLine line;
> +    size_t len;
> +    int total_len = 0;
> +    uint8_t *end = hex_blob + hex_blob_size;
> +    rom_transaction_begin();
> +    line.state = SREC_SOH;
> +    line.bufptr = g_malloc(SRECBUFSIZE);
> +    line.bufremain = SRECBUFSIZE;
> +    line.topaddr = UINT32_MAX;
> +    for (; hex_blob < end; ++hex_blob) {
> +        switch (*hex_blob) {
> +        case '\r':
> +        case '\n':
> +            if (line.state == SREC_SUM) {
> +                switch (line.record_type) {
> +                case 1:
> +                case 2:
> +                case 3:
> +                    len = line.datap - line.data;
> +                    if (line.topaddr == UINT32_MAX) {
> +                        line.topaddr = line.address;
> +                    }
> +                    if (line.bufremain < len || line.address < line.topaddr) {
> +                        rom_add_blob_fixed_as(filename, line.bufptr,
> +                                              SRECBUFSIZE - line.bufremain,
> +                                              line.topaddr, as);
> +                        line.topaddr = line.address;
> +                        line.bufremain = SRECBUFSIZE;
> +                    }
> +                    memcpy(line.bufptr + (line.address  - line.topaddr),
> +                           line.data, len);
> +                    line.bufremain -= len;
> +                    total_len += len;
> +                    break;
> +                case 7:
> +                case 8:
> +                case 9:
> +                    *addr = line.address;
> +                    break;
> +                }
> +                line.state = SREC_SOH;
> +            }
> +            break;
> +        /* start of a new record. */
> +        case 'S':
> +            if (line.state != SREC_SOH) {
> +                total_len = -1;
> +                goto out;
> +            }
> +            line.state = SREC_TYPE;
> +            break;
> +        /* decoding lines */
> +        default:
> +            if (line.state == SREC_TYPE) {
> +                if (g_ascii_isdigit(*hex_blob)) {
> +                    line.record_type = g_ascii_digit_value(*hex_blob);
> +                    switch (line.record_type) {
> +                    case 1:
> +                    case 2:
> +                    case 3:
> +                        line.addr_len = 1 + line.record_type;
> +                        break;
> +                    case 0:
> +                    case 5:
> +                        line.addr_len = 2;
> +                        break;
> +                    case 7:
> +                    case 8:
> +                    case 9:
> +                        line.addr_len = 11 - line.record_type;
> +                        break;
> +                    default:
> +                        line.addr_len = 0;
> +                    }
> +                }
> +                line.state = SREC_LEN;
> +                line.nibble = 0;
> +            } else {
> +                if (!parse_srec_line(&line, *hex_blob)) {
> +                    total_len = -1;
> +                    goto out;
> +                }
> +            }
> +            break;
> +        }
> +    }
> +    if (line.bufremain < SRECBUFSIZE) {
> +        rom_add_blob_fixed_as(filename, line.bufptr,
> +                              SRECBUFSIZE - line.bufremain,
> +                              line.topaddr, as);
> +    }
> +out:
> +    rom_transaction_end(total_len != -1);
> +    g_free(line.bufptr);
> +    return total_len;
> +}
> +
> +/* return size or -1 if error */
> +int load_targphys_srec_as(const char *filename, hwaddr *entry, AddressSpace *as)
> +{
> +    gsize hex_blob_size;
> +    gchar *hex_blob;
> +    int total_size = 0;
> +
> +    if (!g_file_get_contents(filename, &hex_blob, &hex_blob_size, NULL)) {
> +        return -1;
> +    }
> +
> +    total_size = parse_srec_blob(filename, entry, (uint8_t *)hex_blob,
> +                                 hex_blob_size, as);
> +
> +    g_free(hex_blob);
> +    return total_size;
> +}
> 



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

* Re: [PATCH 01/20] loader.c: Add support Motrola S-record format.
  2020-08-27 12:38 ` [PATCH 01/20] loader.c: Add support Motrola S-record format Yoshinori Sato
  2020-09-08 20:44   ` Philippe Mathieu-Daudé
  2020-10-25  0:43   ` Philippe Mathieu-Daudé
@ 2020-10-27 21:05   ` Alistair Francis
  2 siblings, 0 replies; 40+ messages in thread
From: Alistair Francis @ 2020-10-27 21:05 UTC (permalink / raw)
  To: Yoshinori Sato; +Cc: qemu-devel@nongnu.org Developers

On Thu, Aug 27, 2020 at 5:39 AM Yoshinori Sato
<ysato@users.sourceforge.jp> wrote:
>
> Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
> ---
>  include/hw/loader.h |  14 +++
>  hw/core/loader.c    | 208 ++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 222 insertions(+)
>
> diff --git a/include/hw/loader.h b/include/hw/loader.h
> index a9eeea3952..6f1fb62ded 100644
> --- a/include/hw/loader.h
> +++ b/include/hw/loader.h
> @@ -55,6 +55,20 @@ int load_image_targphys_as(const char *filename,
>   */
>  int load_targphys_hex_as(const char *filename, hwaddr *entry, AddressSpace *as);
>
> +/*
> + * load_targphys_srec_as:
> + * @filename: Path to the .hex file
> + * @entry: Store the entry point given by the .hex file
> + * @as: The AddressSpace to load the .hex file to. The value of
> + *      address_space_memory is used if nothing is supplied here.
> + *
> + * Load a fixed .srec file into memory.
> + *
> + * Returns the size of the loaded .hex file on success, -1 otherwise.
> + */
> +int load_targphys_srec_as(const char *filename,
> +                          hwaddr *entry, AddressSpace *as);
> +
>  /** load_image_targphys:
>   * Same as load_image_targphys_as(), but doesn't allow the caller to specify
>   * an AddressSpace.
> diff --git a/hw/core/loader.c b/hw/core/loader.c
> index 8bbb1797a4..6964b04ec7 100644
> --- a/hw/core/loader.c
> +++ b/hw/core/loader.c
> @@ -1618,3 +1618,211 @@ int load_targphys_hex_as(const char *filename, hwaddr *entry, AddressSpace *as)
>      g_free(hex_blob);
>      return total_size;
>  }
> +
> +typedef enum {
> +    SREC_SOH,
> +    SREC_TYPE,
> +    SREC_LEN,
> +    SREC_ADDR,
> +    SREC_DATA,
> +    SREC_SKIP,
> +    SREC_SUM,
> +} srec_state;
> +
> +typedef struct {
> +    srec_state state;
> +    int nibble;
> +    int total_size;
> +    uint32_t address;
> +    uint32_t topaddr;
> +    uint32_t bufremain;
> +    int length;
> +    int addr_len;
> +    int record_type;
> +    uint8_t byte;
> +    uint8_t data[DATA_FIELD_MAX_LEN];
> +    uint8_t *datap;
> +    uint8_t *bufptr;
> +    uint8_t sum;
> +} SrecLine;
> +
> +static bool parse_srec_line(SrecLine *line, char c)
> +{
> +    if (!g_ascii_isxdigit(c)) {
> +        return false;
> +    }
> +    line->byte <<= 4;
> +    line->byte |= g_ascii_xdigit_value(c);
> +    line->nibble++;
> +    if (line->nibble == 2) {
> +        line->nibble = 0;
> +        line->length--;
> +        line->sum += line->byte;
> +        switch (line->state) {
> +        case SREC_SOH:
> +        case SREC_TYPE:
> +            /* first 2chars ignore parse */
> +            break;
> +        case SREC_LEN:
> +            line->sum = line->length = line->byte;
> +            if (line->addr_len > 0) {
> +                line->state = SREC_ADDR;
> +                line->address = 0;
> +            } else {
> +                line->state = SREC_SKIP;
> +            }
> +            break;
> +        case SREC_ADDR:
> +            line->address <<= 8;
> +            line->address |= line->byte;
> +            if (--line->addr_len == 0) {
> +                if (line->length > 1) {
> +                    if (line->record_type != 0) {
> +                        line->state = SREC_DATA;
> +                    } else {
> +                        line->state = SREC_SKIP;
> +                    }
> +                    line->datap = line->data;
> +                } else {
> +                    line->state = SREC_SUM;
> +                }
> +            }
> +            break;
> +        case SREC_DATA:
> +            *line->datap++ = line->byte;
> +            /* fail through */

s/fail/fall/g

> +        case SREC_SKIP:
> +            if (line->length == 1) {
> +                line->state = SREC_SUM;
> +            }
> +            break;
> +        case SREC_SUM:
> +            if ((line->sum & 0xff) != 0xff) {
> +                return false;
> +            }
> +        }
> +    }
> +    return true;
> +}
> +
> +#define SRECBUFSIZE 0x40000
> +
> +/* return size or -1 if error */
> +static int parse_srec_blob(const char *filename, hwaddr *addr,
> +                           uint8_t *hex_blob, size_t hex_blob_size,
> +                           AddressSpace *as)
> +{
> +    SrecLine line;
> +    size_t len;
> +    int total_len = 0;
> +    uint8_t *end = hex_blob + hex_blob_size;
> +    rom_transaction_begin();
> +    line.state = SREC_SOH;
> +    line.bufptr = g_malloc(SRECBUFSIZE);
> +    line.bufremain = SRECBUFSIZE;
> +    line.topaddr = UINT32_MAX;
> +    for (; hex_blob < end; ++hex_blob) {
> +        switch (*hex_blob) {
> +        case '\r':
> +        case '\n':
> +            if (line.state == SREC_SUM) {
> +                switch (line.record_type) {

What if a file starts with a new line, won't line.record_type be invalid?

Alistair

> +                case 1:
> +                case 2:
> +                case 3:
> +                    len = line.datap - line.data;
> +                    if (line.topaddr == UINT32_MAX) {
> +                        line.topaddr = line.address;
> +                    }
> +                    if (line.bufremain < len || line.address < line.topaddr) {
> +                        rom_add_blob_fixed_as(filename, line.bufptr,
> +                                              SRECBUFSIZE - line.bufremain,
> +                                              line.topaddr, as);
> +                        line.topaddr = line.address;
> +                        line.bufremain = SRECBUFSIZE;
> +                    }
> +                    memcpy(line.bufptr + (line.address  - line.topaddr),
> +                           line.data, len);
> +                    line.bufremain -= len;
> +                    total_len += len;
> +                    break;
> +                case 7:
> +                case 8:
> +                case 9:
> +                    *addr = line.address;
> +                    break;
> +                }
> +                line.state = SREC_SOH;
> +            }
> +            break;
> +        /* start of a new record. */
> +        case 'S':
> +            if (line.state != SREC_SOH) {
> +                total_len = -1;
> +                goto out;
> +            }
> +            line.state = SREC_TYPE;
> +            break;
> +        /* decoding lines */
> +        default:
> +            if (line.state == SREC_TYPE) {
> +                if (g_ascii_isdigit(*hex_blob)) {
> +                    line.record_type = g_ascii_digit_value(*hex_blob);
> +                    switch (line.record_type) {
> +                    case 1:
> +                    case 2:
> +                    case 3:
> +                        line.addr_len = 1 + line.record_type;
> +                        break;
> +                    case 0:
> +                    case 5:
> +                        line.addr_len = 2;
> +                        break;
> +                    case 7:
> +                    case 8:
> +                    case 9:
> +                        line.addr_len = 11 - line.record_type;
> +                        break;
> +                    default:
> +                        line.addr_len = 0;
> +                    }
> +                }
> +                line.state = SREC_LEN;
> +                line.nibble = 0;
> +            } else {
> +                if (!parse_srec_line(&line, *hex_blob)) {
> +                    total_len = -1;
> +                    goto out;
> +                }
> +            }
> +            break;
> +        }
> +    }
> +    if (line.bufremain < SRECBUFSIZE) {
> +        rom_add_blob_fixed_as(filename, line.bufptr,
> +                              SRECBUFSIZE - line.bufremain,
> +                              line.topaddr, as);
> +    }
> +out:
> +    rom_transaction_end(total_len != -1);
> +    g_free(line.bufptr);
> +    return total_len;
> +}
> +
> +/* return size or -1 if error */
> +int load_targphys_srec_as(const char *filename, hwaddr *entry, AddressSpace *as)
> +{
> +    gsize hex_blob_size;
> +    gchar *hex_blob;
> +    int total_size = 0;
> +
> +    if (!g_file_get_contents(filename, &hex_blob, &hex_blob_size, NULL)) {
> +        return -1;
> +    }
> +
> +    total_size = parse_srec_blob(filename, entry, (uint8_t *)hex_blob,
> +                                 hex_blob_size, as);
> +
> +    g_free(hex_blob);
> +    return total_size;
> +}
> --
> 2.20.1
>
>


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

end of thread, other threads:[~2020-10-27 21:20 UTC | newest]

Thread overview: 40+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-08-27 12:38 [PATCH 00/20] RX target update Yoshinori Sato
2020-08-27 12:38 ` [PATCH 01/20] loader.c: Add support Motrola S-record format Yoshinori Sato
2020-09-08 20:44   ` Philippe Mathieu-Daudé
2020-10-25  0:43   ` Philippe Mathieu-Daudé
2020-10-27 21:05   ` Alistair Francis
2020-08-27 12:38 ` [PATCH 02/20] include/elf.h: Add EM_RX Yoshinori Sato
2020-08-27 12:38 ` [PATCH 03/20] hw/rx: Firmware and kernel loader Yoshinori Sato
2020-09-08 20:47   ` Philippe Mathieu-Daudé
2020-08-27 12:38 ` [PATCH 04/20] hw/rx: New firmware loader Yoshinori Sato
2020-08-27 12:38 ` [PATCH 05/20] hw/rx: Add RX62N Clock generator Yoshinori Sato
2020-09-08 21:11   ` Philippe Mathieu-Daudé
2020-10-24 21:56   ` Philippe Mathieu-Daudé
2020-10-24 21:58     ` Philippe Mathieu-Daudé
2020-08-27 12:38 ` [PATCH 06/20] hw/timer: Renesas 8bit timer emulation Yoshinori Sato
2020-10-24 21:27   ` Philippe Mathieu-Daudé
2020-10-24 21:29     ` Philippe Mathieu-Daudé
2020-08-27 12:38 ` [PATCH 07/20] hw/rx: RX62N convert new 8bit timer Yoshinori Sato
2020-08-27 12:38 ` [PATCH 08/20] hw/timer: Renesas TMU/CMT module Yoshinori Sato
2020-10-24 22:47   ` Philippe Mathieu-Daudé
2020-08-27 12:38 ` [PATCH 09/20] hw/timer: Remove renesas_cmt Yoshinori Sato
2020-08-27 12:38 ` [PATCH 10/20] hw/rx: Convert to renesas_timer Yoshinori Sato
2020-08-27 12:38 ` [PATCH 11/20] hw/char: Renesas SCI module Yoshinori Sato
2020-10-24 21:40   ` Philippe Mathieu-Daudé
2020-08-27 12:38 ` [PATCH 12/20] hw/rx/rx62n: Use New " Yoshinori Sato
2020-10-25  0:33   ` Philippe Mathieu-Daudé
2020-08-27 12:38 ` [PATCH 13/20] hw/timer: Add Renesas MTU2 Yoshinori Sato
2020-08-27 12:38 ` [PATCH 14/20] hw/rx/rx62n: RX62N Add MTU module Yoshinori Sato
2020-09-08 21:12   ` Philippe Mathieu-Daudé
2020-08-27 12:38 ` [PATCH 15/20] hw/net: Add generic Bit-bang MDIO PHY Yoshinori Sato
2020-08-27 12:38 ` [PATCH 16/20] hw/net: Add Renesas On-chip Ethernet MAC Yoshinori Sato
2020-10-24 21:37   ` Philippe Mathieu-Daudé
2020-08-27 12:38 ` [PATCH 17/20] hw/rx/rx62n: Add Ethernet support Yoshinori Sato
2020-08-27 12:38 ` [PATCH 18/20] hw/rx: Add Tokudenkairo TKDN-RX62N-BRD Yoshinori Sato
2020-09-08 21:18   ` Philippe Mathieu-Daudé
2020-08-27 12:38 ` [PATCH 19/20] hw/rx: Add CQ-FRK-RX62N target Yoshinori Sato
2020-09-08 21:20   ` Philippe Mathieu-Daudé
2020-08-27 12:38 ` [PATCH 20/20] MAINTAINERS: Update RX entry Yoshinori Sato
2020-09-08 21:21   ` Philippe Mathieu-Daudé
2020-08-31 20:38 ` [PATCH 00/20] RX target update Philippe Mathieu-Daudé
2020-09-10 16:06   ` Yoshinori Sato

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.