Received: by 2002:a05:7412:8d10:b0:f3:1519:9f41 with SMTP id bj16csp178766rdb; Tue, 5 Dec 2023 02:13:57 -0800 (PST) X-Google-Smtp-Source: AGHT+IHcqoXuhgdHfARvk4QgnPo8IpoX8aJQZhQhHZYIcM9ypMDRYXj3p/hvXfbh203oEm0AWx4/ X-Received: by 2002:a05:6a20:e11d:b0:187:ce5a:2a90 with SMTP id kr29-20020a056a20e11d00b00187ce5a2a90mr7632513pzb.51.1701771237016; Tue, 05 Dec 2023 02:13:57 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1701771236; cv=none; d=google.com; s=arc-20160816; b=oaTIS2av2kr6RqT7rySfhAsgMZ030rq9OwaJpxZ/QqhgO5tD3sq6ZBra77Y/dGx2KH eMOWlLTaoeDCsoq6LkYfJJddnYUnjltxH8cNdZTZqJwpqLvFw1LqwSS7m0u4ZI0tzRDw rJYm1SsIqlbg4iGDcJNHNMRrb0ygYUlICj0GuedIlZKhwus7laqe21WC8WdaCa48EIbY mKrkpX6y09na/76h9fIwASYc1OWxPvVNJ3RK6s5oFNOPa5NwQpRcJ9BHHVw97U9iw9nI 5ugvGerJXtYwpKA1lQVgYvnrQmhkZIXCd19ztZ/fNTFIq9KGrIYNk2E/zOygUZpDbmMX zU/w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from; bh=Gb4tb/qw2tU3z3qogtHzrjyD9j7rTDhArdNB/42/dNA=; fh=S4scCjmGHuTpFNsRpx5F7zvdPnZW78Fse4yFoJp+Y6U=; b=iZPG9dS15D5lnZX8Oqy/xXdZo9k8hWt6IZHRzsDFnL1uviovl2pNt/TbCB6J0H/ngI 6zOzWhjdeSlGuTcZ5W4GyE0fRgzF30Mpce01rbDUzw7maTZnpV5izADeaA9iKTtUdLsi KQJxH0D1BTn1ohDpk5KYy3SuwzY8sQllSXddtTShVdxZXE3sm2BhHly/0TklCdnWqV3p TgLTaV+r3Ys7kt3KjusLy8uzFWAUe4e0Tum6lpLvODiJu68AzOspmWvtv8lcjwR5GxwS xDYhrH2npPpNhQrkba+8aWZ95QrkP1rNs8Zseg1OtD8ReZ+jfIwIbHVkyEKJQi5/tnYG WYhQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.36 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from pete.vger.email (pete.vger.email. [23.128.96.36]) by mx.google.com with ESMTPS id be3-20020a656e43000000b005b9a1494637si9776338pgb.317.2023.12.05.02.13.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 05 Dec 2023 02:13:56 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.36 as permitted sender) client-ip=23.128.96.36; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.36 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by pete.vger.email (Postfix) with ESMTP id 4E8AB8023882; Tue, 5 Dec 2023 02:13:46 -0800 (PST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.11 at pete.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1346582AbjLEKNF (ORCPT + 99 others); Tue, 5 Dec 2023 05:13:05 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57992 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1346471AbjLEJzn (ORCPT ); Tue, 5 Dec 2023 04:55:43 -0500 Received: from sakura.ysato.name (ik1-413-38519.vs.sakura.ne.jp [153.127.30.23]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id BB523A0; Tue, 5 Dec 2023 01:55:36 -0800 (PST) Received: from SIOS1075.ysato.name (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by sakura.ysato.name (Postfix) with ESMTPSA id 982C11C0642; Tue, 5 Dec 2023 18:46:33 +0900 (JST) From: Yoshinori Sato To: linux-sh@vger.kernel.org Cc: Yoshinori Sato , Damien Le Moal , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Geert Uytterhoeven , Michael Turquette , Stephen Boyd , David Airlie , Daniel Vetter , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , Thomas Gleixner , Lorenzo Pieralisi , =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= , Bjorn Helgaas , Greg Kroah-Hartman , Jiri Slaby , Magnus Damm , Daniel Lezcano , Rich Felker , John Paul Adrian Glaubitz , Lee Jones , Helge Deller , Heiko Stuebner , Jernej Skrabec , Chris Morgan , Linus Walleij , Randy Dunlap , Arnd Bergmann , Hyeonggon Yoo <42.hyeyoo@gmail.com>, David Rientjes , Vlastimil Babka , Baoquan He , Andrew Morton , Guenter Roeck , Stephen Rothwell , Guo Ren , Javier Martinez Canillas , Azeem Shaikh , Palmer Dabbelt , Bin Meng , Max Filippov , Tom Rix , Herve Codina , Jacky Huang , Lukas Bulwahn , Jonathan Corbet , Biju Das , =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= , Sam Ravnborg , Michael Karcher , 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: [DO NOT MERGE v5 16/37] irqchip: Add SH7751 INTC driver Date: Tue, 5 Dec 2023 18:45:35 +0900 Message-Id: X-Mailer: git-send-email 2.39.2 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-0.8 required=5.0 tests=HEADER_FROM_DIFFERENT_DOMAINS, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on pete.vger.email Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Greylist: Sender passed SPF test, not delayed by milter-greylist-4.6.4 (pete.vger.email [0.0.0.0]); Tue, 05 Dec 2023 02:13:46 -0800 (PST) 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 | 290 +++++++++++++++++++++++++++ 3 files changed, 299 insertions(+) create mode 100644 drivers/irqchip/irq-renesas-sh7751.c diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index f7149d0f3d45..658523f65b1d 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -679,4 +679,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 ffd945fe71aa..26c91d075e25 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -120,3 +120,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..2a5cb2444d99 --- /dev/null +++ b/drivers/irqchip/irq-renesas-sh7751.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas SH7751 interrupt controller driver + * + * Copyright 2023 Yoshinori Sato + */ + +#include +#include +#include +#include +#include +#include +#include + +struct ipr { + unsigned int off; + unsigned int idx; +}; + +struct sh7751_intc_priv { + void __iomem *base; + void __iomem *intpri00; + struct ipr *iprmap[2]; + 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 + +/* + * IPR registers have 4bit priority x 4 entry (16bits) + * Interrupts can be masked by setting pri to 0. + */ +static void update_ipr(struct sh7751_intc_priv *priv, unsigned int irq, u16 pri) +{ + struct ipr *ipr = NULL; + void __iomem *ipr_base; + unsigned int offset; + u16 mask; + + if (irq < GRP1_IRQ_START) { + /* Group0 */ + ipr = priv->iprmap[0]; + ipr += irq - IRQ_START; + ipr_base = priv->base + R_IPR; + offset = ipr->off; + } else { + /* Group1 */ + ipr = priv->iprmap[1]; + ipr += irq - GRP1_IRQ_START; + ipr_base = priv->intpri00; + offset = ipr->off - INTPRI00; + } + if (ipr->off != ~0) { + 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); + } else { + pr_warn_once("%s: undefined IPR in irq %u\n", __FILE__, irq); + } +} + +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; +} + +static void endisable_irq(struct irq_data *data, int 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 */ + /* disable for SR.IMASK */ + update_sr_imask(irq - IRQ_START, enable); + else + /* Internal peripheral interrupt */ + /* mask for IPR priority 0 */ + update_ipr(priv, irq, enable); +} + +static void sh7751_mask_irq(struct irq_data *data) +{ + endisable_irq(data, 0); +} + +static void sh7751_unmask_irq(struct irq_data *data) +{ + endisable_irq(data, 1); +} + +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 const struct irq_domain_ops irq_ops = { + .map = irq_sh7751_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int __init load_ipr_map(struct device_node *intc, + struct sh7751_intc_priv *priv) +{ + struct property *ipr_map; + unsigned int num_ipr, i; + struct ipr *ipr; + const __be32 *p; + u32 irq; + + ipr_map = of_find_property(intc, "renesas,ipr-map", &num_ipr); + if (IS_ERR(ipr_map)) + return PTR_ERR(ipr_map); + num_ipr /= sizeof(u32); + /* 3words per entry. */ + if (num_ipr % 3) + goto error1; + num_ipr /= 3; + if (num_ipr >= NR_IPRMAP0 + NR_IPRMAP1) + goto error1; + + /* Allocate map array and fill in unassigned */ + priv->iprmap[0] = kmalloc_array(NR_IPRMAP0, sizeof(struct ipr), GFP_KERNEL); + if (priv->iprmap[0] == NULL) + return -ENOMEM; + memset(priv->iprmap[0], ~0, NR_IPRMAP0 * sizeof(struct ipr)); + priv->iprmap[1] = kmalloc_array(NR_IPRMAP1, sizeof(struct ipr), GFP_KERNEL); + if (priv->iprmap[1] == NULL) { + kfree(priv->iprmap[0]); + return -ENOMEM; + } + memset(priv->iprmap[1], ~0, NR_IPRMAP1 * sizeof(struct ipr)); + + p = NULL; + for (; num_ipr > 0; num_ipr--) { + /* 1st word - INTEVT code */ + p = of_prop_next_u32(ipr_map, p, &irq); + if (!p) + goto error; + irq = evt2irq(irq); + if (!is_valid_irq(irq)) + goto error; + if (irq < GRP1_IRQ_START) { + ipr = priv->iprmap[0]; + irq -= IRQ_START; + } else { + ipr = priv->iprmap[1]; + irq -= GRP1_IRQ_START; + } + ipr += irq; + /* 2nd word - IPR register offset */ + p = of_prop_next_u32(ipr_map, p, &ipr->off); + /* 3rd word - IPR register bit indx */ + p = of_prop_next_u32(ipr_map, p, &ipr->idx); + + if ((ipr->off != INTPRI00 && ipr->off > IPRD) || + ipr->idx > IPR_B12) + goto error; + } + + for (ipr = priv->iprmap[0], i = 0; i < NR_IPRMAP0; ipr++, i++) { + if (ipr->off != ~0) { + pr_debug("INTEVT=%04x (%u) reg=IPR%c idx=%u\n", + irq2evt(i + IRQ_START), i + IRQ_START, + 'A' + ipr->off / 4, ipr->idx); + } + } + for (ipr = priv->iprmap[1], i = 0; i < NR_IPRMAP1; ipr++, i++) { + if (ipr->off != ~0) { + pr_debug("INTEVT=%04x (%u) reg=INTPRI00 idx=%u\n", + irq2evt(i + GRP1_IRQ_START), i + GRP1_IRQ_START, + ipr->idx); + } + } + return 0; +error: + kfree(priv->iprmap[0]); + kfree(priv->iprmap[1]); +error1: + pr_err("%pOFP: Failed to load renesas,ipr-map\n", intc); + return -EINVAL; +} + +static int __init sh7751_intc_of_init(struct device_node *intc, + struct device_node *parent) +{ + struct sh7751_intc_priv *priv; + void __iomem *base, *base2; + struct irq_domain *domain; + u16 icr; + int ret; + + base = of_iomap(intc, 0); + base2 = of_iomap(intc, 1); + if (!base || !base2) { + pr_err("%pOFP: Invalid register definition\n", intc); + return -EINVAL; + } + + priv = kzalloc(sizeof(struct sh7751_intc_priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + ret = load_ipr_map(intc, priv); + if (ret < 0) { + kfree(priv); + return ret; + } + + priv->base = base; + priv->intpri00 = base2; + + 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); + kfree(priv); + return -ENOMEM; + } + + irq_set_default_host(domain); + pr_info("%pOFP: SH7751 Interrupt controller (%s external IRQ)", + intc, priv->irlm ? "4 lines" : "15 level"); + return 0; +} + +IRQCHIP_DECLARE(sh_7751_intc, + "renesas,sh7751-intc", sh7751_intc_of_init); -- 2.39.2