From patchwork Thu Aug 27 12:38:40 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 11740635 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B336713A4 for ; Thu, 27 Aug 2020 12:39:52 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 939C02177B for ; Thu, 27 Aug 2020 12:39:52 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 939C02177B Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=users.sourceforge.jp Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:39184 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHC3-0001Kt-MD for patchwork-qemu-devel@patchwork.kernel.org; Thu, 27 Aug 2020 08:39:51 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:58938) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kBHBR-0007xe-IZ for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:13 -0400 Received: from mail02.asahi-net.or.jp ([202.224.55.14]:56301) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHBN-0005u5-SJ for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:13 -0400 Received: from sakura.ysato.name (ik1-413-38519.vs.sakura.ne.jp [153.127.30.23]) (Authenticated sender: PQ4Y-STU) by mail02.asahi-net.or.jp (Postfix) with ESMTPA id 84A9B25525; Thu, 27 Aug 2020 21:39:05 +0900 (JST) Received: from yo-satoh-debian.localdomain (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by sakura.ysato.name (Postfix) with ESMTPSA id 0BBA51C0792; Thu, 27 Aug 2020 21:39:05 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 01/20] loader.c: Add support Motrola S-record format. Date: Thu, 27 Aug 2020 21:38:40 +0900 Message-Id: <20200827123859.81793-2-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200827123859.81793-1-ysato@users.sourceforge.jp> References: <20200827123859.81793-1-ysato@users.sourceforge.jp> MIME-Version: 1.0 Received-SPF: softfail client-ip=202.224.55.14; envelope-from=ysato@users.sourceforge.jp; helo=mail02.asahi-net.or.jp X-detected-operating-system: by eggs.gnu.org: First seen = 2020/08/27 08:39:06 X-ACL-Warn: Detected OS = ??? X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_NONE=0.001, SPF_SOFTFAIL=0.665 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" Signed-off-by: Yoshinori Sato --- 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; +} From patchwork Thu Aug 27 12:38:41 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 11740631 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D6CCF913 for ; Thu, 27 Aug 2020 12:39:49 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id B68332177B for ; Thu, 27 Aug 2020 12:39:49 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org B68332177B Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=users.sourceforge.jp Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:38828 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHC0-0001CD-Tj for patchwork-qemu-devel@patchwork.kernel.org; Thu, 27 Aug 2020 08:39:48 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:58912) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kBHBQ-0007xI-LS for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:12 -0400 Received: from mail01.asahi-net.or.jp ([202.224.55.13]:47876) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHBN-0005u9-UT for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:12 -0400 Received: from sakura.ysato.name (ik1-413-38519.vs.sakura.ne.jp [153.127.30.23]) (Authenticated sender: PQ4Y-STU) by mail01.asahi-net.or.jp (Postfix) with ESMTPA id A3322108660; Thu, 27 Aug 2020 21:39:05 +0900 (JST) Received: from yo-satoh-debian.localdomain (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by sakura.ysato.name (Postfix) with ESMTPSA id 43B4C1C07A8; Thu, 27 Aug 2020 21:39:05 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 02/20] include/elf.h: Add EM_RX. Date: Thu, 27 Aug 2020 21:38:41 +0900 Message-Id: <20200827123859.81793-3-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200827123859.81793-1-ysato@users.sourceforge.jp> References: <20200827123859.81793-1-ysato@users.sourceforge.jp> MIME-Version: 1.0 Received-SPF: softfail client-ip=202.224.55.13; envelope-from=ysato@users.sourceforge.jp; helo=mail01.asahi-net.or.jp X-detected-operating-system: by eggs.gnu.org: First seen = 2020/08/27 08:39:06 X-ACL-Warn: Detected OS = ??? X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_NONE=0.001, SPF_SOFTFAIL=0.665 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" Signed-off-by: Yoshinori Sato --- 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 */ From patchwork Thu Aug 27 12:38:42 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 11740629 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A3EEE913 for ; Thu, 27 Aug 2020 12:39:48 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 84D912177B for ; Thu, 27 Aug 2020 12:39:48 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 84D912177B Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=users.sourceforge.jp Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:38742 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHBz-0001AB-Os for patchwork-qemu-devel@patchwork.kernel.org; Thu, 27 Aug 2020 08:39:47 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:58900) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kBHBQ-0007xC-6F for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:12 -0400 Received: from mail02.asahi-net.or.jp ([202.224.55.14]:56302) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHBN-0005u8-TJ for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:11 -0400 Received: from sakura.ysato.name (ik1-413-38519.vs.sakura.ne.jp [153.127.30.23]) (Authenticated sender: PQ4Y-STU) by mail02.asahi-net.or.jp (Postfix) with ESMTPA id CC51026329; Thu, 27 Aug 2020 21:39:05 +0900 (JST) Received: from yo-satoh-debian.localdomain (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by sakura.ysato.name (Postfix) with ESMTPSA id 7C96C1C07B2; Thu, 27 Aug 2020 21:39:05 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 03/20] hw/rx: Firmware and kernel loader. Date: Thu, 27 Aug 2020 21:38:42 +0900 Message-Id: <20200827123859.81793-4-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200827123859.81793-1-ysato@users.sourceforge.jp> References: <20200827123859.81793-1-ysato@users.sourceforge.jp> MIME-Version: 1.0 Received-SPF: softfail client-ip=202.224.55.14; envelope-from=ysato@users.sourceforge.jp; helo=mail02.asahi-net.or.jp X-detected-operating-system: by eggs.gnu.org: First seen = 2020/08/27 08:39:06 X-ACL-Warn: Detected OS = ??? X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_NONE=0.001, SPF_SOFTFAIL=0.665 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" Suppoerted format. ELF, HEX, SREC and Raw firmware. fit and Raw kernel image. Signed-off-by: Yoshinori Sato --- 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 . + */ + +#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 . + */ + +#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 + +#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')) From patchwork Thu Aug 27 12:38:43 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 11740639 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A8EEA913 for ; Thu, 27 Aug 2020 12:41:10 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 8930C2177B for ; Thu, 27 Aug 2020 12:41:10 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 8930C2177B Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=users.sourceforge.jp Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:47024 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHDJ-0004Yd-KR for patchwork-qemu-devel@patchwork.kernel.org; Thu, 27 Aug 2020 08:41:09 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:58958) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kBHBS-0007yC-83 for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:14 -0400 Received: from mail01.asahi-net.or.jp ([202.224.55.13]:47877) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHBN-0005uA-Tb for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:13 -0400 Received: from sakura.ysato.name (ik1-413-38519.vs.sakura.ne.jp [153.127.30.23]) (Authenticated sender: PQ4Y-STU) by mail01.asahi-net.or.jp (Postfix) with ESMTPA id 045621080B5; Thu, 27 Aug 2020 21:39:06 +0900 (JST) Received: from yo-satoh-debian.localdomain (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by sakura.ysato.name (Postfix) with ESMTPSA id B23DE1C0696; Thu, 27 Aug 2020 21:39:05 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 04/20] hw/rx: New firmware loader. Date: Thu, 27 Aug 2020 21:38:43 +0900 Message-Id: <20200827123859.81793-5-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200827123859.81793-1-ysato@users.sourceforge.jp> References: <20200827123859.81793-1-ysato@users.sourceforge.jp> MIME-Version: 1.0 Received-SPF: softfail client-ip=202.224.55.13; envelope-from=ysato@users.sourceforge.jp; helo=mail01.asahi-net.or.jp X-detected-operating-system: by eggs.gnu.org: First seen = 2020/08/27 08:39:06 X-ACL-Warn: Detected OS = ??? X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_NONE=0.001, SPF_SOFTFAIL=0.665 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" Separate the loading of the firmware from the target definition as it is an obstacle to adding more targets. Signed-off-by: Yoshinori Sato --- 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(), }; From patchwork Thu Aug 27 12:38:44 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 11740657 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 2B0C8913 for ; Thu, 27 Aug 2020 12:45:09 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id EF23E22B40 for ; Thu, 27 Aug 2020 12:45:08 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org EF23E22B40 Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=users.sourceforge.jp Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:35244 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHHA-0002nR-5Y for patchwork-qemu-devel@patchwork.kernel.org; Thu, 27 Aug 2020 08:45:08 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:59014) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kBHBT-00082T-Jm for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:15 -0400 Received: from mail03.asahi-net.or.jp ([202.224.55.15]:39208) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHBO-0005uR-1U for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:15 -0400 Received: from sakura.ysato.name (ik1-413-38519.vs.sakura.ne.jp [153.127.30.23]) (Authenticated sender: PQ4Y-STU) by mail03.asahi-net.or.jp (Postfix) with ESMTPA id 47DB7268A6; Thu, 27 Aug 2020 21:39:06 +0900 (JST) Received: from yo-satoh-debian.localdomain (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by sakura.ysato.name (Postfix) with ESMTPSA id E2D131C0792; Thu, 27 Aug 2020 21:39:05 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 05/20] hw/rx: Add RX62N Clock generator Date: Thu, 27 Aug 2020 21:38:44 +0900 Message-Id: <20200827123859.81793-6-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200827123859.81793-1-ysato@users.sourceforge.jp> References: <20200827123859.81793-1-ysato@users.sourceforge.jp> MIME-Version: 1.0 Received-SPF: softfail client-ip=202.224.55.15; envelope-from=ysato@users.sourceforge.jp; helo=mail03.asahi-net.or.jp X-detected-operating-system: by eggs.gnu.org: First seen = 2020/08/27 08:39:06 X-ACL-Warn: Detected OS = ??? X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_NONE=0.001, SPF_SOFTFAIL=0.665 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" This module generated core and peripheral clock. Signed-off-by: Yoshinori Sato --- 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 . + */ + +#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 . + */ + +#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} From patchwork Thu Aug 27 12:38:45 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 11740663 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 10A39722 for ; Thu, 27 Aug 2020 12:47:07 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id D709622CAE for ; Thu, 27 Aug 2020 12:47:06 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org D709622CAE Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=users.sourceforge.jp Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:42178 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHJ3-0005fm-M1 for patchwork-qemu-devel@patchwork.kernel.org; Thu, 27 Aug 2020 08:47:05 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:59022) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kBHBT-00083F-RO for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:15 -0400 Received: from mail03.asahi-net.or.jp ([202.224.55.15]:39211) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHBN-0005uV-Vh for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:15 -0400 Received: from sakura.ysato.name (ik1-413-38519.vs.sakura.ne.jp [153.127.30.23]) (Authenticated sender: PQ4Y-STU) by mail03.asahi-net.or.jp (Postfix) with ESMTPA id 9380A26B17; Thu, 27 Aug 2020 21:39:06 +0900 (JST) Received: from yo-satoh-debian.localdomain (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by sakura.ysato.name (Postfix) with ESMTPSA id 3009B1C0696; Thu, 27 Aug 2020 21:39:06 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 06/20] hw/timer: Renesas 8bit timer emulation. Date: Thu, 27 Aug 2020 21:38:45 +0900 Message-Id: <20200827123859.81793-7-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200827123859.81793-1-ysato@users.sourceforge.jp> References: <20200827123859.81793-1-ysato@users.sourceforge.jp> MIME-Version: 1.0 Received-SPF: softfail client-ip=202.224.55.15; envelope-from=ysato@users.sourceforge.jp; helo=mail03.asahi-net.or.jp X-detected-operating-system: by eggs.gnu.org: First seen = 2020/08/27 08:39:06 X-ACL-Warn: Detected OS = ??? X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_NONE=0.001, SPF_SOFTFAIL=0.665 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" Rewrite for clock API. Signed-off-by: Yoshinori Sato --- 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 . - */ - -#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 . + */ + +#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')) From patchwork Thu Aug 27 12:38:46 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 11740633 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D986C1731 for ; Thu, 27 Aug 2020 12:39:49 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id B78C922B40 for ; Thu, 27 Aug 2020 12:39:49 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org B78C922B40 Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=users.sourceforge.jp Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:38804 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHC0-0001Bd-Tz for patchwork-qemu-devel@patchwork.kernel.org; Thu, 27 Aug 2020 08:39:48 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:58920) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kBHBQ-0007xM-Q9 for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:12 -0400 Received: from mail03.asahi-net.or.jp ([202.224.55.15]:39213) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHBN-0005uW-Ut for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:12 -0400 Received: from sakura.ysato.name (ik1-413-38519.vs.sakura.ne.jp [153.127.30.23]) (Authenticated sender: PQ4Y-STU) by mail03.asahi-net.or.jp (Postfix) with ESMTPA id C66D52703C; Thu, 27 Aug 2020 21:39:06 +0900 (JST) Received: from yo-satoh-debian.localdomain (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by sakura.ysato.name (Postfix) with ESMTPSA id 7DB941C0792; Thu, 27 Aug 2020 21:39:06 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 07/20] hw/rx: RX62N convert new 8bit timer. Date: Thu, 27 Aug 2020 21:38:46 +0900 Message-Id: <20200827123859.81793-8-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200827123859.81793-1-ysato@users.sourceforge.jp> References: <20200827123859.81793-1-ysato@users.sourceforge.jp> MIME-Version: 1.0 Received-SPF: softfail client-ip=202.224.55.15; envelope-from=ysato@users.sourceforge.jp; helo=mail03.asahi-net.or.jp X-detected-operating-system: by eggs.gnu.org: First seen = 2020/08/27 08:39:06 X-ACL-Warn: Detected OS = ??? X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_NONE=0.001, SPF_SOFTFAIL=0.665 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" Signed-off-by: Yoshinori Sato --- 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 From patchwork Thu Aug 27 12:38:47 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 11740641 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 9B124913 for ; Thu, 27 Aug 2020 12:41:16 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 6602E2177B for ; Thu, 27 Aug 2020 12:41:16 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 6602E2177B Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=users.sourceforge.jp Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:47604 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHDP-0004mm-Gf for patchwork-qemu-devel@patchwork.kernel.org; Thu, 27 Aug 2020 08:41:15 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:59000) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kBHBT-00081U-9n for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:15 -0400 Received: from mail01.asahi-net.or.jp ([202.224.55.13]:47883) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHBN-0005uu-S5 for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:14 -0400 Received: from sakura.ysato.name (ik1-413-38519.vs.sakura.ne.jp [153.127.30.23]) (Authenticated sender: PQ4Y-STU) by mail01.asahi-net.or.jp (Postfix) with ESMTPA id 15835108666; Thu, 27 Aug 2020 21:39:07 +0900 (JST) Received: from yo-satoh-debian.localdomain (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by sakura.ysato.name (Postfix) with ESMTPSA id B1FA71C0696; Thu, 27 Aug 2020 21:39:06 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 08/20] hw/timer: Renesas TMU/CMT module. Date: Thu, 27 Aug 2020 21:38:47 +0900 Message-Id: <20200827123859.81793-9-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200827123859.81793-1-ysato@users.sourceforge.jp> References: <20200827123859.81793-1-ysato@users.sourceforge.jp> MIME-Version: 1.0 Received-SPF: softfail client-ip=202.224.55.13; envelope-from=ysato@users.sourceforge.jp; helo=mail01.asahi-net.or.jp X-detected-operating-system: by eggs.gnu.org: First seen = 2020/08/27 08:39:06 X-ACL-Warn: Detected OS = ??? X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_NONE=0.001, SPF_SOFTFAIL=0.665 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" 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 --- 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 . + */ + +#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')) From patchwork Thu Aug 27 12:38:48 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 11740637 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5B0B013A4 for ; Thu, 27 Aug 2020 12:41:10 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 3B7252177B for ; Thu, 27 Aug 2020 12:41:10 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 3B7252177B Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=users.sourceforge.jp Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:46998 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHDJ-0004Y6-A8 for patchwork-qemu-devel@patchwork.kernel.org; Thu, 27 Aug 2020 08:41:09 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:58928) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kBHBR-0007xb-BS for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:13 -0400 Received: from mail01.asahi-net.or.jp ([202.224.55.13]:47886) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHBO-0005vG-17 for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:13 -0400 Received: from sakura.ysato.name (ik1-413-38519.vs.sakura.ne.jp [153.127.30.23]) (Authenticated sender: PQ4Y-STU) by mail01.asahi-net.or.jp (Postfix) with ESMTPA id 4C9D3108673; Thu, 27 Aug 2020 21:39:07 +0900 (JST) Received: from yo-satoh-debian.localdomain (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by sakura.ysato.name (Postfix) with ESMTPSA id F34F41C0792; Thu, 27 Aug 2020 21:39:06 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 09/20] hw/timer: Remove renesas_cmt. Date: Thu, 27 Aug 2020 21:38:48 +0900 Message-Id: <20200827123859.81793-10-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200827123859.81793-1-ysato@users.sourceforge.jp> References: <20200827123859.81793-1-ysato@users.sourceforge.jp> MIME-Version: 1.0 Received-SPF: softfail client-ip=202.224.55.13; envelope-from=ysato@users.sourceforge.jp; helo=mail01.asahi-net.or.jp X-detected-operating-system: by eggs.gnu.org: First seen = 2020/08/27 08:39:06 X-ACL-Warn: Detected OS = ??? X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_NONE=0.001, SPF_SOFTFAIL=0.665 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" This module replaced to unified renesas_timer. Signed-off-by: Yoshinori Sato --- 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 . - */ - -#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) From patchwork Thu Aug 27 12:38:49 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 11740655 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 72239722 for ; Thu, 27 Aug 2020 12:45:08 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 4E40722B40 for ; Thu, 27 Aug 2020 12:45:08 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 4E40722B40 Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=users.sourceforge.jp Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:35272 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHH9-0002nr-BF for patchwork-qemu-devel@patchwork.kernel.org; Thu, 27 Aug 2020 08:45:07 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:59006) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kBHBT-000825-GT for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:15 -0400 Received: from mail01.asahi-net.or.jp ([202.224.55.13]:47889) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHBO-0005vN-BQ for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:15 -0400 Received: from sakura.ysato.name (ik1-413-38519.vs.sakura.ne.jp [153.127.30.23]) (Authenticated sender: PQ4Y-STU) by mail01.asahi-net.or.jp (Postfix) with ESMTPA id 5E1B41086BE; Thu, 27 Aug 2020 21:39:07 +0900 (JST) Received: from yo-satoh-debian.localdomain (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by sakura.ysato.name (Postfix) with ESMTPSA id 36E431C0696; Thu, 27 Aug 2020 21:39:07 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 10/20] hw/rx: Convert to renesas_timer Date: Thu, 27 Aug 2020 21:38:49 +0900 Message-Id: <20200827123859.81793-11-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200827123859.81793-1-ysato@users.sourceforge.jp> References: <20200827123859.81793-1-ysato@users.sourceforge.jp> MIME-Version: 1.0 Received-SPF: softfail client-ip=202.224.55.13; envelope-from=ysato@users.sourceforge.jp; helo=mail01.asahi-net.or.jp X-detected-operating-system: by eggs.gnu.org: First seen = 2020/08/27 08:39:06 X-ACL-Warn: Detected OS = ??? X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_NONE=0.001, SPF_SOFTFAIL=0.665 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" Signed-off-by: Yoshinori Sato --- 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 From patchwork Thu Aug 27 12:38:50 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 11740649 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A6EF1913 for ; Thu, 27 Aug 2020 12:43:17 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 66FAC22B40 for ; Thu, 27 Aug 2020 12:43:17 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 66FAC22B40 Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=users.sourceforge.jp Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:56228 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHFM-0008Fa-MT for patchwork-qemu-devel@patchwork.kernel.org; Thu, 27 Aug 2020 08:43:16 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:59120) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kBHBY-0008Fj-6Y for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:20 -0400 Received: from mail01.asahi-net.or.jp ([202.224.55.13]:47907) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHBR-0005xf-De for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:19 -0400 Received: from sakura.ysato.name (ik1-413-38519.vs.sakura.ne.jp [153.127.30.23]) (Authenticated sender: PQ4Y-STU) by mail01.asahi-net.or.jp (Postfix) with ESMTPA id C75071086CC; Thu, 27 Aug 2020 21:39:07 +0900 (JST) Received: from yo-satoh-debian.localdomain (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by sakura.ysato.name (Postfix) with ESMTPSA id 68AA81C0792; Thu, 27 Aug 2020 21:39:07 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 11/20] hw/char: Renesas SCI module. Date: Thu, 27 Aug 2020 21:38:50 +0900 Message-Id: <20200827123859.81793-12-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200827123859.81793-1-ysato@users.sourceforge.jp> References: <20200827123859.81793-1-ysato@users.sourceforge.jp> MIME-Version: 1.0 Received-SPF: softfail client-ip=202.224.55.13; envelope-from=ysato@users.sourceforge.jp; helo=mail01.asahi-net.or.jp X-detected-operating-system: by eggs.gnu.org: First seen = 2020/08/27 08:39:06 X-ACL-Warn: Detected OS = ??? X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_NONE=0.001, SPF_SOFTFAIL=0.665 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" 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 --- 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) From patchwork Thu Aug 27 12:38:51 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 11740651 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B7EAE913 for ; Thu, 27 Aug 2020 12:43:45 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 997E82177B for ; Thu, 27 Aug 2020 12:43:45 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 997E82177B Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=users.sourceforge.jp Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:58590 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHFo-0000mr-Qr for patchwork-qemu-devel@patchwork.kernel.org; Thu, 27 Aug 2020 08:43:44 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:59068) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kBHBV-00087P-D0 for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:17 -0400 Received: from mail01.asahi-net.or.jp ([202.224.55.13]:47908) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHBR-0005xg-F8 for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:16 -0400 Received: from sakura.ysato.name (ik1-413-38519.vs.sakura.ne.jp [153.127.30.23]) (Authenticated sender: PQ4Y-STU) by mail01.asahi-net.or.jp (Postfix) with ESMTPA id F28311086DC; Thu, 27 Aug 2020 21:39:07 +0900 (JST) Received: from yo-satoh-debian.localdomain (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by sakura.ysato.name (Postfix) with ESMTPSA id B20451C0696; Thu, 27 Aug 2020 21:39:07 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 12/20] hw/rx/rx62n: Use New SCI module. Date: Thu, 27 Aug 2020 21:38:51 +0900 Message-Id: <20200827123859.81793-13-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200827123859.81793-1-ysato@users.sourceforge.jp> References: <20200827123859.81793-1-ysato@users.sourceforge.jp> MIME-Version: 1.0 Received-SPF: softfail client-ip=202.224.55.13; envelope-from=ysato@users.sourceforge.jp; helo=mail01.asahi-net.or.jp X-detected-operating-system: by eggs.gnu.org: First seen = 2020/08/27 08:39:06 X-ACL-Warn: Detected OS = ??? X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_NONE=0.001, SPF_SOFTFAIL=0.665 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" Signed-off-by: Yoshinori Sato --- 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) From patchwork Thu Aug 27 12:38:52 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 11740671 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 00742722 for ; Thu, 27 Aug 2020 12:49:31 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id B4C1222CAE for ; Thu, 27 Aug 2020 12:49:31 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org B4C1222CAE Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=users.sourceforge.jp Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:51142 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHLO-0000tn-To for patchwork-qemu-devel@patchwork.kernel.org; Thu, 27 Aug 2020 08:49:30 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:59124) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kBHBY-0008H1-KF for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:20 -0400 Received: from mail01.asahi-net.or.jp ([202.224.55.13]:47912) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHBS-0005y4-31 for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:20 -0400 Received: from sakura.ysato.name (ik1-413-38519.vs.sakura.ne.jp [153.127.30.23]) (Authenticated sender: PQ4Y-STU) by mail01.asahi-net.or.jp (Postfix) with ESMTPA id 66DA5108926; Thu, 27 Aug 2020 21:39:08 +0900 (JST) Received: from yo-satoh-debian.localdomain (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by sakura.ysato.name (Postfix) with ESMTPSA id DFE8D1C0792; Thu, 27 Aug 2020 21:39:07 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 13/20] hw/timer: Add Renesas MTU2 Date: Thu, 27 Aug 2020 21:38:52 +0900 Message-Id: <20200827123859.81793-14-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200827123859.81793-1-ysato@users.sourceforge.jp> References: <20200827123859.81793-1-ysato@users.sourceforge.jp> MIME-Version: 1.0 Received-SPF: softfail client-ip=202.224.55.13; envelope-from=ysato@users.sourceforge.jp; helo=mail01.asahi-net.or.jp X-detected-operating-system: by eggs.gnu.org: First seen = 2020/08/27 08:39:06 X-ACL-Warn: Detected OS = ??? X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_NONE=0.001, SPF_SOFTFAIL=0.665 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" Signed-off-by: Yoshinori Sato --- 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 . + */ + +#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')) From patchwork Thu Aug 27 12:38:53 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 11740667 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 93D76722 for ; Thu, 27 Aug 2020 12:48:29 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 5F8DD22CB1 for ; Thu, 27 Aug 2020 12:48:29 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 5F8DD22CB1 Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=users.sourceforge.jp Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:46760 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHKO-0007Xm-NJ for patchwork-qemu-devel@patchwork.kernel.org; Thu, 27 Aug 2020 08:48:28 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:59086) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kBHBW-0008Ax-Lh for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:18 -0400 Received: from mail01.asahi-net.or.jp ([202.224.55.13]:47911) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHBS-0005y5-3g for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:18 -0400 Received: from sakura.ysato.name (ik1-413-38519.vs.sakura.ne.jp [153.127.30.23]) (Authenticated sender: PQ4Y-STU) by mail01.asahi-net.or.jp (Postfix) with ESMTPA id 95A8910894A; Thu, 27 Aug 2020 21:39:08 +0900 (JST) Received: from yo-satoh-debian.localdomain (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by sakura.ysato.name (Postfix) with ESMTPSA id 50D301C0696; Thu, 27 Aug 2020 21:39:08 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 14/20] hw/rx/rx62n: RX62N Add MTU module Date: Thu, 27 Aug 2020 21:38:53 +0900 Message-Id: <20200827123859.81793-15-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200827123859.81793-1-ysato@users.sourceforge.jp> References: <20200827123859.81793-1-ysato@users.sourceforge.jp> MIME-Version: 1.0 Received-SPF: softfail client-ip=202.224.55.13; envelope-from=ysato@users.sourceforge.jp; helo=mail01.asahi-net.or.jp X-detected-operating-system: by eggs.gnu.org: First seen = 2020/08/27 08:39:06 X-ACL-Warn: Detected OS = ??? X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_NONE=0.001, SPF_SOFTFAIL=0.665 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" Signed-off-by: Yoshinori Sato Reviewed-by: Philippe Mathieu-Daudé --- 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 From patchwork Thu Aug 27 12:38:54 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 11740669 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id EC232913 for ; Thu, 27 Aug 2020 12:49:26 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id BC8FC22CAE for ; Thu, 27 Aug 2020 12:49:26 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org BC8FC22CAE Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=users.sourceforge.jp Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:50832 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHLK-0000m8-0L for patchwork-qemu-devel@patchwork.kernel.org; Thu, 27 Aug 2020 08:49:26 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:59140) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kBHBf-0000CE-GF for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:27 -0400 Received: from mail01.asahi-net.or.jp ([202.224.55.13]:47915) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHBd-0005yl-09 for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:27 -0400 Received: from sakura.ysato.name (ik1-413-38519.vs.sakura.ne.jp [153.127.30.23]) (Authenticated sender: PQ4Y-STU) by mail01.asahi-net.or.jp (Postfix) with ESMTPA id CA76D108974; Thu, 27 Aug 2020 21:39:08 +0900 (JST) Received: from yo-satoh-debian.localdomain (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by sakura.ysato.name (Postfix) with ESMTPSA id 813D91C0792; Thu, 27 Aug 2020 21:39:08 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 15/20] hw/net: Add generic Bit-bang MDIO PHY. Date: Thu, 27 Aug 2020 21:38:54 +0900 Message-Id: <20200827123859.81793-16-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200827123859.81793-1-ysato@users.sourceforge.jp> References: <20200827123859.81793-1-ysato@users.sourceforge.jp> MIME-Version: 1.0 Received-SPF: softfail client-ip=202.224.55.13; envelope-from=ysato@users.sourceforge.jp; helo=mail01.asahi-net.or.jp X-detected-operating-system: by eggs.gnu.org: First seen = 2020/08/27 08:39:06 X-ACL-Warn: Detected OS = ??? X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_NONE=0.001, SPF_SOFTFAIL=0.665 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" Only supported link status. Signed-off-by: Yoshinori Sato --- 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 + * + * 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 . + * + * 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 + * + * 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 . + * + * 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') From patchwork Thu Aug 27 12:38:55 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 11740665 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 96B491731 for ; Thu, 27 Aug 2020 12:48:29 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 23C3822CAF for ; Thu, 27 Aug 2020 12:48:29 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 23C3822CAF Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=users.sourceforge.jp Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:46756 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHKO-0007Xe-4f for patchwork-qemu-devel@patchwork.kernel.org; Thu, 27 Aug 2020 08:48:28 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:59122) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kBHBY-0008Gn-Iq for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:20 -0400 Received: from mail01.asahi-net.or.jp ([202.224.55.13]:47914) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHBS-0005yk-Vx for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:20 -0400 Received: from sakura.ysato.name (ik1-413-38519.vs.sakura.ne.jp [153.127.30.23]) (Authenticated sender: PQ4Y-STU) by mail01.asahi-net.or.jp (Postfix) with ESMTPA id 2C08F10866C; Thu, 27 Aug 2020 21:39:09 +0900 (JST) Received: from yo-satoh-debian.localdomain (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by sakura.ysato.name (Postfix) with ESMTPSA id BA5631C0696; Thu, 27 Aug 2020 21:39:08 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 16/20] hw/net: Add Renesas On-chip Ethernet MAC Date: Thu, 27 Aug 2020 21:38:55 +0900 Message-Id: <20200827123859.81793-17-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200827123859.81793-1-ysato@users.sourceforge.jp> References: <20200827123859.81793-1-ysato@users.sourceforge.jp> MIME-Version: 1.0 Received-SPF: softfail client-ip=202.224.55.13; envelope-from=ysato@users.sourceforge.jp; helo=mail01.asahi-net.or.jp X-detected-operating-system: by eggs.gnu.org: First seen = 2020/08/27 08:39:06 X-ACL-Warn: Detected OS = ??? X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_NONE=0.001, SPF_SOFTFAIL=0.665 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" Signed-off-by: Yoshinori Sato --- 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 + * + * 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 . + * + * 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 + * + * 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 . + * + * 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') From patchwork Thu Aug 27 12:38:56 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 11740647 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E9BD2913 for ; Thu, 27 Aug 2020 12:43:10 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id CAD6322B40 for ; Thu, 27 Aug 2020 12:43:10 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org CAD6322B40 Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=users.sourceforge.jp Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:55346 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHFG-0007ue-0i for patchwork-qemu-devel@patchwork.kernel.org; Thu, 27 Aug 2020 08:43:10 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:58990) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kBHBS-000800-RF for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:14 -0400 Received: from mail03.asahi-net.or.jp ([202.224.55.15]:39222) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHBO-0005wU-AR for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:14 -0400 Received: from sakura.ysato.name (ik1-413-38519.vs.sakura.ne.jp [153.127.30.23]) (Authenticated sender: PQ4Y-STU) by mail03.asahi-net.or.jp (Postfix) with ESMTPA id 5EA3D26A0D; Thu, 27 Aug 2020 21:39:09 +0900 (JST) Received: from yo-satoh-debian.localdomain (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by sakura.ysato.name (Postfix) with ESMTPSA id 167771C0792; Thu, 27 Aug 2020 21:39:09 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 17/20] hw/rx/rx62n: Add Ethernet support. Date: Thu, 27 Aug 2020 21:38:56 +0900 Message-Id: <20200827123859.81793-18-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200827123859.81793-1-ysato@users.sourceforge.jp> References: <20200827123859.81793-1-ysato@users.sourceforge.jp> MIME-Version: 1.0 Received-SPF: softfail client-ip=202.224.55.15; envelope-from=ysato@users.sourceforge.jp; helo=mail03.asahi-net.or.jp X-detected-operating-system: by eggs.gnu.org: First seen = 2020/08/27 08:39:06 X-ACL-Warn: Detected OS = ??? X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_NONE=0.001, SPF_SOFTFAIL=0.665 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" Signed-off-by: Yoshinori Sato --- 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 From patchwork Thu Aug 27 12:38:57 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 11740659 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 4E141722 for ; Thu, 27 Aug 2020 12:45:50 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 2DE9D22B4B for ; Thu, 27 Aug 2020 12:45:50 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 2DE9D22B4B Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=users.sourceforge.jp Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:38338 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHHp-00045x-De for patchwork-qemu-devel@patchwork.kernel.org; Thu, 27 Aug 2020 08:45:49 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:59072) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kBHBV-00087j-FK for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:17 -0400 Received: from mail02.asahi-net.or.jp ([202.224.55.14]:56309) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHBP-0005wW-6A for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:17 -0400 Received: from sakura.ysato.name (ik1-413-38519.vs.sakura.ne.jp [153.127.30.23]) (Authenticated sender: PQ4Y-STU) by mail02.asahi-net.or.jp (Postfix) with ESMTPA id 8DE1826838; Thu, 27 Aug 2020 21:39:09 +0900 (JST) Received: from yo-satoh-debian.localdomain (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by sakura.ysato.name (Postfix) with ESMTPSA id 495171C0696; Thu, 27 Aug 2020 21:39:09 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 18/20] hw/rx: Add Tokudenkairo TKDN-RX62N-BRD Date: Thu, 27 Aug 2020 21:38:57 +0900 Message-Id: <20200827123859.81793-19-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200827123859.81793-1-ysato@users.sourceforge.jp> References: <20200827123859.81793-1-ysato@users.sourceforge.jp> MIME-Version: 1.0 Received-SPF: softfail client-ip=202.224.55.14; envelope-from=ysato@users.sourceforge.jp; helo=mail02.asahi-net.or.jp X-detected-operating-system: by eggs.gnu.org: First seen = 2020/08/27 08:39:06 X-ACL-Warn: Detected OS = ??? X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_NONE=0.001, SPF_SOFTFAIL=0.665 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" Hardware information. http://www.tokudenkairo.co.jp/rx62n/ (Japanese) Signed-off-by: Yoshinori Sato --- 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 . + */ + +#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} From patchwork Thu Aug 27 12:38:58 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 11740643 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 64EB013A4 for ; Thu, 27 Aug 2020 12:41:38 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 4594D2177B for ; Thu, 27 Aug 2020 12:41:38 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 4594D2177B Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=users.sourceforge.jp Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:50276 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHDl-0005qg-Hy for patchwork-qemu-devel@patchwork.kernel.org; Thu, 27 Aug 2020 08:41:37 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:59066) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kBHBV-00086e-4C for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:17 -0400 Received: from mail02.asahi-net.or.jp ([202.224.55.14]:56311) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHBO-0005wY-Np for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:16 -0400 Received: from sakura.ysato.name (ik1-413-38519.vs.sakura.ne.jp [153.127.30.23]) (Authenticated sender: PQ4Y-STU) by mail02.asahi-net.or.jp (Postfix) with ESMTPA id BFCF72683A; Thu, 27 Aug 2020 21:39:09 +0900 (JST) Received: from yo-satoh-debian.localdomain (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by sakura.ysato.name (Postfix) with ESMTPSA id 783C81C0792; Thu, 27 Aug 2020 21:39:09 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 19/20] hw/rx: Add CQ-FRK-RX62N target Date: Thu, 27 Aug 2020 21:38:58 +0900 Message-Id: <20200827123859.81793-20-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200827123859.81793-1-ysato@users.sourceforge.jp> References: <20200827123859.81793-1-ysato@users.sourceforge.jp> MIME-Version: 1.0 Received-SPF: softfail client-ip=202.224.55.14; envelope-from=ysato@users.sourceforge.jp; helo=mail02.asahi-net.or.jp X-detected-operating-system: by eggs.gnu.org: First seen = 2020/08/27 08:39:06 X-ACL-Warn: Detected OS = ??? X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_NONE=0.001, SPF_SOFTFAIL=0.665 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" It most popular RX target board in Japan. Signed-off-by: Yoshinori Sato --- 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 . + */ + +#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} From patchwork Thu Aug 27 12:38:59 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 11740661 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 07A1D913 for ; Thu, 27 Aug 2020 12:47:07 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id DBD3B22CAF for ; Thu, 27 Aug 2020 12:47:06 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org DBD3B22CAF Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=users.sourceforge.jp Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:42186 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHJ4-0005fu-4t for patchwork-qemu-devel@patchwork.kernel.org; Thu, 27 Aug 2020 08:47:06 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:59076) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kBHBV-00087v-Kw for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:17 -0400 Received: from mail03.asahi-net.or.jp ([202.224.55.15]:39228) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kBHBR-0005xj-H4 for qemu-devel@nongnu.org; Thu, 27 Aug 2020 08:39:17 -0400 Received: from sakura.ysato.name (ik1-413-38519.vs.sakura.ne.jp [153.127.30.23]) (Authenticated sender: PQ4Y-STU) by mail03.asahi-net.or.jp (Postfix) with ESMTPA id 0069026AF9; Thu, 27 Aug 2020 21:39:10 +0900 (JST) Received: from yo-satoh-debian.localdomain (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by sakura.ysato.name (Postfix) with ESMTPSA id A78621C0696; Thu, 27 Aug 2020 21:39:09 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 20/20] MAINTAINERS: Update RX entry Date: Thu, 27 Aug 2020 21:38:59 +0900 Message-Id: <20200827123859.81793-21-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200827123859.81793-1-ysato@users.sourceforge.jp> References: <20200827123859.81793-1-ysato@users.sourceforge.jp> MIME-Version: 1.0 Received-SPF: softfail client-ip=202.224.55.15; envelope-from=ysato@users.sourceforge.jp; helo=mail03.asahi-net.or.jp X-detected-operating-system: by eggs.gnu.org: First seen = 2020/08/27 08:39:06 X-ACL-Warn: Detected OS = ??? X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_NONE=0.001, SPF_SOFTFAIL=0.665 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" Signed-off-by: Yoshinori Sato --- 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