Commit ccbe80ba authored by Atish Patra's avatar Atish Patra Committed by Marc Zyngier

irqchip/sifive-plic: Enable/Disable external interrupts upon cpu online/offline

Currently, PLIC threshold is only initialized once in the beginning.
However, threshold can be set to disabled if a CPU is marked offline with
CPU hotplug feature. This will not allow to change the irq affinity to a
CPU that just came online.

Add PLIC specific CPU hotplug callbacks and enable the threshold when a CPU
comes online. Take this opportunity to move the external interrupt enable
code from trap init to PLIC driver as well. On cpu offline path, the driver
performs the exact opposite operations i.e. disable the interrupt and
the threshold.
Signed-off-by: default avatarAtish Patra <atish.patra@wdc.com>
Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
Reviewed-by: default avatarAnup Patel <anup@brainfault.org>
Link: https://lore.kernel.org/r/20200302231146.15530-2-atish.patra@wdc.com
parent 2ef1cb76
...@@ -157,5 +157,5 @@ void __init trap_init(void) ...@@ -157,5 +157,5 @@ void __init trap_init(void)
/* Set the exception vector address */ /* Set the exception vector address */
csr_write(CSR_TVEC, &handle_exception); csr_write(CSR_TVEC, &handle_exception);
/* Enable interrupts */ /* Enable interrupts */
csr_write(CSR_IE, IE_SIE | IE_EIE); csr_write(CSR_IE, IE_SIE);
} }
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
* Copyright (C) 2018 Christoph Hellwig * Copyright (C) 2018 Christoph Hellwig
*/ */
#define pr_fmt(fmt) "plic: " fmt #define pr_fmt(fmt) "plic: " fmt
#include <linux/cpu.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/irq.h> #include <linux/irq.h>
...@@ -55,6 +56,9 @@ ...@@ -55,6 +56,9 @@
#define CONTEXT_THRESHOLD 0x00 #define CONTEXT_THRESHOLD 0x00
#define CONTEXT_CLAIM 0x04 #define CONTEXT_CLAIM 0x04
#define PLIC_DISABLE_THRESHOLD 0xf
#define PLIC_ENABLE_THRESHOLD 0
static void __iomem *plic_regs; static void __iomem *plic_regs;
struct plic_handler { struct plic_handler {
...@@ -230,6 +234,32 @@ static int plic_find_hart_id(struct device_node *node) ...@@ -230,6 +234,32 @@ static int plic_find_hart_id(struct device_node *node)
return -1; return -1;
} }
static void plic_set_threshold(struct plic_handler *handler, u32 threshold)
{
/* priority must be > threshold to trigger an interrupt */
writel(threshold, handler->hart_base + CONTEXT_THRESHOLD);
}
static int plic_dying_cpu(unsigned int cpu)
{
struct plic_handler *handler = this_cpu_ptr(&plic_handlers);
csr_clear(CSR_IE, IE_EIE);
plic_set_threshold(handler, PLIC_DISABLE_THRESHOLD);
return 0;
}
static int plic_starting_cpu(unsigned int cpu)
{
struct plic_handler *handler = this_cpu_ptr(&plic_handlers);
csr_set(CSR_IE, IE_EIE);
plic_set_threshold(handler, PLIC_ENABLE_THRESHOLD);
return 0;
}
static int __init plic_init(struct device_node *node, static int __init plic_init(struct device_node *node,
struct device_node *parent) struct device_node *parent)
{ {
...@@ -267,7 +297,6 @@ static int __init plic_init(struct device_node *node, ...@@ -267,7 +297,6 @@ static int __init plic_init(struct device_node *node,
struct plic_handler *handler; struct plic_handler *handler;
irq_hw_number_t hwirq; irq_hw_number_t hwirq;
int cpu, hartid; int cpu, hartid;
u32 threshold = 0;
if (of_irq_parse_one(node, i, &parent)) { if (of_irq_parse_one(node, i, &parent)) {
pr_err("failed to parse parent for context %d.\n", i); pr_err("failed to parse parent for context %d.\n", i);
...@@ -301,7 +330,7 @@ static int __init plic_init(struct device_node *node, ...@@ -301,7 +330,7 @@ static int __init plic_init(struct device_node *node,
handler = per_cpu_ptr(&plic_handlers, cpu); handler = per_cpu_ptr(&plic_handlers, cpu);
if (handler->present) { if (handler->present) {
pr_warn("handler already present for context %d.\n", i); pr_warn("handler already present for context %d.\n", i);
threshold = 0xffffffff; plic_set_threshold(handler, PLIC_DISABLE_THRESHOLD);
goto done; goto done;
} }
...@@ -313,13 +342,14 @@ static int __init plic_init(struct device_node *node, ...@@ -313,13 +342,14 @@ static int __init plic_init(struct device_node *node,
plic_regs + ENABLE_BASE + i * ENABLE_PER_HART; plic_regs + ENABLE_BASE + i * ENABLE_PER_HART;
done: done:
/* priority must be > threshold to trigger an interrupt */
writel(threshold, handler->hart_base + CONTEXT_THRESHOLD);
for (hwirq = 1; hwirq <= nr_irqs; hwirq++) for (hwirq = 1; hwirq <= nr_irqs; hwirq++)
plic_toggle(handler, hwirq, 0); plic_toggle(handler, hwirq, 0);
nr_handlers++; nr_handlers++;
} }
cpuhp_setup_state(CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING,
"irqchip/sifive/plic:starting",
plic_starting_cpu, plic_dying_cpu);
pr_info("mapped %d interrupts with %d handlers for %d contexts.\n", pr_info("mapped %d interrupts with %d handlers for %d contexts.\n",
nr_irqs, nr_handlers, nr_contexts); nr_irqs, nr_handlers, nr_contexts);
set_handle_irq(plic_handle_irq); set_handle_irq(plic_handle_irq);
......
...@@ -102,6 +102,7 @@ enum cpuhp_state { ...@@ -102,6 +102,7 @@ enum cpuhp_state {
CPUHP_AP_IRQ_ARMADA_XP_STARTING, CPUHP_AP_IRQ_ARMADA_XP_STARTING,
CPUHP_AP_IRQ_BCM2836_STARTING, CPUHP_AP_IRQ_BCM2836_STARTING,
CPUHP_AP_IRQ_MIPS_GIC_STARTING, CPUHP_AP_IRQ_MIPS_GIC_STARTING,
CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING,
CPUHP_AP_ARM_MVEBU_COHERENCY, CPUHP_AP_ARM_MVEBU_COHERENCY,
CPUHP_AP_MICROCODE_LOADER, CPUHP_AP_MICROCODE_LOADER,
CPUHP_AP_PERF_X86_AMD_UNCORE_STARTING, CPUHP_AP_PERF_X86_AMD_UNCORE_STARTING,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment