Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8743DC433F5 for ; Tue, 7 Dec 2021 09:02:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233390AbhLGJF4 (ORCPT ); Tue, 7 Dec 2021 04:05:56 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52004 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231313AbhLGJFy (ORCPT ); Tue, 7 Dec 2021 04:05:54 -0500 Received: from sin.source.kernel.org (sin.source.kernel.org [IPv6:2604:1380:40e1:4800::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AA1E9C061746; Tue, 7 Dec 2021 01:02:24 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by sin.source.kernel.org (Postfix) with ESMTPS id E5721CE19FB; Tue, 7 Dec 2021 09:02:22 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 14D90C341C1; Tue, 7 Dec 2021 09:02:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1638867741; bh=1IukoI6r8Y3+ceVNx/4v3GTKBU5J+Is6mGvn/8YlWg8=; h=Date:From:To:Cc:Subject:In-Reply-To:References:From; b=Hoae/zLFUpSqApEhwKxiw4UtutDQdT6pjNJYO4M3kBwDXuD6/1d08m9o8TgoKI08B 2dZd3W5Cj9tIwul9sfyXde9ifzYozqaudCxQ7OU7wizX07YH/yrNp4SqrYGwZ6YAE9 YsmkKt2G9rYMLPgV73UHSpzIasHaLdFlutBSuwY8K8rZXmD84V4nWeAR1M7sY5X1dl zXc3YxC5yeYdk8AMnF6RC4bIDEFq177RgAo8fuTu/54uNoii4ZGtb9OEFFjc4EGO6Z LIYiLFdgXjm9P9UPwelVeIpzIgD9nJDLnRdqtT2Wuekn0ovKPP6cheB8lg+xikMh1T chgfVT98YdpJQ== Received: from sofa.misterjones.org ([185.219.108.64] helo=why.misterjones.org) by disco-boy.misterjones.org with esmtpsa (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1muWMc-00ASSc-LG; Tue, 07 Dec 2021 09:02:18 +0000 Date: Tue, 07 Dec 2021 09:02:18 +0000 Message-ID: <87r1ao23fp.wl-maz@kernel.org> From: Marc Zyngier To: Qin Jian Cc: robh+dt@kernel.org, mturquette@baylibre.com, sboyd@kernel.org, tglx@linutronix.de, p.zabel@pengutronix.de, linux@armlinux.org.uk, broonie@kernel.org, arnd@arndb.de, linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-clk@vger.kernel.org, wells.lu@sunplus.com Subject: Re: [PATCH v5 08/10] irqchip: Add Sunplus SP7021 interrupt controller driver In-Reply-To: References: User-Agent: Wanderlust/2.15.9 (Almost Unreal) SEMI-EPG/1.14.7 (Harue) FLIM-LB/1.14.9 (=?UTF-8?B?R29qxY0=?=) APEL-LB/10.8 EasyPG/1.0.0 Emacs/27.1 (x86_64-pc-linux-gnu) MULE/6.0 (HANACHIRUSATO) MIME-Version: 1.0 (generated by SEMI-EPG 1.14.7 - "Harue") Content-Type: text/plain; charset=US-ASCII X-SA-Exim-Connect-IP: 185.219.108.64 X-SA-Exim-Rcpt-To: qinjian@cqplus1.com, robh+dt@kernel.org, mturquette@baylibre.com, sboyd@kernel.org, tglx@linutronix.de, p.zabel@pengutronix.de, linux@armlinux.org.uk, broonie@kernel.org, arnd@arndb.de, linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-clk@vger.kernel.org, wells.lu@sunplus.com X-SA-Exim-Mail-From: maz@kernel.org X-SA-Exim-Scanned: No (on disco-boy.misterjones.org); SAEximRunCond expanded to false Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Fri, 03 Dec 2021 07:34:25 +0000, Qin Jian wrote: > > Add interrupt controller driver for Sunplus SP7021 SoC. > > This is the interrupt controller in P-chip which collects all interrupt > sources in P-chip and routes them to parent interrupt controller in C-chip. > > Signed-off-by: Qin Jian > --- > MAINTAINERS | 1 + > drivers/irqchip/Kconfig | 9 + > drivers/irqchip/Makefile | 1 + > drivers/irqchip/irq-sp7021-intc.c | 284 ++++++++++++++++++++++++++++++ > 4 files changed, 295 insertions(+) > create mode 100644 drivers/irqchip/irq-sp7021-intc.c > > diff --git a/MAINTAINERS b/MAINTAINERS > index 6b3bbe021..febbd97bf 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -2665,6 +2665,7 @@ F: Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml > F: Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml > F: Documentation/devicetree/bindings/reset/sunplus,reset.yaml > F: drivers/clk/clk-sp7021.c > +F: drivers/irqchip/irq-sp7021-intc.c > F: drivers/reset/reset-sunplus.c > F: include/dt-bindings/clock/sp-sp7021.h > F: include/dt-bindings/reset/sp-sp7021.h > diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig > index aca7b595c..b9429b8d0 100644 > --- a/drivers/irqchip/Kconfig > +++ b/drivers/irqchip/Kconfig > @@ -602,4 +602,13 @@ config APPLE_AIC > Support for the Apple Interrupt Controller found on Apple Silicon SoCs, > such as the M1. > > +config SUNPLUS_SP7021_INTC > + bool "Sunplus SP7021 interrupt controller" > + help > + Support for the Sunplus SP7021 Interrupt Controller IP core. > + SP7021 SoC has 2 Chips: C-Chip & P-Chip. This is used as a > + chained controller, routing all interrupt source in P-Chip to > + the primary controller on C-Chip. > + This is selected automatically by platform config. > + > endmenu > diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile > index f88cbf36a..75411f654 100644 > --- a/drivers/irqchip/Makefile > +++ b/drivers/irqchip/Makefile > @@ -116,3 +116,4 @@ obj-$(CONFIG_MACH_REALTEK_RTL) += irq-realtek-rtl.o > obj-$(CONFIG_WPCM450_AIC) += irq-wpcm450-aic.o > obj-$(CONFIG_IRQ_IDT3243X) += irq-idt3243x.o > obj-$(CONFIG_APPLE_AIC) += irq-apple-aic.o > +obj-$(CONFIG_SUNPLUS_SP7021_INTC) += irq-sp7021-intc.o > diff --git a/drivers/irqchip/irq-sp7021-intc.c b/drivers/irqchip/irq-sp7021-intc.c > new file mode 100644 > index 000000000..beabc64d5 > --- /dev/null > +++ b/drivers/irqchip/irq-sp7021-intc.c > @@ -0,0 +1,284 @@ > +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) > +/* > + * Copyright (C) Sunplus Technology Co., Ltd. > + * All rights reserved. > + */ > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#define SP_INTC_HWIRQ_MIN 0 > +#define SP_INTC_HWIRQ_MAX 223 > + > +#define SP_INTC_NR_IRQS (SP_INTC_HWIRQ_MAX - SP_INTC_HWIRQ_MIN + 1) > +#define SP_INTC_NR_GROUPS DIV_ROUND_UP(SP_INTC_NR_IRQS, 32) > +#define SP_INTC_REG_SIZE (SP_INTC_NR_GROUPS * 4) > + > +/* REG_GROUP_0 regs */ > +#define REG_INTR_TYPE (sp_intc.g0) > +#define REG_INTR_POLARITY (REG_INTR_TYPE + SP_INTC_REG_SIZE) > +#define REG_INTR_PRIORITY (REG_INTR_POLARITY + SP_INTC_REG_SIZE) > +#define REG_INTR_MASK (REG_INTR_PRIORITY + SP_INTC_REG_SIZE) > + > +/* REG_GROUP_1 regs */ > +#define REG_INTR_CLEAR (sp_intc.g1) > +#define REG_MASKED_EXT1 (REG_INTR_CLEAR + SP_INTC_REG_SIZE) > +#define REG_MASKED_EXT0 (REG_MASKED_EXT1 + SP_INTC_REG_SIZE) > +#define REG_INTR_GROUP (REG_INTR_CLEAR + 31 * 4) > + > +#define GROUP_MASK (BIT(SP_INTC_NR_GROUPS) - 1) > +#define GROUP_SHIFT_EXT1 (0) > +#define GROUP_SHIFT_EXT0 (8) > + > +/* > + * When GPIO_INT0~7 set to edge trigger, doesn't work properly. > + * WORKAROUND: change it to level trigger, and toggle the polarity > + * at ACK/Handler to make the HW work. > + */ > +#define GPIO_INT0_HWIRQ 120 > +#define GPIO_INT7_HWIRQ 127 > +#define IS_GPIO_INT(irq) \ > +({ \ > + u32 i = irq; \ > + (i >= GPIO_INT0_HWIRQ) && (i <= GPIO_INT7_HWIRQ); \ > +}) > + > +/* index of states */ > +enum { > + _IS_EDGE = 0, > + _IS_LOW, > + _IS_ACTIVE > +}; > + > +#define STATE_BIT(irq, idx) (((irq) - GPIO_INT0_HWIRQ) * 3 + (idx)) > +#define ASSIGN_STATE(irq, idx, v) assign_bit(STATE_BIT(irq, idx), sp_intc.states, v) > +#define TEST_STATE(irq, idx) test_bit(STATE_BIT(irq, idx), sp_intc.states) > + > +static struct sp_intctl { > + /* > + * REG_GROUP_0: include type/polarity/priority/mask regs. > + * REG_GROUP_1: include clear/masked_ext0/masked_ext1/group regs. > + */ > + void __iomem *g0; // REG_GROUP_0 base > + void __iomem *g1; // REG_GROUP_1 base > + > + struct irq_domain *domain; > + raw_spinlock_t lock; > + > + /* > + * store GPIO_INT states > + * each interrupt has 3 states: is_edge, is_low, is_active > + */ > + DECLARE_BITMAP(states, (GPIO_INT7_HWIRQ - GPIO_INT0_HWIRQ + 1) * 3); > +} sp_intc; > + > +static struct irq_chip sp_intc_chip; > + > +static void sp_intc_assign_bit(u32 hwirq, void __iomem *base, bool value) > +{ > + unsigned long flags; > + > + raw_spin_lock_irqsave(&sp_intc.lock, flags); > + __assign_bit(hwirq, base, value); __assign_bit() on an MMIO mapping? No. Why do you think we have separate MMIO accessors? > + raw_spin_unlock_irqrestore(&sp_intc.lock, flags); > +} > + > +static void sp_intc_ack_irq(struct irq_data *d) > +{ > + u32 hwirq = d->hwirq; > + > + if (unlikely(IS_GPIO_INT(hwirq) && TEST_STATE(hwirq, _IS_EDGE))) { // WORKAROUND > + sp_intc_assign_bit(hwirq, REG_INTR_POLARITY, !TEST_STATE(hwirq, _IS_LOW)); > + ASSIGN_STATE(hwirq, _IS_ACTIVE, true); > + } > + > + sp_intc_assign_bit(hwirq, REG_INTR_CLEAR, 1); > +} > + > +static void sp_intc_mask_irq(struct irq_data *d) > +{ > + sp_intc_assign_bit(d->hwirq, REG_INTR_MASK, 0); > +} > + > +static void sp_intc_unmask_irq(struct irq_data *d) > +{ > + sp_intc_assign_bit(d->hwirq, REG_INTR_MASK, 1); > +} > + > +static int sp_intc_set_type(struct irq_data *d, unsigned int type) > +{ > + u32 hwirq = d->hwirq; > + bool is_edge = !(type & IRQ_TYPE_LEVEL_MASK); > + bool is_low = (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_EDGE_FALLING); > + > + irq_set_handler_locked(d, is_edge ? handle_edge_irq : handle_level_irq); > + > + if (unlikely(IS_GPIO_INT(hwirq) && is_edge)) { // WORKAROUND > + /* store states */ > + ASSIGN_STATE(hwirq, _IS_EDGE, is_edge); > + ASSIGN_STATE(hwirq, _IS_LOW, is_low); > + ASSIGN_STATE(hwirq, _IS_ACTIVE, false); > + /* change to level */ > + is_edge = false; > + } > + > + sp_intc_assign_bit(hwirq, REG_INTR_TYPE, is_edge); > + sp_intc_assign_bit(hwirq, REG_INTR_POLARITY, is_low); > + > + return 0; > +} > + > +static int sp_intc_get_ext_irq(int ext_num) > +{ > + void __iomem *base = ext_num ? REG_MASKED_EXT1 : REG_MASKED_EXT0; > + u32 shift = ext_num ? GROUP_SHIFT_EXT1 : GROUP_SHIFT_EXT0; > + u32 groups; > + u32 pending_group; > + u32 group; > + u32 pending_irq; > + > + groups = readl_relaxed(REG_INTR_GROUP); > + pending_group = (groups >> shift) & GROUP_MASK; > + if (!pending_group) > + return -1; > + > + group = fls(pending_group) - 1; > + pending_irq = readl_relaxed(base + group * 4); > + if (!pending_irq) > + return -1; > + > + return (group * 32) + fls(pending_irq) - 1; > +} > + > +static void sp_intc_handle_ext_cascaded(struct irq_desc *desc) > +{ > + struct irq_chip *chip = irq_desc_get_chip(desc); > + int ext_num = (int)irq_desc_get_handler_data(desc); > + int hwirq; > + > + chained_irq_enter(chip, desc); > + > + while ((hwirq = sp_intc_get_ext_irq(ext_num)) >= 0) { > + if (unlikely(IS_GPIO_INT(hwirq) && TEST_STATE(hwirq, _IS_ACTIVE))) { // WORKAROUND > + ASSIGN_STATE(hwirq, _IS_ACTIVE, false); > + sp_intc_assign_bit(hwirq, REG_INTR_POLARITY, TEST_STATE(hwirq, _IS_LOW)); > + } else { > + generic_handle_domain_irq(sp_intc.domain, hwirq); > + } > + } > + > + chained_irq_exit(chip, desc); > +} > + > +#ifdef CONFIG_SMP > +static int sp_intc_set_affinity(struct irq_data *d, const struct cpumask *mask, bool force) > +{ > + return -EINVAL; > +} > +#endif > + > +static struct irq_chip sp_intc_chip = { > + .name = "sp_intc", > + .irq_ack = sp_intc_ack_irq, > + .irq_mask = sp_intc_mask_irq, > + .irq_unmask = sp_intc_unmask_irq, > + .irq_set_type = sp_intc_set_type, > +#ifdef CONFIG_SMP > + .irq_set_affinity = sp_intc_set_affinity, > +#endif > +}; > + > +static int sp_intc_irq_domain_map(struct irq_domain *domain, > + unsigned int irq, irq_hw_number_t hwirq) > +{ > + irq_set_chip_and_handler(irq, &sp_intc_chip, handle_level_irq); > + irq_set_chip_data(irq, &sp_intc_chip); > + irq_set_noprobe(irq); > + > + return 0; > +} > + > +static const struct irq_domain_ops sp_intc_dm_ops = { > + .xlate = irq_domain_xlate_twocell, > + .map = sp_intc_irq_domain_map, > +}; > + > +static int sp_intc_irq_map(struct device_node *node, int i) > +{ > + unsigned int irq; > + > + irq = irq_of_parse_and_map(node, i); > + if (!irq) > + return -ENOENT; > + > + irq_set_chained_handler_and_data(irq, sp_intc_handle_ext_cascaded, (void *)i); > + > + return 0; > +} > + > +void sp_intc_set_ext(u32 hwirq, int ext_num) > +{ > + sp_intc_assign_bit(hwirq, REG_INTR_PRIORITY, !ext_num); > +} > +EXPORT_SYMBOL_GPL(sp_intc_set_ext); No way. We don't export random symbols without a good justification, and you didn't give any. > + > +int __init sp_intc_init_dt(struct device_node *node, struct device_node *parent) This should be static. > +{ > + int i, ret; > + > + sp_intc.g0 = of_iomap(node, 0); > + if (!sp_intc.g0) > + return -ENXIO; > + > + sp_intc.g1 = of_iomap(node, 1); > + if (!sp_intc.g1) { > + ret = -ENXIO; > + goto out_unmap0; > + } > + > + ret = sp_intc_irq_map(node, 0); // EXT_INT0 > + if (ret) > + goto out_unmap1; > + > + ret = sp_intc_irq_map(node, 1); // EXT_INT1 > + if (ret) > + goto out_unmap1; > + > + /* initial regs */ > + for (i = 0; i < SP_INTC_NR_GROUPS; i++) { > + /* all mask */ > + writel_relaxed(0, REG_INTR_MASK + i * 4); > + /* all edge */ > + writel_relaxed(~0, REG_INTR_TYPE + i * 4); > + /* all high-active */ > + writel_relaxed(0, REG_INTR_POLARITY + i * 4); > + /* all EXT_INT0 */ > + writel_relaxed(~0, REG_INTR_PRIORITY + i * 4); > + /* all clear */ > + writel_relaxed(~0, REG_INTR_CLEAR + i * 4); > + } > + > + sp_intc.domain = irq_domain_add_linear(node, SP_INTC_NR_IRQS, > + &sp_intc_dm_ops, &sp_intc); > + if (!sp_intc.domain) { > + ret = -ENOMEM; > + goto out_unmap1; > + } > + > + raw_spin_lock_init(&sp_intc.lock); > + > + return 0; > + > +out_unmap1: > + iounmap(sp_intc.g1); > +out_unmap0: > + iounmap(sp_intc.g0); > + > + return ret; > +} > + > +IRQCHIP_DECLARE(sp_intc, "sunplus,sp7021-intc", sp_intc_init_dt); M. -- Without deviation from the norm, progress is not possible.