* [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.