From patchwork Thu Apr 4 04:59:53 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 786151 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 1356E6EB73; Thu, 4 Apr 2024 05:01:11 +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=1712206873; cv=none; b=BoVDBE80kxDO27RzY3Og+fNWN0TOwRdhaXIYiMduRKG0gRaFi4VZE2WWF+coCYmycd2o0MgLfUycIrm6vh2X5jch4N0mv7YgKoZrZcv7cQaiDY4FR3JsZS88HbRRt1YisyIBcs9Nktc24r9GUuMpEGX4u9buWNkCCRt0Fbp8BvA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1712206873; c=relaxed/simple; bh=UJW1y/CNqaXMUaqUfQ32jgB+zVWcGE7dF8voaeMrWZE=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=cUzngTiiOlVXpu9HtpKgSWZtjZXPn7sWFAGtYvlh2zIgTRY1NygGC/V06cr7RIzXL58JYs/RMUZmCTJJZO9X76ysZJ8QPTK9PuLPiHM+G3gEf1KlTiIuvtp0itTsWe1OpFHwNKTi1gIgowPNNfTEIlHLPlrBNgzZqn5OcKuRUw4= 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 22AC81C089C; Thu, 4 Apr 2024 14:01:09 +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 18/37] irqchip: SH7751 external interrupt encoder with enable gate. Date: Thu, 4 Apr 2024 13:59:53 +0900 Message-Id: X-Mailer: git-send-email 2.39.2 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-serial@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 SH7751 have 15 level external interrupt. It is typically connected to the CPU through a priority encoder that can suppress requests. This driver provides a way to control those hardware with irqchip. Signed-off-by: Yoshinori Sato --- drivers/irqchip/Kconfig | 7 + drivers/irqchip/Makefile | 2 + drivers/irqchip/irq-renesas-sh7751irl.c | 221 ++++++++++++++++++++++++ 3 files changed, 230 insertions(+) create mode 100644 drivers/irqchip/irq-renesas-sh7751irl.c diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 33badb5b4f00..7670fcd6757d 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -698,4 +698,11 @@ config RENESAS_SH7751_INTC Support for the Renesas SH7751 On-chip interrupt controller. And external interrupt encoder for some targets. +config RENESAS_SH7751IRL_INTC + bool "Renesas SH7751 based target IRL encoder support." + depends on RENESAS_SH7751_INTC + help + Support for External Interrupt encoder + on the some Renesas SH7751 based target. + endmenu diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 51855034a895..bc21d65441f2 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -122,3 +122,5 @@ 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 +obj-$(CONFIG_RENESAS_SH7751IRL_INTC) += irq-renesas-sh7751irl.o + diff --git a/drivers/irqchip/irq-renesas-sh7751irl.c b/drivers/irqchip/irq-renesas-sh7751irl.c new file mode 100644 index 000000000000..5990f2cd9a3d --- /dev/null +++ b/drivers/irqchip/irq-renesas-sh7751irl.c @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SH7751 based board external interrupt level encoder driver + * (Renesas RTS7751R2D / IO DATA DEVICE LANDISK, USL-5P) + * + * Copyright (C) 2023 Yoshinori Sato + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct sh7751irl_intc_priv { + struct irq_domain *irq_domain; + void __iomem *base; + unsigned int width; + bool invert; + u32 enable_bit[NR_IRL]; +}; + +static inline unsigned long get_reg(void __iomem *addr, unsigned int w) +{ + switch (w) { + case 8: + return __raw_readb(addr); + case 16: + return __raw_readw(addr); + case 32: + return __raw_readl(addr); + default: + /* The size is checked when reading the properties. */ + pr_err("%s: Invalid width %d", __FILE__, w); + return 0; + } +} + +static inline void set_reg(void __iomem *addr, unsigned int w, unsigned long val) +{ + switch (w) { + case 8: + __raw_writeb(val, addr); + break; + case 16: + __raw_writew(val, addr); + break; + case 32: + __raw_writel(val, addr); + break; + default: + pr_err("%s: Invalid width %d", __FILE__, w); + } +} + +static inline struct sh7751irl_intc_priv *irq_data_to_priv(struct irq_data *data) +{ + return data->domain->host_data; +} + +static void irl_endisable(struct irq_data *data, unsigned int enable) +{ + struct sh7751irl_intc_priv *priv; + unsigned long val; + unsigned int irl; + + priv = irq_data_to_priv(data); + irl = irqd_to_hwirq(data) - IRL_BASE_IRQ; + + if (irl < NR_IRL && priv->enable_bit[irl] < priv->width) { + if (priv->invert) + enable = !enable; + + val = get_reg(priv->base, priv->width); + if (enable) + set_bit(priv->enable_bit[irl], &val); + else + clear_bit(priv->enable_bit[irl], &val); + set_reg(priv->base, priv->width, val); + } else { + pr_err("%s: Invalid register define in IRL %u", __FILE__, irl); + } +} + +static void sh7751irl_intc_disable_irq(struct irq_data *data) +{ + irl_endisable(data, 0); +} + +static void sh7751irl_intc_enable_irq(struct irq_data *data) +{ + irl_endisable(data, 1); +} + +static struct irq_chip sh7751irl_intc_chip = { + .name = "SH7751IRL-INTC", + .irq_enable = sh7751irl_intc_enable_irq, + .irq_disable = sh7751irl_intc_disable_irq, +}; + +static int sh7751irl_intc_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw_irq_num) +{ + irq_set_chip_and_handler(virq, &sh7751irl_intc_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 sh7751irl_intc_translate(struct irq_domain *domain, + struct irq_fwspec *fwspec, unsigned long *hwirq, + unsigned int *type) +{ + if (fwspec->param[0] > NR_IRL) + return -EINVAL; + + switch (fwspec->param_count) { + case 2: + *type = fwspec->param[1]; + fallthrough; + case 1: + *hwirq = fwspec->param[0] + IRL_BASE_IRQ; + break; + default: + return -EINVAL; + } + return 0; +} + +static const struct irq_domain_ops sh7751irl_intc_domain_ops = { + .map = sh7751irl_intc_map, + .translate = sh7751irl_intc_translate, +}; + +static int __init load_irl_bit(struct device_node *node, struct sh7751irl_intc_priv *priv) +{ + struct property *enable_map; + const __be32 *p; + u32 nr_bits, bit; + u32 irl; + int ret; + + /* Fill in unused */ + memset(priv->enable_bit, ~0, sizeof(priv->enable_bit)); + + enable_map = of_find_property(node, "renesas,enable-reg", &nr_bits); + if (IS_ERR(enable_map)) + return PTR_ERR(enable_map); + + nr_bits /= sizeof(u32); + if (nr_bits > priv->width) + return -EINVAL; + + ret = nr_bits; + p = NULL; + for (bit = nr_bits; bit > 0; bit--) { + p = of_prop_next_u32(enable_map, p, &irl); + if (p == NULL || irl > NR_IRL) + return -EINVAL; + if (irl == NR_IRL) + /* IRL15 is unassined bit */ + continue; + priv->enable_bit[irl] = bit - 1; + } + return ret; +} + +static int __init sh7751irl_init(struct device_node *node, struct device_node *parent) +{ + struct sh7751irl_intc_priv *priv; + struct resource res; + struct irq_domain *d; + void __iomem *base; + int ret = 0; + + if (of_address_to_resource(node, 0, &res)) + return -EINVAL; + if (resource_size(&res) > 4) + return -EINVAL; + + base = ioremap(res.start, resource_size(&res)); + if (!base) + return -EINVAL; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base = base; + priv->width = 8 << resource_size(&res); + + ret = load_irl_bit(node, priv); + if (ret < 0) { + pr_err("%pOFP: Invalid register define.\n", node); + goto error; + } + + d = irq_domain_add_tree(node, &sh7751irl_intc_domain_ops, priv); + if (d == NULL) { + pr_err("%pOFP: cannot initialize irq domain\n", node); + ret = -ENOMEM; + goto error; + } + + priv->irq_domain = d; + irq_domain_update_bus_token(d, DOMAIN_BUS_WIRED); + pr_info("%pOFP: SH7751 External Interrupt encoder (input=%d)", node, ret); + return 0; +error: + kfree(priv); + return ret; +} + +IRQCHIP_DECLARE(renesas_sh7751_irl, "renesas,sh7751-irl-ext", sh7751irl_init);