From patchwork Thu Apr 4 04:59:49 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 13616947 Received: from sakura.ysato.name (ik1-413-38519.vs.sakura.ne.jp [153.127.30.23]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 36F506BFA4; Thu, 4 Apr 2024 05:01:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=153.127.30.23 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1712206869; cv=none; b=eniINUvhtW/l7yRFWst4VogElE6I6HUzSHRNvnuhMnDsAHJgey28mLMJB2smHxpYeqbJ5Dy9e0r1RQceBgRw2pTCP6zPiAY5SD/tZkNQCwrGdfHEcbBU9R9HMtreqUod2KQ8TutVfcGhaCytrQUXrEvmmnthCTXlhEPzI4ztdMQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1712206869; c=relaxed/simple; bh=oCrxEmk2AZZ3QAEZghfuBRaq+IhD5YVLcxnsl2qregU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=cHadRRg+mKPI6FH6UN2CA0XwNdX54rDFMghQw1I0nnjJya63xpzLaBEDJD4tWlsiXnlGSSZUR6HFJzuQI5S/cO+GsUqewJcj1/MNkYSevK/82M02qd7EqeLGGgiFrdTpAeKCeVCB6SMyWiwJCCOYIcEx9HDqXw6oxRqmFdmvegY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=users.sourceforge.jp; spf=fail smtp.mailfrom=users.sourceforge.jp; arc=none smtp.client-ip=153.127.30.23 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=users.sourceforge.jp Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=users.sourceforge.jp Received: from SIOS1075.ysato.name (al128006.dynamic.ppp.asahi-net.or.jp [111.234.128.6]) by sakura.ysato.name (Postfix) with ESMTPSA id 4855B1C075F; Thu, 4 Apr 2024 14:01:05 +0900 (JST) From: Yoshinori Sato To: linux-sh@vger.kernel.org Cc: Yoshinori Sato , Damien Le Moal , Niklas Cassel , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Geert Uytterhoeven , Michael Turquette , Stephen Boyd , David Airlie , Daniel Vetter , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , Thomas Gleixner , Bjorn Helgaas , Lorenzo Pieralisi , =?utf-8?q?Krzysztof_Wilczy=C5=84?= =?utf-8?q?ski?= , Greg Kroah-Hartman , Jiri Slaby , Magnus Damm , Daniel Lezcano , Rich Felker , John Paul Adrian Glaubitz , Lee Jones , Helge Deller , Heiko Stuebner , Shawn Guo , Sebastian Reichel , Chris Morgan , Linus Walleij , Arnd Bergmann , David Rientjes , Hyeonggon Yoo <42.hyeyoo@gmail.com>, Vlastimil Babka , Baoquan He , Andrew Morton , Guenter Roeck , Kefeng Wang , Stephen Rothwell , Javier Martinez Canillas , Guo Ren , Azeem Shaikh , Max Filippov , Jonathan Corbet , Jacky Huang , Herve Codina , Manikanta Guntupalli , Anup Patel , Biju Das , =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= , Sam Ravnborg , Sergey Shtylyov , Laurent Pinchart , linux-ide@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-renesas-soc@vger.kernel.org, linux-clk@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-pci@vger.kernel.org, linux-serial@vger.kernel.org, linux-fbdev@vger.kernel.org Subject: [PATCH v7 16/37] irqchip: Add SH7751 INTC driver Date: Thu, 4 Apr 2024 13:59:49 +0900 Message-Id: <82bf86c5804460fd61eb6725c6222ad1cbd7d846.1712205900.git.ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.39.2 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-fbdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Renesas SH7751 Internal interrupt controller driver. Signed-off-by: Yoshinori Sato --- drivers/irqchip/Kconfig | 8 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-renesas-sh7751.c | 282 +++++++++++++++++++++++++++ 3 files changed, 291 insertions(+) create mode 100644 drivers/irqchip/irq-renesas-sh7751.c diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 72c07a12f5e1..33badb5b4f00 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -690,4 +690,12 @@ config SUNPLUS_SP7021_INTC chained controller, routing all interrupt source in P-Chip to the primary controller on C-Chip. +config RENESAS_SH7751_INTC + bool "Renesas SH7751 Interrupt Controller" + depends on SH_DEVICE_TREE || COMPILE_TEST + select IRQ_DOMAIN_HIERARCHY + help + Support for the Renesas SH7751 On-chip interrupt controller. + And external interrupt encoder for some targets. + endmenu diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index ec4a18380998..51855034a895 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -121,3 +121,4 @@ obj-$(CONFIG_IRQ_IDT3243X) += irq-idt3243x.o obj-$(CONFIG_APPLE_AIC) += irq-apple-aic.o obj-$(CONFIG_MCHP_EIC) += irq-mchp-eic.o obj-$(CONFIG_SUNPLUS_SP7021_INTC) += irq-sp7021-intc.o +obj-$(CONFIG_RENESAS_SH7751_INTC) += irq-renesas-sh7751.o diff --git a/drivers/irqchip/irq-renesas-sh7751.c b/drivers/irqchip/irq-renesas-sh7751.c new file mode 100644 index 000000000000..91d6dc3ed04c --- /dev/null +++ b/drivers/irqchip/irq-renesas-sh7751.c @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas SH7751 interrupt controller driver + * + * Copyright 2023 Yoshinori Sato + */ + +#include +#include +#include +#include +#include +#include +#include + +struct ipr { + u16 off; + u16 idx; +}; + +struct sh7751_intc_priv { + const struct ipr *iprmap; + void __iomem *base; + void __iomem *intpri00; + bool irlm; +}; + +enum { + R_ICR = 0x00, + R_IPR = 0x04, + R_INTPRI00 = 0x00, + R_INTREQ00 = 0x20, + R_INTMSK00 = 0x40, + R_INTMSKCLR00 = 0x60, +}; + +#define ICR_IRLM BIT(7) + +/* + * SH7751 IRQ mapping + * IRQ16 - 63: Group0 - IPRA to IPRD + * IRQ16 - 31: external IRL input (ICR.IRLM is 0) + * IRQ80 - 92: Group1 - INTPRI00 + */ +#define IRQ_START 16 +#define MAX_IRL (IRQ_START + NR_IRL) +#define GRP0_IRQ_END 63 +#define GRP1_IRQ_START 80 +#define IRQ_END 92 + +#define NR_IPRMAP0 (GRP0_IRQ_END - IRQ_START + 1) +#define NR_IPRMAP1 (IRQ_END - GRP1_IRQ_START) +#define IPR_PRI_MASK 0x000f + +#define IPRA 0 +#define IPRB 4 +#define IPRC 8 +#define IPRD 12 +#define INTPRI00 256 +#define IPR_B12 12 +#define IPR_B8 8 +#define IPR_B4 4 +#define IPR_B0 0 + +/* SH7751 EVT to IPR mapping table */ +static const struct ipr sh7751_iprmap[] = { + [evt2irq(0x240)] = {IPRD, IPR_B12}, /* IRL0 (ICR.IRLM=1) */ + [evt2irq(0x2a0)] = {IPRD, IPR_B8}, /* IRL1 (ICR.IRLM=1) */ + [evt2irq(0x300)] = {IPRD, IPR_B4}, /* IRL2 (ICR.IRLM=1) */ + [evt2irq(0x360)] = {IPRD, IPR_B0}, /* IRL3 (ICR.IRLM=1) */ + [evt2irq(0x400)] = {IPRA, IPR_B12}, /* TMU0 */ + [evt2irq(0x420)] = {IPRA, IPR_B8}, /* TMU1 */ + [evt2irq(0x440)] = {IPRA, IPR_B4}, /* TMU2 TNUI */ + [evt2irq(0x460)] = {IPRA, IPR_B4}, /* TMU2 TICPI */ + [evt2irq(0x480)] = {IPRA, IPR_B0}, /* RTC ATI */ + [evt2irq(0x4a0)] = {IPRA, IPR_B0}, /* RTC PRI */ + [evt2irq(0x4c0)] = {IPRA, IPR_B0}, /* RTC CUI */ + [evt2irq(0x4e0)] = {IPRB, IPR_B4}, /* SCI ERI */ + [evt2irq(0x500)] = {IPRB, IPR_B4}, /* SCI RXI */ + [evt2irq(0x520)] = {IPRB, IPR_B4}, /* SCI TXI */ + [evt2irq(0x540)] = {IPRB, IPR_B4}, /* SCI TEI */ + [evt2irq(0x560)] = {IPRB, IPR_B12}, /* WDT */ + [evt2irq(0x580)] = {IPRB, IPR_B8}, /* REF RCMI */ + [evt2irq(0x5a0)] = {IPRB, IPR_B4}, /* REF ROVI */ + [evt2irq(0x600)] = {IPRC, IPR_B0}, /* H-UDI */ + [evt2irq(0x620)] = {IPRC, IPR_B12}, /* GPIO */ + [evt2irq(0x640)] = {IPRC, IPR_B8}, /* DMAC DMTE0 */ + [evt2irq(0x660)] = {IPRC, IPR_B8}, /* DMAC DMTE1 */ + [evt2irq(0x680)] = {IPRC, IPR_B8}, /* DMAC DMTE2 */ + [evt2irq(0x6a0)] = {IPRC, IPR_B8}, /* DMAC DMTE3 */ + [evt2irq(0x6c0)] = {IPRC, IPR_B8}, /* DMAC DMAE */ + [evt2irq(0x700)] = {IPRC, IPR_B4}, /* SCIF ERI */ + [evt2irq(0x720)] = {IPRC, IPR_B4}, /* SCIF RXI */ + [evt2irq(0x740)] = {IPRC, IPR_B4}, /* SCIF BRI */ + [evt2irq(0x760)] = {IPRC, IPR_B4}, /* SCIF TXI */ + [evt2irq(0x780)] = {IPRC, IPR_B8}, /* DMAC DMTE4 */ + [evt2irq(0x7a0)] = {IPRC, IPR_B8}, /* DMAC DMTE5 */ + [evt2irq(0x7c0)] = {IPRC, IPR_B8}, /* DMAC DMTE6 */ + [evt2irq(0x7e0)] = {IPRC, IPR_B8}, /* DMAC DMTE7 */ + [evt2irq(0xa00)] = {INTPRI00, IPR_B0}, /* PCIC PCISERR */ + [evt2irq(0xa20)] = {INTPRI00, IPR_B4}, /* PCIC PCIDMA3 */ + [evt2irq(0xa40)] = {INTPRI00, IPR_B4}, /* PCIC PCIDMA2 */ + [evt2irq(0xa60)] = {INTPRI00, IPR_B4}, /* PCIC PCIDMA1 */ + [evt2irq(0xa80)] = {INTPRI00, IPR_B4}, /* PCIC PCIDMA0 */ + [evt2irq(0xaa0)] = {INTPRI00, IPR_B4}, /* PCIC PCIPWON */ + [evt2irq(0xac0)] = {INTPRI00, IPR_B4}, /* PCIC PCIPWDWN */ + [evt2irq(0xae0)] = {INTPRI00, IPR_B4}, /* PCIC PCIERR */ + [evt2irq(0xb00)] = {INTPRI00, IPR_B8}, /* TMU3 */ + [evt2irq(0xb80)] = {INTPRI00, IPR_B12}, /* TMU4 */ +}; + +/* + * IPR registers have 4bit priority x 4 entry (16bits) + */ +static void update_ipr(struct sh7751_intc_priv *priv, unsigned int irq, u16 pri) +{ + const struct ipr *ipr = NULL; + void __iomem *ipr_base; + unsigned int offset; + u16 mask; + + ipr = priv->iprmap + irq; + if (irq < GRP1_IRQ_START) { + /* Group0 */ + ipr_base = priv->base + R_IPR; + offset = ipr->off; + } else { + /* Group1 */ + ipr_base = priv->intpri00; + offset = ipr->off - INTPRI00; + } + mask = ~(IPR_PRI_MASK << ipr->idx); + pri = (pri & IPR_PRI_MASK) << ipr->idx; + mask &= __raw_readw(ipr_base + offset); + __raw_writew(mask | pri, ipr_base + offset); +} + +static inline bool is_valid_irq(unsigned int irq) +{ + /* IRQ16 - 63 */ + if (irq >= IRQ_START && irq < IRQ_START + NR_IPRMAP0) + return true; + /* IRQ80 - 92 */ + if (irq >= GRP1_IRQ_START && irq <= IRQ_END) + return true; + return false; +} + +static inline struct sh7751_intc_priv *irq_data_to_priv(struct irq_data *data) +{ + return data->domain->host_data; +} + +/* Interrupt unmask priority is 1, mask priority is 0 */ +#define PRI_ENABLE 1 +#define PRI_DISABLE 0 +static void endisable_irq(struct irq_data *data, bool enable) +{ + struct sh7751_intc_priv *priv; + unsigned int irq; + + priv = irq_data_to_priv(data); + + irq = irqd_to_hwirq(data); + if (!is_valid_irq(irq)) { + /* IRQ out of range */ + pr_warn_once("%s: IRQ %u is out of range\n", __FILE__, irq); + return; + } + + if (irq <= MAX_IRL && !priv->irlm) { + /* IRL encoded external interrupt */ + /* enable and disable from SR.IMASK */ + update_sr_imask(irq - IRQ_START, enable); + } else { + /* Internal peripheral interrupt */ + /* enable and disable from interrupt priority */ + update_ipr(priv, irq, enable ? PRI_ENABLE : PRI_DISABLE); + } +} + +static void sh7751_mask_irq(struct irq_data *data) +{ + endisable_irq(data, false); +} + +static void sh7751_unmask_irq(struct irq_data *data) +{ + endisable_irq(data, true); +} + +static const struct irq_chip sh7751_irq_chip = { + .name = "SH7751-INTC", + .irq_unmask = sh7751_unmask_irq, + .irq_mask = sh7751_mask_irq, +}; + +static int irq_sh7751_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw_irq_num) +{ + irq_set_chip_and_handler(virq, &sh7751_irq_chip, handle_level_irq); + irq_get_irq_data(virq)->chip_data = h->host_data; + irq_modify_status(virq, IRQ_NOREQUEST, IRQ_NOPROBE); + return 0; +} + +static int irq_sh7751_xlate(struct irq_domain *d, struct device_node *ctrlr, + const u32 *intspec, unsigned int intsize, + unsigned long *out_hwirq, unsigned int *out_type) +{ + if (WARN_ON(intsize < 1)) + return -EINVAL; + *out_hwirq = evt2irq(intspec[0]); + *out_type = IRQ_TYPE_NONE; + return 0; +} + +static const struct irq_domain_ops irq_ops = { + .map = irq_sh7751_map, + .xlate = irq_sh7751_xlate, +}; + +static int __init shintc_of_init(struct device_node *intc, struct device_node *parent, + const struct ipr *iprmap) +{ + struct sh7751_intc_priv *priv; + void __iomem *base, *base2; + struct irq_domain *domain; + u16 icr; + int ret; + + priv = kzalloc(sizeof(struct sh7751_intc_priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + base = of_iomap(intc, 0); + base2 = of_iomap(intc, 1); + if (!base || !base2) { + pr_err("%pOFP: Invalid register definition\n", intc); + ret = -EINVAL; + goto error; + } + + priv->base = base; + priv->intpri00 = base2; + priv->iprmap = iprmap; + + if (of_property_read_bool(intc, "renesas,irlm")) { + priv->irlm = true; + icr = __raw_readw(priv->base + R_ICR); + icr |= ICR_IRLM; + __raw_writew(icr, priv->base + R_ICR); + } + + domain = irq_domain_add_linear(intc, NR_IRQS, &irq_ops, priv); + if (domain == NULL) { + pr_err("%pOFP: cannot initialize irq domain\n", intc); + ret = -ENOMEM; + goto error; + } + + irq_set_default_host(domain); + pr_info("%pOFP: SH7751 Interrupt controller (%s external IRQ)", + intc, priv->irlm ? "4 lines" : "15 level"); + return 0; + +error: + if (base) + iounmap(base); + if (base2) + iounmap(base); + kfree(priv); + return ret; +} + +static int __init sh7751_intc_of_init(struct device_node *intc, + struct device_node *parent) +{ + return shintc_of_init(intc, parent, sh7751_iprmap); +} + +IRQCHIP_DECLARE(sh_7751_intc, "renesas,sh7751-intc", sh7751_intc_of_init);