Received: by 2002:ad5:474a:0:0:0:0:0 with SMTP id i10csp526992imu; Wed, 23 Jan 2019 00:38:01 -0800 (PST) X-Google-Smtp-Source: ALg8bN5MPjchOzILKZXCt2P6eh0c/XBFkQJDqhgw+sHVU9kR7OgX0HpuD1RyMBz6VV3CjynkdI9d X-Received: by 2002:a17:902:a411:: with SMTP id p17mr1345883plq.292.1548232681318; Wed, 23 Jan 2019 00:38:01 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1548232681; cv=none; d=google.com; s=arc-20160816; b=yT8lLkr7ObPedxbZdV25gow1SpebZRKUusmXrUnzZKtdTLTqZizfTYxfbrO12H2jG2 Ng1OepPX1/RiUcg0UG3P8KViXxJaFTLRPD2OMeksuY7FmirHZwVAk4XdnQkkGKBGRdZG yMEQXq12UQlLKqJHd72/NbXZv37WQMhVrXsWOa/hS60TSTCaaaVSBns4MP1QB30M/4/G G6LENxGewHDw119qLI9qqbP2naPnW6YNAw5qCK0gsi2mIvVTDtKzzob8SwDl9WVjNyGA iN/wp1MkGX7V+lQAsg06Pbs2wQhh8zK95bb5oYlPJFPBxuBEy1ZD3gFfCruVuni6ZWqz u4ZQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:organization:user-agent :references:in-reply-to:subject:cc:to:from:message-id:date; bh=PseTp6KZF5vyqhhMn3dpTMsGHBAMzzrBClxTnfiVTq8=; b=lDJWzqcMVBJrgvugqK2Rh1XERZrUc58shMY13+b18BcaMyYY4PDezO+w7zfJKvF08I xm+ix8C2V6S0Ha4VQSSb8FtT283xfb02lNTsscja6IQ0OSHxHW34wQp49Ie2Zl+tmbgs j1aurJFNFi326XocIJzZKK3VBLi5W3jWujA8C6xM8YVqV27C3boUdJXwSjpn1mEGFxmU J+KwTfr7ver90tR/DhxJzetldar5tqCjGazhK8sTC0Pyzp/n7FhvtkCXOZDw36lxvrId x0PbRaapAO6DVvYdcXbv5by6TMB8X+yGz1SMEgmfVXCFE0YIQM+8yuReghIW+srHvqxe RkBw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 1si18394389plk.296.2019.01.23.00.37.45; Wed, 23 Jan 2019 00:38:01 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726783AbfAWIgl (ORCPT + 99 others); Wed, 23 Jan 2019 03:36:41 -0500 Received: from usa-sjc-mx-foss1.foss.arm.com ([217.140.101.70]:37294 "EHLO foss.arm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725973AbfAWIgk (ORCPT ); Wed, 23 Jan 2019 03:36:40 -0500 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.72.51.249]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id C12A7A78; Wed, 23 Jan 2019 00:36:39 -0800 (PST) Received: from big-swifty.misterjones.org (big-swifty.copenhagen.arm.com [10.32.148.139]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id BC5183F6A8; Wed, 23 Jan 2019 00:36:36 -0800 (PST) Date: Wed, 23 Jan 2019 08:36:31 +0000 Message-ID: <86y37b93e8.wl-marc.zyngier@arm.com> From: Marc Zyngier To: Jiaxun Yang Cc: linux-mips@vger.kernel.org, Thomas Gleixner , Jason Cooper , linux-kernel@vger.kernel.org Subject: Re: [PATCH v2 1/2] irqchip: Add driver for Loongson-1 interrupt controller In-Reply-To: <20190123062341.8957-3-jiaxun.yang@flygoat.com> References: <20190122154557.22689-1-jiaxun.yang@flygoat.com> <20190123062341.8957-1-jiaxun.yang@flygoat.com> <20190123062341.8957-3-jiaxun.yang@flygoat.com> User-Agent: Wanderlust/2.15.9 (Almost Unreal) SEMI-EPG/1.14.7 (Harue) FLIM/1.14.9 (=?UTF-8?B?R29qxY0=?=) APEL/10.8 EasyPG/1.0.0 Emacs/25.1 (aarch64-unknown-linux-gnu) MULE/6.0 (HANACHIRUSATO) Organization: ARM Ltd MIME-Version: 1.0 (generated by SEMI-EPG 1.14.7 - "Harue") Content-Type: text/plain; charset=US-ASCII Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Hi Jiaxun, On Wed, 23 Jan 2019 06:23:36 +0000, Jiaxun Yang wrote: > > This controller appeared on Loongson-1 family MCUs > including Loongson-1B and Loongson-1C. > > Signed-off-by: Jiaxun Yang > --- > drivers/irqchip/Kconfig | 9 ++ > drivers/irqchip/Makefile | 1 + > drivers/irqchip/irq-ls1x.c | 194 +++++++++++++++++++++++++++++++++++++ > 3 files changed, 204 insertions(+) > create mode 100644 drivers/irqchip/irq-ls1x.c > > diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig > index 3d1e60779078..5dcb5456cd14 100644 > --- a/drivers/irqchip/Kconfig > +++ b/drivers/irqchip/Kconfig > @@ -406,6 +406,15 @@ config IMX_IRQSTEER > help > Support for the i.MX IRQSTEER interrupt multiplexer/remapper. > > +config LS1X_IRQ > + bool "Loongson-1 Interrupt Controller" > + depends on MACH_LOONGSON32 > + default y > + select IRQ_DOMAIN > + select GENERIC_IRQ_CHIP > + help > + Support for the Loongson-1 platform Interrupt Controller. > + > endmenu > > config SIFIVE_PLIC > diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile > index c93713d24b86..7acd0e36d0b4 100644 > --- a/drivers/irqchip/Makefile > +++ b/drivers/irqchip/Makefile > @@ -94,3 +94,4 @@ obj-$(CONFIG_CSKY_APB_INTC) += irq-csky-apb-intc.o > obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o > obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o > obj-$(CONFIG_MADERA_IRQ) += irq-madera.o > +obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o > diff --git a/drivers/irqchip/irq-ls1x.c b/drivers/irqchip/irq-ls1x.c > new file mode 100644 > index 000000000000..b15b01237830 > --- /dev/null > +++ b/drivers/irqchip/irq-ls1x.c > @@ -0,0 +1,194 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (C) 2019, Jiaxun Yang > + * Loongson-1 platform IRQ support > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > + > +struct ls_intc_data { > + void __iomem *base; > + unsigned int chip; > +}; > + > +#define MAX_CHIPS 5 > +#define LS_REG_INTC_STATUS 0x00 > +#define LS_REG_INTC_EN 0x04 > +#define LS_REG_INTC_SET 0x08 > +#define LS_REG_INTC_CLR 0x0c > +#define LS_REG_INTC_POL 0x10 > +#define LS_REG_INTC_EDGE 0x14 > +#define CHIP_SIZE 0x18 > + > +static irqreturn_t intc_cascade(int irq, void *data) > +{ > + struct ls_intc_data *intc = irq_get_handler_data(irq); > + uint32_t irq_reg; > + > + irq_reg = readl(intc->base + (CHIP_SIZE * intc->chip) > + + LS_REG_INTC_STATUS); > + generic_handle_irq(__fls(irq_reg) + (intc->chip * 32) + LS1X_IRQ_BASE); > + > + return IRQ_HANDLED; > +} No. A chained interrupt is not a dealt with as a normal interrupt, as it is a flow handler itself. > + > +static void ls_intc_set_bit( > + struct irq_chip_generic *gc, unsigned int offset, Please put the first parameter on the same line as the function name. > + u32 mask, bool set) > +{ > + if (set) > + writel(readl(gc->reg_base + offset) | > + mask, gc->reg_base + offset); Put "mask" on the same line as the rest of the expression. > + else > + writel(readl(gc->reg_base + offset) & > + ~mask, gc->reg_base + offset); Same here. > +} > + > +static int ls_intc_set_type(struct irq_data *data, unsigned int type) > +{ > + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); > + u32 mask = data->mask; > + > + switch (type) { > + case IRQ_TYPE_LEVEL_HIGH: > + ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, false); > + ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, true); > + break; > + case IRQ_TYPE_LEVEL_LOW: > + ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, false); > + ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, false); > + break; > + case IRQ_TYPE_EDGE_RISING: > + ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, true); > + ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, true); > + break; > + case IRQ_TYPE_EDGE_FALLING: > + ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, true); > + ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, false); > + break; > + case IRQ_TYPE_NONE: > + break; > + default: > + return -EINVAL; > + } > + > + return 0; > +} > + > + > +static struct irqaction intc_cascade_action = { > + .handler = intc_cascade, > + .name = "intc cascade interrupt", > +}; > + > +static int __init ls_intc_of_init(struct device_node *node, > + unsigned int num_chips) > +{ > + struct ls_intc_data *intc[MAX_CHIPS]; > + struct irq_chip_generic *gc; > + struct irq_chip_type *ct; > + struct irq_domain *domain; > + void __iomem *base; > + int parent_irq[MAX_CHIPS], err = 0; Initialise both arrays so that you can simplify error handling. > + unsigned int i = 0; > + > + base = of_iomap(node, 0); > + if (!base) > + return -ENODEV; > + > + for (i = 0; i < num_chips; i++) { > + /* Mask all irqs */ > + writel(0x0, base + (i * CHIP_SIZE) + > + LS_REG_INTC_EN); > + > + /* Set all irqs to high level triggered */ > + writel(0xffffffff, base + (i * CHIP_SIZE) + > + LS_REG_INTC_POL); > + > + intc[i] = kzalloc(sizeof(*intc[i]), GFP_KERNEL); > + if (!intc[i]) { > + err = -ENOMEM; > + goto out_err; > + } > + > + intc[i]->base = base; > + intc[i]->chip = i; > + > + parent_irq[i] = irq_of_parse_and_map(node, i); > + if (!parent_irq[i]) { > + pr_warn("unable to get parent irq for irqchip %u", i); > + kfree(intc[i]); > + intc[i] = NULL; > + err = -EINVAL; You're making life very complicated for yourself. Just set err and branch to the error handler. > + goto out_err; > + } > + > + err = irq_set_handler_data(parent_irq[i], intc[i]); > + if (err) > + goto out_err; > + > + gc = irq_alloc_generic_chip("INTC", 1, > + LS1X_IRQ_BASE + (i * 32), > + base + (i * CHIP_SIZE), > + handle_level_irq); > + > + ct = gc->chip_types; > + ct->regs.mask = LS_REG_INTC_EN; > + ct->regs.ack = LS_REG_INTC_CLR; > + ct->chip.irq_unmask = irq_gc_mask_set_bit; > + ct->chip.irq_mask = irq_gc_mask_clr_bit; > + ct->chip.irq_ack = irq_gc_ack_set_bit; > + ct->chip.irq_set_type = ls_intc_set_type; > + > + irq_setup_generic_chip(gc, IRQ_MSK(32), 0, 0, > + IRQ_NOPROBE | IRQ_LEVEL); > + } > + > + domain = irq_domain_add_legacy(node, num_chips * 32, LS1X_IRQ_BASE, 0, > + &irq_domain_simple_ops, NULL); > + if (!domain) { > + pr_warn("unable to register IRQ domain\n"); > + err = -EINVAL; > + goto out_err; > + } > + > + for (i = 0; i < num_chips; i++) > + setup_irq(parent_irq[i], &intc_cascade_action); This is not how we do things anymore (some of this code feels like it has escaped from a 2.2 kernel). See irq_set_chained_handler, and use that to properly configure your cascades. There are tons of example in the tree. > + > + return 0; > + > +out_err: > + for (i = 0; i < MAX_CHIPS; i++) { > + if (intc[i]) { > + kfree(intc[i]); > + irq_dispose_mapping(parent_irq[i]); > + } else { > + break; > + } > + } for (i = 0; i < MAX_CHIPS; i++) { kfree(intc[i]); if (parent_irq[i]) irq_dispose_mapping(parent_irq[i]); } > + return err; > +} > + > +static int __init intc_4chip_of_init(struct device_node *node, > + struct device_node *parent) > +{ > + return ls_intc_of_init(node, 4); > +} > +IRQCHIP_DECLARE(ls1b_intc, "loongson,ls1b-intc", intc_4chip_of_init); > + > +static int __init intc_5chip_of_init(struct device_node *node, > + struct device_node *parent) > +{ > + return ls_intc_of_init(node, 5); > +} > +IRQCHIP_DECLARE(ls1c_intc, "loongson,ls1c-intc", intc_5chip_of_init); Do you really need this distinction? You could easily find out how many interrupts you have based on the DT, right? OR do you need this for anything else? Thanks, M. -- Jazz is not dead, it just smell funny.