Merge branch irq/plic-edge-fixes into irq/irqchip-next
* irq/plic-edge-fixes: : . : Work around broken PLIC implementations that deal pretty : badly with edge-triggered interrupts. Flag two implementations : as affected. : . irqchip/sifive-plic: Fix T-HEAD PLIC edge trigger handling dt-bindings: interrupt-controller: Require trigger type for T-HEAD PLIC irqchip/sifive-plic: Add support for Renesas RZ/Five SoC dt-bindings: interrupt-controller: sifive,plic: Document Renesas RZ/Five SoC Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
commit
ee4aae5777
|
@ -26,9 +26,14 @@ description:
|
|||
with priority below this threshold will not cause the PLIC to raise its
|
||||
interrupt line leading to the context.
|
||||
|
||||
While the PLIC supports both edge-triggered and level-triggered interrupts,
|
||||
interrupt handlers are oblivious to this distinction and therefore it is not
|
||||
specified in the PLIC device-tree binding.
|
||||
The PLIC supports both edge-triggered and level-triggered interrupts. For
|
||||
edge-triggered interrupts, the RISC-V PLIC spec allows two responses to edges
|
||||
seen while an interrupt handler is active; the PLIC may either queue them or
|
||||
ignore them. In the first case, handlers are oblivious to the trigger type, so
|
||||
it is not included in the interrupt specifier. In the second case, software
|
||||
needs to know the trigger type, so it can reorder the interrupt flow to avoid
|
||||
missing interrupts. This special handling is needed by at least the Renesas
|
||||
RZ/Five SoC (AX45MP AndesCore with a NCEPLIC100) and the T-HEAD C900 PLIC.
|
||||
|
||||
While the RISC-V ISA doesn't specify a memory layout for the PLIC, the
|
||||
"sifive,plic-1.0.0" device is a concrete implementation of the PLIC that
|
||||
|
@ -47,6 +52,10 @@ maintainers:
|
|||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,r9a07g043-plic
|
||||
- const: andestech,nceplic100
|
||||
- items:
|
||||
- enum:
|
||||
- sifive,fu540-c000-plic
|
||||
|
@ -64,8 +73,7 @@ properties:
|
|||
'#address-cells':
|
||||
const: 0
|
||||
|
||||
'#interrupt-cells':
|
||||
const: 1
|
||||
'#interrupt-cells': true
|
||||
|
||||
interrupt-controller: true
|
||||
|
||||
|
@ -82,6 +90,12 @@ properties:
|
|||
description:
|
||||
Specifies how many external interrupts are supported by this controller.
|
||||
|
||||
clocks: true
|
||||
|
||||
power-domains: true
|
||||
|
||||
resets: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- '#address-cells'
|
||||
|
@ -91,6 +105,47 @@ required:
|
|||
- interrupts-extended
|
||||
- riscv,ndev
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- andestech,nceplic100
|
||||
- thead,c900-plic
|
||||
|
||||
then:
|
||||
properties:
|
||||
'#interrupt-cells':
|
||||
const: 2
|
||||
|
||||
else:
|
||||
properties:
|
||||
'#interrupt-cells':
|
||||
const: 1
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: renesas,r9a07g043-plic
|
||||
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- clocks
|
||||
- power-domains
|
||||
- resets
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
|
|
|
@ -60,10 +60,13 @@
|
|||
#define PLIC_DISABLE_THRESHOLD 0x7
|
||||
#define PLIC_ENABLE_THRESHOLD 0
|
||||
|
||||
#define PLIC_QUIRK_EDGE_INTERRUPT 0
|
||||
|
||||
struct plic_priv {
|
||||
struct cpumask lmask;
|
||||
struct irq_domain *irqdomain;
|
||||
void __iomem *regs;
|
||||
unsigned long plic_quirks;
|
||||
};
|
||||
|
||||
struct plic_handler {
|
||||
|
@ -81,6 +84,8 @@ static int plic_parent_irq __ro_after_init;
|
|||
static bool plic_cpuhp_setup_done __ro_after_init;
|
||||
static DEFINE_PER_CPU(struct plic_handler, plic_handlers);
|
||||
|
||||
static int plic_irq_set_type(struct irq_data *d, unsigned int type);
|
||||
|
||||
static void __plic_toggle(void __iomem *enable_base, int hwirq, int enable)
|
||||
{
|
||||
u32 __iomem *reg = enable_base + (hwirq / 32) * sizeof(u32);
|
||||
|
@ -176,6 +181,17 @@ static void plic_irq_eoi(struct irq_data *d)
|
|||
}
|
||||
}
|
||||
|
||||
static struct irq_chip plic_edge_chip = {
|
||||
.name = "SiFive PLIC",
|
||||
.irq_ack = plic_irq_eoi,
|
||||
.irq_mask = plic_irq_mask,
|
||||
.irq_unmask = plic_irq_unmask,
|
||||
#ifdef CONFIG_SMP
|
||||
.irq_set_affinity = plic_set_affinity,
|
||||
#endif
|
||||
.irq_set_type = plic_irq_set_type,
|
||||
};
|
||||
|
||||
static struct irq_chip plic_chip = {
|
||||
.name = "SiFive PLIC",
|
||||
.irq_mask = plic_irq_mask,
|
||||
|
@ -184,8 +200,32 @@ static struct irq_chip plic_chip = {
|
|||
#ifdef CONFIG_SMP
|
||||
.irq_set_affinity = plic_set_affinity,
|
||||
#endif
|
||||
.irq_set_type = plic_irq_set_type,
|
||||
};
|
||||
|
||||
static int plic_irq_set_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
struct plic_priv *priv = irq_data_get_irq_chip_data(d);
|
||||
|
||||
if (!test_bit(PLIC_QUIRK_EDGE_INTERRUPT, &priv->plic_quirks))
|
||||
return IRQ_SET_MASK_OK_NOCOPY;
|
||||
|
||||
switch (type) {
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
irq_set_chip_handler_name_locked(d, &plic_edge_chip,
|
||||
handle_edge_irq, NULL);
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
irq_set_chip_handler_name_locked(d, &plic_chip,
|
||||
handle_fasteoi_irq, NULL);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return IRQ_SET_MASK_OK;
|
||||
}
|
||||
|
||||
static int plic_irqdomain_map(struct irq_domain *d, unsigned int irq,
|
||||
irq_hw_number_t hwirq)
|
||||
{
|
||||
|
@ -198,6 +238,19 @@ static int plic_irqdomain_map(struct irq_domain *d, unsigned int irq,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int plic_irq_domain_translate(struct irq_domain *d,
|
||||
struct irq_fwspec *fwspec,
|
||||
unsigned long *hwirq,
|
||||
unsigned int *type)
|
||||
{
|
||||
struct plic_priv *priv = d->host_data;
|
||||
|
||||
if (test_bit(PLIC_QUIRK_EDGE_INTERRUPT, &priv->plic_quirks))
|
||||
return irq_domain_translate_twocell(d, fwspec, hwirq, type);
|
||||
|
||||
return irq_domain_translate_onecell(d, fwspec, hwirq, type);
|
||||
}
|
||||
|
||||
static int plic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
|
||||
unsigned int nr_irqs, void *arg)
|
||||
{
|
||||
|
@ -206,7 +259,7 @@ static int plic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
|
|||
unsigned int type;
|
||||
struct irq_fwspec *fwspec = arg;
|
||||
|
||||
ret = irq_domain_translate_onecell(domain, fwspec, &hwirq, &type);
|
||||
ret = plic_irq_domain_translate(domain, fwspec, &hwirq, &type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -220,7 +273,7 @@ static int plic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
|
|||
}
|
||||
|
||||
static const struct irq_domain_ops plic_irqdomain_ops = {
|
||||
.translate = irq_domain_translate_onecell,
|
||||
.translate = plic_irq_domain_translate,
|
||||
.alloc = plic_irq_domain_alloc,
|
||||
.free = irq_domain_free_irqs_top,
|
||||
};
|
||||
|
@ -281,8 +334,9 @@ static int plic_starting_cpu(unsigned int cpu)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __init plic_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
static int __init __plic_init(struct device_node *node,
|
||||
struct device_node *parent,
|
||||
unsigned long plic_quirks)
|
||||
{
|
||||
int error = 0, nr_contexts, nr_handlers = 0, i;
|
||||
u32 nr_irqs;
|
||||
|
@ -293,6 +347,8 @@ static int __init plic_init(struct device_node *node,
|
|||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->plic_quirks = plic_quirks;
|
||||
|
||||
priv->regs = of_iomap(node, 0);
|
||||
if (WARN_ON(!priv->regs)) {
|
||||
error = -EIO;
|
||||
|
@ -410,6 +466,20 @@ out_free_priv:
|
|||
return error;
|
||||
}
|
||||
|
||||
static int __init plic_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
return __plic_init(node, parent, 0);
|
||||
}
|
||||
|
||||
IRQCHIP_DECLARE(sifive_plic, "sifive,plic-1.0.0", plic_init);
|
||||
IRQCHIP_DECLARE(riscv_plic0, "riscv,plic0", plic_init); /* for legacy systems */
|
||||
IRQCHIP_DECLARE(thead_c900_plic, "thead,c900-plic", plic_init); /* for firmware driver */
|
||||
|
||||
static int __init plic_edge_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
return __plic_init(node, parent, BIT(PLIC_QUIRK_EDGE_INTERRUPT));
|
||||
}
|
||||
|
||||
IRQCHIP_DECLARE(andestech_nceplic100, "andestech,nceplic100", plic_edge_init);
|
||||
IRQCHIP_DECLARE(thead_c900_plic, "thead,c900-plic", plic_edge_init);
|
||||
|
|
Loading…
Reference in New Issue