Received: by 2002:ac0:a5a6:0:0:0:0:0 with SMTP id m35-v6csp476382imm; Thu, 6 Sep 2018 05:39:50 -0700 (PDT) X-Google-Smtp-Source: ANB0VdaB63X3VX68cdUTqcSN+1a04Qg4rCWMqdknDbOSc+QVhzc0CUB3FfHQDb+QW43F7p8syOu5 X-Received: by 2002:a17:902:934a:: with SMTP id g10-v6mr2399353plp.121.1536237590894; Thu, 06 Sep 2018 05:39:50 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1536237590; cv=none; d=google.com; s=arc-20160816; b=mxwg3aDazMdzPmFbrpv5HoebSPsY0DhDsfKviXcN8M4mfFyeHiR5HbuUmW/j4qte1T uTiZblGNqxTaTm2jmcwTrC8uFN+eiBnqTojNgouNYRS1uo5kJ4CRv41od3ZhMGgYDkvX s0Xkh4q/bSarYWlHSrT+0vB5J2G+Udlh8tg0hu2d6XRcotz+Nv38lTad2y7xZYY/x7iD s3/uOKsKfAwZXhnsYucFw9e+zIcMn4wOOma9nt4iheJMt31jBZqBHTCe0uaj8qeeFZ0t WUqBsRTos1ECv4d2byuIE16epvdZI2ujIIFMR3hQivvdQFv4MWGd1yodDSTlhOygojQT 4lfg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature; bh=nVnP9uRQZC9s+HNP0CnhQxl8pd/3+erTmEfw+1I2u5M=; b=eHuf8G1V5NQrEpTe53HRa7ryIXTAqh58MAENrMLGt5cbDorV4kJFwWUBhAnsbtkSXb 5WkkjMi520+3MrMvn4hgZU22/6G0q1yBxQrEyyhtLMH9LRbKL/3mcuu+fYGb1T7LTgnD Iu2aL5S+2TUiwdUXuilpFraQuZuUy8DdrPvPwQ5Hq6V5Xu3pnsiPkainqVg7pnsThQGn kAfPyr+Iu6JEAAi+0MMYwQdz0LC8Lg8zm5lodtmDYBW8NG6bjJ3dpOFaR1I56XMtkCaz x26+YvA9Zo+Vtr7XEjySwIZsyk+CFKTsUxUlBkd52CAXtr5ocgLYbE2cPHuADRjy4CL5 KohA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@brainfault-org.20150623.gappssmtp.com header.s=20150623 header.b=rBDN1bVr; 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 z6-v6si4912369plo.503.2018.09.06.05.39.34; Thu, 06 Sep 2018 05:39:50 -0700 (PDT) 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; dkim=pass header.i=@brainfault-org.20150623.gappssmtp.com header.s=20150623 header.b=rBDN1bVr; 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 S1728676AbeIFRMs (ORCPT + 99 others); Thu, 6 Sep 2018 13:12:48 -0400 Received: from mail-pl1-f196.google.com ([209.85.214.196]:45783 "EHLO mail-pl1-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728174AbeIFRMs (ORCPT ); Thu, 6 Sep 2018 13:12:48 -0400 Received: by mail-pl1-f196.google.com with SMTP id j8-v6so4920490pll.12 for ; Thu, 06 Sep 2018 05:37:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=brainfault-org.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=nVnP9uRQZC9s+HNP0CnhQxl8pd/3+erTmEfw+1I2u5M=; b=rBDN1bVr81c7sD9UBKWOJ/Dpka1cGp4BaG8SY6ZVgbrMvBnNvVAMtBzjGm04qHsuWG bhOBhR3eFShLafnx+S1yfZQagq95BxVJKUoUxr3bB71IaihHMD1Mf0QMyghTUzTOi4iD 6MiB1LG9ui1LQO7DQV6oUwNdxGz/SkRe5xRiUl/xNpp2xBvbiL4lqkCKdozlA5McUcFm jq+cu025wbKUbI+piFHQayUURuiKUyPCmxU9CUT4IjzVakcK3xeRKLCZSe4+0zNhPBHg sbA0vUu7ToWnBV6we71MNh1FkZPbV3mfah48FNco+r7ylzAjnISwJGT3v/pMfPiDH2La 8vnA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=nVnP9uRQZC9s+HNP0CnhQxl8pd/3+erTmEfw+1I2u5M=; b=Qin/wKFB41puw8emmge7AWgbwUJAbcgmZAyo/twwvNaEGYJv0R/Xsp75qlfjOF2n6O /EXftNIMk+4dxJSxHmAMPE8v92a1rvFCEsxmdsjoeChfyWnV4df0Aioww56q9nojI+uM xVvixBJK5ybBHDbGW5M+mcIGKDMvAyUSdLmr6CH7Q3FWTPex/FHGm67VjWrBXZr75jC1 8QG9zjd79ZpyVfnVmlKXcWhhYO+udSQuoVVHjUdpkts4v0sykQ1YFXmK7jSLp1GcFCQk CQb6/AiK2cIRSPwIgWV2OG/mtBy0qErMkl24mLGs6yVGasE9QOHYZxrJieMnNq9IEJfj tyGg== X-Gm-Message-State: APzg51AnisA2K5f4rhbhkpCqbGCcv+zQIXhPpCsTzCaicJ45xojEFN7r 3dFWLuzb2+HY2HNWZXoP40SBPw== X-Received: by 2002:a17:902:bc4b:: with SMTP id t11-v6mr2406667plz.262.1536237450103; Thu, 06 Sep 2018 05:37:30 -0700 (PDT) Received: from anup-ubuntu64.wlan.qualcomm.com ([106.51.30.16]) by smtp.googlemail.com with ESMTPSA id 193-v6sm11446165pgh.47.2018.09.06.05.37.24 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 06 Sep 2018 05:37:28 -0700 (PDT) From: Anup Patel To: Palmer Dabbelt , Albert Ou , Daniel Lezcano , Thomas Gleixner , Jason Cooper , Marc Zyngier Cc: Atish Patra , Christoph Hellwig , linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org, Anup Patel , Palmer Dabbelt Subject: [PATCH v2 3/5] irqchip: RISC-V Local Interrupt Controller Driver Date: Thu, 6 Sep 2018 18:06:49 +0530 Message-Id: <20180906123651.28500-4-anup@brainfault.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180906123651.28500-1-anup@brainfault.org> References: <20180906123651.28500-1-anup@brainfault.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The RISC-V local interrupt controller manages software interrupts, timer interrupts, external interrupts (which are routed via the platform level interrupt controller) and per-HART local interrupts. This patch add a driver for RISC-V local interrupt controller. It's a major re-write over perviously submitted version. (Refer, https://www.spinics.net/lists/devicetree/msg241230.html) Few advantages of this new driver over previous one are: 1. It registers all local interrupts as per-CPU interrupts 2. We can develop drivers for devices with per-CPU local interrupts without changing arch code or this driver 3. It allows local interrupt controller DT node under each CPU DT node as well as single system-wide DT node for local interrupt controller. The RISC-V INTC driver is compliant with RISC-V Hart-Level Interrupt Controller DT bindings located at: Documentation/devicetree/bindings/interrupt-controller/riscv,cpu-intc.txt Signed-off-by: Anup Patel Signed-off-by: Palmer Dabbelt --- arch/riscv/Kconfig | 1 + arch/riscv/include/asm/irq.h | 15 ++- arch/riscv/kernel/irq.c | 47 +-------- drivers/irqchip/Kconfig | 15 ++- drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-riscv-intc.c | 156 ++++++++++++++++++++++++++++++ drivers/irqchip/irq-sifive-plic.c | 20 +++- include/linux/cpuhotplug.h | 1 + 8 files changed, 206 insertions(+), 50 deletions(-) create mode 100644 drivers/irqchip/irq-riscv-intc.c diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index ea2e617eff72..16bb33cc145f 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -32,6 +32,7 @@ config RISCV select HAVE_DMA_CONTIGUOUS select HAVE_GENERIC_DMA_COHERENT select HAVE_PERF_EVENTS + select HANDLE_DOMAIN_IRQ select IRQ_DOMAIN select NO_BOOTMEM select RISCV_ISA_A if SMP diff --git a/arch/riscv/include/asm/irq.h b/arch/riscv/include/asm/irq.h index 8d5d1a9f7fae..2ad610802689 100644 --- a/arch/riscv/include/asm/irq.h +++ b/arch/riscv/include/asm/irq.h @@ -15,7 +15,20 @@ #ifndef _ASM_RISCV_IRQ_H #define _ASM_RISCV_IRQ_H -#define NR_IRQS 0 +/* + * Possible interrupt causes: + */ +#define INTERRUPT_CAUSE_SOFTWARE 1 +#define INTERRUPT_CAUSE_TIMER 5 +#define INTERRUPT_CAUSE_EXTERNAL 9 + +/* + * The high order bit of the trap cause register is always set for + * interrupts, which allows us to differentiate them from exceptions + * quickly. The INTERRUPT_CAUSE_* macros don't contain that bit, so we + * need to mask it off. + */ +#define INTERRUPT_CAUSE_FLAG (1UL << (__riscv_xlen - 1)) void riscv_timer_interrupt(void); void wait_for_software_interrupt(void); diff --git a/arch/riscv/kernel/irq.c b/arch/riscv/kernel/irq.c index d0de40e1e7f3..9ced284da30b 100644 --- a/arch/riscv/kernel/irq.c +++ b/arch/riscv/kernel/irq.c @@ -7,54 +7,11 @@ #include #include -#include - -/* - * Possible interrupt causes: - */ -#define INTERRUPT_CAUSE_SOFTWARE 1 -#define INTERRUPT_CAUSE_TIMER 5 -#define INTERRUPT_CAUSE_EXTERNAL 9 - -/* - * The high order bit of the trap cause register is always set for - * interrupts, which allows us to differentiate them from exceptions - * quickly. The INTERRUPT_CAUSE_* macros don't contain that bit, so we - * need to mask it off. - */ -#define INTERRUPT_CAUSE_FLAG (1UL << (__riscv_xlen - 1)) asmlinkage void __irq_entry do_IRQ(struct pt_regs *regs) { - struct pt_regs *old_regs; - - switch (regs->scause & ~INTERRUPT_CAUSE_FLAG) { - case INTERRUPT_CAUSE_TIMER: - old_regs = set_irq_regs(regs); - irq_enter(); - riscv_timer_interrupt(); - irq_exit(); - set_irq_regs(old_regs); - break; -#ifdef CONFIG_SMP - case INTERRUPT_CAUSE_SOFTWARE: - /* - * We only use software interrupts to pass IPIs, so if a non-SMP - * system gets one, then we don't know what to do. - */ - handle_IPI(regs); - break; -#endif - case INTERRUPT_CAUSE_EXTERNAL: - old_regs = set_irq_regs(regs); - irq_enter(); + if (handle_arch_irq) handle_arch_irq(regs); - irq_exit(); - set_irq_regs(old_regs); - break; - default: - panic("unexpected interrupt cause"); - } } /* @@ -84,4 +41,6 @@ void wait_for_software_interrupt(void) void __init init_IRQ(void) { irqchip_init(); + if (!handle_arch_irq) + panic("No interrupt controller found."); } diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 383e7b70221d..885e182586f4 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -371,7 +371,18 @@ config QCOM_PDC Power Domain Controller driver to manage and configure wakeup IRQs for Qualcomm Technologies Inc (QTI) mobile chips. -endmenu +config RISCV_INTC + bool "RISC-V Interrupt Controller" + depends on RISCV + default y + help + This enables support for the local interrupt controller found in + standard RISC-V systems. The local interrupt controller handles + timer interrupts, software interrupts, and hardware interrupts. + Without a local interrupt controller the system will be unable to + handle any interrupts, including those passed via the PLIC. + + If you don't know what to do here, say Y. config SIFIVE_PLIC bool "SiFive Platform-Level Interrupt Controller" @@ -384,3 +395,5 @@ config SIFIVE_PLIC interrupt sources are subordinate to the PLIC. If you don't know what to do here, say Y. + +endmenu diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index fbd1ec8070ef..e638ee5c4452 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -87,4 +87,5 @@ obj-$(CONFIG_MESON_IRQ_GPIO) += irq-meson-gpio.o obj-$(CONFIG_GOLDFISH_PIC) += irq-goldfish-pic.o obj-$(CONFIG_NDS32) += irq-ativic32.o obj-$(CONFIG_QCOM_PDC) += qcom-pdc.o +obj-$(CONFIG_RISCV_INTC) += irq-riscv-intc.o obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o diff --git a/drivers/irqchip/irq-riscv-intc.c b/drivers/irqchip/irq-riscv-intc.c new file mode 100644 index 000000000000..7dea2297daaa --- /dev/null +++ b/drivers/irqchip/irq-riscv-intc.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2012 Regents of the University of California + * Copyright (C) 2017-2018 SiFive + * Copyright (C) 2018 Anup Patel + */ + +#define pr_fmt(fmt) "riscv-intc: " fmt +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct irq_domain *intc_domain; +static atomic_t intc_init = ATOMIC_INIT(0); + +static asmlinkage void riscv_intc_irq(struct pt_regs *regs) +{ + struct pt_regs *old_regs; + unsigned long cause = regs->scause & ~INTERRUPT_CAUSE_FLAG; + + if (unlikely(cause >= BITS_PER_LONG)) + panic("unexpected interrupt cause"); + + switch (cause) { + case INTERRUPT_CAUSE_TIMER: + old_regs = set_irq_regs(regs); + irq_enter(); + riscv_timer_interrupt(); + irq_exit(); + set_irq_regs(old_regs); + break; +#ifdef CONFIG_SMP + case INTERRUPT_CAUSE_SOFTWARE: + /* + * We only use software interrupts to pass IPIs, so if a non-SMP + * system gets one, then we don't know what to do. + */ + handle_IPI(regs); + break; +#endif + default: + handle_domain_irq(intc_domain, cause, regs); + break; + } +} + +/* + * On RISC-V systems local interrupts are masked or unmasked by writing the SIE + * (Supervisor Interrupt Enable) CSR. As CSRs can only be written on the local + * hart, these functions can only be called on the hart that corresponds to the + * IRQ chip. They are only called internally to this module, so they BUG_ON if + * this condition is violated rather than attempting to handle the error by + * forwarding to the target hart, as that's already expected to have been done. + */ +static void riscv_intc_irq_mask(struct irq_data *d) +{ + csr_clear(sie, 1 << (long)d->hwirq); +} + +static void riscv_intc_irq_unmask(struct irq_data *d) +{ + csr_set(sie, 1 << (long)d->hwirq); +} + +#ifdef CONFIG_SMP +static int riscv_intc_cpu_starting(unsigned int cpu) +{ + csr_set(sie, 1 << INTERRUPT_CAUSE_SOFTWARE); + return 0; +} + +static int riscv_intc_cpu_dying(unsigned int cpu) +{ + csr_clear(sie, 1 << INTERRUPT_CAUSE_SOFTWARE); + return 0; +} + +static void riscv_intc_smp_init(void) +{ + csr_write(sie, 0); + csr_write(sip, 0); + + cpuhp_setup_state(CPUHP_AP_IRQ_RISCV_STARTING, + "irqchip/riscv/intc:starting", + riscv_intc_cpu_starting, + riscv_intc_cpu_dying); + +} +#else +static void riscv_intc_smp_init(void) +{ + csr_write(sie, 0); + csr_write(sip, 0); +} +#endif + +static struct irq_chip riscv_intc_chip = { + .name = "RISC-V INTC", + .irq_mask = riscv_intc_irq_mask, + .irq_unmask = riscv_intc_irq_unmask, +}; + +static int riscv_intc_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_percpu_devid(irq); + irq_domain_set_info(d, irq, hwirq, &riscv_intc_chip, d->host_data, + handle_percpu_devid_irq, NULL, NULL); + irq_set_status_flags(irq, IRQ_NOAUTOEN); + + return 0; +} + +static const struct irq_domain_ops riscv_intc_domain_ops = { + .map = riscv_intc_domain_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int __init riscv_intc_init(struct device_node *node, + struct device_node *parent) +{ + /* + * RISC-V device trees can have one INTC DT node under + * each CPU DT node so INTC init function will be called + * once for each INTC DT node. We only need to do INTC + * init once for boot CPU so we use atomic counter to + * achieve this. + */ + if (atomic_inc_return(&intc_init) > 1) + return 0; + + intc_domain = irq_domain_add_linear(node, BITS_PER_LONG, + &riscv_intc_domain_ops, NULL); + if (!intc_domain) + goto error_add_linear; + + set_handle_irq(&riscv_intc_irq); + + riscv_intc_smp_init(); + + pr_info("%lu local interrupts mapped\n", (long)BITS_PER_LONG); + + return 0; + +error_add_linear: + pr_warn("unable to add IRQ domain\n"); + return -ENXIO; +} + +IRQCHIP_DECLARE(riscv, "riscv,cpu-intc", riscv_intc_init); diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c index 357e9daf94ae..630a8c28e715 100644 --- a/drivers/irqchip/irq-sifive-plic.c +++ b/drivers/irqchip/irq-sifive-plic.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -147,14 +148,17 @@ static struct irq_domain *plic_irqdomain; * that source ID back to the same claim register. This automatically enables * and disables the interrupt, so there's nothing else to do. */ -static void plic_handle_irq(struct pt_regs *regs) +static void plic_handle_irq(struct irq_desc *desc) { struct plic_handler *handler = this_cpu_ptr(&plic_handlers); + struct irq_chip *chip = irq_desc_get_chip(desc); void __iomem *claim = plic_hart_offset(handler->ctxid) + CONTEXT_CLAIM; irq_hw_number_t hwirq; WARN_ON_ONCE(!handler->present); + chained_irq_enter(chip, desc); + csr_clear(sie, SIE_SEIE); while ((hwirq = readl(claim))) { int irq = irq_find_mapping(plic_irqdomain, hwirq); @@ -167,6 +171,8 @@ static void plic_handle_irq(struct pt_regs *regs) writel(hwirq, claim); } csr_set(sie, SIE_SEIE); + + chained_irq_exit(chip, desc); } /* @@ -184,7 +190,7 @@ static int plic_find_hart_id(struct device_node *node) } static int __init plic_init(struct device_node *node, - struct device_node *parent) + struct device_node *parent) { int error = 0, nr_handlers, nr_mapped = 0, i; u32 nr_irqs; @@ -219,7 +225,7 @@ static int __init plic_init(struct device_node *node, struct of_phandle_args parent; struct plic_handler *handler; irq_hw_number_t hwirq; - int cpu, hartid; + int cpu, hartid, parent_irq; if (of_irq_parse_one(node, i, &parent)) { pr_err("failed to parse parent for context %d.\n", i); @@ -230,6 +236,13 @@ static int __init plic_init(struct device_node *node, if (parent.args[0] == -1) continue; + if (irq_find_host(parent.np)) { + parent_irq = irq_of_parse_and_map(node, i); + if (parent_irq) + irq_set_chained_handler(parent_irq, + plic_handle_irq); + } + hartid = plic_find_hart_id(parent.np); if (hartid < 0) { pr_warn("failed to parse hart ID for context %d.\n", i); @@ -250,7 +263,6 @@ static int __init plic_init(struct device_node *node, pr_info("mapped %d interrupts to %d (out of %d) handlers.\n", nr_irqs, nr_mapped, nr_handlers); - set_handle_irq(plic_handle_irq); return 0; out_iounmap: diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index caf40ad0bbc6..ca7268a38cef 100644 --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h @@ -100,6 +100,7 @@ enum cpuhp_state { CPUHP_AP_IRQ_ARMADA_XP_STARTING, CPUHP_AP_IRQ_BCM2836_STARTING, CPUHP_AP_IRQ_MIPS_GIC_STARTING, + CPUHP_AP_IRQ_RISCV_STARTING, CPUHP_AP_ARM_MVEBU_COHERENCY, CPUHP_AP_PERF_X86_AMD_UNCORE_STARTING, CPUHP_AP_PERF_X86_STARTING, -- 2.17.1