From patchwork Thu May 27 05:21:12 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 12283367 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.9 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,UNWANTED_LANGUAGE_BODY, URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9F007C4708A for ; Thu, 27 May 2021 05:28:57 +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 2122A613D3 for ; Thu, 27 May 2021 05:28:57 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 2122A613D3 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+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:55978 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lm8Zk-0005bc-4A for qemu-devel@archiver.kernel.org; Thu, 27 May 2021 01:28:56 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:56946) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lm8Sl-00008P-7z for qemu-devel@nongnu.org; Thu, 27 May 2021 01:21:44 -0400 Received: from mail01.asahi-net.or.jp ([202.224.55.13]:58615) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lm8Sc-00063x-2a for qemu-devel@nongnu.org; Thu, 27 May 2021 01:21:43 -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 E216811D38F; Thu, 27 May 2021 14:21:29 +0900 (JST) Received: from yo-satoh-debian.localdomain (y245018.dynamic.ppp.asahi-net.or.jp [118.243.245.18]) by sakura.ysato.name (Postfix) with ESMTPSA id 12EBF1C060B; Thu, 27 May 2021 14:21:28 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 01/11] hw/char: Renesas SCI module. Date: Thu, 27 May 2021 14:21:12 +0900 Message-Id: <20210527052122.97103-2-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210527052122.97103-1-ysato@users.sourceforge.jp> References: <20210527052122.97103-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-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+qemu-devel=archiver.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 | 1039 +++++++++++++++++++++++++++------ 2 files changed, 966 insertions(+), 202 deletions(-) diff --git a/include/hw/char/renesas_sci.h b/include/hw/char/renesas_sci.h index a4764e3eee..ae9554db60 100644 --- a/include/hw/char/renesas_sci.h +++ b/include/hw/char/renesas_sci.h @@ -1,54 +1,137 @@ /* * Renesas Serial Communication Interface * - * Copyright (c) 2018 Yoshinori Sato + * Copyright (c) 2020 Yoshinori Sato + * + * This code is licensed under the GPL version 2 or later. * - * SPDX-License-Identifier: GPL-2.0-or-later */ -#ifndef HW_CHAR_RENESAS_SCI_H -#define HW_CHAR_RENESAS_SCI_H - #include "chardev/char-fe.h" +#include "qemu/timer.h" +#include "qemu/fifo8.h" #include "hw/sysbus.h" #include "qom/object.h" +#define TYPE_RENESAS_SCI_BASE "renesas-sci-base" +OBJECT_DECLARE_TYPE(RenesasSCIBaseState, RenesasSCIBaseClass, + RENESAS_SCI_BASE) #define TYPE_RENESAS_SCI "renesas-sci" -typedef struct RSCIState RSCIState; -DECLARE_INSTANCE_CHECKER(RSCIState, RSCI, - TYPE_RENESAS_SCI) +OBJECT_DECLARE_TYPE(RenesasSCIState, RenesasSCIClass, + RENESAS_SCI) +#define TYPE_RENESAS_SCIA "renesas-scia" +OBJECT_DECLARE_TYPE(RenesasSCIAState, RenesasSCIAClass, + RENESAS_SCIA) +#define TYPE_RENESAS_SCIF "renesas-scif" +OBJECT_DECLARE_TYPE(RenesasSCIFState, RenesasSCIFClass, + RENESAS_SCIF) enum { ERI = 0, RXI = 1, TXI = 2, - TEI = 3, - SCI_NR_IRQ = 4 + BRI_TEI = 3, + SCI_NR_IRQ = 4, +}; + +enum { + RXTOUT, + RXNEXT, + TXEMPTY, + TXEND, + NR_SCI_EVENT, }; -struct RSCIState { +enum { + SCI_REGSIZE_8 = 0, + SCI_REGSIZE_16 = 1, + SCI_REGSIZE_32 = 2, +}; + +typedef struct RenesasSCIBaseState { /*< private >*/ SysBusDevice parent_obj; - /*< public >*/ - MemoryRegion memory; - QEMUTimer timer; - CharBackend chr; - qemu_irq irq[SCI_NR_IRQ]; + MemoryRegion memory_p4; + MemoryRegion memory_a7; + /* SCI register */ uint8_t smr; uint8_t brr; uint8_t scr; uint8_t tdr; - uint8_t ssr; - uint8_t rdr; - uint8_t scmr; - uint8_t semr; + uint16_t Xsr; - uint8_t read_ssr; + /* internal use */ + uint16_t read_Xsr; + int64_t etu; int64_t trtime; - int64_t rx_next; + int64_t tx_start_time; + struct { + int64_t time; + int64_t (*handler)(struct RenesasSCIBaseState *sci); + } event[NR_SCI_EVENT]; + QEMUTimer *event_timer; + CharBackend chr; uint64_t input_freq; + qemu_irq irq[SCI_NR_IRQ]; + Fifo8 rxfifo; + int regshift; + uint32_t unit; + Clock *pck; +} RenesasSCIBaseState; + +struct RenesasSCIState { + RenesasSCIBaseState parent_obj; + + /* SCI specific register */ + uint8_t sptr; }; -#endif +struct RenesasSCIAState { + RenesasSCIBaseState parent_obj; + + /* SCIa specific register */ + uint8_t scmr; + uint8_t semr; +}; + +struct RenesasSCIFState { + RenesasSCIBaseState parent_obj; + + /* SCIF specific register */ + uint16_t fcr; + uint16_t sptr; + uint16_t lsr; + + /* private */ + uint16_t read_lsr; + int tdcnt; +}; + +typedef struct RenesasSCIBaseClass { + SysBusDeviceClass parent; + + const struct MemoryRegionOps *ops; + void (*irq_fn)(struct RenesasSCIBaseState *sci, int request); + int (*divrate)(struct RenesasSCIBaseState *sci); +} RenesasSCIBaseClass; + +typedef struct RenesasSCIClass { + RenesasSCIBaseClass parent; + + void (*p_irq_fn)(struct RenesasSCIBaseState *sci, int request); +} RenesasSCIClass; + +typedef struct RenesasSCIAClass { + RenesasSCIBaseClass parent; + + void (*p_irq_fn)(struct RenesasSCIBaseState *sci, int request); + int (*p_divrate)(struct RenesasSCIBaseState *sci); +} RenesasSCIAClass; + +typedef struct RenesasSCIFClass { + RenesasSCIBaseClass parent; + + void (*p_irq_fn)(struct RenesasSCIBaseState *sci, int request); +} RenesasSCIFClass; diff --git a/hw/char/renesas_sci.c b/hw/char/renesas_sci.c index 1c63467290..f8ab00362a 100644 --- a/hw/char/renesas_sci.c +++ b/hw/char/renesas_sci.c @@ -1,10 +1,12 @@ /* - * Renesas Serial Communication Interface + * Renesas Serial Communication Interface (SCI / SCIa / SCIF) * * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware * (Rev.1.40 R01UH0033EJ0140) + * And SH7751 Group, SH7751R Group User's Manual: Hardware + * (Rev.4.01 R01UH0457EJ0401) * - * Copyright (c) 2019 Yoshinori Sato + * Copyright (c) 2020 Yoshinori Sato * * SPDX-License-Identifier: GPL-2.0-or-later * @@ -23,15 +25,25 @@ #include "qemu/osdep.h" #include "qemu/log.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "hw/hw.h" #include "hw/irq.h" +#include "hw/sysbus.h" #include "hw/registerfields.h" -#include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" +#include "hw/qdev-clock.h" #include "hw/char/renesas_sci.h" #include "migration/vmstate.h" +#include "qemu/error-report.h" -/* SCI register map */ -REG8(SMR, 0) +/* + * SCI register map + * SCI(a) register size all 8bit. + * SCIF regsister size 8bit and 16bit. + * Allocate 16bit to match the larger one. + */ +REG32(SMR, 0) /* 8bit */ FIELD(SMR, CKS, 0, 2) FIELD(SMR, MP, 2, 1) FIELD(SMR, STOP, 3, 1) @@ -39,263 +51,840 @@ REG8(SMR, 0) FIELD(SMR, PE, 5, 1) FIELD(SMR, CHR, 6, 1) FIELD(SMR, CM, 7, 1) -REG8(BRR, 1) -REG8(SCR, 2) - FIELD(SCR, CKE, 0, 2) +REG32(BRR, 4) /* 8bit */ +REG32(SCR, 8) + FIELD(SCR, CKE, 0, 2) FIELD(SCR, TEIE, 2, 1) FIELD(SCR, MPIE, 3, 1) + FIELD(SCR, REIE, 3, 1) FIELD(SCR, RE, 4, 1) FIELD(SCR, TE, 5, 1) FIELD(SCR, RIE, 6, 1) FIELD(SCR, TIE, 7, 1) -REG8(TDR, 3) -REG8(SSR, 4) +REG32(TDR, 12) /* 8bit */ +REG32(SSR, 16) /* 8bit */ FIELD(SSR, MPBT, 0, 1) FIELD(SSR, MPB, 1, 1) FIELD(SSR, TEND, 2, 1) - FIELD(SSR, ERR, 3, 3) + FIELD(SSR, ERR, 3, 3) FIELD(SSR, PER, 3, 1) FIELD(SSR, FER, 4, 1) FIELD(SSR, ORER, 5, 1) FIELD(SSR, RDRF, 6, 1) FIELD(SSR, TDRE, 7, 1) -REG8(RDR, 5) -REG8(SCMR, 6) +REG32(FSR, 16) + FIELD(FSR, DR, 0, 1) + FIELD(FSR, RDF, 1, 1) + FIELD(FSR, RDF_DR, 0, 2) + FIELD(FSR, PER, 2, 1) + FIELD(FSR, FER, 3, 1) + FIELD(FSR, BRK, 4, 1) + FIELD(FSR, TDFE, 5, 1) + FIELD(FSR, TEND, 6, 1) + FIELD(FSR, ER, 7, 1) + FIELD(FSR, FERn, 8, 4) + FIELD(FSR, PERn, 12, 4) +REG32(RDR, 20) /* 8bit */ +REG32(SCMR, 24) /* 8bit */ FIELD(SCMR, SMIF, 0, 1) FIELD(SCMR, SINV, 2, 1) FIELD(SCMR, SDIR, 3, 1) FIELD(SCMR, BCP2, 7, 1) -REG8(SEMR, 7) +REG32(FCR, 24) + FIELD(FCR, LOOP, 0, 1) + FIELD(FCR, RFRST, 1, 1) + FIELD(FCR, TFRST, 2, 1) + FIELD(FCR, MCE, 3, 1) + FIELD(FCR, TTRG, 4, 2) + FIELD(FCR, RTRG, 6, 2) + FIELD(FCR, RSTRG, 8, 3) +REG32(SEMR, 28) /* 8bit */ FIELD(SEMR, ACS0, 0, 1) FIELD(SEMR, ABCS, 4, 1) +REG32(FDR, 28) + FIELD(FDR, Rn, 0, 4) + FIELD(FDR, Tn, 8, 4) +REG32(SPTR, 32) + FIELD(SPTR, SPB2DT, 0, 1) + FIELD(SPTR, SPB2IO, 1, 1) + FIELD(SPTR, SCKDT, 2, 1) + FIELD(SPTR, SCKIO, 3, 1) + FIELD(SPTR, CTSDT, 4, 1) + FIELD(SPTR, CTSIO, 5, 1) + FIELD(SPTR, RTSDT, 6, 1) + FIELD(SPTR, RTSIO, 7, 1) + FIELD(SPTR, EIO, 7, 1) +REG32(LSR, 36) + FIELD(LSR, ORER, 0, 1) + +#define SCIF_FIFO_DEPTH 16 -static int can_receive(void *opaque) +static const int sci_rtrg[] = {1, 4, 8, 14}; + +static void update_event_time(RenesasSCIBaseState *sci, int evt, int64_t t) { - RSCIState *sci = RSCI(opaque); - if (sci->rx_next > qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) { - return 0; + if (t > 0) { + t += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + sci->event[evt].time = t; + if (timer_expire_time_ns(sci->event_timer) > t) { + timer_mod(sci->event_timer, t); + } } else { - return FIELD_EX8(sci->scr, SCR, RE); + sci->event[evt].time = 0; + } +} + +static void sci_irq(RenesasSCIBaseState *sci_common, int req) +{ + int irq = 0; + int rie; + int tie; + RenesasSCIState *sci = RENESAS_SCI(sci_common); + + rie = FIELD_EX16(sci_common->scr, SCR, RIE); + tie = FIELD_EX16(sci_common->scr, SCR, TIE); + switch (req) { + case ERI: + irq = rie && (FIELD_EX16(sci_common->Xsr, SSR, ERR) != 0); + break; + case RXI: + irq = FIELD_EX16(sci_common->Xsr, SSR, RDRF) && rie && + !FIELD_EX16(sci->sptr, SPTR, EIO); + break; + case TXI: + irq = FIELD_EX16(sci_common->Xsr, SSR, TDRE) && tie; + break; + case BRI_TEI: + irq = FIELD_EX16(sci_common->Xsr, SSR, TEND) && + FIELD_EX16(sci_common->scr, SCR, TEIE); + break; } + qemu_set_irq(sci_common->irq[req], irq); } -static void receive(void *opaque, const uint8_t *buf, int size) +static void scia_irq(RenesasSCIBaseState *sci, int req) { - RSCIState *sci = RSCI(opaque); - sci->rx_next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + sci->trtime; - if (FIELD_EX8(sci->ssr, SSR, RDRF) || size > 1) { - sci->ssr = FIELD_DP8(sci->ssr, SSR, ORER, 1); - if (FIELD_EX8(sci->scr, SCR, RIE)) { - qemu_set_irq(sci->irq[ERI], 1); + int irq = 0; + int rie; + int tie; + + rie = FIELD_EX16(sci->scr, SCR, RIE); + tie = FIELD_EX16(sci->scr, SCR, TIE); + switch (req) { + case ERI: + irq = (FIELD_EX16(sci->Xsr, SSR, ERR) != 0) && rie; + qemu_set_irq(sci->irq[req], irq); + break; + case RXI: + if (FIELD_EX16(sci->Xsr, SSR, RDRF) && rie) { + qemu_irq_pulse(sci->irq[req]); } - } else { - sci->rdr = buf[0]; - sci->ssr = FIELD_DP8(sci->ssr, SSR, RDRF, 1); - if (FIELD_EX8(sci->scr, SCR, RIE)) { - qemu_irq_pulse(sci->irq[RXI]); + break; + case TXI: + if (FIELD_EX16(sci->Xsr, SSR, TDRE) && tie) { + qemu_irq_pulse(sci->irq[req]); + } + break; + case BRI_TEI: + irq = FIELD_EX16(sci->Xsr, SSR, TEND) && + FIELD_EX16(sci->scr, SCR, TEIE); + qemu_set_irq(sci->irq[req], irq); + break; + } +} + +static void scif_irq(RenesasSCIBaseState *sci, int req) +{ + int irq = 0; + int rie; + int reie; + int tie; + + rie = FIELD_EX16(sci->scr, SCR, RIE); + reie = FIELD_EX16(sci->scr, SCR, REIE); + tie = FIELD_EX16(sci->scr, SCR, TIE); + switch (req) { + case ERI: + irq = (rie || reie) && FIELD_EX16(sci->Xsr, FSR, ER); + break; + case RXI: + irq = (FIELD_EX16(sci->Xsr, FSR, RDF_DR) != 0) && rie; + break; + case TXI: + irq = FIELD_EX16(sci->Xsr, FSR, TDFE) & tie; + break; + case BRI_TEI: + irq = (rie || reie) && FIELD_EX16(sci->Xsr, FSR, BRK); + break; + } + qemu_set_irq(sci->irq[req], irq); +} + +static int sci_can_receive(void *opaque) +{ + RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque); + int fifo_free = 0; + if (clock_is_enabled(sci->pck) && FIELD_EX16(sci->scr, SCR, RE)) { + /* Receiver enabled */ + fifo_free = fifo8_num_free(&sci->rxfifo); + } + return fifo_free; +} + +static void sci_receive(void *opaque, const uint8_t *buf, int size) +{ + RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque); + RenesasSCIBaseClass *rc = RENESAS_SCI_BASE_GET_CLASS(sci); + fifo8_push_all(&sci->rxfifo, buf, size); + if (sci->event[RXNEXT].time == 0) { + sci->Xsr = FIELD_DP16(sci->Xsr, SSR, RDRF, 1); + update_event_time(sci, RXNEXT, sci->trtime); + rc->irq_fn(sci, RXI); + } +} + +static int scif_can_receive(void *opaque) +{ + RenesasSCIFState *scif = RENESAS_SCIF(opaque); + RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque); + int fifo_free = 0; + if (clock_is_enabled(sci->pck) && FIELD_EX16(sci->scr, SCR, RE)) { + /* Receiver enabled */ + fifo_free = fifo8_num_free(&sci->rxfifo); + if (fifo_free == 0) { + /* FIFO overrun */ + scif->lsr = FIELD_DP16(scif->lsr, LSR, ORER, 1); + scif_irq(sci, ERI); + } + } + return fifo_free; +} + +static void scif_receive(void *opaque, const uint8_t *buf, int size) +{ + RenesasSCIFState *scif = RENESAS_SCIF(opaque); + RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque); + int rtrg; + + fifo8_push_all(&sci->rxfifo, buf, size); + if (sci->event[RXNEXT].time == 0) { + rtrg = sci_rtrg[FIELD_EX16(scif->fcr, FCR, RTRG)]; + if (fifo8_num_used(&sci->rxfifo) >= rtrg) { + sci->Xsr = FIELD_DP16(sci->Xsr, FSR, RDF, 1); + } else { + update_event_time(sci, RXTOUT, 15 * sci->etu); } + scif_irq(sci, RXI); } } -static void send_byte(RSCIState *sci) +static void sci_send_byte(RenesasSCIBaseState *sci) { if (qemu_chr_fe_backend_connected(&sci->chr)) { qemu_chr_fe_write_all(&sci->chr, &sci->tdr, 1); } - timer_mod(&sci->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + sci->trtime); - sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 0); - sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 1); - qemu_set_irq(sci->irq[TEI], 0); - if (FIELD_EX8(sci->scr, SCR, TIE)) { - qemu_irq_pulse(sci->irq[TXI]); + sci->Xsr = FIELD_DP16(sci->Xsr, SSR, TEND, 0); + sci->Xsr = FIELD_DP16(sci->Xsr, SSR, TDRE, 1); +} + +static int transmit_byte(RenesasSCIFState *scif) +{ + RenesasSCIBaseState *sci = RENESAS_SCI_BASE(scif); + int64_t elapsed; + int byte = 0; + if (sci->tx_start_time > 0) { + elapsed = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - sci->tx_start_time; + byte = elapsed / sci->trtime; + if (byte > scif->tdcnt) { + byte = scif->tdcnt; + } } + return byte; } -static void txend(void *opaque) +static int64_t scif_rx_timeout(RenesasSCIBaseState *sci) { - RSCIState *sci = RSCI(opaque); - if (!FIELD_EX8(sci->ssr, SSR, TDRE)) { - send_byte(sci); + sci->Xsr = FIELD_DP16(sci->Xsr, FSR, DR, 1); + scif_irq(sci, RXI); + return 0; +} + +static int64_t sci_rx_next(RenesasSCIBaseState *sci) +{ + int64_t next_event = 0; + RenesasSCIBaseClass *rc = RENESAS_SCI_BASE_GET_CLASS(sci); + if (FIELD_EX16(sci->Xsr, SSR, RDRF)) { + /* Receiver overrun */ + sci->Xsr = FIELD_DP16(sci->Xsr, SSR, ORER, 1); + rc->irq_fn(sci, ERI); } else { - sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 1); - if (FIELD_EX8(sci->scr, SCR, TEIE)) { - qemu_set_irq(sci->irq[TEI], 1); + if (!fifo8_is_empty(&sci->rxfifo)) { + /* set next event */ + sci->Xsr = FIELD_DP16(sci->Xsr, SSR, RDRF, 1); + rc->irq_fn(sci, RXI); + next_event = sci->trtime; } } + return next_event; } -static void update_trtime(RSCIState *sci) +static int64_t sci_tx_empty(RenesasSCIBaseState *sci) { - /* char per bits */ - sci->trtime = 8 - FIELD_EX8(sci->smr, SMR, CHR); - sci->trtime += FIELD_EX8(sci->smr, SMR, PE); - sci->trtime += FIELD_EX8(sci->smr, SMR, STOP) + 1; - /* x bit transmit time (32 * divrate * brr) / base freq */ - sci->trtime *= 32 * sci->brr; - sci->trtime *= 1 << (2 * FIELD_EX8(sci->smr, SMR, CKS)); - sci->trtime *= NANOSECONDS_PER_SECOND; - sci->trtime /= sci->input_freq; + int64_t ret = 0; + RenesasSCIBaseClass *rc = RENESAS_SCI_BASE_GET_CLASS(sci); + if (!FIELD_EX16(sci->Xsr, SSR, TDRE)) { + sci_send_byte(sci); + ret = sci->trtime; + rc->irq_fn(sci, TXI); + } else { + sci->Xsr = FIELD_DP16(sci->Xsr, SSR, TEND, 1); + rc->irq_fn(sci, BRI_TEI); + } + return ret; } -static bool sci_is_tr_enabled(RSCIState *sci) +static int64_t scif_tx_empty(RenesasSCIBaseState *sci) { - return FIELD_EX8(sci->scr, SCR, TE) || FIELD_EX8(sci->scr, SCR, RE); + RenesasSCIFState *scif = RENESAS_SCIF(sci); + scif->tdcnt -= transmit_byte(scif); + sci->Xsr = FIELD_DP16(sci->Xsr, FSR, TDFE, 1); + scif_irq(sci, TXI); + return 0; } -static void sci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) +static int64_t scif_tx_end(RenesasSCIBaseState *sci) { - RSCIState *sci = RSCI(opaque); + RenesasSCIFState *scif = RENESAS_SCIF(sci); + scif->tdcnt = 0; + sci->Xsr = FIELD_DP16(sci->Xsr, FSR, TEND, 1); + return 0; +} - switch (offset) { - case A_SMR: - if (!sci_is_tr_enabled(sci)) { - sci->smr = val; - update_trtime(sci); +static void sci_timer_event(void *opaque) +{ + RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque); + int64_t now, next, t; + int i; + + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + next = INT64_MAX; + for (i = 0; i < NR_SCI_EVENT; i++) { + if (sci->event[i].time > 0 && sci->event[i].time <= now) { + t = sci->event[i].handler(sci); + sci->event[i].time = (t > 0) ? now + t : 0; } - break; - case A_BRR: - if (!sci_is_tr_enabled(sci)) { - sci->brr = val; - update_trtime(sci); + if (sci->event[i].time > 0) { + next = MIN(next, sci->event[i].time); } - break; + } + if (next < INT64_MAX) { + timer_mod(sci->event_timer, next); + } else { + timer_del(sci->event_timer); + } +} + +static int static_divrate(RenesasSCIBaseState *sci) +{ + /* SCI / SCIF have static divide rate */ + return 32; +} + +static int scia_divrate(RenesasSCIBaseState *sci) +{ + /* + * SEMR.ABCS = 0 -> 32 + * SEMR.ABCS = 1 -> 16 + */ + RenesasSCIAState *scia = RENESAS_SCIA(sci); + return 16 * (2 - FIELD_EX8(scia->semr, SEMR, ABCS)); +} + +static void update_trtime(RenesasSCIBaseState *sci) +{ + RenesasSCIBaseClass *rc = RENESAS_SCI_BASE_GET_CLASS(sci); + int cks = 1 << (2 * FIELD_EX16(sci->smr, SMR, CKS)); + if (sci->input_freq > 0) { + /* x bit transmit time (divrate * brr) / base freq */ + sci->etu = rc->divrate(sci) * cks; + sci->etu *= sci->brr + 1; + sci->etu *= NANOSECONDS_PER_SECOND; + sci->etu /= sci->input_freq; + + /* char per bits */ + sci->trtime = 8 - FIELD_EX16(sci->smr, SMR, CHR); + sci->trtime += FIELD_EX16(sci->smr, SMR, PE); + sci->trtime += FIELD_EX16(sci->smr, SMR, STOP) + 1 + 1; + sci->trtime *= sci->etu; + } +} + +static void sci_pck_update(void *opaque, ClockEvent evt) +{ + RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque); + + sci->input_freq = clock_get_hz(sci->pck); + update_trtime(sci); +} + +#define IS_TR_ENABLED(scr) \ + (FIELD_EX16(scr, SCR, TE) || FIELD_EX16(scr, SCR, RE)) + +static hwaddr map_address(RenesasSCIBaseState *sci, hwaddr addr) +{ + return addr << (2 - sci->regshift); +} + +static void sci_common_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque); + RenesasSCIBaseClass *rc = RENESAS_SCI_BASE_GET_CLASS(opaque); + switch (addr) { case A_SCR: sci->scr = val; - if (FIELD_EX8(sci->scr, SCR, TE)) { - sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 1); - sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 1); - if (FIELD_EX8(sci->scr, SCR, TIE)) { - qemu_irq_pulse(sci->irq[TXI]); - } + if (FIELD_EX16(sci->scr, SCR, TE)) { + /* Transmitter enable */ + sci->Xsr = FIELD_DP16(sci->Xsr, SSR, TDRE, 1); + sci->Xsr = FIELD_DP16(sci->Xsr, SSR, TEND, 1); + rc->irq_fn(sci, TXI); + rc->irq_fn(sci, BRI_TEI); + } else { + /* Transmitter disable */ + update_event_time(sci, TXEND, 0); + update_event_time(sci, TXEMPTY, 0); + } + break; + case A_SMR: + sci->smr = val; + update_trtime(sci); + break; + case A_BRR: + sci->brr = val; + update_trtime(sci); + break; + default: + qemu_log_mask(LOG_UNIMP, "renesas_sci: Register 0x%" HWADDR_PRIX + " not implemented\n", addr); + } +} + +static void sci_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque); + RenesasSCIBaseClass *rc = RENESAS_SCI_BASE_GET_CLASS(sci); + bool tx_start; + + if (!clock_is_enabled(sci->pck)) { + qemu_log_mask(LOG_GUEST_ERROR, "renesas_sci: SCI %d is stopped.\n", + sci->unit); + return ; + } + addr = map_address(sci, addr); + switch (addr) { + case A_TDR: + sci->tdr = val; + break; + case A_SSR: + /* Mask for read only bits */ + sci->Xsr = FIELD_DP16(RENESAS_SCI_BASE(sci)->Xsr, SSR, MPBT, + FIELD_EX16(val, SSR, MPBT)); + sci->Xsr &= (val | 0x07); + /* Clear ERI */ + rc->irq_fn(sci, ERI); + tx_start = FIELD_EX16(sci->read_Xsr, SSR, TDRE) && + !FIELD_EX16(sci->Xsr, SSR, TDRE) && + (FIELD_EX16(sci->Xsr, SSR, ERR) == 0); + if (tx_start) { + sci_send_byte(sci); + update_event_time(sci, TXEMPTY, sci->trtime); + rc->irq_fn(sci, TXI); } - if (!FIELD_EX8(sci->scr, SCR, TEIE)) { - qemu_set_irq(sci->irq[TEI], 0); + break; + case A_SPTR: + RENESAS_SCI(sci)->sptr = val; + break; + default: + sci_common_write(sci, addr, val, size); + } +} + +static void scia_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque); + RenesasSCIAState *scia = RENESAS_SCIA(opaque); + + if (!clock_is_enabled(sci->pck)) { + qemu_log_mask(LOG_GUEST_ERROR, "renesas_sci: SCIa %d is stopped.\n", + sci->unit); + return ; + } + addr = map_address(sci, addr); + switch (addr) { + case A_SMR: + if (IS_TR_ENABLED(sci->scr)) { + qemu_log_mask(LOG_GUEST_ERROR, + "reneas_sci: SMR write protected.\n"); + } else { + sci_common_write(sci, addr, val, size); } - if (!FIELD_EX8(sci->scr, SCR, RIE)) { - qemu_set_irq(sci->irq[ERI], 0); + break; + case A_BRR: + if (IS_TR_ENABLED(sci->scr)) { + qemu_log_mask(LOG_GUEST_ERROR, + "reneas_sci: BRR write protected.\n"); + break; + } else { + sci_common_write(sci, addr, val, size); } break; case A_TDR: sci->tdr = val; - if (FIELD_EX8(sci->ssr, SSR, TEND)) { - send_byte(sci); + if (FIELD_EX16(sci->Xsr, SSR, TEND)) { + update_event_time(sci, TXEMPTY, sci->trtime); + sci_send_byte(sci); } else { - sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 0); + sci->Xsr = FIELD_DP16(sci->Xsr, SSR, TDRE, 0); } + scia_irq(sci, TXI); + scia_irq(sci, BRI_TEI); break; case A_SSR: - sci->ssr = FIELD_DP8(sci->ssr, SSR, MPBT, - FIELD_EX8(val, SSR, MPBT)); - sci->ssr = FIELD_DP8(sci->ssr, SSR, ERR, - FIELD_EX8(val, SSR, ERR) & 0x07); - if (FIELD_EX8(sci->read_ssr, SSR, ERR) && - FIELD_EX8(sci->ssr, SSR, ERR) == 0) { - qemu_set_irq(sci->irq[ERI], 0); - } - break; - case A_RDR: - qemu_log_mask(LOG_GUEST_ERROR, "reneas_sci: RDR is read only.\n"); + /* Mask for read only bits */ + sci->Xsr = FIELD_DP16(sci->Xsr, SSR, MPBT, + FIELD_EX16(val, SSR, MPBT)); + sci->Xsr &= (val | 0xc7); + /* Clear ERI */ + scia_irq(sci, ERI); break; case A_SCMR: - sci->scmr = val; break; - case A_SEMR: /* SEMR */ - sci->semr = val; break; + scia->scmr = val; + break; + case A_SEMR: + scia->semr = val; + break; default: - qemu_log_mask(LOG_UNIMP, "renesas_sci: Register 0x%" HWADDR_PRIX " " - "not implemented\n", - offset); + sci_common_write(sci, addr, val, size); + break; } } -static uint64_t sci_read(void *opaque, hwaddr offset, unsigned size) +static void scif_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - RSCIState *sci = RSCI(opaque); + RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque); + RenesasSCIFState *scif = RENESAS_SCIF(opaque); + int txtrg; + int rxtrg; + uint16_t ssr_mask; + uint8_t txd; + + if (!clock_is_enabled(sci->pck)) { + qemu_log_mask(LOG_GUEST_ERROR, "renesas_sci: SCIF %d is stopped.\n", + sci->unit); + return ; + } + txtrg = 1 << (3 - FIELD_EX16(scif->fcr, FCR, TTRG)); + addr = map_address(sci, addr); + switch (addr) { + case A_SCR: + sci->scr = val; + if (FIELD_EX16(sci->scr, SCR, TE)) { + /* Transmitter enable */ + sci->Xsr = FIELD_DP16(sci->Xsr, FSR, TEND, 1); + sci->Xsr = FIELD_DP16(sci->Xsr, FSR, TDFE, 1); + sci->tx_start_time = 0; + scif_irq(sci, TXI); + } else { + /* Transmitter disable */ + update_event_time(sci, TXEND, 0); + update_event_time(sci, TXEMPTY, 0); + } + break; + case A_TDR: + if (sci->tx_start_time > 0) { + scif->tdcnt -= transmit_byte(scif); + } else { + sci->tx_start_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + } + if (scif->tdcnt >= SCIF_FIFO_DEPTH) { + break; + } + txd = val; + if (qemu_chr_fe_backend_connected(&sci->chr)) { + qemu_chr_fe_write_all(&sci->chr, &txd, 1); + } + if (FIELD_EX16(scif->fcr, FCR, LOOP) && scif_can_receive(sci) > 0) { + /* Loopback mode */ + scif_receive(sci, &txd, 1); + } + scif->tdcnt++; + sci->Xsr = FIELD_DP16(sci->Xsr, FSR, TEND, 0); + update_event_time(sci, TXEND, scif->tdcnt); + if (scif->tdcnt > txtrg) { + sci->Xsr = FIELD_DP16(sci->Xsr, FSR, TDFE, 0); + update_event_time(sci, TXEMPTY, scif->tdcnt - txtrg + 1); + scif_irq(sci, TXI); + } + break; + case A_FSR: + rxtrg = sci_rtrg[FIELD_EX16(scif->fcr, FCR, RTRG)]; + ssr_mask = ~(sci->read_Xsr & 0xf3); + scif->tdcnt -= transmit_byte(scif); + if (scif->tdcnt < txtrg) { + ssr_mask = FIELD_DP16(ssr_mask, FSR, TDFE, 1); + } + if (fifo8_num_used(&sci->rxfifo) >= rxtrg) { + ssr_mask = FIELD_DP16(ssr_mask, FSR, RDF, 1); + } + sci->Xsr &= (val | ssr_mask); + scif_irq(sci, ERI); + scif_irq(sci, RXI); + scif_irq(sci, TXI); + break; + case A_FCR: + scif->fcr = val; + if (FIELD_EX16(scif->fcr, FCR, RFRST)) { + fifo8_reset(&sci->rxfifo); + update_event_time(sci, RXTOUT, 0); + update_event_time(sci, RXNEXT, 0); + } + if (FIELD_EX16(scif->fcr, FCR, TFRST)) { + scif->tdcnt = 0; + } + break; + case A_FDR: + qemu_log_mask(LOG_GUEST_ERROR, "reneas_sci: FDR is read only.\n"); + break; + case A_SPTR: + scif->sptr = val; + break; + case A_LSR: + if (FIELD_EX16(scif->read_lsr, LSR, ORER) != 1) { + val = FIELD_DP16(val, LSR, ORER, 1); + } + scif->lsr &= val; + scif_irq(sci, ERI); + break; + default: + sci_common_write(sci, addr, val, size); + break; + } +} - switch (offset) { +static uint64_t sci_common_read(void *opaque, hwaddr addr, unsigned size) +{ + RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque); + switch (addr) { case A_SMR: return sci->smr; case A_BRR: return sci->brr; case A_SCR: return sci->scr; - case A_TDR: - return sci->tdr; - case A_SSR: - sci->read_ssr = sci->ssr; - return sci->ssr; + case A_FSR: /* A_SSR */ + sci->read_Xsr = sci->Xsr; + return sci->Xsr; case A_RDR: - sci->ssr = FIELD_DP8(sci->ssr, SSR, RDRF, 0); - return sci->rdr; - case A_SCMR: - return sci->scmr; - case A_SEMR: - return sci->semr; + if (fifo8_num_used(&sci->rxfifo) > 0) { + return fifo8_pop(&sci->rxfifo); + } else { + return 0xff; + } default: qemu_log_mask(LOG_UNIMP, "renesas_sci: Register 0x%" HWADDR_PRIX - " not implemented.\n", offset); + " not implemented.\n", addr); } return UINT64_MAX; } -static const MemoryRegionOps sci_ops = { - .write = sci_write, - .read = sci_read, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl.max_access_size = 1, - .valid.max_access_size = 1, -}; +static uint64_t sci_read(void *opaque, hwaddr addr, unsigned size) +{ + RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque); + addr = map_address(sci, addr); + + if (clock_is_enabled(sci->pck)) { + switch (addr) { + case A_TDR: + return sci->tdr; + case A_SPTR: + return RENESAS_SCI(sci)->sptr; + default: + return sci_common_read(sci, addr, size); + } + } else { + qemu_log_mask(LOG_GUEST_ERROR, "renesas_sci: SCI %d is stopped.\n", + sci->unit); + } + return UINT64_MAX; +} -static void rsci_reset(DeviceState *dev) +static uint64_t scia_read(void *opaque, hwaddr addr, unsigned size) { - RSCIState *sci = RSCI(dev); - sci->smr = sci->scr = 0x00; - sci->brr = 0xff; - sci->tdr = 0xff; - sci->rdr = 0x00; - sci->ssr = 0x84; - sci->scmr = 0x00; - sci->semr = 0x00; - sci->rx_next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque); + RenesasSCIAState *scia = RENESAS_SCIA(opaque); + + if (clock_is_enabled(sci->pck)) { + addr = map_address(sci, addr); + switch (addr) { + case A_TDR: + return sci->tdr; + case A_RDR: + sci->Xsr = FIELD_DP16(sci->Xsr, SSR, RDRF, 0); + return fifo8_pop(&sci->rxfifo); + case A_SCMR: + return scia->scmr; + default: + return sci_common_read(sci, addr, size); + } + } else { + qemu_log_mask(LOG_GUEST_ERROR, "renesas_sci: SCIa %d is stopped.\n", + sci->unit); + } + return UINT64_MAX; +} + +static uint64_t scif_read(void *opaque, hwaddr addr, unsigned size) +{ + RenesasSCIFState *scif = RENESAS_SCIF(opaque); + RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque); + uint64_t ret; + + if (clock_is_enabled(sci->pck)) { + addr = map_address(sci, addr); + switch (addr) { + case A_FCR: + return scif->fcr & 0x7ff; + case A_FDR: + ret = 0; + ret = FIELD_DP16(ret, FDR, Rn, fifo8_num_used(&sci->rxfifo)); + ret = FIELD_DP16(ret, FDR, Tn, scif->tdcnt - transmit_byte(scif)); + return ret; + case A_SPTR: + return scif->sptr; + case A_LSR: + scif->read_lsr = scif->lsr; + return scif->lsr; + default: + return sci_common_read(sci, addr, size); + } + } else { + qemu_log_mask(LOG_GUEST_ERROR, "renesas_sci: SCIF %d is stopped.\n", + sci->unit); + } + return UINT64_MAX; +} + +static void rsci_common_init(Object *obj) +{ + RenesasSCIBaseState *sci = RENESAS_SCI_BASE(obj); + SysBusDevice *d = SYS_BUS_DEVICE(obj); + int i; + + for (i = 0; i < SCI_NR_IRQ; i++) { + sysbus_init_irq(d, &sci->irq[i]); + } + fifo8_create(&sci->rxfifo, SCIF_FIFO_DEPTH); + sci->event_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sci_timer_event, sci); + sci->pck = qdev_init_clock_in(DEVICE(d), "pck", + sci_pck_update, sci, ClockUpdate); } static void sci_event(void *opaque, QEMUChrEvent event) { - RSCIState *sci = RSCI(opaque); + RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque); + RenesasSCIBaseClass *rc = RENESAS_SCI_BASE_GET_CLASS(sci); + if (clock_is_enabled(sci->pck) && event == CHR_EVENT_BREAK) { + sci->Xsr = FIELD_DP16(sci->Xsr, SSR, FER, 1); + rc->irq_fn(sci, BRI_TEI); + } +} + +static void scif_event(void *opaque, QEMUChrEvent event) +{ + RenesasSCIBaseState *sci = RENESAS_SCI_BASE(opaque); if (event == CHR_EVENT_BREAK) { - sci->ssr = FIELD_DP8(sci->ssr, SSR, FER, 1); - if (FIELD_EX8(sci->scr, SCR, RIE)) { - qemu_set_irq(sci->irq[ERI], 1); - } + sci->Xsr = FIELD_DP16(sci->Xsr, FSR, BRK, 1); + scif_irq(sci, BRI_TEI); } } -static void rsci_realize(DeviceState *dev, Error **errp) +static void rsci_common_realize(DeviceState *dev, Error **errp) { - RSCIState *sci = RSCI(dev); + RenesasSCIBaseState *sci = RENESAS_SCI_BASE(dev); - if (sci->input_freq == 0) { + if (sci->regshift != 8 && sci->regshift != 16 && sci->regshift != 32) { qemu_log_mask(LOG_GUEST_ERROR, - "renesas_sci: input-freq property must be set."); + "renesas_sci: Invalid register size."); return; } - qemu_chr_fe_set_handlers(&sci->chr, can_receive, receive, - sci_event, NULL, sci, NULL, true); + + sci->smr = sci->scr = 0x00; + sci->brr = 0xff; + sci->tdr = 0xff; + sci->Xsr = 0x84; + update_trtime(sci); + } -static void rsci_init(Object *obj) +static void register_mmio(RenesasSCIBaseState *sci, int size) { - SysBusDevice *d = SYS_BUS_DEVICE(obj); - RSCIState *sci = RSCI(obj); - int i; + SysBusDevice *d = SYS_BUS_DEVICE(sci); + RenesasSCIBaseClass *rc = RENESAS_SCI_BASE_GET_CLASS(sci); - memory_region_init_io(&sci->memory, OBJECT(sci), &sci_ops, - sci, "renesas-sci", 0x8); + memory_region_init_io(&sci->memory, OBJECT(sci), rc->ops, + sci, "renesas-sci", size); sysbus_init_mmio(d, &sci->memory); + memory_region_init_alias(&sci->memory_p4, NULL, "renesas-sci-p4", + &sci->memory, 0, size); + sysbus_init_mmio(d, &sci->memory_p4); + memory_region_init_alias(&sci->memory_a7, NULL, "renesas-sci-a7", + &sci->memory, 0, size); + sysbus_init_mmio(d, &sci->memory_a7); +} - for (i = 0; i < SCI_NR_IRQ; i++) { - sysbus_init_irq(d, &sci->irq[i]); - } - timer_init_ns(&sci->timer, QEMU_CLOCK_VIRTUAL, txend, sci); +static void rsci_realize(DeviceState *dev, Error **errp) +{ + RenesasSCIState *sci = RENESAS_SCI(dev); + RenesasSCIBaseState *common = RENESAS_SCI_BASE(dev); + + rsci_common_realize(dev, errp); + + register_mmio(common, 8 * (1 << common->regshift)); + qemu_chr_fe_set_handlers(&common->chr, sci_can_receive, sci_receive, + sci_event, NULL, sci, NULL, true); + + sci->sptr = 0x00; +} + +static void rscia_realize(DeviceState *dev, Error **errp) +{ + RenesasSCIAState *sci = RENESAS_SCIA(dev); + RenesasSCIBaseState *common = RENESAS_SCI_BASE(dev); + + rsci_common_realize(dev, errp); + + register_mmio(common, 8 * (1 << common->regshift)); + qemu_chr_fe_set_handlers(&common->chr, sci_can_receive, sci_receive, + sci_event, NULL, sci, NULL, true); + + sci->scmr = 0x00; + sci->semr = 0x00; +} + +static void rscif_realize(DeviceState *dev, Error **errp) +{ + RenesasSCIFState *sci = RENESAS_SCIF(dev); + RenesasSCIBaseState *common = RENESAS_SCI_BASE(sci); + + rsci_common_realize(dev, errp); + + register_mmio(common, 10 * (1 << common->regshift)); + qemu_chr_fe_set_handlers(&common->chr, scif_can_receive, scif_receive, + scif_event, NULL, sci, NULL, true); + common->Xsr = 0x0060; + sci->fcr = 0x0000; + sci->sptr = 0x0000; + sci->lsr = 0x0000; } static const VMStateDescription vmstate_rsci = { @@ -303,49 +892,141 @@ static const VMStateDescription vmstate_rsci = { .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_INT64(trtime, RSCIState), - VMSTATE_INT64(rx_next, RSCIState), - VMSTATE_UINT8(smr, RSCIState), - VMSTATE_UINT8(brr, RSCIState), - VMSTATE_UINT8(scr, RSCIState), - VMSTATE_UINT8(tdr, RSCIState), - VMSTATE_UINT8(ssr, RSCIState), - VMSTATE_UINT8(rdr, RSCIState), - VMSTATE_UINT8(scmr, RSCIState), - VMSTATE_UINT8(semr, RSCIState), - VMSTATE_UINT8(read_ssr, RSCIState), - VMSTATE_TIMER(timer, RSCIState), VMSTATE_END_OF_LIST() } }; static Property rsci_properties[] = { - DEFINE_PROP_UINT64("input-freq", RSCIState, input_freq, 0), - DEFINE_PROP_CHR("chardev", RSCIState, chr), + DEFINE_PROP_INT32("register-size", RenesasSCIBaseState, + regshift, SCI_REGSIZE_32), + DEFINE_PROP_UINT32("unit", RenesasSCIBaseState, unit, 0), + DEFINE_PROP_CHR("chardev", RenesasSCIBaseState, chr), DEFINE_PROP_END_OF_LIST(), }; -static void rsci_class_init(ObjectClass *klass, void *data) +static void rsci_init(Object *obj) { + RenesasSCIBaseState *sci = RENESAS_SCI_BASE(obj); + sci->event[RXNEXT].handler = sci_rx_next; + sci->event[TXEMPTY].handler = sci_tx_empty; +} + +static void rscif_init(Object *obj) +{ + RenesasSCIBaseState *sci = RENESAS_SCI_BASE(obj); + sci->event[RXTOUT].handler = scif_rx_timeout; + sci->event[TXEMPTY].handler = scif_tx_empty; + sci->event[TXEND].handler = scif_tx_end; +} + +static void rsci_common_class_init(ObjectClass *klass, void *data) +{ + RenesasSCIBaseClass *rc = RENESAS_SCI_BASE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - dc->realize = rsci_realize; dc->vmsd = &vmstate_rsci; - dc->reset = rsci_reset; device_class_set_props(dc, rsci_properties); + rc->divrate = static_divrate; } -static const TypeInfo rsci_info = { - .name = TYPE_RENESAS_SCI, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(RSCIState), - .instance_init = rsci_init, - .class_init = rsci_class_init, +static const MemoryRegionOps sci_ops = { + .read = sci_read, + .write = sci_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, }; -static void rsci_register_types(void) +static void rsci_class_init(ObjectClass *klass, void *data) { - type_register_static(&rsci_info); + RenesasSCIBaseClass *comm_rc = RENESAS_SCI_BASE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + comm_rc->ops = &sci_ops; + comm_rc->irq_fn = sci_irq; + dc->realize = rsci_realize; } -type_init(rsci_register_types) +static const MemoryRegionOps scia_ops = { + .read = scia_read, + .write = scia_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static void rscia_class_init(ObjectClass *klass, void *data) +{ + RenesasSCIBaseClass *comm_rc = RENESAS_SCI_BASE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + comm_rc->ops = &scia_ops; + comm_rc->irq_fn = scia_irq; + comm_rc->divrate = scia_divrate; + + dc->realize = rscia_realize; +} + +static const MemoryRegionOps scif_ops = { + .read = scif_read, + .write = scif_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static void rscif_class_init(ObjectClass *klass, void *data) +{ + RenesasSCIBaseClass *comm_rc = RENESAS_SCI_BASE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + comm_rc->ops = &scif_ops; + comm_rc->irq_fn = scif_irq; + + dc->realize = rscif_realize; +} + +static const TypeInfo renesas_sci_info[] = { + { + .name = TYPE_RENESAS_SCI_BASE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(RenesasSCIBaseState), + .instance_init = rsci_common_init, + .class_init = rsci_common_class_init, + .class_size = sizeof(RenesasSCIBaseClass), + .abstract = true, + }, + { + .name = TYPE_RENESAS_SCI, + .parent = TYPE_RENESAS_SCI_BASE, + .instance_size = sizeof(RenesasSCIState), + .instance_init = rsci_init, + .class_init = rsci_class_init, + .class_size = sizeof(RenesasSCIClass), + }, + { + .name = TYPE_RENESAS_SCIA, + .parent = TYPE_RENESAS_SCI_BASE, + .instance_size = sizeof(RenesasSCIAState), + /* Initializer same of SCI */ + .instance_init = rsci_init, + .class_init = rscia_class_init, + .class_size = sizeof(RenesasSCIAClass), + }, + { + .name = TYPE_RENESAS_SCIF, + .parent = TYPE_RENESAS_SCI_BASE, + .instance_size = sizeof(RenesasSCIFState), + .instance_init = rscif_init, + .class_init = rscif_class_init, + .class_size = sizeof(RenesasSCIFClass), + }, +}; + +DEFINE_TYPES(renesas_sci_info) From patchwork Thu May 27 05:21:13 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 12283353 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7AD5FC4708A for ; Thu, 27 May 2021 05:23:54 +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 1621F613D3 for ; Thu, 27 May 2021 05:23:54 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 1621F613D3 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+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:39044 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lm8Uq-0002hQ-IV for qemu-devel@archiver.kernel.org; Thu, 27 May 2021 01:23:52 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:56846) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lm8Sg-0008Ls-Sr for qemu-devel@nongnu.org; Thu, 27 May 2021 01:21:39 -0400 Received: from mail03.asahi-net.or.jp ([202.224.55.15]:32926) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lm8Sc-00063v-27 for qemu-devel@nongnu.org; Thu, 27 May 2021 01:21:38 -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 EEBFA385A6; Thu, 27 May 2021 14:21:29 +0900 (JST) Received: from yo-satoh-debian.localdomain (y245018.dynamic.ppp.asahi-net.or.jp [118.243.245.18]) by sakura.ysato.name (Postfix) with ESMTPSA id 77AA81C0643; Thu, 27 May 2021 14:21:29 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 02/11] hw/char: remove sh_serial. Date: Thu, 27 May 2021 14:21:13 +0900 Message-Id: <20210527052122.97103-3-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210527052122.97103-1-ysato@users.sourceforge.jp> References: <20210527052122.97103-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-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+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" Migrate to renesas_sci. Signed-off-by: Yoshinori Sato --- hw/char/sh_serial.c | 431 -------------------------------------------- MAINTAINERS | 4 +- hw/char/Kconfig | 3 - hw/char/meson.build | 1 - 4 files changed, 2 insertions(+), 437 deletions(-) delete mode 100644 hw/char/sh_serial.c diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c deleted file mode 100644 index 167f4d8cb9..0000000000 --- a/hw/char/sh_serial.c +++ /dev/null @@ -1,431 +0,0 @@ -/* - * QEMU SCI/SCIF serial port emulation - * - * Copyright (c) 2007 Magnus Damm - * - * Based on serial.c - QEMU 16450 UART emulation - * Copyright (c) 2003-2004 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "hw/irq.h" -#include "hw/sh4/sh.h" -#include "chardev/char-fe.h" -#include "qapi/error.h" -#include "qemu/timer.h" - -//#define DEBUG_SERIAL - -#define SH_SERIAL_FLAG_TEND (1 << 0) -#define SH_SERIAL_FLAG_TDE (1 << 1) -#define SH_SERIAL_FLAG_RDF (1 << 2) -#define SH_SERIAL_FLAG_BRK (1 << 3) -#define SH_SERIAL_FLAG_DR (1 << 4) - -#define SH_RX_FIFO_LENGTH (16) - -typedef struct { - MemoryRegion iomem; - MemoryRegion iomem_p4; - MemoryRegion iomem_a7; - uint8_t smr; - uint8_t brr; - uint8_t scr; - uint8_t dr; /* ftdr / tdr */ - uint8_t sr; /* fsr / ssr */ - uint16_t fcr; - uint8_t sptr; - - uint8_t rx_fifo[SH_RX_FIFO_LENGTH]; /* frdr / rdr */ - uint8_t rx_cnt; - uint8_t rx_tail; - uint8_t rx_head; - - int freq; - int feat; - int flags; - int rtrg; - - CharBackend chr; - QEMUTimer *fifo_timeout_timer; - uint64_t etu; /* Elementary Time Unit (ns) */ - - qemu_irq eri; - qemu_irq rxi; - qemu_irq txi; - qemu_irq tei; - qemu_irq bri; -} sh_serial_state; - -static void sh_serial_clear_fifo(sh_serial_state * s) -{ - memset(s->rx_fifo, 0, SH_RX_FIFO_LENGTH); - s->rx_cnt = 0; - s->rx_head = 0; - s->rx_tail = 0; -} - -static void sh_serial_write(void *opaque, hwaddr offs, - uint64_t val, unsigned size) -{ - sh_serial_state *s = opaque; - unsigned char ch; - -#ifdef DEBUG_SERIAL - printf("sh_serial: write offs=0x%02x val=0x%02x\n", - offs, val); -#endif - switch(offs) { - case 0x00: /* SMR */ - s->smr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0x7b : 0xff); - return; - case 0x04: /* BRR */ - s->brr = val; - return; - case 0x08: /* SCR */ - /* TODO : For SH7751, SCIF mask should be 0xfb. */ - s->scr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0xfa : 0xff); - if (!(val & (1 << 5))) - s->flags |= SH_SERIAL_FLAG_TEND; - if ((s->feat & SH_SERIAL_FEAT_SCIF) && s->txi) { - qemu_set_irq(s->txi, val & (1 << 7)); - } - if (!(val & (1 << 6))) { - qemu_set_irq(s->rxi, 0); - } - return; - case 0x0c: /* FTDR / TDR */ - if (qemu_chr_fe_backend_connected(&s->chr)) { - ch = val; - /* XXX this blocks entire thread. Rewrite to use - * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(&s->chr, &ch, 1); - } - s->dr = val; - s->flags &= ~SH_SERIAL_FLAG_TDE; - return; -#if 0 - case 0x14: /* FRDR / RDR */ - ret = 0; - break; -#endif - } - if (s->feat & SH_SERIAL_FEAT_SCIF) { - switch(offs) { - case 0x10: /* FSR */ - if (!(val & (1 << 6))) - s->flags &= ~SH_SERIAL_FLAG_TEND; - if (!(val & (1 << 5))) - s->flags &= ~SH_SERIAL_FLAG_TDE; - if (!(val & (1 << 4))) - s->flags &= ~SH_SERIAL_FLAG_BRK; - if (!(val & (1 << 1))) - s->flags &= ~SH_SERIAL_FLAG_RDF; - if (!(val & (1 << 0))) - s->flags &= ~SH_SERIAL_FLAG_DR; - - if (!(val & (1 << 1)) || !(val & (1 << 0))) { - if (s->rxi) { - qemu_set_irq(s->rxi, 0); - } - } - return; - case 0x18: /* FCR */ - s->fcr = val; - switch ((val >> 6) & 3) { - case 0: - s->rtrg = 1; - break; - case 1: - s->rtrg = 4; - break; - case 2: - s->rtrg = 8; - break; - case 3: - s->rtrg = 14; - break; - } - if (val & (1 << 1)) { - sh_serial_clear_fifo(s); - s->sr &= ~(1 << 1); - } - - return; - case 0x20: /* SPTR */ - s->sptr = val & 0xf3; - return; - case 0x24: /* LSR */ - return; - } - } - else { - switch(offs) { -#if 0 - case 0x0c: - ret = s->dr; - break; - case 0x10: - ret = 0; - break; -#endif - case 0x1c: - s->sptr = val & 0x8f; - return; - } - } - - fprintf(stderr, "sh_serial: unsupported write to 0x%02" - HWADDR_PRIx "\n", offs); - abort(); -} - -static uint64_t sh_serial_read(void *opaque, hwaddr offs, - unsigned size) -{ - sh_serial_state *s = opaque; - uint32_t ret = ~0; - -#if 0 - switch(offs) { - case 0x00: - ret = s->smr; - break; - case 0x04: - ret = s->brr; - break; - case 0x08: - ret = s->scr; - break; - case 0x14: - ret = 0; - break; - } -#endif - if (s->feat & SH_SERIAL_FEAT_SCIF) { - switch(offs) { - case 0x00: /* SMR */ - ret = s->smr; - break; - case 0x08: /* SCR */ - ret = s->scr; - break; - case 0x10: /* FSR */ - ret = 0; - if (s->flags & SH_SERIAL_FLAG_TEND) - ret |= (1 << 6); - if (s->flags & SH_SERIAL_FLAG_TDE) - ret |= (1 << 5); - if (s->flags & SH_SERIAL_FLAG_BRK) - ret |= (1 << 4); - if (s->flags & SH_SERIAL_FLAG_RDF) - ret |= (1 << 1); - if (s->flags & SH_SERIAL_FLAG_DR) - ret |= (1 << 0); - - if (s->scr & (1 << 5)) - s->flags |= SH_SERIAL_FLAG_TDE | SH_SERIAL_FLAG_TEND; - - break; - case 0x14: - if (s->rx_cnt > 0) { - ret = s->rx_fifo[s->rx_tail++]; - s->rx_cnt--; - if (s->rx_tail == SH_RX_FIFO_LENGTH) - s->rx_tail = 0; - if (s->rx_cnt < s->rtrg) - s->flags &= ~SH_SERIAL_FLAG_RDF; - } - break; - case 0x18: - ret = s->fcr; - break; - case 0x1c: - ret = s->rx_cnt; - break; - case 0x20: - ret = s->sptr; - break; - case 0x24: - ret = 0; - break; - } - } - else { - switch(offs) { -#if 0 - case 0x0c: - ret = s->dr; - break; - case 0x10: - ret = 0; - break; - case 0x14: - ret = s->rx_fifo[0]; - break; -#endif - case 0x1c: - ret = s->sptr; - break; - } - } -#ifdef DEBUG_SERIAL - printf("sh_serial: read offs=0x%02x val=0x%x\n", - offs, ret); -#endif - - if (ret & ~((1 << 16) - 1)) { - fprintf(stderr, "sh_serial: unsupported read from 0x%02" - HWADDR_PRIx "\n", offs); - abort(); - } - - return ret; -} - -static int sh_serial_can_receive(sh_serial_state *s) -{ - return s->scr & (1 << 4); -} - -static void sh_serial_receive_break(sh_serial_state *s) -{ - if (s->feat & SH_SERIAL_FEAT_SCIF) - s->sr |= (1 << 4); -} - -static int sh_serial_can_receive1(void *opaque) -{ - sh_serial_state *s = opaque; - return sh_serial_can_receive(s); -} - -static void sh_serial_timeout_int(void *opaque) -{ - sh_serial_state *s = opaque; - - s->flags |= SH_SERIAL_FLAG_RDF; - if (s->scr & (1 << 6) && s->rxi) { - qemu_set_irq(s->rxi, 1); - } -} - -static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size) -{ - sh_serial_state *s = opaque; - - if (s->feat & SH_SERIAL_FEAT_SCIF) { - int i; - for (i = 0; i < size; i++) { - if (s->rx_cnt < SH_RX_FIFO_LENGTH) { - s->rx_fifo[s->rx_head++] = buf[i]; - if (s->rx_head == SH_RX_FIFO_LENGTH) { - s->rx_head = 0; - } - s->rx_cnt++; - if (s->rx_cnt >= s->rtrg) { - s->flags |= SH_SERIAL_FLAG_RDF; - if (s->scr & (1 << 6) && s->rxi) { - timer_del(s->fifo_timeout_timer); - qemu_set_irq(s->rxi, 1); - } - } else { - timer_mod(s->fifo_timeout_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 15 * s->etu); - } - } - } - } else { - s->rx_fifo[0] = buf[0]; - } -} - -static void sh_serial_event(void *opaque, QEMUChrEvent event) -{ - sh_serial_state *s = opaque; - if (event == CHR_EVENT_BREAK) - sh_serial_receive_break(s); -} - -static const MemoryRegionOps sh_serial_ops = { - .read = sh_serial_read, - .write = sh_serial_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -void sh_serial_init(MemoryRegion *sysmem, - hwaddr base, int feat, - uint32_t freq, Chardev *chr, - qemu_irq eri_source, - qemu_irq rxi_source, - qemu_irq txi_source, - qemu_irq tei_source, - qemu_irq bri_source) -{ - sh_serial_state *s; - - s = g_malloc0(sizeof(sh_serial_state)); - - s->feat = feat; - s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE; - s->rtrg = 1; - - s->smr = 0; - s->brr = 0xff; - s->scr = 1 << 5; /* pretend that TX is enabled so early printk works */ - s->sptr = 0; - - if (feat & SH_SERIAL_FEAT_SCIF) { - s->fcr = 0; - } - else { - s->dr = 0xff; - } - - sh_serial_clear_fifo(s); - - memory_region_init_io(&s->iomem, NULL, &sh_serial_ops, s, - "serial", 0x100000000ULL); - - memory_region_init_alias(&s->iomem_p4, NULL, "serial-p4", &s->iomem, - 0, 0x28); - memory_region_add_subregion(sysmem, P4ADDR(base), &s->iomem_p4); - - memory_region_init_alias(&s->iomem_a7, NULL, "serial-a7", &s->iomem, - 0, 0x28); - memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7); - - if (chr) { - qemu_chr_fe_init(&s->chr, chr, &error_abort); - qemu_chr_fe_set_handlers(&s->chr, sh_serial_can_receive1, - sh_serial_receive1, - sh_serial_event, NULL, s, NULL, true); - } - - s->fifo_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, - sh_serial_timeout_int, s); - s->etu = NANOSECONDS_PER_SECOND / 9600; - s->eri = eri_source; - s->rxi = rxi_source; - s->txi = txi_source; - s->tei = tei_source; - s->bri = bri_source; -} diff --git a/MAINTAINERS b/MAINTAINERS index a77f246569..ecfa97cefa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1410,7 +1410,7 @@ R2D R: Yoshinori Sato R: Magnus Damm S: Odd Fixes -F: hw/char/sh_serial.c +F: hw/char/renesas_sci.c F: hw/sh4/r2d.c F: hw/intc/sh_intc.c F: hw/pci-host/sh_pci.c @@ -1422,7 +1422,7 @@ R: Yoshinori Sato R: Magnus Damm S: Odd Fixes F: hw/block/tc58128.c -F: hw/char/sh_serial.c +F: hw/char/reness_sci.c F: hw/sh4/shix.c F: hw/intc/sh_intc.c F: hw/timer/sh_timer.c diff --git a/hw/char/Kconfig b/hw/char/Kconfig index 4cf36ac637..a8bf0c6a77 100644 --- a/hw/char/Kconfig +++ b/hw/char/Kconfig @@ -50,9 +50,6 @@ config SCLPCONSOLE config TERMINAL3270 bool -config SH_SCI - bool - config RENESAS_SCI bool diff --git a/hw/char/meson.build b/hw/char/meson.build index 8361d0ab28..b9a88aa200 100644 --- a/hw/char/meson.build +++ b/hw/char/meson.build @@ -29,7 +29,6 @@ softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_uart.c')) softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_aux.c')) softmmu_ss.add(when: 'CONFIG_RENESAS_SCI', if_true: files('renesas_sci.c')) softmmu_ss.add(when: 'CONFIG_SIFIVE_UART', if_true: files('sifive_uart.c')) -softmmu_ss.add(when: 'CONFIG_SH_SCI', if_true: files('sh_serial.c')) softmmu_ss.add(when: 'CONFIG_STM32F2XX_USART', if_true: files('stm32f2xx_usart.c')) softmmu_ss.add(when: 'CONFIG_MCHP_PFSOC_MMUART', if_true: files('mchp_pfsoc_mmuart.c')) From patchwork Thu May 27 05:21:14 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 12283365 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.9 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,UNWANTED_LANGUAGE_BODY, URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 771C5C47089 for ; Thu, 27 May 2021 05:28:57 +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 E4198610FA for ; Thu, 27 May 2021 05:28:56 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org E4198610FA 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+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:56064 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lm8Zk-0005ed-3f for qemu-devel@archiver.kernel.org; Thu, 27 May 2021 01:28:56 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:56902) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lm8Sj-0008S0-0X for qemu-devel@nongnu.org; Thu, 27 May 2021 01:21:41 -0400 Received: from mail01.asahi-net.or.jp ([202.224.55.13]:58620) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lm8Sc-00064b-2X for qemu-devel@nongnu.org; Thu, 27 May 2021 01:21:40 -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 7AB7D11D366; Thu, 27 May 2021 14:21:30 +0900 (JST) Received: from yo-satoh-debian.localdomain (y245018.dynamic.ppp.asahi-net.or.jp [118.243.245.18]) by sakura.ysato.name (Postfix) with ESMTPSA id E18F31C064D; Thu, 27 May 2021 14:21:29 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 03/11] hw/timer: Renesas TMU/CMT module. Date: Thu, 27 May 2021 14:21:14 +0900 Message-Id: <20210527052122.97103-4-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210527052122.97103-1-ysato@users.sourceforge.jp> References: <20210527052122.97103-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-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+qemu-devel=archiver.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 | 96 +++++ hw/timer/renesas_timer.c | 644 +++++++++++++++++++++++++++++++ hw/timer/Kconfig | 3 + hw/timer/meson.build | 1 + 4 files changed, 744 insertions(+) create mode 100644 include/hw/timer/renesas_timer.h create mode 100644 hw/timer/renesas_timer.c diff --git a/include/hw/timer/renesas_timer.h b/include/hw/timer/renesas_timer.h new file mode 100644 index 0000000000..d71feec54e --- /dev/null +++ b/include/hw/timer/renesas_timer.h @@ -0,0 +1,96 @@ +/* + * Renesas Timer unit Object + * + * Copyright (c) 2020 Yoshinori Sato + * + * This code is licensed under the GPL version 2 or later. + * + */ + +#ifndef HW_RENESAS_TIMER_H +#define HW_RENESAS_TIMER_H + +#include "hw/sysbus.h" +#include "hw/qdev-clock.h" + +#define TYPE_RENESAS_TIMER_BASE "renesas-timer" +OBJECT_DECLARE_TYPE(RenesasTimerBaseState, RenesasTimerBaseClass, + RENESAS_TIMER_BASE) +#define TYPE_RENESAS_CMT "renesas-cmt" +OBJECT_DECLARE_TYPE(RenesasCMTState, RenesasCMTClass, + RENESAS_CMT) +#define TYPE_RENESAS_TMU "renesas-tmu" +OBJECT_DECLARE_TYPE(RenesasTMUState, RenesasTMUClass, + RENESAS_TMU) + +enum { + TIMER_CH_CMT = 2, + TIMER_CH_TMU = 3, +}; + +enum { + CMT_NR_IRQ = 1 * TIMER_CH_CMT, +}; + +struct RenesasTimerBaseState; + +enum dirction { + countup, countdown, +}; + +struct rtimer_ch { + uint32_t cnt; + uint32_t cor; + uint16_t ctrl; + qemu_irq irq; + int64_t base; + int64_t next; + uint64_t clk; + bool start; + QEMUTimer *timer; + struct RenesasTimerBaseState *tmrp; +}; + +typedef struct RenesasTimerBaseState { + SysBusDevice parent_obj; + + uint64_t input_freq; + MemoryRegion memory; + Clock *pck; + + struct rtimer_ch ch[TIMER_CH_TMU]; + int num_ch; + enum dirction direction; + int unit; +} RenesasTimerBaseState; + +typedef struct RenesasCMTState { + RenesasTimerBaseState parent_obj; +} RenesasCMTState; + +typedef struct RenesasTMUState { + RenesasTimerBaseState parent_obj; + uint8_t tocr; + MemoryRegion memory_p4; + MemoryRegion memory_a7; +} RenesasTMUState; + +typedef struct RenesasTimerBaseClass { + SysBusDeviceClass parent; + int (*divrate)(RenesasTimerBaseState *tmr, int ch); + void (*timer_event)(void *opaque); + int64_t (*delta_to_tcnt)(RenesasTimerBaseState *tmr, int ch, int64_t delta); + int64_t (*get_next)(RenesasTimerBaseState *tmr, int ch); + void (*update_clk)(RenesasTimerBaseState *tmr, int ch); +} RenesasTimerBaseClass; + +typedef struct RenesasCMTClass { + RenesasTimerBaseClass parent; +} RenesasCMTClass; + +typedef struct RenesasTMUClass { + RenesasTimerBaseClass parent; + void (*p_update_clk)(RenesasTimerBaseState *tmr, int ch); +} RenesasTMUClass; + +#endif diff --git a/hw/timer/renesas_timer.c b/hw/timer/renesas_timer.c new file mode 100644 index 0000000000..b1224e1e6b --- /dev/null +++ b/hw/timer/renesas_timer.c @@ -0,0 +1,644 @@ +/* + * Renesas 16bit/32bit Compare-match timer (CMT/TMU) + * + * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware + * (Rev.1.40 R01UH0033EJ0140) + * And SH7751 Group, SH7751R Group User's Manual: Hardware + * (Rev.4.01 R01UH0457EJ0401) + * + * Copyright (c) 2021 Yoshinori Sato + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "qemu/timer.h" +#include "hw/hw.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/qdev-properties.h" +#include "hw/timer/renesas_timer.h" +#include "migration/vmstate.h" +#include "qemu/error-report.h" + +REG32(TOCR, 0) + FIELD(TOCR, TCOE, 0, 1) +REG32(CMSTR, 0) +REG32(TSTR, 4) +REG32(TCOR, 8) +REG32(TCNT, 12) +REG32(TCR, 16) + FIELD(TCR, TPSC, 0, 3) + FIELD(TCR, CKEG, 3, 2) + FIELD(TCR, UNIE, 5, 1) + FIELD(TCR, ICPE, 6, 2) + FIELD(TCR, UNF, 8, 1) + FIELD(TCR, ICPF, 9, 1) +REG32(CMCR, 16) + FIELD(CMCR, CKS, 0, 2) + FIELD(CMCR, CMIE, 6, 1) +REG32(TCPR, 20) + +static int cmt_div(RenesasTimerBaseState *tmr, int ch) +{ + return 8 << (2 * FIELD_EX16(tmr->ch[ch].ctrl, CMCR, CKS)); +} + +static int tmu_div(RenesasTimerBaseState *tmr, int ch) +{ + if (FIELD_EX16(tmr->ch[ch].ctrl, TCR, TPSC) <= 5) { + return 4 << (2 * FIELD_EX16(tmr->ch[ch].ctrl, TCR, TPSC)); + } else { + return 0; + } + +} + +static int64_t cmt_get_next(RenesasTimerBaseState *tmr, int ch) +{ + return tmr->ch[ch].cor - tmr->ch[ch].cnt; +} + +static int64_t tmu_get_next(RenesasTimerBaseState *tmr, int ch) +{ + return tmr->ch[ch].cnt; +} + +static void cmt_timer_event(void *opaque) +{ + struct rtimer_ch *ch = opaque; + ch->cnt = 0; + if (FIELD_EX16(ch->ctrl, CMCR, CMIE)) { + qemu_irq_pulse(ch->irq); + } + ch->base = ch->next; + ch->next += (ch->cor - ch->cnt) * ch->clk; + timer_mod(ch->timer, ch->next); +} + +static void tmu_timer_event(void *opaque) +{ + struct rtimer_ch *ch = opaque; + ch->cnt = ch->cor; + if (!FIELD_EX16(ch->ctrl, TCR, UNF)) { + ch->ctrl = FIELD_DP16(ch->ctrl, TCR, UNF, 1); + qemu_set_irq(ch->irq, FIELD_EX16(ch->ctrl, TCR, UNIE)); + } + ch->base = ch->next; + ch->next += ch->cnt * ch->clk; + timer_mod(ch->timer, ch->next); +} + +static int64_t cmt_delta_to_cnt(RenesasTimerBaseState *tmr, + int ch, int64_t delta) +{ + return tmr->ch[ch].cnt + delta; +} + +static int64_t tmu_delta_to_cnt(RenesasTimerBaseState *tmr, + int ch, int64_t delta) +{ + return tmr->ch[ch].cnt - delta; +} + +static int64_t read_tcnt(RenesasTimerBaseState *tmr, int ch) +{ + int64_t delta, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + RenesasTimerBaseClass *tc = RENESAS_TIMER_BASE_GET_CLASS(tmr); + + if (tmr->ch[ch].clk > 0) { + delta = (now - tmr->ch[ch].base); + delta /= tmr->ch[ch].clk; + return tc->delta_to_tcnt(tmr, ch, delta); + } else { + return tmr->ch[ch].cnt; + } +} + +static void tmr_start_stop(RenesasTimerBaseState *tmr, int ch, int start) +{ + RenesasTimerBaseClass *tc = RENESAS_TIMER_BASE_GET_CLASS(tmr); + int64_t now; + if (tmr->ch[ch].start != start) { + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + if (start) { + if (!tmr->ch[ch].timer) { + tmr->ch[ch].timer = + timer_new_ns(QEMU_CLOCK_VIRTUAL, + tc->timer_event, &tmr->ch[ch]); + } + tmr->ch[ch].base = now; + tmr->ch[ch].next = now + tc->get_next(tmr, ch) * tmr->ch[ch].clk; + timer_mod(tmr->ch[ch].timer, tmr->ch[ch].next); + } else { + tmr->ch[ch].cnt = read_tcnt(tmr, ch); + tmr->ch[ch].next = 0; + if (tmr->ch[ch].timer) { + timer_del(tmr->ch[ch].timer); + } + } + tmr->ch[ch].start = start; + } +} + +static uint64_t read_tstr(RenesasTimerBaseState *tmr) +{ + uint64_t ret = 0; + int ch; + for (ch = 0; ch < tmr->num_ch; ch++) { + ret = deposit64(ret, ch, 1, tmr->ch[ch].start); + } + return ret; +} + +static void update_clk(RenesasTimerBaseState *tmr, int ch) +{ + RenesasTimerBaseClass *tc = RENESAS_TIMER_BASE_GET_CLASS(tmr); + int t; + t = tc->divrate(tmr, ch); + if (t > 0) { + t = tmr->input_freq / t; + tmr->ch[ch].clk = NANOSECONDS_PER_SECOND / t; + } else { + tmr->ch[ch].clk = 0; + } +} + +static void tmu_update_clk(RenesasTimerBaseState *tmr, int ch) +{ + /* Clock setting validation */ + int tpsc = FIELD_EX16(tmr->ch[ch].ctrl, TCR, TPSC); + switch (tpsc) { + case 5: + qemu_log_mask(LOG_GUEST_ERROR, + "renesas_timer: Invalid TPSC valule %d.\n", tpsc); + break; + case 6: + case 7: + qemu_log_mask(LOG_UNIMP, + "renesas_timer: External clock not implemented.\n"); + break; + } + /* Interrupt clear */ + if (FIELD_EX16(tmr->ch[ch].ctrl, TCR, UNF) == 0) { + qemu_set_irq(tmr->ch[ch].irq, 0); + } + update_clk(tmr, ch); +} + +static uint64_t channel_read(RenesasTimerBaseState *tmr, int ch, int reg) +{ + switch (reg) { + case R_TCR: + return tmr->ch[ch].ctrl; + case R_TCNT: + if (tmr->ch[ch].start) { + return read_tcnt(tmr, ch); + } else { + return tmr->ch[ch].cnt; + } + case R_TCOR: + return tmr->ch[ch].cor; + } + return UINT64_MAX; +} + +static void tmr_pck_update(void *opaque, ClockEvent evt) +{ + RenesasTimerBaseState *tmr = RENESAS_TIMER_BASE(opaque); + int64_t now; + int i; + struct rtimer_ch *ch; + for (i = 0; i < TIMER_CH_CMT; i++) { + if (tmr->ch[i].start) { + tmr->ch[i].cnt = read_tcnt(tmr, i); + } + } + if (clock_is_enabled(tmr->pck)) { + tmr->input_freq = clock_get_hz(tmr->pck); + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + for (i = 0; i < TIMER_CH_CMT; i++) { + update_clk(tmr, i); + ch = &tmr->ch[i]; + if (ch->start) { + ch->next = ch->base = now; + if (tmr->direction == countup) { + ch->next += (ch->cor - ch->cnt) * ch->clk; + } else { + ch->next += ch->cnt * ch->clk; + } + timer_mod(ch->timer, ch->next); + } + } + } else { + for (i = 0; i < TIMER_CH_CMT; i++) { + if (tmr->ch[i].timer) { + timer_del(tmr->ch[i].timer); + } + } + } +} + +static uint64_t cmt_read(void *opaque, hwaddr addr, unsigned size) +{ + RenesasCMTState *cmt = RENESAS_CMT(opaque); + RenesasTimerBaseState *tmr = RENESAS_TIMER_BASE(cmt); + int ch, reg; + + /* +0 - CMSTR (TSTR) */ + /* +2 - CMCR0 (TCR) */ + /* +4 - CMCNT0 (TCNT) */ + /* +6 - CMCOR0 (TCOR) */ + /* +8 - CMCR1 (TCR) */ + /* +10 - CMCNT1 (TCNT) */ + /* +12 - CMCOR1 (TCOR) */ + if (!clock_is_enabled(tmr->pck)) { + qemu_log_mask(LOG_UNIMP, "renesas_timer: Unit %d stopped.\n", + tmr->unit); + return UINT64_MAX; + } + addr /= 2; + if (addr == R_CMSTR) { + return read_tstr(RENESAS_TIMER_BASE(cmt)); + } else { + ch = addr / 4; + if (addr < 4) { + /* skip CMSTR */ + addr--; + } + reg = 2 - (addr % 4); + return channel_read(RENESAS_TIMER_BASE(cmt), ch, reg); + } +} + +static uint64_t tmu_read(void *opaque, hwaddr addr, unsigned size) +{ + RenesasTMUState *tmu = RENESAS_TMU(opaque); + RenesasTimerBaseState *tmr = RENESAS_TIMER_BASE(tmu); + int ch = -1, reg = -1; + + /* +0 - TCOR */ + /* +4 - TSTR */ + /* +8 - TCOR0 */ + /* +12 - TCNT0 */ + /* +16 - TCR0 */ + /* +20 - TCOR1 */ + /* +24 - TCNT1 */ + /* +28 - TCR1 */ + /* +32 - TCOR2 */ + /* +36 - TCNT2 */ + /* +40 - TCR2 */ + /* +44 - TCPR2 */ + + if (tmr->unit != 0 && addr >= 32) { + /* UNIT1 channel2 is not exit */ + qemu_log_mask(LOG_UNIMP, "renesas_timer: Register 0x%" + HWADDR_PRIX " not implemented\n", addr); + return UINT64_MAX; + } + if (!clock_is_enabled(tmr->pck)) { + qemu_log_mask(LOG_UNIMP, "renesas_timer: Unit %d stopped.\n", + tmr->unit); + return UINT64_MAX; + } + addr /= 4; + switch (addr) { + case R_TOCR: + return tmu->tocr; + case R_TSTR: + return read_tstr(RENESAS_TIMER_BASE(tmu)); + case R_TCPR: + qemu_log_mask(LOG_UNIMP, + "renesas_timer: Input capture not implemented.\n"); + return UINT64_MAX; + default: + ch = (addr - 2) / 3; + reg = (addr - 2) % 3 + 2; + return channel_read(RENESAS_TIMER_BASE(tmu), ch, reg); + } +} + +static void write_tstr(RenesasTimerBaseState *tmr, uint16_t val) +{ + int ch; + for (ch = 0; ch < tmr->num_ch; ch++) { + tmr_start_stop(tmr, ch, extract16(val, ch, 1)); + } +} + +static void write_tcr(RenesasTimerBaseState *tmr, int ch, + uint16_t val, uint16_t mask) +{ + RenesasTimerBaseClass *tc = RENESAS_TIMER_BASE_GET_CLASS(tmr); + tmr->ch[ch].ctrl |= (mask & 0x00ff); + tmr->ch[ch].ctrl &= val & mask; + tc->update_clk(tmr, ch); +} + +static void channel_write(RenesasTimerBaseState *tmr, int ch, + int reg, uint64_t val) +{ + switch (reg) { + case R_TCNT: + tmr->ch[ch].cnt = val; + break; + case R_TCOR: + tmr->ch[ch].cor = val; + break; + } +} + +static void cmt_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + RenesasTimerBaseState *tmr = RENESAS_TIMER_BASE(opaque); + int ch, reg; + uint32_t next_timeout; + uint16_t cnt; + + if (!clock_is_enabled(tmr->pck)) { + qemu_log_mask(LOG_UNIMP, "renesas_timer: Unit %d stopped.\n", + tmr->unit); + return; + } + addr /= 2; + if (addr == R_CMSTR) { + write_tstr(tmr, val); + } else { + ch = addr / 4; + if (addr < 4) { + /* skip CMSTR */ + addr--; + } + reg = (2 - (addr % 4)) + 2; + if (reg == R_TCR) { + /* bit7 always 1 */ + val |= 0x0080; + write_tcr(RENESAS_TIMER_BASE(tmr), ch, val, 0x0043); + } else { + channel_write(RENESAS_TIMER_BASE(tmr), ch, reg, val); + if (tmr->ch[ch].start) { + if (reg == R_TCNT) { + cnt = tmr->ch[ch].cnt; + } else { + cnt = read_tcnt(tmr, ch); + } + if (tmr->ch[ch].cor < cnt) { + next_timeout = 0x10000 + tmr->ch[ch].cor - cnt; + } else { + next_timeout = tmr->ch[ch].cor - cnt; + } + tmr->ch[ch].next = tmr->ch[ch].base + + next_timeout * tmr->ch[ch].clk; + timer_mod(tmr->ch[ch].timer, tmr->ch[ch].next); + } + } + } +} + +static void tmu_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + RenesasTMUState *tmu = RENESAS_TMU(opaque); + RenesasTimerBaseState *tmr = RENESAS_TIMER_BASE(tmu); + + int ch, reg; + uint16_t tcr_mask; + + if (tmr->unit != 0 && addr >= 32) { + /* UNIT1 channel2 is not exit */ + qemu_log_mask(LOG_UNIMP, "renesas_timer: Register 0x%" + HWADDR_PRIX " not implemented\n", addr); + return; + } + if (!clock_is_enabled(tmr->pck)) { + qemu_log_mask(LOG_UNIMP, "renesas_timer: Unit %d stopped.\n", + tmr->unit); + return; + } + addr /= 4; + switch (addr) { + case R_TOCR: + tmu->tocr = FIELD_DP8(tmu->tocr, TOCR, TCOE, + FIELD_EX8(val, TOCR, TCOE)); + break; + case R_TSTR: + write_tstr(tmr, val); + break; + case R_TCPR: + qemu_log_mask(LOG_GUEST_ERROR, + "renesas_timer: TCPR is read only.\n"); + break; + default: + ch = (addr - 2) / 3; + reg = (addr - 2) % 3 + 2; + if (reg == R_TCR) { + if (tmr->unit == 0) { + tcr_mask = (ch < 2) ? 0x013f : 0x03ff; + } else { + tcr_mask = 0x0127; + } + write_tcr(tmr, ch, val, tcr_mask); + } else { + channel_write(tmr, ch, reg, val); + if (reg == R_TCNT && tmr->ch[ch].start) { + tmr->ch[ch].next = tmr->ch[ch].base + + tmr->ch[ch].cnt * tmr->ch[ch].clk; + timer_mod(tmr->ch[ch].timer, tmr->ch[ch].next); + } + } + break; + } +} + +static const MemoryRegionOps cmt_ops = { + .write = cmt_write, + .read = cmt_read, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 2, + .max_access_size = 2, + }, +}; + +static const MemoryRegionOps tmu_ops = { + .write = tmu_write, + .read = tmu_read, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 2, + .max_access_size = 4, + }, +}; + +static void timer_base_realize(RenesasTimerBaseState *tmr, int num_ch) +{ + tmr->num_ch = num_ch; +} + +static void cmt_realize(DeviceState *dev, Error **errp) +{ + RenesasCMTState *cmt = RENESAS_CMT(dev); + RenesasTimerBaseState *tmr = RENESAS_TIMER_BASE(cmt); + int i; + + timer_base_realize(tmr, TIMER_CH_CMT); + + for (i = 0; i < TIMER_CH_CMT; i++) { + tmr->ch[i].cor = 0xffff; + if (clock_is_enabled(tmr->pck)) { + update_clk(tmr, i); + } + } +} + +static void cmt_init(Object *obj) +{ + SysBusDevice *d = SYS_BUS_DEVICE(obj); + RenesasCMTState *cmt = RENESAS_CMT(obj); + RenesasTimerBaseState *tmr = RENESAS_TIMER_BASE(cmt); + int i; + + tmr->direction = countup; + memory_region_init_io(&tmr->memory, obj, &cmt_ops, + tmr, "renesas-cmt", 0x10); + sysbus_init_mmio(d, &tmr->memory); + + for (i = 0; i < TIMER_CH_CMT; i++) { + sysbus_init_irq(d, &tmr->ch[i].irq); + } + tmr->pck = qdev_init_clock_in(DEVICE(obj), "pck", + tmr_pck_update, tmr, ClockUpdate); +} + +static void tmu_realize(DeviceState *dev, Error **errp) +{ + SysBusDevice *d = SYS_BUS_DEVICE(dev); + RenesasTMUState *tmu = RENESAS_TMU(dev); + RenesasTimerBaseState *tmr = RENESAS_TIMER_BASE(tmu); + int i; + int num_ch; + + /* Unit0 have 3ch, Unit1 have 2ch */ + num_ch = TIMER_CH_TMU - tmr->unit; + timer_base_realize(tmr, num_ch); + for (i = 0; i < num_ch; i++) { + sysbus_init_irq(d, &tmr->ch[i].irq); + tmr->ch[i].cor = tmr->ch[i].cnt = 0xffffffff; + if (clock_is_enabled(tmr->pck)) { + update_clk(tmr, i); + } + } +} + +static void tmu_init(Object *obj) +{ + SysBusDevice *d = SYS_BUS_DEVICE(obj); + RenesasTimerBaseState *tmr = RENESAS_TIMER_BASE(obj); + RenesasTMUState *tmu = RENESAS_TMU(obj); + + tmr->direction = countdown; + memory_region_init_io(&tmr->memory, obj, &tmu_ops, + tmr, "renesas-tmu", 0x30); + sysbus_init_mmio(d, &tmr->memory); + memory_region_init_alias(&tmu->memory_p4, NULL, "renesas-tmu-p4", + &tmr->memory, 0, 0x30); + sysbus_init_mmio(d, &tmu->memory_p4); + memory_region_init_alias(&tmu->memory_a7, NULL, "renesas-tmu-a7", + &tmr->memory, 0, 0x30); + sysbus_init_mmio(d, &tmu->memory_a7); + tmr->pck = qdev_init_clock_in(DEVICE(obj), "pck", + tmr_pck_update, tmr, ClockUpdate); +} + +static const VMStateDescription vmstate_rtimer = { + .name = "rx-cmt", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_END_OF_LIST() + } +}; + +static Property renesas_timer_properties[] = { + DEFINE_PROP_INT32("unit", RenesasTimerBaseState, unit, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void renesas_timer_base_class_init(ObjectClass *klass, void *data) +{ + RenesasTimerBaseClass *base = RENESAS_TIMER_BASE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_rtimer; + base->update_clk = update_clk; + device_class_set_props(dc, renesas_timer_properties); +} + +static void cmt_class_init(ObjectClass *klass, void *data) +{ + RenesasTimerBaseClass *base = RENESAS_TIMER_BASE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + base->divrate = cmt_div; + base->timer_event = cmt_timer_event; + base->delta_to_tcnt = cmt_delta_to_cnt; + base->get_next = cmt_get_next; + dc->realize = cmt_realize; +} + +static void tmu_class_init(ObjectClass *klass, void *data) +{ + RenesasTimerBaseClass *base = RENESAS_TIMER_BASE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + base->divrate = tmu_div; + base->timer_event = tmu_timer_event; + base->delta_to_tcnt = tmu_delta_to_cnt; + base->get_next = tmu_get_next; + base->update_clk = tmu_update_clk; + dc->realize = tmu_realize; +} + +static const TypeInfo renesas_timer_info[] = { + { + .name = TYPE_RENESAS_TIMER_BASE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(RenesasTimerBaseState), + .class_init = renesas_timer_base_class_init, + .class_size = sizeof(RenesasTimerBaseClass), + .abstract = true, + }, + { + .name = TYPE_RENESAS_CMT, + .parent = TYPE_RENESAS_TIMER_BASE, + .instance_size = sizeof(RenesasCMTState), + .instance_init = cmt_init, + .class_init = cmt_class_init, + .class_size = sizeof(RenesasCMTClass), + }, + { + .name = TYPE_RENESAS_TMU, + .parent = TYPE_RENESAS_TIMER_BASE, + .instance_size = sizeof(RenesasTMUState), + .instance_init = tmu_init, + .class_init = tmu_class_init, + .class_size = sizeof(RenesasTMUClass), + }, +}; + +DEFINE_TYPES(renesas_timer_info) diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig index bac2511715..347add12dd 100644 --- a/hw/timer/Kconfig +++ b/hw/timer/Kconfig @@ -54,3 +54,6 @@ config SSE_TIMER config AVR_TIMER16 bool + +config RENESAS_TIMER + bool diff --git a/hw/timer/meson.build b/hw/timer/meson.build index 157f540ecd..9019dce993 100644 --- a/hw/timer/meson.build +++ b/hw/timer/meson.build @@ -33,5 +33,6 @@ softmmu_ss.add(when: 'CONFIG_SSE_COUNTER', if_true: files('sse-counter.c')) softmmu_ss.add(when: 'CONFIG_SSE_TIMER', if_true: files('sse-timer.c')) softmmu_ss.add(when: 'CONFIG_STM32F2XX_TIMER', if_true: files('stm32f2xx_timer.c')) softmmu_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_timer.c')) +softmmu_ss.add(when: 'CONFIG_RENESAS_TIMER', if_true: files('renesas_timer.c')) specific_ss.add(when: 'CONFIG_AVR_TIMER16', if_true: files('avr_timer16.c')) From patchwork Thu May 27 05:21:15 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 12283357 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 83085C4707F for ; Thu, 27 May 2021 05:26:27 +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 2357B613CC for ; Thu, 27 May 2021 05:26:27 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 2357B613CC 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+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:47342 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lm8XK-0008Bi-93 for qemu-devel@archiver.kernel.org; Thu, 27 May 2021 01:26:26 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:56898) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lm8Si-0008R7-RU for qemu-devel@nongnu.org; Thu, 27 May 2021 01:21:40 -0400 Received: from mail01.asahi-net.or.jp ([202.224.55.13]:58621) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lm8Sc-00064s-2Z for qemu-devel@nongnu.org; Thu, 27 May 2021 01:21:40 -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 CAA1B11D396; Thu, 27 May 2021 14:21:30 +0900 (JST) Received: from yo-satoh-debian.localdomain (y245018.dynamic.ppp.asahi-net.or.jp [118.243.245.18]) by sakura.ysato.name (Postfix) with ESMTPSA id 692871C0077; Thu, 27 May 2021 14:21:30 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 04/11] hw/timer: Remove sh_timer. Date: Thu, 27 May 2021 14:21:15 +0900 Message-Id: <20210527052122.97103-5-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210527052122.97103-1-ysato@users.sourceforge.jp> References: <20210527052122.97103-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-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+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" Migrate to renesas_timer. Signed-off-by: Yoshinori Sato --- include/hw/timer/tmu012.h | 23 --- hw/timer/sh_timer.c | 368 -------------------------------------- MAINTAINERS | 4 +- hw/timer/Kconfig | 4 - hw/timer/meson.build | 1 - 5 files changed, 2 insertions(+), 398 deletions(-) delete mode 100644 include/hw/timer/tmu012.h delete mode 100644 hw/timer/sh_timer.c diff --git a/include/hw/timer/tmu012.h b/include/hw/timer/tmu012.h deleted file mode 100644 index 808ed8de1d..0000000000 --- a/include/hw/timer/tmu012.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * SuperH Timer - * - * Copyright (c) 2007 Magnus Damm - * - * This code is licensed under the GPL. - */ - -#ifndef HW_TIMER_TMU012_H -#define HW_TIMER_TMU012_H - -#include "exec/hwaddr.h" - -#define TMU012_FEAT_TOCR (1 << 0) -#define TMU012_FEAT_3CHAN (1 << 1) -#define TMU012_FEAT_EXTCLK (1 << 2) - -void tmu012_init(MemoryRegion *sysmem, hwaddr base, - int feat, uint32_t freq, - qemu_irq ch0_irq, qemu_irq ch1_irq, - qemu_irq ch2_irq0, qemu_irq ch2_irq1); - -#endif diff --git a/hw/timer/sh_timer.c b/hw/timer/sh_timer.c deleted file mode 100644 index 58af1a1edb..0000000000 --- a/hw/timer/sh_timer.c +++ /dev/null @@ -1,368 +0,0 @@ -/* - * SuperH Timer modules. - * - * Copyright (c) 2007 Magnus Damm - * Based on arm_timer.c by Paul Brook - * Copyright (c) 2005-2006 CodeSourcery. - * - * This code is licensed under the GPL. - */ - -#include "qemu/osdep.h" -#include "exec/memory.h" -#include "hw/hw.h" -#include "hw/irq.h" -#include "hw/sh4/sh.h" -#include "hw/timer/tmu012.h" -#include "hw/ptimer.h" - -//#define DEBUG_TIMER - -#define TIMER_TCR_TPSC (7 << 0) -#define TIMER_TCR_CKEG (3 << 3) -#define TIMER_TCR_UNIE (1 << 5) -#define TIMER_TCR_ICPE (3 << 6) -#define TIMER_TCR_UNF (1 << 8) -#define TIMER_TCR_ICPF (1 << 9) -#define TIMER_TCR_RESERVED (0x3f << 10) - -#define TIMER_FEAT_CAPT (1 << 0) -#define TIMER_FEAT_EXTCLK (1 << 1) - -#define OFFSET_TCOR 0 -#define OFFSET_TCNT 1 -#define OFFSET_TCR 2 -#define OFFSET_TCPR 3 - -typedef struct { - ptimer_state *timer; - uint32_t tcnt; - uint32_t tcor; - uint32_t tcr; - uint32_t tcpr; - int freq; - int int_level; - int old_level; - int feat; - int enabled; - qemu_irq irq; -} sh_timer_state; - -/* Check all active timers, and schedule the next timer interrupt. */ - -static void sh_timer_update(sh_timer_state *s) -{ - int new_level = s->int_level && (s->tcr & TIMER_TCR_UNIE); - - if (new_level != s->old_level) - qemu_set_irq (s->irq, new_level); - - s->old_level = s->int_level; - s->int_level = new_level; -} - -static uint32_t sh_timer_read(void *opaque, hwaddr offset) -{ - sh_timer_state *s = (sh_timer_state *)opaque; - - switch (offset >> 2) { - case OFFSET_TCOR: - return s->tcor; - case OFFSET_TCNT: - return ptimer_get_count(s->timer); - case OFFSET_TCR: - return s->tcr | (s->int_level ? TIMER_TCR_UNF : 0); - case OFFSET_TCPR: - if (s->feat & TIMER_FEAT_CAPT) - return s->tcpr; - /* fall through */ - default: - hw_error("sh_timer_read: Bad offset %x\n", (int)offset); - return 0; - } -} - -static void sh_timer_write(void *opaque, hwaddr offset, - uint32_t value) -{ - sh_timer_state *s = (sh_timer_state *)opaque; - int freq; - - switch (offset >> 2) { - case OFFSET_TCOR: - s->tcor = value; - ptimer_transaction_begin(s->timer); - ptimer_set_limit(s->timer, s->tcor, 0); - ptimer_transaction_commit(s->timer); - break; - case OFFSET_TCNT: - s->tcnt = value; - ptimer_transaction_begin(s->timer); - ptimer_set_count(s->timer, s->tcnt); - ptimer_transaction_commit(s->timer); - break; - case OFFSET_TCR: - ptimer_transaction_begin(s->timer); - if (s->enabled) { - /* Pause the timer if it is running. This may cause some - inaccuracy dure to rounding, but avoids a whole lot of other - messyness. */ - ptimer_stop(s->timer); - } - freq = s->freq; - /* ??? Need to recalculate expiry time after changing divisor. */ - switch (value & TIMER_TCR_TPSC) { - case 0: freq >>= 2; break; - case 1: freq >>= 4; break; - case 2: freq >>= 6; break; - case 3: freq >>= 8; break; - case 4: freq >>= 10; break; - case 6: - case 7: - if (s->feat & TIMER_FEAT_EXTCLK) { - break; - } - /* fallthrough */ - default: - hw_error("sh_timer_write: Reserved TPSC value\n"); - } - switch ((value & TIMER_TCR_CKEG) >> 3) { - case 0: - break; - case 1: - case 2: - case 3: - if (s->feat & TIMER_FEAT_EXTCLK) { - break; - } - /* fallthrough */ - default: - hw_error("sh_timer_write: Reserved CKEG value\n"); - } - switch ((value & TIMER_TCR_ICPE) >> 6) { - case 0: - break; - case 2: - case 3: - if (s->feat & TIMER_FEAT_CAPT) { - break; - } - /* fallthrough */ - default: - hw_error("sh_timer_write: Reserved ICPE value\n"); - } - if ((value & TIMER_TCR_UNF) == 0) { - s->int_level = 0; - } - - value &= ~TIMER_TCR_UNF; - - if ((value & TIMER_TCR_ICPF) && (!(s->feat & TIMER_FEAT_CAPT))) { - hw_error("sh_timer_write: Reserved ICPF value\n"); - } - - value &= ~TIMER_TCR_ICPF; /* capture not supported */ - - if (value & TIMER_TCR_RESERVED) { - hw_error("sh_timer_write: Reserved TCR bits set\n"); - } - s->tcr = value; - ptimer_set_limit(s->timer, s->tcor, 0); - ptimer_set_freq(s->timer, freq); - if (s->enabled) { - /* Restart the timer if still enabled. */ - ptimer_run(s->timer, 0); - } - ptimer_transaction_commit(s->timer); - break; - case OFFSET_TCPR: - if (s->feat & TIMER_FEAT_CAPT) { - s->tcpr = value; - break; - } - /* fallthrough */ - default: - hw_error("sh_timer_write: Bad offset %x\n", (int)offset); - } - sh_timer_update(s); -} - -static void sh_timer_start_stop(void *opaque, int enable) -{ - sh_timer_state *s = (sh_timer_state *)opaque; - -#ifdef DEBUG_TIMER - printf("sh_timer_start_stop %d (%d)\n", enable, s->enabled); -#endif - - ptimer_transaction_begin(s->timer); - if (s->enabled && !enable) { - ptimer_stop(s->timer); - } - if (!s->enabled && enable) { - ptimer_run(s->timer, 0); - } - ptimer_transaction_commit(s->timer); - s->enabled = !!enable; - -#ifdef DEBUG_TIMER - printf("sh_timer_start_stop done %d\n", s->enabled); -#endif -} - -static void sh_timer_tick(void *opaque) -{ - sh_timer_state *s = (sh_timer_state *)opaque; - s->int_level = s->enabled; - sh_timer_update(s); -} - -static void *sh_timer_init(uint32_t freq, int feat, qemu_irq irq) -{ - sh_timer_state *s; - - s = (sh_timer_state *)g_malloc0(sizeof(sh_timer_state)); - s->freq = freq; - s->feat = feat; - s->tcor = 0xffffffff; - s->tcnt = 0xffffffff; - s->tcpr = 0xdeadbeef; - s->tcr = 0; - s->enabled = 0; - s->irq = irq; - - s->timer = ptimer_init(sh_timer_tick, s, PTIMER_POLICY_DEFAULT); - - sh_timer_write(s, OFFSET_TCOR >> 2, s->tcor); - sh_timer_write(s, OFFSET_TCNT >> 2, s->tcnt); - sh_timer_write(s, OFFSET_TCPR >> 2, s->tcpr); - sh_timer_write(s, OFFSET_TCR >> 2, s->tcpr); - /* ??? Save/restore. */ - return s; -} - -typedef struct { - MemoryRegion iomem; - MemoryRegion iomem_p4; - MemoryRegion iomem_a7; - void *timer[3]; - int level[3]; - uint32_t tocr; - uint32_t tstr; - int feat; -} tmu012_state; - -static uint64_t tmu012_read(void *opaque, hwaddr offset, - unsigned size) -{ - tmu012_state *s = (tmu012_state *)opaque; - -#ifdef DEBUG_TIMER - printf("tmu012_read 0x%lx\n", (unsigned long) offset); -#endif - - if (offset >= 0x20) { - if (!(s->feat & TMU012_FEAT_3CHAN)) { - hw_error("tmu012_write: Bad channel offset %x\n", (int)offset); - } - return sh_timer_read(s->timer[2], offset - 0x20); - } - - if (offset >= 0x14) - return sh_timer_read(s->timer[1], offset - 0x14); - - if (offset >= 0x08) - return sh_timer_read(s->timer[0], offset - 0x08); - - if (offset == 4) - return s->tstr; - - if ((s->feat & TMU012_FEAT_TOCR) && offset == 0) - return s->tocr; - - hw_error("tmu012_write: Bad offset %x\n", (int)offset); - return 0; -} - -static void tmu012_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - tmu012_state *s = (tmu012_state *)opaque; - -#ifdef DEBUG_TIMER - printf("tmu012_write 0x%lx 0x%08x\n", (unsigned long) offset, value); -#endif - - if (offset >= 0x20) { - if (!(s->feat & TMU012_FEAT_3CHAN)) { - hw_error("tmu012_write: Bad channel offset %x\n", (int)offset); - } - sh_timer_write(s->timer[2], offset - 0x20, value); - return; - } - - if (offset >= 0x14) { - sh_timer_write(s->timer[1], offset - 0x14, value); - return; - } - - if (offset >= 0x08) { - sh_timer_write(s->timer[0], offset - 0x08, value); - return; - } - - if (offset == 4) { - sh_timer_start_stop(s->timer[0], value & (1 << 0)); - sh_timer_start_stop(s->timer[1], value & (1 << 1)); - if (s->feat & TMU012_FEAT_3CHAN) { - sh_timer_start_stop(s->timer[2], value & (1 << 2)); - } else { - if (value & (1 << 2)) { - hw_error("tmu012_write: Bad channel\n"); - } - } - - s->tstr = value; - return; - } - - if ((s->feat & TMU012_FEAT_TOCR) && offset == 0) { - s->tocr = value & (1 << 0); - } -} - -static const MemoryRegionOps tmu012_ops = { - .read = tmu012_read, - .write = tmu012_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -void tmu012_init(MemoryRegion *sysmem, hwaddr base, - int feat, uint32_t freq, - qemu_irq ch0_irq, qemu_irq ch1_irq, - qemu_irq ch2_irq0, qemu_irq ch2_irq1) -{ - tmu012_state *s; - int timer_feat = (feat & TMU012_FEAT_EXTCLK) ? TIMER_FEAT_EXTCLK : 0; - - s = (tmu012_state *)g_malloc0(sizeof(tmu012_state)); - s->feat = feat; - s->timer[0] = sh_timer_init(freq, timer_feat, ch0_irq); - s->timer[1] = sh_timer_init(freq, timer_feat, ch1_irq); - if (feat & TMU012_FEAT_3CHAN) { - s->timer[2] = sh_timer_init(freq, timer_feat | TIMER_FEAT_CAPT, - ch2_irq0); /* ch2_irq1 not supported */ - } - - memory_region_init_io(&s->iomem, NULL, &tmu012_ops, s, - "timer", 0x100000000ULL); - - memory_region_init_alias(&s->iomem_p4, NULL, "timer-p4", - &s->iomem, 0, 0x1000); - memory_region_add_subregion(sysmem, P4ADDR(base), &s->iomem_p4); - - memory_region_init_alias(&s->iomem_a7, NULL, "timer-a7", - &s->iomem, 0, 0x1000); - memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7); - /* ??? Save/restore. */ -} diff --git a/MAINTAINERS b/MAINTAINERS index ecfa97cefa..4e41c3ff91 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1414,7 +1414,7 @@ F: hw/char/renesas_sci.c F: hw/sh4/r2d.c F: hw/intc/sh_intc.c F: hw/pci-host/sh_pci.c -F: hw/timer/sh_timer.c +F: hw/timer/renesas_timer.c F: include/hw/sh4/sh_intc.h Shix @@ -1425,7 +1425,7 @@ F: hw/block/tc58128.c F: hw/char/reness_sci.c F: hw/sh4/shix.c F: hw/intc/sh_intc.c -F: hw/timer/sh_timer.c +F: hw/timer/renesas_timer.c F: include/hw/sh4/sh_intc.h SPARC Machines diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig index 347add12dd..ec19708a4d 100644 --- a/hw/timer/Kconfig +++ b/hw/timer/Kconfig @@ -36,10 +36,6 @@ config CMSDK_APB_DUALTIMER bool select PTIMER -config SH_TIMER - bool - select PTIMER - config RENESAS_TMR bool diff --git a/hw/timer/meson.build b/hw/timer/meson.build index 9019dce993..ec70821c0b 100644 --- a/hw/timer/meson.build +++ b/hw/timer/meson.build @@ -27,7 +27,6 @@ softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_gptimer.c')) softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_synctimer.c')) softmmu_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_timer.c')) softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_systmr.c')) -softmmu_ss.add(when: 'CONFIG_SH_TIMER', if_true: files('sh_timer.c')) softmmu_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_timer.c')) softmmu_ss.add(when: 'CONFIG_SSE_COUNTER', if_true: files('sse-counter.c')) softmmu_ss.add(when: 'CONFIG_SSE_TIMER', if_true: files('sse-timer.c')) From patchwork Thu May 27 05:21:16 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 12283351 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5B897C4707F for ; Thu, 27 May 2021 05:23:53 +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 05A9061355 for ; Thu, 27 May 2021 05:23:52 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 05A9061355 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+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:38980 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lm8Uq-0002fA-7n for qemu-devel@archiver.kernel.org; Thu, 27 May 2021 01:23:52 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:56842) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lm8Sg-0008LU-I9 for qemu-devel@nongnu.org; Thu, 27 May 2021 01:21:38 -0400 Received: from mail01.asahi-net.or.jp ([202.224.55.13]:58622) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lm8Sc-00064v-2b for qemu-devel@nongnu.org; Thu, 27 May 2021 01:21:38 -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 2FD3511D398; Thu, 27 May 2021 14:21:31 +0900 (JST) Received: from yo-satoh-debian.localdomain (y245018.dynamic.ppp.asahi-net.or.jp [118.243.245.18]) by sakura.ysato.name (Postfix) with ESMTPSA id C3DEE1C060B; Thu, 27 May 2021 14:21:30 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 05/11] hw/timer: Remove renesas_cmt. Date: Thu, 27 May 2021 14:21:16 +0900 Message-Id: <20210527052122.97103-6-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210527052122.97103-1-ysato@users.sourceforge.jp> References: <20210527052122.97103-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-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+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" Migrate to renesas_timer. Signed-off-by: Yoshinori Sato --- include/hw/timer/renesas_cmt.h | 43 ----- hw/timer/renesas_cmt.c | 283 --------------------------------- hw/timer/meson.build | 1 - 3 files changed, 327 deletions(-) delete mode 100644 include/hw/timer/renesas_cmt.h delete mode 100644 hw/timer/renesas_cmt.c diff --git a/include/hw/timer/renesas_cmt.h b/include/hw/timer/renesas_cmt.h deleted file mode 100644 index 1c0b65c1d5..0000000000 --- a/include/hw/timer/renesas_cmt.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Renesas Compare-match timer Object - * - * Copyright (c) 2019 Yoshinori Sato - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#ifndef HW_TIMER_RENESAS_CMT_H -#define HW_TIMER_RENESAS_CMT_H - -#include "qemu/timer.h" -#include "hw/sysbus.h" -#include "qom/object.h" - -#define TYPE_RENESAS_CMT "renesas-cmt" -typedef struct RCMTState RCMTState; -DECLARE_INSTANCE_CHECKER(RCMTState, RCMT, - TYPE_RENESAS_CMT) - -enum { - CMT_CH = 2, - CMT_NR_IRQ = 1 * CMT_CH -}; - -struct RCMTState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - uint64_t input_freq; - MemoryRegion memory; - - uint16_t cmstr; - uint16_t cmcr[CMT_CH]; - uint16_t cmcnt[CMT_CH]; - uint16_t cmcor[CMT_CH]; - int64_t tick[CMT_CH]; - qemu_irq cmi[CMT_CH]; - QEMUTimer timer[CMT_CH]; -}; - -#endif diff --git a/hw/timer/renesas_cmt.c b/hw/timer/renesas_cmt.c deleted file mode 100644 index 2e0fd21a36..0000000000 --- a/hw/timer/renesas_cmt.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Renesas 16bit Compare-match timer - * - * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware - * (Rev.1.40 R01UH0033EJ0140) - * - * Copyright (c) 2019 Yoshinori Sato - * - * SPDX-License-Identifier: GPL-2.0-or-later - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2 or later, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ - -#include "qemu/osdep.h" -#include "qemu/log.h" -#include "hw/irq.h" -#include "hw/registerfields.h" -#include "hw/qdev-properties.h" -#include "hw/timer/renesas_cmt.h" -#include "migration/vmstate.h" - -/* - * +0 CMSTR - common control - * +2 CMCR - ch0 - * +4 CMCNT - ch0 - * +6 CMCOR - ch0 - * +8 CMCR - ch1 - * +10 CMCNT - ch1 - * +12 CMCOR - ch1 - * If we think that the address of CH 0 has an offset of +2, - * we can treat it with the same address as CH 1, so define it like that. - */ -REG16(CMSTR, 0) - FIELD(CMSTR, STR0, 0, 1) - FIELD(CMSTR, STR1, 1, 1) - FIELD(CMSTR, STR, 0, 2) -/* This addeess is channel offset */ -REG16(CMCR, 0) - FIELD(CMCR, CKS, 0, 2) - FIELD(CMCR, CMIE, 6, 1) -REG16(CMCNT, 2) -REG16(CMCOR, 4) - -static void update_events(RCMTState *cmt, int ch) -{ - int64_t next_time; - - if ((cmt->cmstr & (1 << ch)) == 0) { - /* count disable, so not happened next event. */ - return ; - } - next_time = cmt->cmcor[ch] - cmt->cmcnt[ch]; - next_time *= NANOSECONDS_PER_SECOND; - next_time /= cmt->input_freq; - /* - * CKS -> div rate - * 0 -> 8 (1 << 3) - * 1 -> 32 (1 << 5) - * 2 -> 128 (1 << 7) - * 3 -> 512 (1 << 9) - */ - next_time *= 1 << (3 + FIELD_EX16(cmt->cmcr[ch], CMCR, CKS) * 2); - next_time += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - timer_mod(&cmt->timer[ch], next_time); -} - -static int64_t read_cmcnt(RCMTState *cmt, int ch) -{ - int64_t delta, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - - if (cmt->cmstr & (1 << ch)) { - delta = (now - cmt->tick[ch]); - delta /= NANOSECONDS_PER_SECOND; - delta /= cmt->input_freq; - delta /= 1 << (3 + FIELD_EX16(cmt->cmcr[ch], CMCR, CKS) * 2); - cmt->tick[ch] = now; - return cmt->cmcnt[ch] + delta; - } else { - return cmt->cmcnt[ch]; - } -} - -static uint64_t cmt_read(void *opaque, hwaddr offset, unsigned size) -{ - RCMTState *cmt = opaque; - int ch = offset / 0x08; - uint64_t ret; - - if (offset == A_CMSTR) { - ret = 0; - ret = FIELD_DP16(ret, CMSTR, STR, - FIELD_EX16(cmt->cmstr, CMSTR, STR)); - return ret; - } else { - offset &= 0x07; - if (ch == 0) { - offset -= 0x02; - } - switch (offset) { - case A_CMCR: - ret = 0; - ret = FIELD_DP16(ret, CMCR, CKS, - FIELD_EX16(cmt->cmstr, CMCR, CKS)); - ret = FIELD_DP16(ret, CMCR, CMIE, - FIELD_EX16(cmt->cmstr, CMCR, CMIE)); - return ret; - case A_CMCNT: - return read_cmcnt(cmt, ch); - case A_CMCOR: - return cmt->cmcor[ch]; - } - } - qemu_log_mask(LOG_UNIMP, "renesas_cmt: Register 0x%" HWADDR_PRIX " " - "not implemented\n", - offset); - return UINT64_MAX; -} - -static void start_stop(RCMTState *cmt, int ch, int st) -{ - if (st) { - update_events(cmt, ch); - } else { - timer_del(&cmt->timer[ch]); - } -} - -static void cmt_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) -{ - RCMTState *cmt = opaque; - int ch = offset / 0x08; - - if (offset == A_CMSTR) { - cmt->cmstr = FIELD_EX16(val, CMSTR, STR); - start_stop(cmt, 0, FIELD_EX16(cmt->cmstr, CMSTR, STR0)); - start_stop(cmt, 1, FIELD_EX16(cmt->cmstr, CMSTR, STR1)); - } else { - offset &= 0x07; - if (ch == 0) { - offset -= 0x02; - } - switch (offset) { - case A_CMCR: - cmt->cmcr[ch] = FIELD_DP16(cmt->cmcr[ch], CMCR, CKS, - FIELD_EX16(val, CMCR, CKS)); - cmt->cmcr[ch] = FIELD_DP16(cmt->cmcr[ch], CMCR, CMIE, - FIELD_EX16(val, CMCR, CMIE)); - break; - case 2: - cmt->cmcnt[ch] = val; - break; - case 4: - cmt->cmcor[ch] = val; - break; - default: - qemu_log_mask(LOG_UNIMP, "renesas_cmt: Register 0x%" HWADDR_PRIX " " - "not implemented\n", - offset); - return; - } - if (FIELD_EX16(cmt->cmstr, CMSTR, STR) & (1 << ch)) { - update_events(cmt, ch); - } - } -} - -static const MemoryRegionOps cmt_ops = { - .write = cmt_write, - .read = cmt_read, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - .min_access_size = 2, - .max_access_size = 2, - }, - .valid = { - .min_access_size = 2, - .max_access_size = 2, - }, -}; - -static void timer_events(RCMTState *cmt, int ch) -{ - cmt->cmcnt[ch] = 0; - cmt->tick[ch] = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - update_events(cmt, ch); - if (FIELD_EX16(cmt->cmcr[ch], CMCR, CMIE)) { - qemu_irq_pulse(cmt->cmi[ch]); - } -} - -static void timer_event0(void *opaque) -{ - RCMTState *cmt = opaque; - - timer_events(cmt, 0); -} - -static void timer_event1(void *opaque) -{ - RCMTState *cmt = opaque; - - timer_events(cmt, 1); -} - -static void rcmt_reset(DeviceState *dev) -{ - RCMTState *cmt = RCMT(dev); - cmt->cmstr = 0; - cmt->cmcr[0] = cmt->cmcr[1] = 0; - cmt->cmcnt[0] = cmt->cmcnt[1] = 0; - cmt->cmcor[0] = cmt->cmcor[1] = 0xffff; -} - -static void rcmt_init(Object *obj) -{ - SysBusDevice *d = SYS_BUS_DEVICE(obj); - RCMTState *cmt = RCMT(obj); - int i; - - memory_region_init_io(&cmt->memory, OBJECT(cmt), &cmt_ops, - cmt, "renesas-cmt", 0x10); - sysbus_init_mmio(d, &cmt->memory); - - for (i = 0; i < ARRAY_SIZE(cmt->cmi); i++) { - sysbus_init_irq(d, &cmt->cmi[i]); - } - timer_init_ns(&cmt->timer[0], QEMU_CLOCK_VIRTUAL, timer_event0, cmt); - timer_init_ns(&cmt->timer[1], QEMU_CLOCK_VIRTUAL, timer_event1, cmt); -} - -static const VMStateDescription vmstate_rcmt = { - .name = "rx-cmt", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT16(cmstr, RCMTState), - VMSTATE_UINT16_ARRAY(cmcr, RCMTState, CMT_CH), - VMSTATE_UINT16_ARRAY(cmcnt, RCMTState, CMT_CH), - VMSTATE_UINT16_ARRAY(cmcor, RCMTState, CMT_CH), - VMSTATE_INT64_ARRAY(tick, RCMTState, CMT_CH), - VMSTATE_TIMER_ARRAY(timer, RCMTState, CMT_CH), - VMSTATE_END_OF_LIST() - } -}; - -static Property rcmt_properties[] = { - DEFINE_PROP_UINT64("input-freq", RCMTState, input_freq, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void rcmt_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->vmsd = &vmstate_rcmt; - dc->reset = rcmt_reset; - device_class_set_props(dc, rcmt_properties); -} - -static const TypeInfo rcmt_info = { - .name = TYPE_RENESAS_CMT, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(RCMTState), - .instance_init = rcmt_init, - .class_init = rcmt_class_init, -}; - -static void rcmt_register_types(void) -{ - type_register_static(&rcmt_info); -} - -type_init(rcmt_register_types) diff --git a/hw/timer/meson.build b/hw/timer/meson.build index ec70821c0b..03b40cfbee 100644 --- a/hw/timer/meson.build +++ b/hw/timer/meson.build @@ -9,7 +9,6 @@ softmmu_ss.add(when: 'CONFIG_CADENCE', if_true: files('cadence_ttc.c')) softmmu_ss.add(when: 'CONFIG_CMSDK_APB_DUALTIMER', if_true: files('cmsdk-apb-dualtimer.c')) softmmu_ss.add(when: 'CONFIG_CMSDK_APB_TIMER', if_true: files('cmsdk-apb-timer.c')) softmmu_ss.add(when: 'CONFIG_RENESAS_TMR', if_true: files('renesas_tmr.c')) -softmmu_ss.add(when: 'CONFIG_RENESAS_CMT', if_true: files('renesas_cmt.c')) softmmu_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic-timer.c')) softmmu_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_timer.c')) softmmu_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_mct.c')) From patchwork Thu May 27 05:21:17 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 12283361 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.9 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,UNWANTED_LANGUAGE_BODY, URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 61E61C4708B for ; Thu, 27 May 2021 05:26: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 0652861355 for ; Thu, 27 May 2021 05:26:28 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 0652861355 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+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:47612 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lm8XM-0008N7-4H for qemu-devel@archiver.kernel.org; Thu, 27 May 2021 01:26:28 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:56856) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lm8Sh-0008MK-7q for qemu-devel@nongnu.org; Thu, 27 May 2021 01:21:39 -0400 Received: from mail02.asahi-net.or.jp ([202.224.55.14]:38758) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lm8Sc-00065U-2W for qemu-devel@nongnu.org; Thu, 27 May 2021 01:21:38 -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 8A3423D256; Thu, 27 May 2021 14:21:31 +0900 (JST) Received: from yo-satoh-debian.localdomain (y245018.dynamic.ppp.asahi-net.or.jp [118.243.245.18]) by sakura.ysato.name (Postfix) with ESMTPSA id 258491C0077; Thu, 27 May 2021 14:21:31 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 06/11] hw/rx: Add RX62N Clock generator Date: Thu, 27 May 2021 14:21:17 +0900 Message-Id: <20210527052122.97103-7-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210527052122.97103-1-ysato@users.sourceforge.jp> References: <20210527052122.97103-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-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+qemu-devel=archiver.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 | 8 +- hw/rx/rx62n-cpg.c | 344 ++++++++++++++++++++++++++++++++++++++ hw/rx/rx62n.c | 39 +++-- hw/rx/meson.build | 2 +- 5 files changed, 449 insertions(+), 16 deletions(-) create mode 100644 include/hw/rx/rx62n-cpg.h create mode 100644 hw/rx/rx62n-cpg.c diff --git a/include/hw/rx/rx62n-cpg.h b/include/hw/rx/rx62n-cpg.h new file mode 100644 index 0000000000..d90a067313 --- /dev/null +++ b/include/hw/rx/rx62n-cpg.h @@ -0,0 +1,72 @@ +/* + * RX62N Clock generator circuit + * + * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware + * (Rev.1.40 R01UH0033EJ0140) + * + * Copyright (c) 2020 Yoshinori Sato + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef HW_RX_RX62N_CPG_H +#define HW_RX_RX62N_CPG_H + +#include "hw/sysbus.h" +#include "hw/qdev-clock.h" + +#define TYPE_RX62N_CPG "rx62n-cpg" +#define RX62NCPG(obj) OBJECT_CHECK(RX62NCPGState, (obj), TYPE_RX62N_CPG) + +enum { + CK_TMR8_1, + CK_TMR8_0, + CK_MTU_1, + CK_MTU_0, + CK_CMT_1, + CK_CMT_0, + CK_EDMAC, + CK_SCI6, + CK_SCI5, + CK_SCI3, + CK_SCI2, + CK_SCI1, + CK_SCI0, + NUM_SUBCLOCK, +}; + +typedef struct RX62NCPGState { + SysBusDevice parent_obj; + uint32_t mstpcr[3]; + uint32_t sckcr; + uint8_t bckcr; + uint8_t ostdcr; + + int ick; + Clock *clk_ick; + int bck; + Clock *clk_bck; + int pck; + Clock *clk_pck; + Clock *dev_clocks[NUM_SUBCLOCK]; + uint32_t xtal_freq_hz; + MemoryRegion memory; +} RX62NCPGState; + +typedef struct RX62NCPGClass { + SysBusDeviceClass parent; +} RX62NCPGClass; + +#define OSTDCR_KEY 0xac + +#endif diff --git a/include/hw/rx/rx62n.h b/include/hw/rx/rx62n.h index 3ed80dba0d..44f5fcc74d 100644 --- a/include/hw/rx/rx62n.h +++ b/include/hw/rx/rx62n.h @@ -29,6 +29,7 @@ #include "hw/timer/renesas_tmr.h" #include "hw/timer/renesas_cmt.h" #include "hw/char/renesas_sci.h" +#include "hw/rx/rx62n-cpg.h" #include "qemu/units.h" #include "qom/object.h" @@ -58,9 +59,9 @@ struct RX62NState { RTMRState tmr[RX62N_NR_TMR]; RCMTState cmt[RX62N_NR_CMT]; RSCIState sci[RX62N_NR_SCI]; + RX62NCPGState cpg; MemoryRegion *sysmem; - bool kernel; MemoryRegion iram; MemoryRegion iomem1; @@ -72,8 +73,7 @@ struct RX62NState { /* Input Clock (XTAL) frequency */ uint32_t xtal_freq_hz; - /* Peripheral Module Clock frequency */ - uint32_t pclk_freq_hz; -}; +} RX62NState; + #endif diff --git a/hw/rx/rx62n-cpg.c b/hw/rx/rx62n-cpg.c new file mode 100644 index 0000000000..9d70004302 --- /dev/null +++ b/hw/rx/rx62n-cpg.c @@ -0,0 +1,344 @@ +/* + * RX62N Clock Generation Circuit + * + * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware + * (Rev.1.40 R01UH0033EJ0140) + * + * Copyright (c) 2020 Yoshinori Sato + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "hw/hw.h" +#include "hw/rx/rx62n-cpg.h" +#include "hw/sysbus.h" +#include "hw/qdev-properties.h" +#include "hw/registerfields.h" +#include "hw/qdev-properties.h" +#include "hw/clock.h" +#include "migration/vmstate.h" + +#define RX62N_XTAL_MIN_HZ (8 * 1000 * 1000) +#define RX62N_XTAL_MAX_HZ (14 * 1000 * 1000) + +REG32(MSTPCRA, 0) +REG32(MSTPCRB, 4) +REG32(MSTPCRC, 8) +REG32(SCKCR, 16) + FIELD(SCKCR, PCK, 8, 3) + FIELD(SCKCR, BCK, 16, 3) + FIELD(SCKCR, PSTOP, 22, 2) + FIELD(SCKCR, ICK, 24, 3) +REG8(BCKCR, 32) + FIELD(BCKCR, BCLKDIV, 0, 1) +REG16(OSTDCR, 48) + FIELD(OSTDCR, OSTDF, 6, 1) + FIELD(OSTDCR, OSTDE, 7, 1) + +static const int access_size[] = {4, 4, 1, 2}; + +typedef struct { + const char *name; + int devnum; + int reg; + int offset; + int parentck; +} dev_clock_t; + +enum { + parent_ick, parent_bck, parent_pck, +}; + +static const dev_clock_t dev_clock_list[] = { + { .name = "pck_tmr8-1", + .devnum = CK_TMR8_1, .reg = 0, .offset = 4, .parentck = parent_pck, }, + { .name = "pck_tmr8-0", + .devnum = CK_TMR8_0, .reg = 0, .offset = 5, .parentck = parent_pck, }, + { .name = "pck_mtu-1", + .devnum = CK_MTU_1, .reg = 0, .offset = 8, .parentck = parent_pck, }, + { .name = "pck_mtu-0", + .devnum = CK_MTU_0, .reg = 0, .offset = 9, .parentck = parent_pck, }, + { .name = "pck_cmt-1", + .devnum = CK_CMT_1, .reg = 0, .offset = 14, .parentck = parent_pck, }, + { .name = "pck_cmt-0", + .devnum = CK_CMT_0, .reg = 0, .offset = 15, .parentck = parent_pck, }, + { .name = "ick_edmac", + .devnum = CK_EDMAC, .reg = 1, .offset = 15, .parentck = parent_ick, }, + { .name = "pck_sci-6", + .devnum = CK_SCI6, .reg = 1, .offset = 25, .parentck = parent_pck, }, + { .name = "pck_sci-5", + .devnum = CK_SCI5, .reg = 1, .offset = 26, .parentck = parent_pck, }, + { .name = "pck_sci-3", + .devnum = CK_SCI3, .reg = 1, .offset = 28, .parentck = parent_pck, }, + { .name = "pck_sci-2", + .devnum = CK_SCI2, .reg = 1, .offset = 29, .parentck = parent_pck, }, + { .name = "pck_sci-1", + .devnum = CK_SCI1, .reg = 1, .offset = 30, .parentck = parent_pck, }, + { .name = "pck_sci-0", + .devnum = CK_SCI0, .reg = 1, .offset = 31, .parentck = parent_pck, }, + { }, +}; + +static void set_clock_in(RX62NCPGState *cpg, const dev_clock_t *ck) +{ + Clock *out; + uint64_t period; + + out = qdev_get_clock_out(DEVICE(cpg), ck->name); + g_assert(out); + period = 0; + if (extract32(cpg->mstpcr[ck->reg], ck->offset, 1) == 0) { + switch (ck->parentck) { + case parent_ick: + period = clock_get(cpg->clk_ick); + break; + case parent_pck: + period = clock_get(cpg->clk_pck); + break; + } + } + if (clock_get(out) != period) { + clock_update(out, period); + } +} + +#define update_ck(ckname) \ + if (cpg->ckname != ckname) { \ + cpg->ckname = ckname; \ + ckname = 8 / (1 << ckname); \ + clock_update_hz(cpg->clk_ ## ckname, \ + cpg->xtal_freq_hz * ckname); \ + } + +#define validate_setting(ckname) \ + if (ick > ckname) { \ + qemu_log_mask(LOG_GUEST_ERROR, \ + "rx62n-cpg: Invalid " #ckname " setting." \ + " (ick=%d " #ckname "=%d)\n", ick, ckname); \ + cpg->ckname = ckname = ick; \ + } + +static void update_divrate(RX62NCPGState *cpg) +{ + int ick = FIELD_EX32(cpg->sckcr, SCKCR, ICK); + int bck = FIELD_EX32(cpg->sckcr, SCKCR, BCK); + int pck = FIELD_EX32(cpg->sckcr, SCKCR, PCK); + const dev_clock_t *p = dev_clock_list; + validate_setting(pck); + validate_setting(bck); + update_ck(ick); + update_ck(bck); + update_ck(pck); + while (p->name) { + set_clock_in(cpg, p); + p++; + } +} + +static const dev_clock_t *find_clock_list(int crno, int bit) +{ + const dev_clock_t *ret = dev_clock_list; + while (ret->name) { + if (ret->reg == crno && ret->offset == bit) { + return ret; + } + ret++; + } + return NULL; +} + +static void update_mstpcr(RX62NCPGState *cpg, int crno, uint32_t diff) +{ + int bit = 0; + const dev_clock_t *p; + + while (diff) { + if (diff & 1) { + p = find_clock_list(crno, bit); + if (p) { + set_clock_in(cpg, p); + } else { + qemu_log_mask(LOG_UNIMP, "rx62n-cpg: MSTPCR%c " + " bit %d is not implement.\n", 'A' + crno, bit); + } + } + bit++; + diff >>= 1; + } +} + +static uint64_t cpg_read(void *opaque, hwaddr addr, unsigned size) +{ + RX62NCPGState *cpg = RX62NCPG(opaque); + + if (access_size[addr >> 4] != size) { + qemu_log_mask(LOG_GUEST_ERROR, "rx62n-cpg: Register 0x%" + HWADDR_PRIX " Invalid access size.\n", addr); + return UINT64_MAX; + } + switch (addr) { + case A_MSTPCRA: + return cpg->mstpcr[0] | 0x473530cf; + case A_MSTPCRB: + return cpg->mstpcr[1] | 0x09407ffe; + case A_MSTPCRC: + return (cpg->mstpcr[2] | 0xffff0000) & 0xffff0003; + case A_SCKCR: + return cpg->sckcr & 0x0fcf0f00; + case A_BCKCR: + return cpg->bckcr & 0x01; + case A_OSTDCR: + /* Main OSC always good */ + return cpg->ostdcr & 0x0080; + default: + qemu_log_mask(LOG_GUEST_ERROR, "rx62n-cpg: Register 0x%" + HWADDR_PRIX " Invalid address.\n", addr); + return UINT64_MAX; + } +} + +static void cpg_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + RX62NCPGState *cpg = RX62NCPG(opaque); + uint32_t old_mstpcr; + int cr_no; + if (access_size[addr >> 4] != size) { + qemu_log_mask(LOG_GUEST_ERROR, "rx62n-cpg: Register 0x%" + HWADDR_PRIX " Invalid access size.\n", addr); + return; + } + switch (addr) { + case A_MSTPCRA: + case A_MSTPCRB: + case A_MSTPCRC: + cr_no = (addr & 0x0f) >> 2; + old_mstpcr = cpg->mstpcr[cr_no]; + old_mstpcr ^= val; + cpg->mstpcr[cr_no] = val; + update_mstpcr(cpg, cr_no, old_mstpcr); + break; + case A_SCKCR: + cpg->sckcr = val; + update_divrate(cpg); + break; + case A_BCKCR: + cpg->bckcr = val; + break; + case A_OSTDCR: + if (extract16(val, 8, 8) == OSTDCR_KEY) { + cpg->ostdcr = val; + } else { + qemu_log_mask(LOG_GUEST_ERROR, "rx62n-cpg: Register 0x%" + HWADDR_PRIX " Invalid key value.\n", addr); + } + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "rx62n-cpg: Register 0x%" + HWADDR_PRIX " Invalid address.\n", addr); + } +} + +static const MemoryRegionOps cpg_ops = { + .write = cpg_write, + .read = cpg_read, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static const ClockPortInitArray rx62n_cpg_clocks = { + QDEV_CLOCK_OUT(RX62NCPGState, clk_ick), + QDEV_CLOCK_OUT(RX62NCPGState, clk_bck), + QDEV_CLOCK_OUT(RX62NCPGState, clk_pck), + QDEV_CLOCK_END +}; + +static void cpg_realize(DeviceState *dev, Error **errp) +{ + RX62NCPGState *cpg = RX62NCPG(dev); + const dev_clock_t *p = dev_clock_list; + + if (cpg->xtal_freq_hz == 0) { + error_setg(errp, "\"xtal-frequency-hz\" property must be provided."); + return; + } + /* XTAL range: 8-14 MHz */ + if (cpg->xtal_freq_hz < RX62N_XTAL_MIN_HZ || + cpg->xtal_freq_hz > RX62N_XTAL_MAX_HZ) { + error_setg(errp, "\"xtal-frequency-hz\" property in incorrect range."); + return; + } + + cpg->sckcr = FIELD_DP32(cpg->sckcr, SCKCR, ICK, 2); + cpg->sckcr = FIELD_DP32(cpg->sckcr, SCKCR, BCK, 2); + cpg->sckcr = FIELD_DP32(cpg->sckcr, SCKCR, PCK, 2); + cpg->ostdcr = FIELD_DP8(cpg->ostdcr, OSTDCR, OSTDE, 1); + cpg->mstpcr[0] = 0x47ffffff; + cpg->mstpcr[1] = 0xffffffff; + cpg->mstpcr[2] = 0xffff0000; + + /* set initial state */ + while (p->name) { + set_clock_in(cpg, p); + p++; + } + update_divrate(cpg); +} + +static void rx62n_cpg_init(Object *obj) +{ + RX62NCPGState *cpg = RX62NCPG(obj); + const dev_clock_t *p = dev_clock_list; + qdev_init_clocks(DEVICE(obj), rx62n_cpg_clocks); + /* connect parent clock */ + while (p->name) { + cpg->dev_clocks[p->devnum] = qdev_init_clock_out(DEVICE(obj), + p->name); + p++; + } + + memory_region_init_io(&cpg->memory, OBJECT(cpg), &cpg_ops, + cpg, "rx62n-cpg", 0x40); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &cpg->memory); +} + +static Property rx62n_cpg_properties[] = { + DEFINE_PROP_UINT32("xtal-frequency-hz", RX62NCPGState, xtal_freq_hz, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void rx62n_cpg_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = cpg_realize; + device_class_set_props(dc, rx62n_cpg_properties); +} + +static const TypeInfo rx62n_cpg_info[] = { + { + .name = TYPE_RX62N_CPG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(RX62NCPGState), + .instance_init = rx62n_cpg_init, + .class_init = rx62n_cpg_class_init, + .class_size = sizeof(RX62NCPGClass), + }, +}; + +DEFINE_TYPES(rx62n_cpg_info) diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c index fa5add9f9d..cfd41930bf 100644 --- a/hw/rx/rx62n.c +++ b/hw/rx/rx62n.c @@ -45,6 +45,7 @@ #define RX62N_TMR_BASE 0x00088200 #define RX62N_CMT_BASE 0x00088000 #define RX62N_SCI_BASE 0x00088240 +#define RX62N_CPG_BASE 0x00080010 /* * RX62N Peripheral IRQ @@ -56,7 +57,6 @@ #define RX62N_XTAL_MIN_HZ (8 * 1000 * 1000) #define RX62N_XTAL_MAX_HZ (14 * 1000 * 1000) -#define RX62N_PCLK_MAX_HZ (50 * 1000 * 1000) struct RX62NClass { /*< private >*/ @@ -161,36 +161,45 @@ static void register_tmr(RX62NState *s, int unit) { SysBusDevice *tmr; int i, irqbase; + char ckname[16]; object_initialize_child(OBJECT(s), "tmr[*]", &s->tmr[unit], TYPE_RENESAS_TMR); tmr = SYS_BUS_DEVICE(&s->tmr[unit]); - qdev_prop_set_uint64(DEVICE(tmr), "input-freq", s->pclk_freq_hz); - sysbus_realize(tmr, &error_abort); irqbase = RX62N_TMR_IRQ + TMR_NR_IRQ * unit; for (i = 0; i < TMR_NR_IRQ; i++) { sysbus_connect_irq(tmr, i, s->irq[irqbase + i]); } sysbus_mmio_map(tmr, 0, RX62N_TMR_BASE + unit * 0x10); + + qdev_prop_set_uint32(DEVICE(tmr), "unit", unit); + sysbus_realize(tmr, &error_abort); + snprintf(ckname, sizeof(ckname), "pck_tmr8-%d", unit); + qdev_connect_clock_in(DEVICE(tmr), "pck", + qdev_get_clock_out(DEVICE(&s->cpg), ckname)); } static void register_cmt(RX62NState *s, int unit) { SysBusDevice *cmt; int i, irqbase; + char ckname[16]; object_initialize_child(OBJECT(s), "cmt[*]", &s->cmt[unit], TYPE_RENESAS_CMT); cmt = SYS_BUS_DEVICE(&s->cmt[unit]); - qdev_prop_set_uint64(DEVICE(cmt), "input-freq", s->pclk_freq_hz); - sysbus_realize(cmt, &error_abort); + qdev_prop_set_uint32(DEVICE(cmt), "unit", unit); irqbase = RX62N_CMT_IRQ + CMT_NR_IRQ * unit; for (i = 0; i < CMT_NR_IRQ; i++) { sysbus_connect_irq(cmt, i, s->irq[irqbase + i]); } sysbus_mmio_map(cmt, 0, RX62N_CMT_BASE + unit * 0x10); + sysbus_realize(cmt, &error_abort); + snprintf(ckname, sizeof(ckname), "pck_cmt-%d", unit); + qdev_connect_clock_in(DEVICE(cmt), "pck", + qdev_get_clock_out(DEVICE(&s->cpg), ckname)); } static void register_sci(RX62NState *s, int unit) @@ -202,7 +211,6 @@ static void register_sci(RX62NState *s, int unit) &s->sci[unit], TYPE_RENESAS_SCI); sci = SYS_BUS_DEVICE(&s->sci[unit]); qdev_prop_set_chr(DEVICE(sci), "chardev", serial_hd(unit)); - qdev_prop_set_uint64(DEVICE(sci), "input-freq", s->pclk_freq_hz); sysbus_realize(sci, &error_abort); irqbase = RX62N_SCI_IRQ + SCI_NR_IRQ * unit; @@ -212,6 +220,18 @@ static void register_sci(RX62NState *s, int unit) sysbus_mmio_map(sci, 0, RX62N_SCI_BASE + unit * 0x08); } +static void register_cpg(RX62NState *s) +{ + SysBusDevice *cpg; + + object_initialize_child(OBJECT(s), "rx62n-cpg", &s->cpg, + TYPE_RX62N_CPG); + cpg = SYS_BUS_DEVICE(&s->cpg); + qdev_prop_set_uint64(DEVICE(cpg), "xtal-frequency-hz", s->xtal_freq_hz); + + sysbus_mmio_map(cpg, 0, RX62N_CPG_BASE); +} + static void rx62n_realize(DeviceState *dev, Error **errp) { RX62NState *s = RX62N_MCU(dev); @@ -227,11 +247,6 @@ static void rx62n_realize(DeviceState *dev, Error **errp) error_setg(errp, "\"xtal-frequency-hz\" property in incorrect range."); return; } - /* Use a 4x fixed multiplier */ - s->pclk_freq_hz = 4 * s->xtal_freq_hz; - /* PCLK range: 8-50 MHz */ - assert(s->pclk_freq_hz <= RX62N_PCLK_MAX_HZ); - memory_region_init_ram(&s->iram, OBJECT(dev), "iram", rxc->ram_size, &error_abort); memory_region_add_subregion(s->sysmem, RX62N_IRAM_BASE, &s->iram); @@ -248,11 +263,13 @@ static void rx62n_realize(DeviceState *dev, Error **errp) register_icu(s); s->cpu.env.ack = qdev_get_gpio_in_named(DEVICE(&s->icu), "ack", 0); + register_cpg(s); register_tmr(s, 0); register_tmr(s, 1); register_cmt(s, 0); register_cmt(s, 1); register_sci(s, 0); + sysbus_realize(SYS_BUS_DEVICE(&s->cpg), &error_abort); } static Property rx62n_properties[] = { diff --git a/hw/rx/meson.build b/hw/rx/meson.build index d223512a78..e1c5278b6f 100644 --- a/hw/rx/meson.build +++ b/hw/rx/meson.build @@ -1,5 +1,5 @@ rx_ss = ss.source_set() rx_ss.add(when: 'CONFIG_RX_GDBSIM', if_true: files('rx-gdbsim.c')) -rx_ss.add(when: 'CONFIG_RX62N_MCU', if_true: files('rx62n.c')) +rx_ss.add(when: 'CONFIG_RX62N_MCU', if_true: files('rx62n.c', 'rx62n-cpg.c')) hw_arch += {'rx': rx_ss} From patchwork Thu May 27 05:21:18 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 12283369 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BFBF7C4708B for ; Thu, 27 May 2021 05:29:00 +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 3C28B610FA for ; Thu, 27 May 2021 05:29:00 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 3C28B610FA 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+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:56352 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lm8Zn-0005qV-7P for qemu-devel@archiver.kernel.org; Thu, 27 May 2021 01:28:59 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:56878) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lm8Si-0008OW-3s for qemu-devel@nongnu.org; Thu, 27 May 2021 01:21:40 -0400 Received: from mail03.asahi-net.or.jp ([202.224.55.15]:32933) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lm8Sc-00065u-2L for qemu-devel@nongnu.org; Thu, 27 May 2021 01:21:39 -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 E568638574; Thu, 27 May 2021 14:21:31 +0900 (JST) Received: from yo-satoh-debian.localdomain (y245018.dynamic.ppp.asahi-net.or.jp [118.243.245.18]) by sakura.ysato.name (Postfix) with ESMTPSA id 7B8AB1C060B; Thu, 27 May 2021 14:21:31 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 07/11] hw/timer: Renesas 8bit timer. Date: Thu, 27 May 2021 14:21:18 +0900 Message-Id: <20210527052122.97103-8-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210527052122.97103-1-ysato@users.sourceforge.jp> References: <20210527052122.97103-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-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+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" Rewrite timer api. Signed-off-by: Yoshinori Sato --- include/hw/timer/renesas_tmr.h | 58 ---- include/hw/timer/renesas_tmr8.h | 67 ++++ hw/timer/renesas_tmr.c | 493 ----------------------------- hw/timer/renesas_tmr8.c | 540 ++++++++++++++++++++++++++++++++ hw/timer/Kconfig | 5 +- hw/timer/meson.build | 2 +- 6 files changed, 609 insertions(+), 556 deletions(-) delete mode 100644 include/hw/timer/renesas_tmr.h create mode 100644 include/hw/timer/renesas_tmr8.h delete mode 100644 hw/timer/renesas_tmr.c create mode 100644 hw/timer/renesas_tmr8.c diff --git a/include/hw/timer/renesas_tmr.h b/include/hw/timer/renesas_tmr.h deleted file mode 100644 index caf7eec0dc..0000000000 --- a/include/hw/timer/renesas_tmr.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Renesas 8bit timer Object - * - * Copyright (c) 2018 Yoshinori Sato - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#ifndef HW_TIMER_RENESAS_TMR_H -#define HW_TIMER_RENESAS_TMR_H - -#include "qemu/timer.h" -#include "hw/sysbus.h" -#include "qom/object.h" - -#define TYPE_RENESAS_TMR "renesas-tmr" -typedef struct RTMRState RTMRState; -DECLARE_INSTANCE_CHECKER(RTMRState, RTMR, - TYPE_RENESAS_TMR) - -enum timer_event { - cmia = 0, - cmib = 1, - ovi = 2, - none = 3, - TMR_NR_EVENTS = 4 -}; - -enum { - TMR_CH = 2, - TMR_NR_IRQ = 3 * TMR_CH -}; - -struct RTMRState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - uint64_t input_freq; - MemoryRegion memory; - - int64_t tick; - uint8_t tcnt[TMR_CH]; - uint8_t tcora[TMR_CH]; - uint8_t tcorb[TMR_CH]; - uint8_t tcr[TMR_CH]; - uint8_t tccr[TMR_CH]; - uint8_t tcor[TMR_CH]; - uint8_t tcsr[TMR_CH]; - int64_t div_round[TMR_CH]; - uint8_t next[TMR_CH]; - qemu_irq cmia[TMR_CH]; - qemu_irq cmib[TMR_CH]; - qemu_irq ovi[TMR_CH]; - QEMUTimer timer[TMR_CH]; -}; - -#endif diff --git a/include/hw/timer/renesas_tmr8.h b/include/hw/timer/renesas_tmr8.h new file mode 100644 index 0000000000..21e4337b0c --- /dev/null +++ b/include/hw/timer/renesas_tmr8.h @@ -0,0 +1,67 @@ +/* + * Renesas 8bit timer Object + * + * Copyright (c) 2018 Yoshinori Sato + * + * This code is licensed under the GPL version 2 or later. + * + */ + +#ifndef HW_RENESAS_TMR8_H +#define HW_RENESAS_TMR8_H + +#include "hw/sysbus.h" + +#define TYPE_RENESAS_TMR8 "renesas-tmr8" +OBJECT_DECLARE_TYPE(RenesasTMR8State, RenesasTMR8Class, + RENESAS_TMR8) + +enum { + TMR_CH = 2, +}; + +enum { + IRQ_CMIA, IRQ_CMIB, IRQ_OVI, + TMR_NR_IRQ, +}; + +enum timer_event { + EVT_NONE, EVT_CMIA, EVT_CMIB, EVT_OVI, EVT_WOVI, + TMR_NR_EVENTS, +}; + +enum cor { + REG_A, REG_B, NR_COR, +}; + +struct RenesasTMR8State; + +struct tmr8_ch { + uint16_t cnt; + uint16_t cor[NR_COR]; + uint8_t tcr; + uint8_t tccr; + uint8_t tcsr; + qemu_irq irq[TMR_NR_IRQ]; + QEMUTimer *timer; + int64_t base; + int64_t next; + int64_t clk; + enum timer_event event; + int id; + struct RenesasTMR8State *tmrp; + bool word; +}; + +typedef struct RenesasTMR8State { + SysBusDevice parent_obj; + + uint32_t unit; + Clock *pck; + uint64_t input_freq; + MemoryRegion memory; + + struct tmr8_ch ch[TMR_CH]; +} RenesasTMR8State; + +#endif diff --git a/hw/timer/renesas_tmr.c b/hw/timer/renesas_tmr.c deleted file mode 100644 index d96002e1ee..0000000000 --- a/hw/timer/renesas_tmr.c +++ /dev/null @@ -1,493 +0,0 @@ -/* - * Renesas 8bit timer - * - * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware - * (Rev.1.40 R01UH0033EJ0140) - * - * Copyright (c) 2019 Yoshinori Sato - * - * SPDX-License-Identifier: GPL-2.0-or-later - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2 or later, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ - -#include "qemu/osdep.h" -#include "qemu/log.h" -#include "hw/irq.h" -#include "hw/registerfields.h" -#include "hw/qdev-properties.h" -#include "hw/timer/renesas_tmr.h" -#include "migration/vmstate.h" - -REG8(TCR, 0) - FIELD(TCR, CCLR, 3, 2) - FIELD(TCR, OVIE, 5, 1) - FIELD(TCR, CMIEA, 6, 1) - FIELD(TCR, CMIEB, 7, 1) -REG8(TCSR, 2) - FIELD(TCSR, OSA, 0, 2) - FIELD(TCSR, OSB, 2, 2) - FIELD(TCSR, ADTE, 4, 2) -REG8(TCORA, 4) -REG8(TCORB, 6) -REG8(TCNT, 8) -REG8(TCCR, 10) - FIELD(TCCR, CKS, 0, 3) - FIELD(TCCR, CSS, 3, 2) - FIELD(TCCR, TMRIS, 7, 1) - -#define CSS_EXTERNAL 0x00 -#define CSS_INTERNAL 0x01 -#define CSS_INVALID 0x02 -#define CSS_CASCADING 0x03 -#define CCLR_A 0x01 -#define CCLR_B 0x02 - -static const int clkdiv[] = {0, 1, 2, 8, 32, 64, 1024, 8192}; - -static uint8_t concat_reg(uint8_t *reg) -{ - return (reg[0] << 8) | reg[1]; -} - -static void update_events(RTMRState *tmr, int ch) -{ - uint16_t diff[TMR_NR_EVENTS], min; - int64_t next_time; - int i, event; - - if (tmr->tccr[ch] == 0) { - return ; - } - if (FIELD_EX8(tmr->tccr[ch], TCCR, CSS) == 0) { - /* external clock mode */ - /* event not happened */ - return ; - } - if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) == CSS_CASCADING) { - /* cascading mode */ - if (ch == 1) { - tmr->next[ch] = none; - return ; - } - diff[cmia] = concat_reg(tmr->tcora) - concat_reg(tmr->tcnt); - diff[cmib] = concat_reg(tmr->tcorb) - concat_reg(tmr->tcnt); - diff[ovi] = 0x10000 - concat_reg(tmr->tcnt); - } else { - /* separate mode */ - diff[cmia] = tmr->tcora[ch] - tmr->tcnt[ch]; - diff[cmib] = tmr->tcorb[ch] - tmr->tcnt[ch]; - diff[ovi] = 0x100 - tmr->tcnt[ch]; - } - /* Search for the most recently occurring event. */ - for (event = 0, min = diff[0], i = 1; i < none; i++) { - if (min > diff[i]) { - event = i; - min = diff[i]; - } - } - tmr->next[ch] = event; - next_time = diff[event]; - next_time *= clkdiv[FIELD_EX8(tmr->tccr[ch], TCCR, CKS)]; - next_time *= NANOSECONDS_PER_SECOND; - next_time /= tmr->input_freq; - next_time += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - timer_mod(&tmr->timer[ch], next_time); -} - -static int elapsed_time(RTMRState *tmr, int ch, int64_t delta) -{ - int divrate = clkdiv[FIELD_EX8(tmr->tccr[ch], TCCR, CKS)]; - int et; - - tmr->div_round[ch] += delta; - if (divrate > 0) { - et = tmr->div_round[ch] / divrate; - tmr->div_round[ch] %= divrate; - } else { - /* disble clock. so no update */ - et = 0; - } - return et; -} - -static uint16_t read_tcnt(RTMRState *tmr, unsigned size, int ch) -{ - int64_t delta, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - int elapsed, ovf = 0; - uint16_t tcnt[2]; - uint32_t ret; - - delta = (now - tmr->tick) * NANOSECONDS_PER_SECOND / tmr->input_freq; - if (delta > 0) { - tmr->tick = now; - - switch (FIELD_EX8(tmr->tccr[1], TCCR, CSS)) { - case CSS_INTERNAL: - /* timer1 count update */ - elapsed = elapsed_time(tmr, 1, delta); - if (elapsed >= 0x100) { - ovf = elapsed >> 8; - } - tcnt[1] = tmr->tcnt[1] + (elapsed & 0xff); - break; - case CSS_INVALID: /* guest error to have set this */ - case CSS_EXTERNAL: /* QEMU doesn't implement these */ - case CSS_CASCADING: - tcnt[1] = tmr->tcnt[1]; - break; - default: - g_assert_not_reached(); - } - switch (FIELD_EX8(tmr->tccr[0], TCCR, CSS)) { - case CSS_INTERNAL: - elapsed = elapsed_time(tmr, 0, delta); - tcnt[0] = tmr->tcnt[0] + elapsed; - break; - case CSS_CASCADING: - tcnt[0] = tmr->tcnt[0] + ovf; - break; - case CSS_INVALID: /* guest error to have set this */ - case CSS_EXTERNAL: /* QEMU doesn't implement this */ - tcnt[0] = tmr->tcnt[0]; - break; - default: - g_assert_not_reached(); - } - } else { - tcnt[0] = tmr->tcnt[0]; - tcnt[1] = tmr->tcnt[1]; - } - if (size == 1) { - return tcnt[ch]; - } else { - ret = 0; - ret = deposit32(ret, 0, 8, tcnt[1]); - ret = deposit32(ret, 8, 8, tcnt[0]); - return ret; - } -} - -static uint8_t read_tccr(uint8_t r) -{ - uint8_t tccr = 0; - tccr = FIELD_DP8(tccr, TCCR, TMRIS, - FIELD_EX8(r, TCCR, TMRIS)); - tccr = FIELD_DP8(tccr, TCCR, CSS, - FIELD_EX8(r, TCCR, CSS)); - tccr = FIELD_DP8(tccr, TCCR, CKS, - FIELD_EX8(r, TCCR, CKS)); - return tccr; -} - -static uint64_t tmr_read(void *opaque, hwaddr addr, unsigned size) -{ - RTMRState *tmr = opaque; - int ch = addr & 1; - uint64_t ret; - - if (size == 2 && (ch != 0 || addr == A_TCR || addr == A_TCSR)) { - qemu_log_mask(LOG_GUEST_ERROR, "renesas_tmr: Invalid read size 0x%" - HWADDR_PRIX "\n", - addr); - return UINT64_MAX; - } - switch (addr & 0x0e) { - case A_TCR: - ret = 0; - ret = FIELD_DP8(ret, TCR, CCLR, - FIELD_EX8(tmr->tcr[ch], TCR, CCLR)); - ret = FIELD_DP8(ret, TCR, OVIE, - FIELD_EX8(tmr->tcr[ch], TCR, OVIE)); - ret = FIELD_DP8(ret, TCR, CMIEA, - FIELD_EX8(tmr->tcr[ch], TCR, CMIEA)); - ret = FIELD_DP8(ret, TCR, CMIEB, - FIELD_EX8(tmr->tcr[ch], TCR, CMIEB)); - return ret; - case A_TCSR: - ret = 0; - ret = FIELD_DP8(ret, TCSR, OSA, - FIELD_EX8(tmr->tcsr[ch], TCSR, OSA)); - ret = FIELD_DP8(ret, TCSR, OSB, - FIELD_EX8(tmr->tcsr[ch], TCSR, OSB)); - switch (ch) { - case 0: - ret = FIELD_DP8(ret, TCSR, ADTE, - FIELD_EX8(tmr->tcsr[ch], TCSR, ADTE)); - break; - case 1: /* CH1 ADTE unimplement always 1 */ - ret = FIELD_DP8(ret, TCSR, ADTE, 1); - break; - } - return ret; - case A_TCORA: - if (size == 1) { - return tmr->tcora[ch]; - } else if (ch == 0) { - return concat_reg(tmr->tcora); - } - /* fall through */ - case A_TCORB: - if (size == 1) { - return tmr->tcorb[ch]; - } else { - return concat_reg(tmr->tcorb); - } - case A_TCNT: - return read_tcnt(tmr, size, ch); - case A_TCCR: - if (size == 1) { - return read_tccr(tmr->tccr[ch]); - } else { - return read_tccr(tmr->tccr[0]) << 8 | read_tccr(tmr->tccr[1]); - } - default: - qemu_log_mask(LOG_UNIMP, "renesas_tmr: Register 0x%" HWADDR_PRIX - " not implemented\n", - addr); - break; - } - return UINT64_MAX; -} - -static void tmr_write_count(RTMRState *tmr, int ch, unsigned size, - uint8_t *reg, uint64_t val) -{ - if (size == 1) { - reg[ch] = val; - update_events(tmr, ch); - } else { - reg[0] = extract32(val, 8, 8); - reg[1] = extract32(val, 0, 8); - update_events(tmr, 0); - update_events(tmr, 1); - } -} - -static void tmr_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) -{ - RTMRState *tmr = opaque; - int ch = addr & 1; - - if (size == 2 && (ch != 0 || addr == A_TCR || addr == A_TCSR)) { - qemu_log_mask(LOG_GUEST_ERROR, - "renesas_tmr: Invalid write size 0x%" HWADDR_PRIX "\n", - addr); - return; - } - switch (addr & 0x0e) { - case A_TCR: - tmr->tcr[ch] = val; - break; - case A_TCSR: - tmr->tcsr[ch] = val; - break; - case A_TCORA: - tmr_write_count(tmr, ch, size, tmr->tcora, val); - break; - case A_TCORB: - tmr_write_count(tmr, ch, size, tmr->tcorb, val); - break; - case A_TCNT: - tmr_write_count(tmr, ch, size, tmr->tcnt, val); - break; - case A_TCCR: - tmr_write_count(tmr, ch, size, tmr->tccr, val); - break; - default: - qemu_log_mask(LOG_UNIMP, "renesas_tmr: Register 0x%" HWADDR_PRIX - " not implemented\n", - addr); - break; - } -} - -static const MemoryRegionOps tmr_ops = { - .write = tmr_write, - .read = tmr_read, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 2, - }, - .valid = { - .min_access_size = 1, - .max_access_size = 2, - }, -}; - -static void timer_events(RTMRState *tmr, int ch); - -static uint16_t issue_event(RTMRState *tmr, int ch, int sz, - uint16_t tcnt, uint16_t tcora, uint16_t tcorb) -{ - uint16_t ret = tcnt; - - switch (tmr->next[ch]) { - case none: - break; - case cmia: - if (tcnt >= tcora) { - if (FIELD_EX8(tmr->tcr[ch], TCR, CCLR) == CCLR_A) { - ret = tcnt - tcora; - } - if (FIELD_EX8(tmr->tcr[ch], TCR, CMIEA)) { - qemu_irq_pulse(tmr->cmia[ch]); - } - if (sz == 8 && ch == 0 && - FIELD_EX8(tmr->tccr[1], TCCR, CSS) == CSS_CASCADING) { - tmr->tcnt[1]++; - timer_events(tmr, 1); - } - } - break; - case cmib: - if (tcnt >= tcorb) { - if (FIELD_EX8(tmr->tcr[ch], TCR, CCLR) == CCLR_B) { - ret = tcnt - tcorb; - } - if (FIELD_EX8(tmr->tcr[ch], TCR, CMIEB)) { - qemu_irq_pulse(tmr->cmib[ch]); - } - } - break; - case ovi: - if ((tcnt >= (1 << sz)) && FIELD_EX8(tmr->tcr[ch], TCR, OVIE)) { - qemu_irq_pulse(tmr->ovi[ch]); - } - break; - default: - g_assert_not_reached(); - } - return ret; -} - -static void timer_events(RTMRState *tmr, int ch) -{ - uint16_t tcnt; - - tmr->tcnt[ch] = read_tcnt(tmr, 1, ch); - if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) != CSS_CASCADING) { - tmr->tcnt[ch] = issue_event(tmr, ch, 8, - tmr->tcnt[ch], - tmr->tcora[ch], - tmr->tcorb[ch]) & 0xff; - } else { - if (ch == 1) { - return ; - } - tcnt = issue_event(tmr, ch, 16, - concat_reg(tmr->tcnt), - concat_reg(tmr->tcora), - concat_reg(tmr->tcorb)); - tmr->tcnt[0] = (tcnt >> 8) & 0xff; - tmr->tcnt[1] = tcnt & 0xff; - } - update_events(tmr, ch); -} - -static void timer_event0(void *opaque) -{ - RTMRState *tmr = opaque; - - timer_events(tmr, 0); -} - -static void timer_event1(void *opaque) -{ - RTMRState *tmr = opaque; - - timer_events(tmr, 1); -} - -static void rtmr_reset(DeviceState *dev) -{ - RTMRState *tmr = RTMR(dev); - tmr->tcr[0] = tmr->tcr[1] = 0x00; - tmr->tcsr[0] = 0x00; - tmr->tcsr[1] = 0x10; - tmr->tcnt[0] = tmr->tcnt[1] = 0x00; - tmr->tcora[0] = tmr->tcora[1] = 0xff; - tmr->tcorb[0] = tmr->tcorb[1] = 0xff; - tmr->tccr[0] = tmr->tccr[1] = 0x00; - tmr->next[0] = tmr->next[1] = none; - tmr->tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); -} - -static void rtmr_init(Object *obj) -{ - SysBusDevice *d = SYS_BUS_DEVICE(obj); - RTMRState *tmr = RTMR(obj); - int i; - - memory_region_init_io(&tmr->memory, OBJECT(tmr), &tmr_ops, - tmr, "renesas-tmr", 0x10); - sysbus_init_mmio(d, &tmr->memory); - - for (i = 0; i < ARRAY_SIZE(tmr->ovi); i++) { - sysbus_init_irq(d, &tmr->cmia[i]); - sysbus_init_irq(d, &tmr->cmib[i]); - sysbus_init_irq(d, &tmr->ovi[i]); - } - timer_init_ns(&tmr->timer[0], QEMU_CLOCK_VIRTUAL, timer_event0, tmr); - timer_init_ns(&tmr->timer[1], QEMU_CLOCK_VIRTUAL, timer_event1, tmr); -} - -static const VMStateDescription vmstate_rtmr = { - .name = "rx-tmr", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT64(tick, RTMRState), - VMSTATE_UINT8_ARRAY(tcnt, RTMRState, TMR_CH), - VMSTATE_UINT8_ARRAY(tcora, RTMRState, TMR_CH), - VMSTATE_UINT8_ARRAY(tcorb, RTMRState, TMR_CH), - VMSTATE_UINT8_ARRAY(tcr, RTMRState, TMR_CH), - VMSTATE_UINT8_ARRAY(tccr, RTMRState, TMR_CH), - VMSTATE_UINT8_ARRAY(tcor, RTMRState, TMR_CH), - VMSTATE_UINT8_ARRAY(tcsr, RTMRState, TMR_CH), - VMSTATE_INT64_ARRAY(div_round, RTMRState, TMR_CH), - VMSTATE_UINT8_ARRAY(next, RTMRState, TMR_CH), - VMSTATE_TIMER_ARRAY(timer, RTMRState, TMR_CH), - VMSTATE_END_OF_LIST() - } -}; - -static Property rtmr_properties[] = { - DEFINE_PROP_UINT64("input-freq", RTMRState, input_freq, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void rtmr_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->vmsd = &vmstate_rtmr; - dc->reset = rtmr_reset; - device_class_set_props(dc, rtmr_properties); -} - -static const TypeInfo rtmr_info = { - .name = TYPE_RENESAS_TMR, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(RTMRState), - .instance_init = rtmr_init, - .class_init = rtmr_class_init, -}; - -static void rtmr_register_types(void) -{ - type_register_static(&rtmr_info); -} - -type_init(rtmr_register_types) diff --git a/hw/timer/renesas_tmr8.c b/hw/timer/renesas_tmr8.c new file mode 100644 index 0000000000..ba1d2faa07 --- /dev/null +++ b/hw/timer/renesas_tmr8.c @@ -0,0 +1,540 @@ +/* + * Renesas 8bit timer + * + * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware + * (Rev.1.40 R01UH0033EJ0140) + * + * Copyright (c) 2020 Yoshinori Sato + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "qemu/timer.h" +#include "qemu/bitops.h" +#include "hw/hw.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-clock.h" +#include "hw/timer/renesas_tmr8.h" +#include "migration/vmstate.h" +#include "qemu/error-report.h" + +REG8(TCR, 0) + FIELD(TCR, CCLR, 3, 2) + FIELD(TCR, OVIE, 5, 1) + FIELD(TCR, CMIEA, 6, 1) + FIELD(TCR, CMIEB, 7, 1) + FIELD(TCR, CMIE, 6, 2) + FIELD(TCR, ALLIE, 5, 3) +REG8(TCSR, 2) + FIELD(TCSR, OSA, 0, 2) + FIELD(TCSR, OSB, 2, 2) + FIELD(TCSR, ADTE, 4, 1) +REG8(TCORA, 4) +REG8(TCORB, 6) +REG8(TCNT, 8) +REG8(TCCR, 10) + FIELD(TCCR, CKS, 0, 3) + FIELD(TCCR, CSS, 3, 2) + FIELD(TCCR, TMRIS, 7, 1) + +#define CLK_EVT -1 + +enum CSS { + CSS_EXT = 0, /* extarnal clock */ + CSS_INT = 1, /* internal clock */ + CSS_UND = 2, /* undefined */ + CSS_EVT = 3, /* event count */ +}; + +static void update_clk(RenesasTMR8State *tmr, int ch) +{ + int64_t t; + static const int divlist[] = {1, 2, 8, 32, 64, 1024, 8192, 0}; + switch (FIELD_EX8(tmr->ch[ch].tccr, TCCR, CSS)) { + case CSS_EXT: + qemu_log_mask(LOG_UNIMP, + "renesas_tmr8: External clock not implemented.\n"); + tmr->ch[ch].clk = 0; + break; + case CSS_INT: + t = divlist[FIELD_EX8(tmr->ch[ch].tccr, TCCR, CKS)]; + if (t > 0 && clock_is_enabled(tmr->pck)) { + t = tmr->input_freq / t; + tmr->ch[ch].clk = NANOSECONDS_PER_SECOND / t; + } else { + tmr->ch[ch].clk = 0; + } + break; + case CSS_UND: + qemu_log_mask(LOG_UNIMP, + "renesas_8timer: CSS undefined."); + tmr->ch[ch].clk = 0; + break; + case CSS_EVT: + tmr->ch[ch].clk = CLK_EVT; + break; + } +} + +static uint16_t catreg(uint8_t hi, uint8_t lo) +{ + uint16_t ret = 0; + ret = deposit32(ret, 8, 8, hi); + ret = deposit32(ret, 0, 8, lo); + return ret; +} + +static bool is_clr_count(uint8_t tcr, enum timer_event event) +{ + switch (event) { + case EVT_CMIA: + case EVT_CMIB: + return FIELD_EX8(tcr, TCR, CCLR) == event; + case EVT_OVI: + return true; + default: + g_assert_not_reached(); + } +} + +static bool is_irq_enabled(uint8_t tcr, enum timer_event event) +{ + switch (event) { + case EVT_CMIA: + return FIELD_EX8(tcr, TCR, CMIEA); + case EVT_CMIB: + return FIELD_EX8(tcr, TCR, CMIEB); + case EVT_OVI: + return FIELD_EX8(tcr, TCR, OVIE); + default: + g_assert_not_reached(); + } +} + +static bool event_enabled(uint8_t tcr, enum timer_event event) +{ + return is_clr_count(tcr, event) || is_irq_enabled(tcr, event); +} + +static int event_cor(struct tmr8_ch *ch, enum timer_event event) +{ + switch (event) { + case EVT_CMIA: + return ch->cor[REG_A]; + case EVT_CMIB: + return ch->cor[REG_B]; + default: + return 0xff; + } +} + +static bool is_word_mode(RenesasTMR8State *tmr) +{ + /* + * If the following conditions are met, it is treated as a 16-bit counter. + * ch0 - free running and no compare match event + * ch1 - free running no event + */ + return tmr->ch[0].clk == CLK_EVT && + tmr->ch[1].clk > 0 && + FIELD_EX8(tmr->ch[0].tcr, TCR, CCLR) == 0 && + FIELD_EX8(tmr->ch[0].tcr, TCR, CMIE) == 0 && + FIELD_EX8(tmr->ch[0].tccr, TCCR, CSS) == CSS_EVT && + FIELD_EX8(tmr->ch[1].tcr, TCR, CCLR) == 0 && + FIELD_EX8(tmr->ch[1].tcr, TCR, ALLIE) == 0; +} + +static void set_next_event(RenesasTMR8State *tmr, int ch) +{ + int64_t next = 0; + enum timer_event evt; + int cor; + int min; + if (ch == 1 && is_word_mode(tmr)) { + /* 16bit count mode */ + next = 0x10000 - catreg(tmr->ch[0].cnt, tmr->ch[1].cnt); + next *= tmr->ch[1].clk; + tmr->ch[0].event = tmr->ch[1].event = EVT_WOVI; + } else if (tmr->ch[ch].clk > 0) { + /* Find the next event. */ + min = 0x100 + 1; + for (evt = EVT_CMIA; evt < EVT_WOVI; evt++) { + cor = event_cor(&tmr->ch[ch], evt); + /* event happen in next count up */ + cor++; + if (tmr->ch[ch].cnt < cor && min > cor && + event_enabled(tmr->ch[ch].tcr, evt)) { + min = cor; + next = cor - tmr->ch[ch].cnt; + next *= tmr->ch[ch].clk; + tmr->ch[ch].event = evt; + } + } + } + if (next > 0) { + tmr->ch[ch].base = tmr->ch[ch].next; + tmr->ch[ch].next += next; + printf("%s %ld\n", __func__, next); + timer_mod(tmr->ch[ch].timer, tmr->ch[ch].next); + } else { + timer_del(tmr->ch[ch].timer); + } +} + +static void sent_irq(struct tmr8_ch *ch, enum timer_event evt) +{ + if (is_irq_enabled(ch->tcr, evt)) { + qemu_irq_pulse(ch->irq[evt - 1]); + } +} + +static void event_countup(struct tmr8_ch *ch) +{ + enum timer_event evt; + int cor; + + ch->cnt++; + for (evt = EVT_CMIA; evt < EVT_WOVI; evt++) { + cor = event_cor(ch, evt) + 1; + if (ch->cnt == cor) { + if (is_clr_count(ch->tcr, evt)) { + ch->cnt = 0; + } + sent_irq(ch, evt); + } + } +} + +static void timer_event(void *opaque) +{ + struct tmr8_ch *ch = opaque; + RenesasTMR8State *tmr = ch->tmrp; + + switch (ch->event) { + case EVT_CMIA: + if (ch->id == 0 && tmr->ch[1].clk == CLK_EVT) { + /* CH1 event count */ + event_countup(&tmr->ch[1]); + } + /* Falls through. */ + case EVT_CMIB: + if (FIELD_EX8(ch->tcr, TCR, CCLR) == ch->event) { + ch->cnt = 0; + } else { + /* update current value */ + ch->cnt = ch->cor[ch->event] + 1; + } + sent_irq(ch, ch->event); + break; + case EVT_OVI: + ch->cnt = 0; + sent_irq(ch, EVT_OVI); + if (ch->id == 1 && tmr->ch[0].clk == CLK_EVT) { + /* CH0 event count */ + event_countup(&tmr->ch[0]); + } + break; + case EVT_WOVI: + tmr->ch[0].cnt = tmr->ch[1].cnt = 0; + sent_irq(ch, EVT_OVI); + break; + default: + g_assert_not_reached(); + } + set_next_event(tmr, ch->id); +} + +static uint16_t read_tcnt(RenesasTMR8State *tmr, unsigned int size, int ch) +{ + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + int64_t delta; + uint8_t ret[2]; + int i; + + switch (size) { + case 1: + if (tmr->ch[ch].clk > 0) { + delta = now - tmr->ch[ch].base; + delta /= tmr->ch[ch].clk; + } else { + delta = 0; + } + return tmr->ch[ch].cnt + delta; + case 2: + if (is_word_mode(tmr)) { + /* 16bit count mode */ + delta = now - tmr->ch[1].base; + delta /= tmr->ch[1].clk; + return catreg(tmr->ch[0].cnt, tmr->ch[1].cnt) + delta; + } else { + for (i = 0; i < TMR_CH; i++) { + if (tmr->ch[i].clk > 0) { + delta = now - tmr->ch[i].base; + delta /= tmr->ch[i].clk; + } else { + delta = 0; + } + ret[i] = tmr->ch[i].cnt + delta; + } + return catreg(ret[0], ret[1]); + } + default: + g_assert_not_reached(); + } +} + +static void tmr_pck_update(void *opaque, ClockEvent evt) +{ + RenesasTMR8State *tmr = RENESAS_TMR8(opaque); + int i; + uint16_t tcnt = read_tcnt(tmr, 2, 0); + + tmr->ch[0].cnt = extract16(tcnt, 8, 8); + tmr->ch[1].cnt = extract16(tcnt, 0, 8); + + tmr->input_freq = clock_get_hz(tmr->pck); + for (i = 0; i < TMR_CH; i++) { + if (clock_is_enabled(tmr->pck)) { + update_clk(tmr, i); + set_next_event(tmr, i); + } else { + if (tmr->ch[i].timer) { + timer_del(tmr->ch[i].timer); + } + } + } +} + +static int validate_access(hwaddr addr, unsigned int size) +{ + /* Byte access always OK */ + if (size == 1) { + return 1; + } + /* word access allowed TCNT / TCOR / TCCR */ + return ((addr & 1) == 0 && addr >= A_TCORA); +} + +static uint64_t tmr8_read(void *opaque, hwaddr addr, unsigned int size) +{ + RenesasTMR8State *tmr = RENESAS_TMR8(opaque); + int ch = addr & 1; + int cor; + + if (!validate_access(addr, size)) { + qemu_log_mask(LOG_GUEST_ERROR, "renesas_tmr8: Invalid read size 0x%" + HWADDR_PRIX "\n", addr); + return UINT64_MAX; + } + if (!clock_is_enabled(tmr->pck)) { + qemu_log_mask(LOG_GUEST_ERROR, "renesas_tmr8: Unit %d is stopped.\n", + tmr->unit); + return UINT64_MAX; + } + switch (addr & ~1) { + case A_TCR: + return tmr->ch[ch].tcr; + case A_TCSR: + return tmr->ch[ch].tcsr; + case A_TCORA: + case A_TCORB: + cor = extract32(addr, 1, 1); + if (size == 1) { + /* 8bit read - single register */ + return tmr->ch[ch].cor[cor]; + } else { + /* 16bit read - high byte ch0 reg, low byte ch1 reg */ + return catreg(tmr->ch[0].cor[cor], tmr->ch[1].cor[cor]); + } + case A_TCNT: + return read_tcnt(tmr, size, ch); + case A_TCCR: + if (size == 1) { + return tmr->ch[ch].tccr; + } else { + return catreg(tmr->ch[0].tccr, tmr->ch[1].tccr); + } + default: + qemu_log_mask(LOG_UNIMP, "renesas_tmr8: Register 0x%" HWADDR_PRIX + " not implemented\n", addr); + break; + } + return UINT64_MAX; +} + +static void tmr8_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + RenesasTMR8State *tmr = RENESAS_TMR8(opaque); + int ch = addr & 1; + int cor; + int64_t now; + + if (!validate_access(addr, size)) { + qemu_log_mask(LOG_GUEST_ERROR, + "renesas_tmr: Invalid write size 0x%" HWADDR_PRIX + "\n", addr); + return; + } + if (!clock_is_enabled(tmr->pck)) { + qemu_log_mask(LOG_GUEST_ERROR, "renesas_tmr8: Unit %d is stopped.\n", + tmr->unit); + return; + } + switch (addr & ~1) { + case A_TCR: + tmr->ch[ch].tcr = val; + break; + case A_TCSR: + if (ch == 1) { + /* CH1 ADTR always 1 */ + val = FIELD_DP8(val, TCSR, ADTE, 1); + } + tmr->ch[ch].tcsr = val; + break; + case A_TCORA: + case A_TCORB: + cor = extract32(addr, 1, 1); + if (size == 1) { + tmr->ch[ch].cor[cor] = val; + } else { + tmr->ch[0].cor[cor] = extract32(val, 0, 8); + tmr->ch[1].cor[cor] = extract32(val, 8, 8); + } + break; + case A_TCNT: + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + if (size == 1) { + tmr->ch[ch].base = now; + tmr->ch[ch].cnt = val; + } else { + tmr->ch[0].base = tmr->ch[1].base = now; + tmr->ch[0].cnt = extract32(val, 0, 8); + tmr->ch[1].cnt = extract32(val, 8, 8); + } + break; + case A_TCCR: + val &= ~0x6060; + if (size == 1) { + tmr->ch[ch].tccr = val; + update_clk(tmr, ch); + } else { + tmr->ch[0].tccr = extract32(val, 0, 8); + tmr->ch[1].tccr = extract32(val, 8, 8); + update_clk(tmr, 0); + update_clk(tmr, 1); + } + break; + default: + qemu_log_mask(LOG_UNIMP, "renesas_tmr: Register 0x%" HWADDR_PRIX + " not implemented\n", addr); + return; + } + if (size == 1) { + set_next_event(tmr, ch); + } else { + set_next_event(tmr, 0); + set_next_event(tmr, 1); + } +} + +static const MemoryRegionOps tmr_ops = { + .write = tmr8_write, + .read = tmr8_read, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 2, + }, +}; + +static void tmr8_realize(DeviceState *dev, Error **errp) +{ + RenesasTMR8State *tmr = RENESAS_TMR8(dev); + int i; + + for (i = 0; i < TMR_CH; i++) { + tmr->ch[i].id = i; + tmr->ch[i].timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + timer_event, &tmr->ch[i]); + tmr->ch[i].tmrp = tmr; + tmr->ch[i].tcr = 0x00; + tmr->ch[i].tcsr = (i == 0) ? 0x00 : 0x10; + tmr->ch[i].cnt = 0x00; + tmr->ch[i].cor[0] = 0xff; + tmr->ch[i].cor[1] = 0xff; + tmr->ch[i].tccr = 0x00; + } +} + +static void tmr8_init(Object *obj) +{ + RenesasTMR8State *tmr = RENESAS_TMR8(obj); + SysBusDevice *d = SYS_BUS_DEVICE(obj); + int i; + + memory_region_init_io(&tmr->memory, obj, &tmr_ops, + tmr, "renesas-tmr8", 0x10); + sysbus_init_mmio(d, &tmr->memory); + + for (i = 0; i < TMR_CH; i++) { + sysbus_init_irq(d, &tmr->ch[i].irq[IRQ_CMIA]); + sysbus_init_irq(d, &tmr->ch[i].irq[IRQ_CMIB]); + sysbus_init_irq(d, &tmr->ch[i].irq[IRQ_OVI]); + } + tmr->pck = qdev_init_clock_in(DEVICE(d), "pck", + tmr_pck_update, tmr, ClockUpdate); +} + +static const VMStateDescription vmstate_rtmr = { + .name = "renesas-8tmr", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_END_OF_LIST() + } +}; + +static Property tmr8_properties[] = { + DEFINE_PROP_UINT32("unit", RenesasTMR8State, unit, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void tmr8_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_rtmr; + dc->realize = tmr8_realize; + device_class_set_props(dc, tmr8_properties); +} + +static const TypeInfo tmr8_info[] = { + { + .name = TYPE_RENESAS_TMR8, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(RenesasTMR8State), + .instance_init = tmr8_init, + .class_init = tmr8_class_init, + } +}; + +DEFINE_TYPES(tmr8_info) diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig index ec19708a4d..6aee6f8a13 100644 --- a/hw/timer/Kconfig +++ b/hw/timer/Kconfig @@ -36,10 +36,7 @@ config CMSDK_APB_DUALTIMER bool select PTIMER -config RENESAS_TMR - bool - -config RENESAS_CMT +config RENESAS_TMR8 bool config SSE_COUNTER diff --git a/hw/timer/meson.build b/hw/timer/meson.build index 03b40cfbee..0b4e8850ed 100644 --- a/hw/timer/meson.build +++ b/hw/timer/meson.build @@ -8,7 +8,7 @@ softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_timer.c')) softmmu_ss.add(when: 'CONFIG_CADENCE', if_true: files('cadence_ttc.c')) softmmu_ss.add(when: 'CONFIG_CMSDK_APB_DUALTIMER', if_true: files('cmsdk-apb-dualtimer.c')) softmmu_ss.add(when: 'CONFIG_CMSDK_APB_TIMER', if_true: files('cmsdk-apb-timer.c')) -softmmu_ss.add(when: 'CONFIG_RENESAS_TMR', if_true: files('renesas_tmr.c')) +softmmu_ss.add(when: 'CONFIG_RENESAS_TMR8', if_true: files('renesas_tmr8.c')) softmmu_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic-timer.c')) softmmu_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_timer.c')) softmmu_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_mct.c')) From patchwork Thu May 27 05:21:19 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 12283359 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.9 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,UNWANTED_LANGUAGE_BODY, URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0A5FEC4708A for ; Thu, 27 May 2021 05:26: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 BD2D2613CC for ; Thu, 27 May 2021 05:26:28 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org BD2D2613CC 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+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:47640 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lm8XL-0008Px-Uc for qemu-devel@archiver.kernel.org; Thu, 27 May 2021 01:26:27 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:56888) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lm8Si-0008P3-83 for qemu-devel@nongnu.org; Thu, 27 May 2021 01:21:40 -0400 Received: from mail03.asahi-net.or.jp ([202.224.55.15]:32938) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lm8Sc-00066C-97 for qemu-devel@nongnu.org; Thu, 27 May 2021 01:21:39 -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 41FEC385BB; Thu, 27 May 2021 14:21:32 +0900 (JST) Received: from yo-satoh-debian.localdomain (y245018.dynamic.ppp.asahi-net.or.jp [118.243.245.18]) by sakura.ysato.name (Postfix) with ESMTPSA id D82651C0077; Thu, 27 May 2021 14:21:31 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 08/11] hw/rx: rx62n use new hw modules. Date: Thu, 27 May 2021 14:21:19 +0900 Message-Id: <20210527052122.97103-9-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210527052122.97103-1-ysato@users.sourceforge.jp> References: <20210527052122.97103-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-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+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" Signed-off-by: Yoshinori Sato --- include/hw/rx/rx62n.h | 10 +++++----- hw/rx/rx62n.c | 18 ++++++++++++------ hw/rx/Kconfig | 4 ++-- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/include/hw/rx/rx62n.h b/include/hw/rx/rx62n.h index 44f5fcc74d..942ed0639f 100644 --- a/include/hw/rx/rx62n.h +++ b/include/hw/rx/rx62n.h @@ -26,8 +26,8 @@ #include "target/rx/cpu.h" #include "hw/intc/rx_icu.h" -#include "hw/timer/renesas_tmr.h" -#include "hw/timer/renesas_cmt.h" +#include "hw/timer/renesas_tmr8.h" +#include "hw/timer/renesas_timer.h" #include "hw/char/renesas_sci.h" #include "hw/rx/rx62n-cpg.h" #include "qemu/units.h" @@ -56,9 +56,9 @@ struct RX62NState { RXCPU cpu; RXICUState icu; - RTMRState tmr[RX62N_NR_TMR]; - RCMTState cmt[RX62N_NR_CMT]; - RSCIState sci[RX62N_NR_SCI]; + RenesasTMR8State tmr[RX62N_NR_TMR]; + RenesasCMTState cmt[RX62N_NR_CMT]; + RenesasSCIState sci[RX62N_NR_SCI]; RX62NCPGState cpg; MemoryRegion *sysmem; diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c index cfd41930bf..58eff0b4a3 100644 --- a/hw/rx/rx62n.c +++ b/hw/rx/rx62n.c @@ -164,7 +164,7 @@ static void register_tmr(RX62NState *s, int unit) char ckname[16]; object_initialize_child(OBJECT(s), "tmr[*]", - &s->tmr[unit], TYPE_RENESAS_TMR); + &s->tmr[unit], TYPE_RENESAS_TMR8); tmr = SYS_BUS_DEVICE(&s->tmr[unit]); irqbase = RX62N_TMR_IRQ + TMR_NR_IRQ * unit; @@ -174,10 +174,10 @@ static void register_tmr(RX62NState *s, int unit) sysbus_mmio_map(tmr, 0, RX62N_TMR_BASE + unit * 0x10); qdev_prop_set_uint32(DEVICE(tmr), "unit", unit); - sysbus_realize(tmr, &error_abort); snprintf(ckname, sizeof(ckname), "pck_tmr8-%d", unit); qdev_connect_clock_in(DEVICE(tmr), "pck", qdev_get_clock_out(DEVICE(&s->cpg), ckname)); + sysbus_realize(tmr, &error_abort); } static void register_cmt(RX62NState *s, int unit) @@ -190,6 +190,9 @@ static void register_cmt(RX62NState *s, int unit) &s->cmt[unit], TYPE_RENESAS_CMT); cmt = SYS_BUS_DEVICE(&s->cmt[unit]); qdev_prop_set_uint32(DEVICE(cmt), "unit", unit); + snprintf(ckname, sizeof(ckname), "pck_cmt-%d", unit); + qdev_connect_clock_in(DEVICE(cmt), "pck", + qdev_get_clock_out(DEVICE(&s->cpg), ckname)); irqbase = RX62N_CMT_IRQ + CMT_NR_IRQ * unit; for (i = 0; i < CMT_NR_IRQ; i++) { @@ -197,20 +200,23 @@ static void register_cmt(RX62NState *s, int unit) } sysbus_mmio_map(cmt, 0, RX62N_CMT_BASE + unit * 0x10); sysbus_realize(cmt, &error_abort); - snprintf(ckname, sizeof(ckname), "pck_cmt-%d", unit); - qdev_connect_clock_in(DEVICE(cmt), "pck", - qdev_get_clock_out(DEVICE(&s->cpg), ckname)); } static void register_sci(RX62NState *s, int unit) { SysBusDevice *sci; int i, irqbase; + char ckname[16]; object_initialize_child(OBJECT(s), "sci[*]", - &s->sci[unit], TYPE_RENESAS_SCI); + &s->sci[unit], TYPE_RENESAS_SCIA); sci = SYS_BUS_DEVICE(&s->sci[unit]); qdev_prop_set_chr(DEVICE(sci), "chardev", serial_hd(unit)); + qdev_prop_set_uint32(DEVICE(sci), "unit", unit); + qdev_prop_set_uint32(DEVICE(sci), "register-size", SCI_REGSIZE_8); + snprintf(ckname, sizeof(ckname), "pck_sci-%d", unit); + qdev_connect_clock_in(DEVICE(sci), "pck", + qdev_get_clock_out(DEVICE(&s->cpg), ckname)); sysbus_realize(sci, &error_abort); irqbase = RX62N_SCI_IRQ + SCI_NR_IRQ * unit; diff --git a/hw/rx/Kconfig b/hw/rx/Kconfig index 2b297c5a6a..9cd1082623 100644 --- a/hw/rx/Kconfig +++ b/hw/rx/Kconfig @@ -1,8 +1,8 @@ config RX62N_MCU bool select RX_ICU - select RENESAS_TMR - select RENESAS_CMT + select RENESAS_TMR8 + select RENESAS_TIMER select RENESAS_SCI config RX_GDBSIM From patchwork Thu May 27 05:21:20 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 12283363 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.9 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,UNWANTED_LANGUAGE_BODY, URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 32401C4707F for ; Thu, 27 May 2021 05:28:57 +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 AD4AC613CA for ; Thu, 27 May 2021 05:28:56 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org AD4AC613CA 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+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:56084 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lm8Zj-0005fM-T8 for qemu-devel@archiver.kernel.org; Thu, 27 May 2021 01:28:55 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:56942) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lm8Sk-00007M-RP for qemu-devel@nongnu.org; Thu, 27 May 2021 01:21:42 -0400 Received: from mail03.asahi-net.or.jp ([202.224.55.15]:32945) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lm8Sc-00066X-8u for qemu-devel@nongnu.org; Thu, 27 May 2021 01:21:42 -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 98F97385F8; Thu, 27 May 2021 14:21:32 +0900 (JST) Received: from yo-satoh-debian.localdomain (y245018.dynamic.ppp.asahi-net.or.jp [118.243.245.18]) by sakura.ysato.name (Postfix) with ESMTPSA id 3702B1C060B; Thu, 27 May 2021 14:21:32 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 09/11] hw/sh4: sh7750 Add CPG. Date: Thu, 27 May 2021 14:21:20 +0900 Message-Id: <20210527052122.97103-10-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210527052122.97103-1-ysato@users.sourceforge.jp> References: <20210527052122.97103-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-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+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" CPG required new hw modules. Signed-off-by: Yoshinori Sato --- include/hw/sh4/sh7751-cpg.h | 94 ++++++++ hw/sh4/sh7750.c | 25 ++ hw/sh4/sh7751-cpg.c | 457 ++++++++++++++++++++++++++++++++++++ hw/sh4/meson.build | 1 + 4 files changed, 577 insertions(+) create mode 100644 include/hw/sh4/sh7751-cpg.h create mode 100644 hw/sh4/sh7751-cpg.c diff --git a/include/hw/sh4/sh7751-cpg.h b/include/hw/sh4/sh7751-cpg.h new file mode 100644 index 0000000000..79f9abe210 --- /dev/null +++ b/include/hw/sh4/sh7751-cpg.h @@ -0,0 +1,94 @@ +/* + * SH7751(R) Clock generator circuit + * + * Datasheet: SH7751 Group, SH7751R Group User's Manual: Hardware + * (Rev.4.01 R01UH0457EJ0401) + * + * Copyright (c) 2020 Yoshinori Sato + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef HW_SH4_SH7751_CPG_H +#define HW_SH4_SH7751_CPG_H + +#include "hw/sysbus.h" +#include "hw/qdev-clock.h" + +#define TYPE_SH7751_CPG_BASE "sh7751-cpg-base" +#define SH7751CPGBase(obj) \ + OBJECT_CHECK(SH7751CPGBaseState, (obj), TYPE_SH7751_CPG_BASE) +#define TYPE_SH7751_CPG "sh7751-cpg" +#define SH7751CPG(obj) OBJECT_CHECK(SH7751CPGState, (obj), TYPE_SH7751_CPG) +#define TYPE_SH7751R_CPG "sh7751r-cpg" +#define SH7751RCPG(obj) OBJECT_CHECK(SH7751RCPGState, (obj), TYPE_SH7751R_CPG) +#define SH7751CPG_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SH7751CPGBaseClass, obj, TYPE_SH7751_CPG_BASE) +#define SH7751CPGBaseClass(klass) \ + OBJECT_CLASS_CHECK(SH7751CPGBaseClass, klass, TYPE_SH7751_CPG_BASE) + +enum { + CK_DMAC, + CK_SCIF, + CK_TMU_0, + CK_RTC, + CK_SCI, + CK_SQ, + CK_UBC, + CK_PCIC, + CK_TMU_1, + CK_INTC, + NUM_SUBCLOCK, +}; + +typedef struct SH7751CPGBaseState { + SysBusDevice parent_obj; + uint8_t stbcr[2]; + uint32_t clkstp00; + uint16_t freqcr; + + uint32_t clock_mode; + int ick; + Clock *clk_ick; + int bck; + Clock *clk_bck; + int pck; + Clock *clk_pck; + Clock *dev_clocks[NUM_SUBCLOCK]; + uint32_t xtal_freq_hz; + MemoryRegion memory[3 * 2]; +} SH7751CPGBaseState; + +typedef struct { + SH7751CPGBaseState parent_obj; +} SH7751CPGState; + +typedef struct { + SH7751CPGBaseState parent_obj; +} SH7751RCPGState; + +typedef struct { + SysBusDeviceClass parent; + int (*pll1mul)(int mode, uint16_t freqcr); + uint16_t *initfreqcr; +} SH7751CPGBaseClass; + +typedef struct { + SH7751CPGBaseClass parent; +} SH7751CPGClass; + +typedef struct { + SH7751CPGBaseClass parent; +} SH7751RCPGClass; + +#endif diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c index d53a436d8c..2f6c382aa6 100644 --- a/hw/sh4/sh7750.c +++ b/hw/sh4/sh7750.c @@ -24,6 +24,7 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/irq.h" #include "hw/sh4/sh.h" #include "sysemu/sysemu.h" @@ -32,6 +33,8 @@ #include "hw/sh4/sh_intc.h" #include "hw/timer/tmu012.h" #include "exec/exec-all.h" +#include "hw/sh4/sh7751-cpg.h" +#include "hw/qdev-properties.h" #define NB_DEVICES 4 @@ -752,9 +755,29 @@ static const MemoryRegionOps sh7750_mmct_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; +static SH7751CPGBaseState *sh_cpg_init(MemoryRegion *sysmem, + int cputype) +{ + const char *cpgtype; + SH7751CPGBaseState *cpg; + if (cputype & (SH_CPU_SH7750R | SH_CPU_SH7751R)) { + cpgtype = TYPE_SH7751R_CPG; + } else { + cpgtype = TYPE_SH7751_CPG; + } + cpg = SH7751CPGBase(qdev_new(cpgtype)); + qdev_prop_set_uint32(DEVICE(cpg), "xtal-frequency-hz", 20 * 1000 * 1000); + qdev_prop_set_uint32(DEVICE(cpg), "clock-mode", 5); + sysbus_mmio_map(SYS_BUS_DEVICE(cpg), 0, 0x1fc00000); + sysbus_mmio_map(SYS_BUS_DEVICE(cpg), 1, P4ADDR(0x1fc00000)); + sysbus_mmio_map(SYS_BUS_DEVICE(cpg), 2, A7ADDR(0x1fc00000)); + return cpg; +} + SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem) { SH7750State *s; + SH7751CPGBaseState *cpg; s = g_malloc0(sizeof(SH7750State)); s->cpu = cpu; @@ -800,6 +823,7 @@ SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem) cpu->env.intc_handle = &s->intc; + cpg = sh_cpg_init(sysmem, cpu->env.id); sh_serial_init(sysmem, 0x1fe00000, 0, s->periph_freq, serial_hd(0), s->intc.irqs[SCI1_ERI], @@ -824,6 +848,7 @@ SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem) s->intc.irqs[TMU2_TUNI], s->intc.irqs[TMU2_TICPI]); + sysbus_realize(SYS_BUS_DEVICE(cpg), &error_abort); if (cpu->env.id & (SH_CPU_SH7750 | SH_CPU_SH7750S | SH_CPU_SH7751)) { sh_intc_register_sources(&s->intc, _INTC_ARRAY(vectors_dma4), diff --git a/hw/sh4/sh7751-cpg.c b/hw/sh4/sh7751-cpg.c new file mode 100644 index 0000000000..4e057908ff --- /dev/null +++ b/hw/sh4/sh7751-cpg.c @@ -0,0 +1,457 @@ +/* + * SH7750 / SH7751 Clock Generation Circuit + * + * Datasheet: SH7751 Group, SH7751R Group User's Manual: Hardware + * (Rev.4.01 R01UH0457EJ0401) + * + * Copyright (c) 2020 Yoshinori Sato + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "hw/hw.h" +#include "hw/sh4/sh7751-cpg.h" +#include "hw/sysbus.h" +#include "hw/qdev-properties.h" +#include "hw/registerfields.h" +#include "hw/qdev-properties.h" +#include "hw/clock.h" +#include "migration/vmstate.h" + +#define SH7751_XTAL_MIN_HZ (1 * 1000 * 1000) +#define SH7751_XTAL_MAX_HZ (34 * 1000 * 1000) + +REG16(FREQCR, 0) + FIELD(FREQCR, PFC, 0, 3) + FIELD(FREQCR, BFC, 3, 3) + FIELD(FREQCR, IFC, 6, 3) + FIELD(FREQCR, PLL2EN, 9, 1) + FIELD(FREQCR, PLL1EN, 10, 1) + FIELD(FREQCR, CKOEN, 11, 1) +REG8(STBCR, 4) +REG8(STBCR2, 16) + +REG32(CLKSTP00, 0) +REG32(CLKSTPCLR00, 8) + +typedef struct { + const char *name; + int devnum; + int reg; + int offset; +} dev_clock_t; + +static const dev_clock_t dev_clock_list[] = { + { .name = "pck_sci", .devnum = CK_SCI, .reg = 0, .offset = 0}, + { .name = "pck_rtc", .devnum = CK_RTC, .reg = 0, .offset = 1}, + { .name = "pck_tmu-0", .devnum = CK_TMU_0, .reg = 0, .offset = 2}, + { .name = "pck_scif", .devnum = CK_SCIF, .reg = 0, .offset = 3}, + { .name = "pck_dmac", .devnum = CK_DMAC, .reg = 0, .offset = 4}, + { .name = "pck_ubc", .devnum = CK_UBC, .reg = 1, .offset = 0}, + { .name = "pck_sq", .devnum = CK_SQ, .reg = 1, .offset = 1}, + { .name = "pck_intc", .devnum = CK_INTC, .reg = 2, .offset = 0}, + { .name = "pck_tmu-1", .devnum = CK_TMU_1, .reg = 2, .offset = 1}, + { .name = "pck_pcic", .devnum = CK_PCIC, .reg = 2, .offset = 2}, + { }, +}; + +static void set_clock_in(SH7751CPGBaseState *cpg, const dev_clock_t *ck) +{ + Clock *out; + uint64_t period; + + out = qdev_get_clock_out(DEVICE(cpg), ck->name); + g_assert(out); + period = 0; + switch (ck->reg) { + case 0: + case 1: + if (extract8(cpg->stbcr[ck->reg], ck->offset, 1) == 0) { + period = clock_get(cpg->clk_ick); + } + break; + case 2: + if (extract32(cpg->clkstp00, ck->offset, 1) == 0) { + period = clock_get(cpg->clk_ick); + } + break; + } + if (clock_get(out) != period) { + clock_update(out, period); + } +} + +static void update_divrate(SH7751CPGBaseState *cpg) +{ + SH7751CPGBaseClass *k = SH7751CPG_GET_CLASS(cpg); + int ick = FIELD_EX32(cpg->freqcr, FREQCR, IFC); + int bck = FIELD_EX32(cpg->freqcr, FREQCR, BFC); + int pck = FIELD_EX32(cpg->freqcr, FREQCR, PFC); + const dev_clock_t *p = dev_clock_list; + uint32_t divinput; + + divinput = cpg->xtal_freq_hz * k->pll1mul(cpg->clock_mode, cpg->freqcr); + + ick = ick < 4 ? ick + 1 : (ick - 1) * 2; + clock_update_hz(cpg->clk_ick, divinput / ick); + bck = bck < 4 ? bck + 1 : (bck - 1) * 2; + clock_update_hz(cpg->clk_bck, divinput / bck); + pck = pck < 3 ? pck + 2 : pck * 2; + clock_update_hz(cpg->clk_pck, divinput / pck); + + while (p->name) { + set_clock_in(cpg, p); + p++; + } +} + +static const dev_clock_t *find_clock_list(int crno, int bit) +{ + const dev_clock_t *ret = dev_clock_list; + while (ret->name) { + if (ret->reg == crno && ret->offset == bit) { + return ret; + } + ret++; + } + return NULL; +} + +static void update_stbcr(SH7751CPGBaseState *cpg, int no, uint32_t diff) +{ + int bit = 0; + const dev_clock_t *p; + static const char *reg[] = {"STBCR", "STBCR2", "CLKSTP00"}; + + while (diff) { + if (diff & 1) { + p = find_clock_list(no, bit); + if (p) { + set_clock_in(cpg, p); + } else { + qemu_log_mask(LOG_UNIMP, "sh7751-cpg: %s " + " bit %d is not implement.\n", reg[no], bit); + } + } + bit++; + diff >>= 1; + } +} + +static uint64_t cpg_read(void *opaque, hwaddr addr, unsigned size) +{ + SH7751CPGBaseState *cpg = SH7751CPGBase(opaque); + int reg; + + switch (addr) { + case A_FREQCR: + if (size != 2) { + qemu_log_mask(LOG_GUEST_ERROR, "sh7751-cpg: Register 0x%" + HWADDR_PRIX " Invalid access size.\n", addr); + return UINT64_MAX; + } + return cpg->freqcr; + case A_STBCR: + case A_STBCR2: + if (size != 1) { + qemu_log_mask(LOG_GUEST_ERROR, "sh7751-cpg: Register 0x%" + HWADDR_PRIX " Invalid access size.\n", addr); + return UINT64_MAX; + } + reg = extract32(addr, 4, 1); /* STBCR -> 0x04 / STBCR2 -> 0x10 */ + return cpg->stbcr[reg]; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "sh7751-cpg: Register 0x%" + HWADDR_PRIX " Invalid address.\n", addr); + return UINT64_MAX; + } + +} + +static void cpg_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + SH7751CPGBaseState *cpg = SH7751CPGBase(opaque); + uint32_t old_stbcr; + int reg; + + switch (addr) { + case A_FREQCR: + if (size != 2) { + qemu_log_mask(LOG_GUEST_ERROR, "sh7751-cpg: Register 0x%" + HWADDR_PRIX " Invalid access size.\n", addr); + return; + } + if ((cpg->freqcr ^ val) & 0x0600) { + qemu_log_mask(LOG_UNIMP, + "sh7751-cpg: PLL operation not supported.\n"); + } + cpg->freqcr = val; + update_divrate(cpg); + break; + case A_STBCR: + case A_STBCR2: + if (size != 1) { + qemu_log_mask(LOG_GUEST_ERROR, "sh7751-cpg: Register 0x%" + HWADDR_PRIX " Invalid access size.\n", addr); + return; + } + reg = extract32(addr, 4, 1); /* STBCR -> 0x04 / STBCR2 -> 0x10 */ + old_stbcr = cpg->stbcr[reg]; + old_stbcr ^= val; + cpg->stbcr[reg] = val; + update_stbcr(cpg, reg, old_stbcr); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "sh7751-cpg: Register 0x%" + HWADDR_PRIX " Invalid address.\n", addr); + } +} + +static uint64_t stp_read(void *opaque, hwaddr addr, unsigned size) +{ + SH7751CPGBaseState *cpg = SH7751CPGBase(opaque); + + switch (addr) { + case A_CLKSTP00: + return cpg->clkstp00; + case A_CLKSTPCLR00: + qemu_log_mask(LOG_GUEST_ERROR, + "sh7751-cpg: CLKSTPCLR00 is write only.\n"); + return UINT64_MAX; + default: + qemu_log_mask(LOG_GUEST_ERROR, "sh7751-cpg: Register 0x%" + HWADDR_PRIX " Invalid address.\n", addr); + return UINT64_MAX; + } +} + +static void stp_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + SH7751CPGBaseState *cpg = SH7751CPGBase(opaque); + + val &= 0x7; + switch (addr) { + case A_CLKSTP00: + cpg->clkstp00 |= val; + update_stbcr(cpg, 2, val); + break; + case A_CLKSTPCLR00: + cpg->clkstp00 &= ~val; + update_stbcr(cpg, 2, val); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "sh7751-cpg: Register 0x%" + HWADDR_PRIX " Invalid address.\n", addr); + } +} + +static int sh7751_pll1mul(int mode, uint16_t freqcr) +{ + int div1; + int pll1; + switch (mode) { + case 3: + case 5: + case 6: + div1 = 2; + break; + default: + div1 = 1; + } + if (FIELD_EX16(freqcr, FREQCR, PLL1EN)) { + pll1 = 6; + } else { + pll1 = 1; + } + return pll1 / div1; +} + +static int sh7751r_pll1mul(int mode, uint16_t freqcr) +{ + int pll1; + switch (mode) { + case 0: + case 1: + case 3: + case 5: + pll1 = 12; + break; + case 2: + case 4: + case 6: + pll1 = 6; + break; + default: + g_assert_not_reached(); + } + if (!FIELD_EX16(freqcr, FREQCR, PLL1EN)) { + pll1 = 1; + } + return pll1; +} + +static const MemoryRegionOps cpg_ops = { + .write = cpg_write, + .read = cpg_read, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static const MemoryRegionOps stp_ops = { + .write = stp_write, + .read = stp_read, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static const ClockPortInitArray sh7751_cpg_clocks = { + QDEV_CLOCK_OUT(SH7751CPGBaseState, clk_ick), + QDEV_CLOCK_OUT(SH7751CPGBaseState, clk_bck), + QDEV_CLOCK_OUT(SH7751CPGBaseState, clk_pck), + QDEV_CLOCK_END +}; + +static void sh7751cpg_realize(DeviceState *dev, Error **errp) +{ + SH7751CPGBaseState *cpg = SH7751CPGBase(dev); + SH7751CPGBaseClass *k = SH7751CPG_GET_CLASS(cpg); + + if (cpg->xtal_freq_hz == 0) { + error_setg(errp, "\"xtal-frequency-hz\" property must be provided."); + return; + } + /* XTAL range: 1-34 MHz */ + if (cpg->xtal_freq_hz < SH7751_XTAL_MIN_HZ || + cpg->xtal_freq_hz > SH7751_XTAL_MAX_HZ) { + error_setg(errp, "\"xtal-frequency-hz\" property in incorrect range."); + return; + } + /* Clock mode: 0 - 6 */ + if (cpg->clock_mode > 6) { + error_setg(errp, "\"clock-mode\" property in incorrect range."); + return; + } + + cpg->freqcr = k->initfreqcr[cpg->clock_mode]; + update_divrate(cpg); +} + +static void sh7751_cpg_init(Object *obj) +{ + SH7751CPGBaseState *cpg = SH7751CPGBase(obj); + const dev_clock_t *p = dev_clock_list; + qdev_init_clocks(DEVICE(obj), sh7751_cpg_clocks); + /* connect parent clock */ + while (p->name) { + cpg->dev_clocks[p->devnum] = qdev_init_clock_out(DEVICE(obj), + p->name); + p++; + } + + memory_region_init_io(&cpg->memory[0], OBJECT(cpg), &cpg_ops, + cpg, "sh7751-cpg", 0x14); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &cpg->memory[0]); + memory_region_init_alias(&cpg->memory[1], NULL, + "sh7751-cpg-a4", &cpg->memory[0], 0, 0x14); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &cpg->memory[1]); + memory_region_init_alias(&cpg->memory[2], NULL, + "sh7751-cpg-p7", &cpg->memory[0], 0, 0x14); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &cpg->memory[2]); + memory_region_init_io(&cpg->memory[3], OBJECT(cpg), &stp_ops, + cpg, "sh7751-stp", 0x10); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &cpg->memory[3]); + memory_region_init_alias(&cpg->memory[4], NULL, + "sh7751-stp-a4", &cpg->memory[3], 0, 0x10); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &cpg->memory[4]); + memory_region_init_alias(&cpg->memory[5], NULL, + "sh7751-stp-p7", &cpg->memory[3], 0, 0x10u); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &cpg->memory[5]); +} + +static Property sh7751_cpg_properties[] = { + DEFINE_PROP_UINT32("xtal-frequency-hz", + SH7751CPGBaseState, xtal_freq_hz, 0), + DEFINE_PROP_UINT32("clock-mode", + SH7751CPGBaseState, clock_mode, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void sh7751cpg_base_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + device_class_set_props(dc, sh7751_cpg_properties); +} + +static void sh7751cpg_class_init(ObjectClass *klass, void *data) +{ + SH7751CPGBaseClass *base = SH7751CPGBaseClass(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + static uint16_t initfreqcr[] = {0x0e1a, 0x0e23, 0x0e13, 0x0e13, + 0x0e0a, 0x0e0a, 0x0808}; + + base->pll1mul = sh7751_pll1mul; + base->initfreqcr = initfreqcr; + dc->realize = sh7751cpg_realize; +} + +static void sh7751rcpg_class_init(ObjectClass *klass, void *data) +{ + SH7751CPGBaseClass *base = SH7751CPGBaseClass(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + static uint16_t initfreqcr[] = {0x0e1a, 0x0e2c, 0x0e13, 0x0e13, + 0x0e0a, 0x0e0a, 0x0808}; + + base->pll1mul = sh7751r_pll1mul; + base->initfreqcr = initfreqcr; + dc->realize = sh7751cpg_realize; +} + +static const TypeInfo sh7751cpg_info[] = { + { + .name = TYPE_SH7751_CPG_BASE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SH7751CPGBaseState), + .class_init = sh7751cpg_base_class_init, + .class_size = sizeof(SH7751CPGBaseClass), + .abstract = true, + }, + { + .name = TYPE_SH7751_CPG, + .parent = TYPE_SH7751_CPG_BASE, + .instance_size = sizeof(SH7751CPGState), + .instance_init = sh7751_cpg_init, + .class_init = sh7751cpg_class_init, + .class_size = sizeof(SH7751CPGClass), + }, + { + .name = TYPE_SH7751R_CPG, + .parent = TYPE_SH7751_CPG_BASE, + .instance_size = sizeof(SH7751RCPGState), + .instance_init = sh7751_cpg_init, + .class_init = sh7751rcpg_class_init, + .class_size = sizeof(SH7751RCPGClass), + }, +}; + +DEFINE_TYPES(sh7751cpg_info) diff --git a/hw/sh4/meson.build b/hw/sh4/meson.build index 424d5674de..7ed8246152 100644 --- a/hw/sh4/meson.build +++ b/hw/sh4/meson.build @@ -2,6 +2,7 @@ sh4_ss = ss.source_set() sh4_ss.add(files( 'sh7750.c', 'sh7750_regnames.c', + 'sh7751-cpg.c', )) sh4_ss.add(when: 'CONFIG_R2D', if_true: files('r2d.c')) sh4_ss.add(when: 'CONFIG_SHIX', if_true: files('shix.c')) From patchwork Thu May 27 05:21:21 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 12283355 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 551D5C47089 for ; Thu, 27 May 2021 05:26:27 +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 E30F861355 for ; Thu, 27 May 2021 05:26:26 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org E30F861355 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+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:47420 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lm8XK-0008Ec-3C for qemu-devel@archiver.kernel.org; Thu, 27 May 2021 01:26:26 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:56836) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lm8Sg-0008Kr-3S for qemu-devel@nongnu.org; Thu, 27 May 2021 01:21:38 -0400 Received: from mail03.asahi-net.or.jp ([202.224.55.15]:32950) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lm8Sc-000675-2Z for qemu-devel@nongnu.org; Thu, 27 May 2021 01:21:37 -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 DADF23860B; Thu, 27 May 2021 14:21:32 +0900 (JST) Received: from yo-satoh-debian.localdomain (y245018.dynamic.ppp.asahi-net.or.jp [118.243.245.18]) by sakura.ysato.name (Postfix) with ESMTPSA id 86DCD1C0077; Thu, 27 May 2021 14:21:32 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 10/11] hw/sh4: sh7750 use new hw modules. Date: Thu, 27 May 2021 14:21:21 +0900 Message-Id: <20210527052122.97103-11-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210527052122.97103-1-ysato@users.sourceforge.jp> References: <20210527052122.97103-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-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+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" Signed-off-by: Yoshinori Sato --- include/hw/sh4/sh.h | 8 ----- hw/sh4/sh7750.c | 87 +++++++++++++++++++++++++++++++++++++++------ hw/sh4/Kconfig | 4 +-- 3 files changed, 79 insertions(+), 20 deletions(-) diff --git a/include/hw/sh4/sh.h b/include/hw/sh4/sh.h index becb596979..74e1ba59a8 100644 --- a/include/hw/sh4/sh.h +++ b/include/hw/sh4/sh.h @@ -55,14 +55,6 @@ int sh7750_register_io_device(struct SH7750State *s, /* sh_serial.c */ #define SH_SERIAL_FEAT_SCIF (1 << 0) -void sh_serial_init(MemoryRegion *sysmem, - hwaddr base, int feat, - uint32_t freq, Chardev *chr, - qemu_irq eri_source, - qemu_irq rxi_source, - qemu_irq txi_source, - qemu_irq tei_source, - qemu_irq bri_source); /* sh7750.c */ qemu_irq sh7750_irl(struct SH7750State *s); diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c index 2f6c382aa6..b2f6ebe936 100644 --- a/hw/sh4/sh7750.c +++ b/hw/sh4/sh7750.c @@ -31,9 +31,10 @@ #include "sh7750_regs.h" #include "sh7750_regnames.h" #include "hw/sh4/sh_intc.h" -#include "hw/timer/tmu012.h" +#include "hw/timer/renesas_timer.h" #include "exec/exec-all.h" #include "hw/sh4/sh7751-cpg.h" +#include "hw/char/renesas_sci.h" #include "hw/qdev-properties.h" #define NB_DEVICES 4 @@ -774,6 +775,74 @@ static SH7751CPGBaseState *sh_cpg_init(MemoryRegion *sysmem, return cpg; } +static void tmu012_init(MemoryRegion *sysmem, hwaddr base, + int unit, + qemu_irq ch0_irq, qemu_irq ch1_irq, + qemu_irq ch2_irq0, qemu_irq ch2_irq1, + SH7751CPGBaseState *cpg) +{ + RenesasTMUState *tmu; + char ckname[16]; + + tmu = RENESAS_TMU(qdev_new(TYPE_RENESAS_TMU)); + qdev_prop_set_uint32(DEVICE(tmu), "unit", unit); + snprintf(ckname, sizeof(ckname), "pck_tmu-%d", unit); + qdev_connect_clock_in(DEVICE(tmu), "pck", + qdev_get_clock_out(DEVICE(cpg), "pck_tmu-0")); + + sysbus_realize(SYS_BUS_DEVICE(tmu), &error_abort); + sysbus_connect_irq(SYS_BUS_DEVICE(tmu), 0, ch0_irq); + sysbus_connect_irq(SYS_BUS_DEVICE(tmu), 1, ch1_irq); + if (unit == 0) { + sysbus_connect_irq(SYS_BUS_DEVICE(tmu), 2, ch2_irq0); + } + /* ch2_irq1 is not used. */ + + sysbus_mmio_map(SYS_BUS_DEVICE(tmu), 0, base); + sysbus_mmio_map(SYS_BUS_DEVICE(tmu), 1, P4ADDR(base)); + sysbus_mmio_map(SYS_BUS_DEVICE(tmu), 2, A7ADDR(base)); +} + +static void sh_serial_init(MemoryRegion *sysmem, + hwaddr base, int feat, + uint32_t freq, Chardev *chr, + qemu_irq eri_source, + qemu_irq rxi_source, + qemu_irq txi_source, + qemu_irq tei_source, + qemu_irq bri_source, + SH7751CPGBaseState *cpg) +{ + RenesasSCIBaseState *sci; + char ckname[16]; + + switch(feat) { + case 0: /* SCI */ + sci = RENESAS_SCI_BASE(qdev_new(TYPE_RENESAS_SCI)); + snprintf(ckname, sizeof(ckname), "pck_sci"); + break; + case SH_SERIAL_FEAT_SCIF: + sci = RENESAS_SCI_BASE(qdev_new(TYPE_RENESAS_SCIF)); + snprintf(ckname, sizeof(ckname), "pck_scif"); + break; + } + qdev_prop_set_chr(DEVICE(sci), "chardev", chr); + qdev_prop_set_uint32(DEVICE(sci), "register-size", SCI_REGSIZE_32); + qdev_connect_clock_in(DEVICE(sci), "pck", + qdev_get_clock_out(DEVICE(cpg), ckname)); + sysbus_connect_irq(SYS_BUS_DEVICE(sci), 0, eri_source); + sysbus_connect_irq(SYS_BUS_DEVICE(sci), 1, rxi_source); + sysbus_connect_irq(SYS_BUS_DEVICE(sci), 2, txi_source); + if (tei_source) + sysbus_connect_irq(SYS_BUS_DEVICE(sci), 3, tei_source); + if (bri_source) + sysbus_connect_irq(SYS_BUS_DEVICE(sci), 3, bri_source); + sysbus_realize(SYS_BUS_DEVICE(sci), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(sci), 0, base); + sysbus_mmio_map(SYS_BUS_DEVICE(sci), 1, P4ADDR(base)); + sysbus_mmio_map(SYS_BUS_DEVICE(sci), 2, A7ADDR(base)); +} + SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem) { SH7750State *s; @@ -830,7 +899,7 @@ SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem) s->intc.irqs[SCI1_RXI], s->intc.irqs[SCI1_TXI], s->intc.irqs[SCI1_TEI], - NULL); + NULL, cpg); sh_serial_init(sysmem, 0x1fe80000, SH_SERIAL_FEAT_SCIF, s->periph_freq, serial_hd(1), @@ -838,17 +907,14 @@ SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem) s->intc.irqs[SCIF_RXI], s->intc.irqs[SCIF_TXI], NULL, - s->intc.irqs[SCIF_BRI]); + s->intc.irqs[SCIF_BRI], cpg); - tmu012_init(sysmem, 0x1fd80000, - TMU012_FEAT_TOCR | TMU012_FEAT_3CHAN | TMU012_FEAT_EXTCLK, - s->periph_freq, + tmu012_init(sysmem, 0x1fd80000, 0, s->intc.irqs[TMU0], s->intc.irqs[TMU1], s->intc.irqs[TMU2_TUNI], - s->intc.irqs[TMU2_TICPI]); + s->intc.irqs[TMU2_TICPI], cpg); - sysbus_realize(SYS_BUS_DEVICE(cpg), &error_abort); if (cpu->env.id & (SH_CPU_SH7750 | SH_CPU_SH7750S | SH_CPU_SH7751)) { sh_intc_register_sources(&s->intc, _INTC_ARRAY(vectors_dma4), @@ -865,10 +931,10 @@ SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem) sh_intc_register_sources(&s->intc, _INTC_ARRAY(vectors_tmu34), NULL, 0); - tmu012_init(sysmem, 0x1e100000, 0, s->periph_freq, + tmu012_init(sysmem, 0x1e100000, 1, s->intc.irqs[TMU3], s->intc.irqs[TMU4], - NULL, NULL); + NULL, NULL, cpg); } if (cpu->env.id & (SH_CPU_SH7751_ALL)) { @@ -886,6 +952,7 @@ SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem) sh_intc_register_sources(&s->intc, _INTC_ARRAY(vectors_irl), _INTC_ARRAY(groups_irl)); + sysbus_realize(SYS_BUS_DEVICE(cpg), &error_abort); return s; } diff --git a/hw/sh4/Kconfig b/hw/sh4/Kconfig index ab733a3f76..b2da634d22 100644 --- a/hw/sh4/Kconfig +++ b/hw/sh4/Kconfig @@ -20,5 +20,5 @@ config SHIX config SH7750 bool select SH_INTC - select SH_SCI - select SH_TIMER + select RENESAS_TIMER + select RENESAS_SCI From patchwork Thu May 27 05:21:22 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 12283349 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 10C42C47089 for ; Thu, 27 May 2021 05:23: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 694F661355 for ; Thu, 27 May 2021 05:23:49 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 694F661355 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+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:38848 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lm8Um-0002Zc-LY for qemu-devel@archiver.kernel.org; Thu, 27 May 2021 01:23:48 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:56798) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lm8Se-0008KW-Ep for qemu-devel@nongnu.org; Thu, 27 May 2021 01:21:36 -0400 Received: from mail01.asahi-net.or.jp ([202.224.55.13]:58635) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lm8Sc-00067C-2a for qemu-devel@nongnu.org; Thu, 27 May 2021 01:21:36 -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 1EE1C11D3D0; Thu, 27 May 2021 14:21:33 +0900 (JST) Received: from yo-satoh-debian.localdomain (y245018.dynamic.ppp.asahi-net.or.jp [118.243.245.18]) by sakura.ysato.name (Postfix) with ESMTPSA id C36801C060B; Thu, 27 May 2021 14:21:32 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Subject: [PATCH 11/11] hw/rx: rx-gdbsim Add bootstrup for linux Date: Thu, 27 May 2021 14:21:22 +0900 Message-Id: <20210527052122.97103-12-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210527052122.97103-1-ysato@users.sourceforge.jp> References: <20210527052122.97103-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-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+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" linux kernel require initializing some peripherals. Signed-off-by: Yoshinori Sato --- include/hw/rx/rx62n.h | 16 ++++---- hw/rx/rx-gdbsim.c | 89 +++++++++++++++++++++++++------------------ hw/rx/rx62n.c | 15 -------- 3 files changed, 59 insertions(+), 61 deletions(-) diff --git a/include/hw/rx/rx62n.h b/include/hw/rx/rx62n.h index 942ed0639f..3bbeb5da52 100644 --- a/include/hw/rx/rx62n.h +++ b/include/hw/rx/rx62n.h @@ -33,14 +33,6 @@ #include "qemu/units.h" #include "qom/object.h" -#define TYPE_RX62N_MCU "rx62n-mcu" -typedef struct RX62NState RX62NState; -DECLARE_INSTANCE_CHECKER(RX62NState, RX62N_MCU, - TYPE_RX62N_MCU) - -#define TYPE_R5F562N7_MCU "r5f562n7-mcu" -#define TYPE_R5F562N8_MCU "r5f562n8-mcu" - #define EXT_CS_BASE 0x01000000 #define VECTOR_TABLE_BASE 0xffffff80 #define RX62N_CFLASH_BASE 0xfff80000 @@ -49,7 +41,7 @@ DECLARE_INSTANCE_CHECKER(RX62NState, RX62N_MCU, #define RX62N_NR_CMT 2 #define RX62N_NR_SCI 6 -struct RX62NState { +typedef struct RX62NState { /*< private >*/ DeviceState parent_obj; /*< public >*/ @@ -75,5 +67,11 @@ struct RX62NState { uint32_t xtal_freq_hz; } RX62NState; +#define TYPE_RX62N_MCU "rx62n-mcu" + +#define TYPE_R5F562N7_MCU "r5f562n7-mcu" +#define TYPE_R5F562N8_MCU "r5f562n8-mcu" +DECLARE_INSTANCE_CHECKER(RX62NState, RX62N_MCU, + TYPE_RX62N_MCU) #endif diff --git a/hw/rx/rx-gdbsim.c b/hw/rx/rx-gdbsim.c index 75d1fec6ca..34705d953b 100644 --- a/hw/rx/rx-gdbsim.c +++ b/hw/rx/rx-gdbsim.c @@ -31,14 +31,16 @@ /* Same address of GDB integrated simulator */ #define SDRAM_BASE EXT_CS_BASE +typedef struct RxGdbSimMachineClass RxGdbSimMachineClass; + struct RxGdbSimMachineClass { /*< private >*/ MachineClass parent_class; /*< public >*/ const char *mcu_name; uint32_t xtal_freq_hz; + size_t romsize; }; -typedef struct RxGdbSimMachineClass RxGdbSimMachineClass; struct RxGdbSimMachineState { /*< private >*/ @@ -54,26 +56,50 @@ DECLARE_OBJ_CHECKERS(RxGdbSimMachineState, RxGdbSimMachineClass, RX_GDBSIM_MACHINE, TYPE_RX_GDBSIM_MACHINE) -static void rx_load_image(RXCPU *cpu, const char *filename, - uint32_t start, uint32_t size) +#define TINYBOOT_TOP (0xffffff00) + +static void set_bootstrap(hwaddr entry, hwaddr dtb) { - static uint32_t extable[32]; - long kernel_size; + /* Minimal hardware initialize for kernel requirement */ + /* linux kernel only works little-endian mode */ + static uint8_t tinyboot[256] = { + 0xfb, 0x2e, 0x20, 0x00, 0x08, /* mov.l #0x80020, r2 */ + 0xf8, 0x2e, 0x00, 0x01, 0x01, /* mov.l #0x00010100, [r2] */ + 0xfb, 0x2e, 0x10, 0x00, 0x08, /* mov.l #0x80010, r2 */ + 0xf8, 0x22, 0xdf, 0x7d, 0xff, 0xff, /* mov.l #0xffff7ddf, [r2] */ + 0x62, 0x42, /* add #4, r2 */ + 0xf8, 0x22, 0xff, 0x7f, 0xff, 0x7f, /* mov.l #0x7fff7fff, [r2] */ + 0xfb, 0x2e, 0x40, 0x82, 0x08, /* mov.l #0x88240, r2 */ + 0x3c, 0x22, 0x00, /* mov.b #0, 2[r2] */ + 0x3c, 0x21, 0x4e, /* mov.b #78, 1[r2] */ + 0xfb, 0x22, 0x70, 0xff, 0xff, 0xff, /* mov.l #0xffffff70, r2 */ + 0xec, 0x21, /* mov.l [r2], r1 */ + 0xfb, 0x22, 0x74, 0xff, 0xff, 0xff, /* mov.l #0xffffff74, r2 */ + 0xec, 0x22, /* mov.l [r2], r2 */ + 0x7f, 0x02, /* jmp r2 */ + }; int i; + *((uint32_t *)&tinyboot[0x70]) = cpu_to_le32(dtb); + *((uint32_t *)&tinyboot[0x74]) = cpu_to_le32(entry); + + /* setup exception trap trampoline */ + for (i = 0; i < 31; i++) { + *((uint32_t *)&tinyboot[0x80 + i * 4]) = cpu_to_le32(0x10 + i * 4); + } + *((uint32_t *)&tinyboot[0xfc]) = cpu_to_le32(TINYBOOT_TOP); + rom_add_blob_fixed("tinyboot", tinyboot, sizeof(tinyboot), TINYBOOT_TOP); +} + +static void load_kernel(const char *filename, uint32_t start, uint32_t size) +{ + long kernel_size; + kernel_size = load_image_targphys(filename, start, size); if (kernel_size < 0) { fprintf(stderr, "qemu: could not load kernel '%s'\n", filename); exit(1); } - cpu->env.pc = start; - - /* setup exception trap trampoline */ - /* linux kernel only works little-endian mode */ - for (i = 0; i < ARRAY_SIZE(extable); i++) { - extable[i] = cpu_to_le32(0x10 + i * 4); - } - rom_add_blob_fixed("extable", extable, sizeof(extable), VECTOR_TABLE_BASE); } static void rx_gdbsim_init(MachineState *machine) @@ -101,33 +127,15 @@ static void rx_gdbsim_init(MachineState *machine) &error_abort); object_property_set_uint(OBJECT(&s->mcu), "xtal-frequency-hz", rxc->xtal_freq_hz, &error_abort); - object_property_set_bool(OBJECT(&s->mcu), "load-kernel", - kernel_filename != NULL, &error_abort); - - if (!kernel_filename) { - if (machine->firmware) { - rom_add_file_fixed(machine->firmware, RX62N_CFLASH_BASE, 0); - } else if (!qtest_enabled()) { - error_report("No bios or kernel specified"); - exit(1); - } - } - - qdev_realize(DEVICE(&s->mcu), NULL, &error_abort); - /* Load kernel and dtb */ if (kernel_filename) { ram_addr_t kernel_offset; - - /* - * The kernel image is loaded into - * the latter half of the SDRAM space. - */ + ram_addr_t dtb_offset = 0; kernel_offset = machine->ram_size / 2; - rx_load_image(RX_CPU(first_cpu), kernel_filename, - SDRAM_BASE + kernel_offset, kernel_offset); + + load_kernel(machine->kernel_filename, + SDRAM_BASE + kernel_offset, kernel_offset); if (dtb_filename) { - ram_addr_t dtb_offset; int dtb_size; g_autofree void *dtb = load_device_tree(dtb_filename, &dtb_size); @@ -145,10 +153,17 @@ static void rx_gdbsim_init(MachineState *machine) dtb_offset = machine->ram_size - dtb_size; rom_add_blob_fixed("dtb", dtb, dtb_size, SDRAM_BASE + dtb_offset); - /* Set dtb address to R1 */ - RX_CPU(first_cpu)->env.regs[1] = SDRAM_BASE + dtb_offset; + } + set_bootstrap(SDRAM_BASE + kernel_offset, SDRAM_BASE + dtb_offset); + } else { + if (machine->firmware) { + rom_add_file_fixed(machine->firmware, RX62N_CFLASH_BASE, 0); + } else if (!qtest_enabled()) { + error_report("No bios or kernel specified"); + exit(1); } } + qdev_realize(DEVICE(&s->mcu), NULL, &error_abort); } static void rx_gdbsim_class_init(ObjectClass *oc, void *data) diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c index 58eff0b4a3..d84ffa148c 100644 --- a/hw/rx/rx62n.c +++ b/hw/rx/rx62n.c @@ -58,20 +58,6 @@ #define RX62N_XTAL_MIN_HZ (8 * 1000 * 1000) #define RX62N_XTAL_MAX_HZ (14 * 1000 * 1000) -struct RX62NClass { - /*< private >*/ - DeviceClass parent_class; - /*< public >*/ - const char *name; - uint64_t ram_size; - uint64_t rom_flash_size; - uint64_t data_flash_size; -}; -typedef struct RX62NClass RX62NClass; - -DECLARE_CLASS_CHECKERS(RX62NClass, RX62N_MCU, - TYPE_RX62N_MCU) - /* * IRQ -> IPR mapping table * 0x00 - 0x91: IPR no (IPR00 to IPR91) @@ -281,7 +267,6 @@ static void rx62n_realize(DeviceState *dev, Error **errp) static Property rx62n_properties[] = { DEFINE_PROP_LINK("main-bus", RX62NState, sysmem, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_BOOL("load-kernel", RX62NState, kernel, false), DEFINE_PROP_UINT32("xtal-frequency-hz", RX62NState, xtal_freq_hz, 0), DEFINE_PROP_END_OF_LIST(), };