Commit 80f77e54 authored by Christoffer Dall's avatar Christoffer Dall
parents a2b83133 722c908f
...@@ -1713,6 +1713,13 @@ ...@@ -1713,6 +1713,13 @@
irqaffinity= [SMP] Set the default irq affinity mask irqaffinity= [SMP] Set the default irq affinity mask
The argument is a cpu list, as described above. The argument is a cpu list, as described above.
irqchip.gicv2_force_probe=
[ARM, ARM64]
Format: <bool>
Force the kernel to look for the second 4kB page
of a GICv2 controller even if the memory range
exposed by the device tree is too small.
irqfixup [HW] irqfixup [HW]
When an interrupt is not handled search all handlers When an interrupt is not handled search all handlers
for it. Intended to get systems with badly broken for it. Intended to get systems with badly broken
......
...@@ -70,6 +70,7 @@ stable kernels. ...@@ -70,6 +70,7 @@ stable kernels.
| | | | | | | | | |
| Hisilicon | Hip0{5,6,7} | #161010101 | HISILICON_ERRATUM_161010101 | | Hisilicon | Hip0{5,6,7} | #161010101 | HISILICON_ERRATUM_161010101 |
| Hisilicon | Hip0{6,7} | #161010701 | N/A | | Hisilicon | Hip0{6,7} | #161010701 | N/A |
| Hisilicon | Hip07 | #161600802 | HISILICON_ERRATUM_161600802 |
| | | | | | | | | |
| Qualcomm Tech. | Falkor v1 | E1003 | QCOM_FALKOR_ERRATUM_1003 | | Qualcomm Tech. | Falkor v1 | E1003 | QCOM_FALKOR_ERRATUM_1003 |
| Qualcomm Tech. | Falkor v1 | E1009 | QCOM_FALKOR_ERRATUM_1009 | | Qualcomm Tech. | Falkor v1 | E1009 | QCOM_FALKOR_ERRATUM_1009 |
......
Amlogic meson GPIO interrupt controller
Meson SoCs contains an interrupt controller which is able to watch the SoC
pads and generate an interrupt on edge or level. The controller is essentially
a 256 pads to 8 GIC interrupt multiplexer, with a filter block to select edge
or level and polarity. It does not expose all 256 mux inputs because the
documentation shows that the upper part is not mapped to any pad. The actual
number of interrupt exposed depends on the SoC.
Required properties:
- compatible : must have "amlogic,meson8-gpio-intc” and either
“amlogic,meson8-gpio-intc” for meson8 SoCs (S802) or
“amlogic,meson8b-gpio-intc” for meson8b SoCs (S805) or
“amlogic,meson-gxbb-gpio-intc” for GXBB SoCs (S905) or
“amlogic,meson-gxl-gpio-intc” for GXL SoCs (S905X, S912)
- interrupt-parent : a phandle to the GIC the interrupts are routed to.
Usually this is provided at the root level of the device tree as it is
common to most of the SoC.
- reg : Specifies base physical address and size of the registers.
- interrupt-controller : Identifies the node as an interrupt controller.
- #interrupt-cells : Specifies the number of cells needed to encode an
interrupt source. The value must be 2.
- meson,channel-interrupts: Array with the 8 upstream hwirq numbers. These
are the hwirqs used on the parent interrupt controller.
Example:
gpio_interrupt: interrupt-controller@9880 {
compatible = "amlogic,meson-gxbb-gpio-intc",
"amlogic,meson-gpio-intc";
reg = <0x0 0x9880 0x0 0x10>;
interrupt-controller;
#interrupt-cells = <2>;
meson,channel-interrupts = <64 65 66 67 68 69 70 71>;
};
...@@ -75,6 +75,10 @@ These nodes must have the following properties: ...@@ -75,6 +75,10 @@ These nodes must have the following properties:
- reg: Specifies the base physical address and size of the ITS - reg: Specifies the base physical address and size of the ITS
registers. registers.
Optional:
- socionext,synquacer-pre-its: (u32, u32) tuple describing the untranslated
address and size of the pre-ITS window.
The main GIC node must contain the appropriate #address-cells, The main GIC node must contain the appropriate #address-cells,
#size-cells and ranges properties for the reg property of all ITS #size-cells and ranges properties for the reg property of all ITS
nodes. nodes.
......
...@@ -2,7 +2,8 @@ Broadcom Generic Level 2 Interrupt Controller ...@@ -2,7 +2,8 @@ Broadcom Generic Level 2 Interrupt Controller
Required properties: Required properties:
- compatible: should be "brcm,l2-intc" - compatible: should be "brcm,l2-intc" for latched interrupt controllers
should be "brcm,bcm7271-l2-intc" for level interrupt controllers
- reg: specifies the base physical address and size of the registers - reg: specifies the base physical address and size of the registers
- interrupt-controller: identifies the node as an interrupt controller - interrupt-controller: identifies the node as an interrupt controller
- #interrupt-cells: specifies the number of cells needed to encode an - #interrupt-cells: specifies the number of cells needed to encode an
......
...@@ -13,6 +13,9 @@ Required properties: ...@@ -13,6 +13,9 @@ Required properties:
- "renesas,irqc-r8a7793" (R-Car M2-N) - "renesas,irqc-r8a7793" (R-Car M2-N)
- "renesas,irqc-r8a7794" (R-Car E2) - "renesas,irqc-r8a7794" (R-Car E2)
- "renesas,intc-ex-r8a7795" (R-Car H3) - "renesas,intc-ex-r8a7795" (R-Car H3)
- "renesas,intc-ex-r8a7796" (R-Car M3-W)
- "renesas,intc-ex-r8a77970" (R-Car V3M)
- "renesas,intc-ex-r8a77995" (R-Car D3)
- #interrupt-cells: has to be <2>: an interrupt index and flags, as defined in - #interrupt-cells: has to be <2>: an interrupt index and flags, as defined in
interrupts.txt in this directory interrupts.txt in this directory
- clocks: Must contain a reference to the functional clock. - clocks: Must contain a reference to the functional clock.
......
...@@ -196,6 +196,11 @@ static inline void gic_write_ctlr(u32 val) ...@@ -196,6 +196,11 @@ static inline void gic_write_ctlr(u32 val)
isb(); isb();
} }
static inline u32 gic_read_ctlr(void)
{
return read_sysreg(ICC_CTLR);
}
static inline void gic_write_grpen1(u32 val) static inline void gic_write_grpen1(u32 val)
{ {
write_sysreg(val, ICC_IGRPEN1); write_sysreg(val, ICC_IGRPEN1);
......
...@@ -539,6 +539,25 @@ config QCOM_QDF2400_ERRATUM_0065 ...@@ -539,6 +539,25 @@ config QCOM_QDF2400_ERRATUM_0065
If unsure, say Y. If unsure, say Y.
config SOCIONEXT_SYNQUACER_PREITS
bool "Socionext Synquacer: Workaround for GICv3 pre-ITS"
default y
help
Socionext Synquacer SoCs implement a separate h/w block to generate
MSI doorbell writes with non-zero values for the device ID.
If unsure, say Y.
config HISILICON_ERRATUM_161600802
bool "Hip07 161600802: Erroneous redistributor VLPI base"
default y
help
The HiSilicon Hip07 SoC usees the wrong redistributor base
when issued ITS commands such as VMOVP and VMAPP, and requires
a 128kB offset to be applied to the target address in this commands.
If unsure, say Y.
endmenu endmenu
......
...@@ -87,6 +87,11 @@ static inline void gic_write_ctlr(u32 val) ...@@ -87,6 +87,11 @@ static inline void gic_write_ctlr(u32 val)
isb(); isb();
} }
static inline u32 gic_read_ctlr(void)
{
return read_sysreg_s(SYS_ICC_CTLR_EL1);
}
static inline void gic_write_grpen1(u32 val) static inline void gic_write_grpen1(u32 val)
{ {
write_sysreg_s(val, SYS_ICC_IGRPEN1_EL1); write_sysreg_s(val, SYS_ICC_IGRPEN1_EL1);
......
...@@ -41,8 +41,8 @@ extern int mp_irqdomain_alloc(struct irq_domain *domain, unsigned int virq, ...@@ -41,8 +41,8 @@ extern int mp_irqdomain_alloc(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs, void *arg); unsigned int nr_irqs, void *arg);
extern void mp_irqdomain_free(struct irq_domain *domain, unsigned int virq, extern void mp_irqdomain_free(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs); unsigned int nr_irqs);
extern void mp_irqdomain_activate(struct irq_domain *domain, extern int mp_irqdomain_activate(struct irq_domain *domain,
struct irq_data *irq_data); struct irq_data *irq_data, bool early);
extern void mp_irqdomain_deactivate(struct irq_domain *domain, extern void mp_irqdomain_deactivate(struct irq_domain *domain,
struct irq_data *irq_data); struct irq_data *irq_data);
extern int mp_irqdomain_ioapic_idx(struct irq_domain *domain); extern int mp_irqdomain_ioapic_idx(struct irq_domain *domain);
......
...@@ -112,8 +112,8 @@ static void htirq_domain_free(struct irq_domain *domain, unsigned int virq, ...@@ -112,8 +112,8 @@ static void htirq_domain_free(struct irq_domain *domain, unsigned int virq,
irq_domain_free_irqs_top(domain, virq, nr_irqs); irq_domain_free_irqs_top(domain, virq, nr_irqs);
} }
static void htirq_domain_activate(struct irq_domain *domain, static int htirq_domain_activate(struct irq_domain *domain,
struct irq_data *irq_data) struct irq_data *irq_data, bool early)
{ {
struct ht_irq_msg msg; struct ht_irq_msg msg;
struct irq_cfg *cfg = irqd_cfg(irq_data); struct irq_cfg *cfg = irqd_cfg(irq_data);
...@@ -132,6 +132,7 @@ static void htirq_domain_activate(struct irq_domain *domain, ...@@ -132,6 +132,7 @@ static void htirq_domain_activate(struct irq_domain *domain,
HT_IRQ_LOW_MT_ARBITRATED) | HT_IRQ_LOW_MT_ARBITRATED) |
HT_IRQ_LOW_IRQ_MASKED; HT_IRQ_LOW_IRQ_MASKED;
write_ht_irq_msg(irq_data->irq, &msg); write_ht_irq_msg(irq_data->irq, &msg);
return 0;
} }
static void htirq_domain_deactivate(struct irq_domain *domain, static void htirq_domain_deactivate(struct irq_domain *domain,
......
...@@ -2096,7 +2096,7 @@ static inline void __init check_timer(void) ...@@ -2096,7 +2096,7 @@ static inline void __init check_timer(void)
unmask_ioapic_irq(irq_get_irq_data(0)); unmask_ioapic_irq(irq_get_irq_data(0));
} }
irq_domain_deactivate_irq(irq_data); irq_domain_deactivate_irq(irq_data);
irq_domain_activate_irq(irq_data); irq_domain_activate_irq(irq_data, false);
if (timer_irq_works()) { if (timer_irq_works()) {
if (disable_timer_pin_1 > 0) if (disable_timer_pin_1 > 0)
clear_IO_APIC_pin(0, pin1); clear_IO_APIC_pin(0, pin1);
...@@ -2118,7 +2118,7 @@ static inline void __init check_timer(void) ...@@ -2118,7 +2118,7 @@ static inline void __init check_timer(void)
*/ */
replace_pin_at_irq_node(data, node, apic1, pin1, apic2, pin2); replace_pin_at_irq_node(data, node, apic1, pin1, apic2, pin2);
irq_domain_deactivate_irq(irq_data); irq_domain_deactivate_irq(irq_data);
irq_domain_activate_irq(irq_data); irq_domain_activate_irq(irq_data, false);
legacy_pic->unmask(0); legacy_pic->unmask(0);
if (timer_irq_works()) { if (timer_irq_works()) {
apic_printk(APIC_QUIET, KERN_INFO "....... works.\n"); apic_printk(APIC_QUIET, KERN_INFO "....... works.\n");
...@@ -2977,8 +2977,8 @@ void mp_irqdomain_free(struct irq_domain *domain, unsigned int virq, ...@@ -2977,8 +2977,8 @@ void mp_irqdomain_free(struct irq_domain *domain, unsigned int virq,
irq_domain_free_irqs_top(domain, virq, nr_irqs); irq_domain_free_irqs_top(domain, virq, nr_irqs);
} }
void mp_irqdomain_activate(struct irq_domain *domain, int mp_irqdomain_activate(struct irq_domain *domain,
struct irq_data *irq_data) struct irq_data *irq_data, bool early)
{ {
unsigned long flags; unsigned long flags;
struct irq_pin_list *entry; struct irq_pin_list *entry;
...@@ -2988,6 +2988,7 @@ void mp_irqdomain_activate(struct irq_domain *domain, ...@@ -2988,6 +2988,7 @@ void mp_irqdomain_activate(struct irq_domain *domain,
for_each_irq_pin(entry, data->irq_2_pin) for_each_irq_pin(entry, data->irq_2_pin)
__ioapic_write_entry(entry->apic, entry->pin, data->entry); __ioapic_write_entry(entry->apic, entry->pin, data->entry);
raw_spin_unlock_irqrestore(&ioapic_lock, flags); raw_spin_unlock_irqrestore(&ioapic_lock, flags);
return 0;
} }
void mp_irqdomain_deactivate(struct irq_domain *domain, void mp_irqdomain_deactivate(struct irq_domain *domain,
......
...@@ -127,10 +127,11 @@ static void uv_domain_free(struct irq_domain *domain, unsigned int virq, ...@@ -127,10 +127,11 @@ static void uv_domain_free(struct irq_domain *domain, unsigned int virq,
* Re-target the irq to the specified CPU and enable the specified MMR located * Re-target the irq to the specified CPU and enable the specified MMR located
* on the specified blade to allow the sending of MSIs to the specified CPU. * on the specified blade to allow the sending of MSIs to the specified CPU.
*/ */
static void uv_domain_activate(struct irq_domain *domain, static int uv_domain_activate(struct irq_domain *domain,
struct irq_data *irq_data) struct irq_data *irq_data, bool early)
{ {
uv_program_mmr(irqd_cfg(irq_data), irq_data->chip_data); uv_program_mmr(irqd_cfg(irq_data), irq_data->chip_data);
return 0;
} }
/* /*
......
...@@ -140,8 +140,9 @@ static int xgene_gpio_sb_to_irq(struct gpio_chip *gc, u32 gpio) ...@@ -140,8 +140,9 @@ static int xgene_gpio_sb_to_irq(struct gpio_chip *gc, u32 gpio)
return irq_create_fwspec_mapping(&fwspec); return irq_create_fwspec_mapping(&fwspec);
} }
static void xgene_gpio_sb_domain_activate(struct irq_domain *d, static int xgene_gpio_sb_domain_activate(struct irq_domain *d,
struct irq_data *irq_data) struct irq_data *irq_data,
bool early)
{ {
struct xgene_gpio_sb *priv = d->host_data; struct xgene_gpio_sb *priv = d->host_data;
u32 gpio = HWIRQ_TO_GPIO(priv, irq_data->hwirq); u32 gpio = HWIRQ_TO_GPIO(priv, irq_data->hwirq);
...@@ -150,11 +151,12 @@ static void xgene_gpio_sb_domain_activate(struct irq_domain *d, ...@@ -150,11 +151,12 @@ static void xgene_gpio_sb_domain_activate(struct irq_domain *d,
dev_err(priv->gc.parent, dev_err(priv->gc.parent,
"Unable to configure XGene GPIO standby pin %d as IRQ\n", "Unable to configure XGene GPIO standby pin %d as IRQ\n",
gpio); gpio);
return; return -ENOSPC;
} }
xgene_gpio_set_bit(&priv->gc, priv->regs + MPA_GPIO_SEL_LO, xgene_gpio_set_bit(&priv->gc, priv->regs + MPA_GPIO_SEL_LO,
gpio * 2, 1); gpio * 2, 1);
return 0;
} }
static void xgene_gpio_sb_domain_deactivate(struct irq_domain *d, static void xgene_gpio_sb_domain_deactivate(struct irq_domain *d,
......
...@@ -4170,8 +4170,8 @@ static void irq_remapping_free(struct irq_domain *domain, unsigned int virq, ...@@ -4170,8 +4170,8 @@ static void irq_remapping_free(struct irq_domain *domain, unsigned int virq,
irq_domain_free_irqs_common(domain, virq, nr_irqs); irq_domain_free_irqs_common(domain, virq, nr_irqs);
} }
static void irq_remapping_activate(struct irq_domain *domain, static int irq_remapping_activate(struct irq_domain *domain,
struct irq_data *irq_data) struct irq_data *irq_data, bool early)
{ {
struct amd_ir_data *data = irq_data->chip_data; struct amd_ir_data *data = irq_data->chip_data;
struct irq_2_irte *irte_info = &data->irq_2_irte; struct irq_2_irte *irte_info = &data->irq_2_irte;
...@@ -4180,6 +4180,7 @@ static void irq_remapping_activate(struct irq_domain *domain, ...@@ -4180,6 +4180,7 @@ static void irq_remapping_activate(struct irq_domain *domain,
if (iommu) if (iommu)
iommu->irte_ops->activate(data->entry, irte_info->devid, iommu->irte_ops->activate(data->entry, irte_info->devid,
irte_info->index); irte_info->index);
return 0;
} }
static void irq_remapping_deactivate(struct irq_domain *domain, static void irq_remapping_deactivate(struct irq_domain *domain,
......
...@@ -1389,12 +1389,13 @@ static void intel_irq_remapping_free(struct irq_domain *domain, ...@@ -1389,12 +1389,13 @@ static void intel_irq_remapping_free(struct irq_domain *domain,
irq_domain_free_irqs_common(domain, virq, nr_irqs); irq_domain_free_irqs_common(domain, virq, nr_irqs);
} }
static void intel_irq_remapping_activate(struct irq_domain *domain, static int intel_irq_remapping_activate(struct irq_domain *domain,
struct irq_data *irq_data) struct irq_data *irq_data, bool early)
{ {
struct intel_ir_data *data = irq_data->chip_data; struct intel_ir_data *data = irq_data->chip_data;
modify_irte(&data->irq_2_iommu, &data->irte_entry); modify_irte(&data->irq_2_iommu, &data->irte_entry);
return 0;
} }
static void intel_irq_remapping_deactivate(struct irq_domain *domain, static void intel_irq_remapping_deactivate(struct irq_domain *domain,
......
menu "IRQ chip support"
config IRQCHIP config IRQCHIP
def_bool y def_bool y
depends on OF_IRQ depends on OF_IRQ
...@@ -321,3 +323,13 @@ config IRQ_UNIPHIER_AIDET ...@@ -321,3 +323,13 @@ config IRQ_UNIPHIER_AIDET
select IRQ_DOMAIN_HIERARCHY select IRQ_DOMAIN_HIERARCHY
help help
Support for the UniPhier AIDET (ARM Interrupt Detector). Support for the UniPhier AIDET (ARM Interrupt Detector).
config MESON_IRQ_GPIO
bool "Meson GPIO Interrupt Multiplexer"
depends on ARCH_MESON
select IRQ_DOMAIN
select IRQ_DOMAIN_HIERARCHY
help
Support Meson SoC Family GPIO Interrupt Multiplexer
endmenu
...@@ -79,3 +79,4 @@ obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o irq-aspeed-i2c-ic.o ...@@ -79,3 +79,4 @@ obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o irq-aspeed-i2c-ic.o
obj-$(CONFIG_STM32_EXTI) += irq-stm32-exti.o obj-$(CONFIG_STM32_EXTI) += irq-stm32-exti.o
obj-$(CONFIG_QCOM_IRQ_COMBINER) += qcom-irq-combiner.o obj-$(CONFIG_QCOM_IRQ_COMBINER) += qcom-irq-combiner.o
obj-$(CONFIG_IRQ_UNIPHIER_AIDET) += irq-uniphier-aidet.o obj-$(CONFIG_IRQ_UNIPHIER_AIDET) += irq-uniphier-aidet.o
obj-$(CONFIG_MESON_IRQ_GPIO) += irq-meson-gpio.o
...@@ -76,8 +76,8 @@ static int __init aspeed_i2c_ic_of_init(struct device_node *node, ...@@ -76,8 +76,8 @@ static int __init aspeed_i2c_ic_of_init(struct device_node *node,
return -ENOMEM; return -ENOMEM;
i2c_ic->base = of_iomap(node, 0); i2c_ic->base = of_iomap(node, 0);
if (IS_ERR(i2c_ic->base)) { if (!i2c_ic->base) {
ret = PTR_ERR(i2c_ic->base); ret = -ENOMEM;
goto err_free_ic; goto err_free_ic;
} }
......
/* /*
* Generic Broadcom Set Top Box Level 2 Interrupt controller driver * Generic Broadcom Set Top Box Level 2 Interrupt controller driver
* *
* Copyright (C) 2014 Broadcom Corporation * Copyright (C) 2014-2017 Broadcom
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
...@@ -31,35 +31,82 @@ ...@@ -31,35 +31,82 @@
#include <linux/irqchip.h> #include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h> #include <linux/irqchip/chained_irq.h>
/* Register offsets in the L2 interrupt controller */ struct brcmstb_intc_init_params {
#define CPU_STATUS 0x00 irq_flow_handler_t handler;
#define CPU_SET 0x04 int cpu_status;
#define CPU_CLEAR 0x08 int cpu_clear;
#define CPU_MASK_STATUS 0x0c int cpu_mask_status;
#define CPU_MASK_SET 0x10 int cpu_mask_set;
#define CPU_MASK_CLEAR 0x14 int cpu_mask_clear;
};
/* Register offsets in the L2 latched interrupt controller */
static const struct brcmstb_intc_init_params l2_edge_intc_init = {
.handler = handle_edge_irq,
.cpu_status = 0x00,
.cpu_clear = 0x08,
.cpu_mask_status = 0x0c,
.cpu_mask_set = 0x10,
.cpu_mask_clear = 0x14
};
/* Register offsets in the L2 level interrupt controller */
static const struct brcmstb_intc_init_params l2_lvl_intc_init = {
.handler = handle_level_irq,
.cpu_status = 0x00,
.cpu_clear = -1, /* Register not present */
.cpu_mask_status = 0x04,
.cpu_mask_set = 0x08,
.cpu_mask_clear = 0x0C
};
/* L2 intc private data structure */ /* L2 intc private data structure */
struct brcmstb_l2_intc_data { struct brcmstb_l2_intc_data {
int parent_irq;
void __iomem *base;
struct irq_domain *domain; struct irq_domain *domain;
struct irq_chip_generic *gc;
int status_offset;
int mask_offset;
bool can_wake; bool can_wake;
u32 saved_mask; /* for suspend/resume */ u32 saved_mask; /* for suspend/resume */
}; };
/**
* brcmstb_l2_mask_and_ack - Mask and ack pending interrupt
* @d: irq_data
*
* Chip has separate enable/disable registers instead of a single mask
* register and pending interrupt is acknowledged by setting a bit.
*
* Note: This function is generic and could easily be added to the
* generic irqchip implementation if there ever becomes a will to do so.
* Perhaps with a name like irq_gc_mask_disable_and_ack_set().
*
* e.g.: https://patchwork.kernel.org/patch/9831047/
*/
static void brcmstb_l2_mask_and_ack(struct irq_data *d)
{
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct irq_chip_type *ct = irq_data_get_chip_type(d);
u32 mask = d->mask;
irq_gc_lock(gc);
irq_reg_writel(gc, mask, ct->regs.disable);
*ct->mask_cache &= ~mask;
irq_reg_writel(gc, mask, ct->regs.ack);
irq_gc_unlock(gc);
}
static void brcmstb_l2_intc_irq_handle(struct irq_desc *desc) static void brcmstb_l2_intc_irq_handle(struct irq_desc *desc)
{ {
struct brcmstb_l2_intc_data *b = irq_desc_get_handler_data(desc); struct brcmstb_l2_intc_data *b = irq_desc_get_handler_data(desc);
struct irq_chip_generic *gc = irq_get_domain_generic_chip(b->domain, 0);
struct irq_chip *chip = irq_desc_get_chip(desc); struct irq_chip *chip = irq_desc_get_chip(desc);
unsigned int irq; unsigned int irq;
u32 status; u32 status;
chained_irq_enter(chip, desc); chained_irq_enter(chip, desc);
status = irq_reg_readl(gc, CPU_STATUS) & status = irq_reg_readl(b->gc, b->status_offset) &
~(irq_reg_readl(gc, CPU_MASK_STATUS)); ~(irq_reg_readl(b->gc, b->mask_offset));
if (status == 0) { if (status == 0) {
raw_spin_lock(&desc->lock); raw_spin_lock(&desc->lock);
...@@ -70,10 +117,8 @@ static void brcmstb_l2_intc_irq_handle(struct irq_desc *desc) ...@@ -70,10 +117,8 @@ static void brcmstb_l2_intc_irq_handle(struct irq_desc *desc)
do { do {
irq = ffs(status) - 1; irq = ffs(status) - 1;
/* ack at our level */
irq_reg_writel(gc, 1 << irq, CPU_CLEAR);
status &= ~(1 << irq); status &= ~(1 << irq);
generic_handle_irq(irq_find_mapping(b->domain, irq)); generic_handle_irq(irq_linear_revmap(b->domain, irq));
} while (status); } while (status);
out: out:
chained_irq_exit(chip, desc); chained_irq_exit(chip, desc);
...@@ -82,16 +127,17 @@ static void brcmstb_l2_intc_irq_handle(struct irq_desc *desc) ...@@ -82,16 +127,17 @@ static void brcmstb_l2_intc_irq_handle(struct irq_desc *desc)
static void brcmstb_l2_intc_suspend(struct irq_data *d) static void brcmstb_l2_intc_suspend(struct irq_data *d)
{ {
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct irq_chip_type *ct = irq_data_get_chip_type(d);
struct brcmstb_l2_intc_data *b = gc->private; struct brcmstb_l2_intc_data *b = gc->private;
irq_gc_lock(gc); irq_gc_lock(gc);
/* Save the current mask */ /* Save the current mask */
b->saved_mask = irq_reg_readl(gc, CPU_MASK_STATUS); b->saved_mask = irq_reg_readl(gc, ct->regs.mask);
if (b->can_wake) { if (b->can_wake) {
/* Program the wakeup mask */ /* Program the wakeup mask */
irq_reg_writel(gc, ~gc->wake_active, CPU_MASK_SET); irq_reg_writel(gc, ~gc->wake_active, ct->regs.disable);
irq_reg_writel(gc, gc->wake_active, CPU_MASK_CLEAR); irq_reg_writel(gc, gc->wake_active, ct->regs.enable);
} }
irq_gc_unlock(gc); irq_gc_unlock(gc);
} }
...@@ -99,49 +145,56 @@ static void brcmstb_l2_intc_suspend(struct irq_data *d) ...@@ -99,49 +145,56 @@ static void brcmstb_l2_intc_suspend(struct irq_data *d)
static void brcmstb_l2_intc_resume(struct irq_data *d) static void brcmstb_l2_intc_resume(struct irq_data *d)
{ {
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct irq_chip_type *ct = irq_data_get_chip_type(d);
struct brcmstb_l2_intc_data *b = gc->private; struct brcmstb_l2_intc_data *b = gc->private;
irq_gc_lock(gc); irq_gc_lock(gc);
/* Clear unmasked non-wakeup interrupts */ if (ct->chip.irq_ack) {
irq_reg_writel(gc, ~b->saved_mask & ~gc->wake_active, CPU_CLEAR); /* Clear unmasked non-wakeup interrupts */
irq_reg_writel(gc, ~b->saved_mask & ~gc->wake_active,
ct->regs.ack);
}
/* Restore the saved mask */ /* Restore the saved mask */
irq_reg_writel(gc, b->saved_mask, CPU_MASK_SET); irq_reg_writel(gc, b->saved_mask, ct->regs.disable);
irq_reg_writel(gc, ~b->saved_mask, CPU_MASK_CLEAR); irq_reg_writel(gc, ~b->saved_mask, ct->regs.enable);
irq_gc_unlock(gc); irq_gc_unlock(gc);
} }
static int __init brcmstb_l2_intc_of_init(struct device_node *np, static int __init brcmstb_l2_intc_of_init(struct device_node *np,
struct device_node *parent) struct device_node *parent,
const struct brcmstb_intc_init_params
*init_params)
{ {
unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
struct brcmstb_l2_intc_data *data; struct brcmstb_l2_intc_data *data;
struct irq_chip_generic *gc;
struct irq_chip_type *ct; struct irq_chip_type *ct;
int ret; int ret;
unsigned int flags; unsigned int flags;
int parent_irq;
void __iomem *base;
data = kzalloc(sizeof(*data), GFP_KERNEL); data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
data->base = of_iomap(np, 0); base = of_iomap(np, 0);
if (!data->base) { if (!base) {
pr_err("failed to remap intc L2 registers\n"); pr_err("failed to remap intc L2 registers\n");
ret = -ENOMEM; ret = -ENOMEM;
goto out_free; goto out_free;
} }
/* Disable all interrupts by default */ /* Disable all interrupts by default */
writel(0xffffffff, data->base + CPU_MASK_SET); writel(0xffffffff, base + init_params->cpu_mask_set);
/* Wakeup interrupts may be retained from S5 (cold boot) */ /* Wakeup interrupts may be retained from S5 (cold boot) */
data->can_wake = of_property_read_bool(np, "brcm,irq-can-wake"); data->can_wake = of_property_read_bool(np, "brcm,irq-can-wake");
if (!data->can_wake) if (!data->can_wake && (init_params->cpu_clear >= 0))
writel(0xffffffff, data->base + CPU_CLEAR); writel(0xffffffff, base + init_params->cpu_clear);
data->parent_irq = irq_of_parse_and_map(np, 0); parent_irq = irq_of_parse_and_map(np, 0);
if (!data->parent_irq) { if (!parent_irq) {
pr_err("failed to find parent interrupt\n"); pr_err("failed to find parent interrupt\n");
ret = -EINVAL; ret = -EINVAL;
goto out_unmap; goto out_unmap;
...@@ -163,29 +216,39 @@ static int __init brcmstb_l2_intc_of_init(struct device_node *np, ...@@ -163,29 +216,39 @@ static int __init brcmstb_l2_intc_of_init(struct device_node *np,
/* Allocate a single Generic IRQ chip for this node */ /* Allocate a single Generic IRQ chip for this node */
ret = irq_alloc_domain_generic_chips(data->domain, 32, 1, ret = irq_alloc_domain_generic_chips(data->domain, 32, 1,
np->full_name, handle_edge_irq, clr, 0, flags); np->full_name, init_params->handler, clr, 0, flags);
if (ret) { if (ret) {
pr_err("failed to allocate generic irq chip\n"); pr_err("failed to allocate generic irq chip\n");
goto out_free_domain; goto out_free_domain;
} }
/* Set the IRQ chaining logic */ /* Set the IRQ chaining logic */
irq_set_chained_handler_and_data(data->parent_irq, irq_set_chained_handler_and_data(parent_irq,
brcmstb_l2_intc_irq_handle, data); brcmstb_l2_intc_irq_handle, data);
gc = irq_get_domain_generic_chip(data->domain, 0); data->gc = irq_get_domain_generic_chip(data->domain, 0);
gc->reg_base = data->base; data->gc->reg_base = base;
gc->private = data; data->gc->private = data;
ct = gc->chip_types; data->status_offset = init_params->cpu_status;
data->mask_offset = init_params->cpu_mask_status;
ct->chip.irq_ack = irq_gc_ack_set_bit;
ct->regs.ack = CPU_CLEAR; ct = data->gc->chip_types;
if (init_params->cpu_clear >= 0) {
ct->regs.ack = init_params->cpu_clear;
ct->chip.irq_ack = irq_gc_ack_set_bit;
ct->chip.irq_mask_ack = brcmstb_l2_mask_and_ack;
} else {
/* No Ack - but still slightly more efficient to define this */
ct->chip.irq_mask_ack = irq_gc_mask_disable_reg;
}
ct->chip.irq_mask = irq_gc_mask_disable_reg; ct->chip.irq_mask = irq_gc_mask_disable_reg;
ct->regs.disable = CPU_MASK_SET; ct->regs.disable = init_params->cpu_mask_set;
ct->regs.mask = init_params->cpu_mask_status;
ct->chip.irq_unmask = irq_gc_unmask_enable_reg; ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
ct->regs.enable = CPU_MASK_CLEAR; ct->regs.enable = init_params->cpu_mask_clear;
ct->chip.irq_suspend = brcmstb_l2_intc_suspend; ct->chip.irq_suspend = brcmstb_l2_intc_suspend;
ct->chip.irq_resume = brcmstb_l2_intc_resume; ct->chip.irq_resume = brcmstb_l2_intc_resume;
...@@ -195,21 +258,35 @@ static int __init brcmstb_l2_intc_of_init(struct device_node *np, ...@@ -195,21 +258,35 @@ static int __init brcmstb_l2_intc_of_init(struct device_node *np,
/* This IRQ chip can wake the system, set all child interrupts /* This IRQ chip can wake the system, set all child interrupts
* in wake_enabled mask * in wake_enabled mask
*/ */
gc->wake_enabled = 0xffffffff; data->gc->wake_enabled = 0xffffffff;
ct->chip.irq_set_wake = irq_gc_set_wake; ct->chip.irq_set_wake = irq_gc_set_wake;
} }
pr_info("registered L2 intc (mem: 0x%p, parent irq: %d)\n", pr_info("registered L2 intc (mem: 0x%p, parent irq: %d)\n",
data->base, data->parent_irq); base, parent_irq);
return 0; return 0;
out_free_domain: out_free_domain:
irq_domain_remove(data->domain); irq_domain_remove(data->domain);
out_unmap: out_unmap:
iounmap(data->base); iounmap(base);
out_free: out_free:
kfree(data); kfree(data);
return ret; return ret;
} }
IRQCHIP_DECLARE(brcmstb_l2_intc, "brcm,l2-intc", brcmstb_l2_intc_of_init);
int __init brcmstb_l2_edge_intc_of_init(struct device_node *np,
struct device_node *parent)
{
return brcmstb_l2_intc_of_init(np, parent, &l2_edge_intc_init);
}
IRQCHIP_DECLARE(brcmstb_l2_intc, "brcm,l2-intc", brcmstb_l2_edge_intc_of_init);
int __init brcmstb_l2_lvl_intc_of_init(struct device_node *np,
struct device_node *parent)
{
return brcmstb_l2_intc_of_init(np, parent, &l2_lvl_intc_init);
}
IRQCHIP_DECLARE(bcm7271_l2_intc, "brcm,bcm7271-l2-intc",
brcmstb_l2_lvl_intc_of_init);
...@@ -40,8 +40,9 @@ void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks, ...@@ -40,8 +40,9 @@ void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks,
for (; quirks->desc; quirks++) { for (; quirks->desc; quirks++) {
if (quirks->iidr != (quirks->mask & iidr)) if (quirks->iidr != (quirks->mask & iidr))
continue; continue;
quirks->init(data); if (quirks->init(data))
pr_info("GIC: enabling workaround for %s\n", quirks->desc); pr_info("GIC: enabling workaround for %s\n",
quirks->desc);
} }
} }
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
struct gic_quirk { struct gic_quirk {
const char *desc; const char *desc;
void (*init)(void *data); bool (*init)(void *data);
u32 iidr; u32 iidr;
u32 mask; u32 mask;
}; };
......
This diff is collapsed.
...@@ -55,6 +55,7 @@ struct gic_chip_data { ...@@ -55,6 +55,7 @@ struct gic_chip_data {
struct irq_domain *domain; struct irq_domain *domain;
u64 redist_stride; u64 redist_stride;
u32 nr_redist_regions; u32 nr_redist_regions;
bool has_rss;
unsigned int irq_nr; unsigned int irq_nr;
struct partition_desc *ppi_descs[16]; struct partition_desc *ppi_descs[16];
}; };
...@@ -63,7 +64,9 @@ static struct gic_chip_data gic_data __read_mostly; ...@@ -63,7 +64,9 @@ static struct gic_chip_data gic_data __read_mostly;
static struct static_key supports_deactivate = STATIC_KEY_INIT_TRUE; static struct static_key supports_deactivate = STATIC_KEY_INIT_TRUE;
static struct gic_kvm_info gic_v3_kvm_info; static struct gic_kvm_info gic_v3_kvm_info;
static DEFINE_PER_CPU(bool, has_rss);
#define MPIDR_RS(mpidr) (((mpidr) & 0xF0UL) >> 4)
#define gic_data_rdist() (this_cpu_ptr(gic_data.rdists.rdist)) #define gic_data_rdist() (this_cpu_ptr(gic_data.rdists.rdist))
#define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base) #define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base)
#define gic_data_rdist_sgi_base() (gic_data_rdist_rd_base() + SZ_64K) #define gic_data_rdist_sgi_base() (gic_data_rdist_rd_base() + SZ_64K)
...@@ -526,6 +529,10 @@ static void gic_update_vlpi_properties(void) ...@@ -526,6 +529,10 @@ static void gic_update_vlpi_properties(void)
static void gic_cpu_sys_reg_init(void) static void gic_cpu_sys_reg_init(void)
{ {
int i, cpu = smp_processor_id();
u64 mpidr = cpu_logical_map(cpu);
u64 need_rss = MPIDR_RS(mpidr);
/* /*
* Need to check that the SRE bit has actually been set. If * Need to check that the SRE bit has actually been set. If
* not, it means that SRE is disabled at EL2. We're going to * not, it means that SRE is disabled at EL2. We're going to
...@@ -557,6 +564,30 @@ static void gic_cpu_sys_reg_init(void) ...@@ -557,6 +564,30 @@ static void gic_cpu_sys_reg_init(void)
/* ... and let's hit the road... */ /* ... and let's hit the road... */
gic_write_grpen1(1); gic_write_grpen1(1);
/* Keep the RSS capability status in per_cpu variable */
per_cpu(has_rss, cpu) = !!(gic_read_ctlr() & ICC_CTLR_EL1_RSS);
/* Check all the CPUs have capable of sending SGIs to other CPUs */
for_each_online_cpu(i) {
bool have_rss = per_cpu(has_rss, i) && per_cpu(has_rss, cpu);
need_rss |= MPIDR_RS(cpu_logical_map(i));
if (need_rss && (!have_rss))
pr_crit("CPU%d (%lx) can't SGI CPU%d (%lx), no RSS\n",
cpu, (unsigned long)mpidr,
i, (unsigned long)cpu_logical_map(i));
}
/**
* GIC spec says, when ICC_CTLR_EL1.RSS==1 and GICD_TYPER.RSS==0,
* writing ICC_ASGI1R_EL1 register with RS != 0 is a CONSTRAINED
* UNPREDICTABLE choice of :
* - The write is ignored.
* - The RS field is treated as 0.
*/
if (need_rss && (!gic_data.has_rss))
pr_crit_once("RSS is required but GICD doesn't support it\n");
} }
static int gic_dist_supports_lpis(void) static int gic_dist_supports_lpis(void)
...@@ -591,6 +622,9 @@ static void gic_cpu_init(void) ...@@ -591,6 +622,9 @@ static void gic_cpu_init(void)
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
#define MPIDR_TO_SGI_RS(mpidr) (MPIDR_RS(mpidr) << ICC_SGI1R_RS_SHIFT)
#define MPIDR_TO_SGI_CLUSTER_ID(mpidr) ((mpidr) & ~0xFUL)
static int gic_starting_cpu(unsigned int cpu) static int gic_starting_cpu(unsigned int cpu)
{ {
gic_cpu_init(); gic_cpu_init();
...@@ -605,13 +639,6 @@ static u16 gic_compute_target_list(int *base_cpu, const struct cpumask *mask, ...@@ -605,13 +639,6 @@ static u16 gic_compute_target_list(int *base_cpu, const struct cpumask *mask,
u16 tlist = 0; u16 tlist = 0;
while (cpu < nr_cpu_ids) { while (cpu < nr_cpu_ids) {
/*
* If we ever get a cluster of more than 16 CPUs, just
* scream and skip that CPU.
*/
if (WARN_ON((mpidr & 0xff) >= 16))
goto out;
tlist |= 1 << (mpidr & 0xf); tlist |= 1 << (mpidr & 0xf);
next_cpu = cpumask_next(cpu, mask); next_cpu = cpumask_next(cpu, mask);
...@@ -621,7 +648,7 @@ static u16 gic_compute_target_list(int *base_cpu, const struct cpumask *mask, ...@@ -621,7 +648,7 @@ static u16 gic_compute_target_list(int *base_cpu, const struct cpumask *mask,
mpidr = cpu_logical_map(cpu); mpidr = cpu_logical_map(cpu);
if (cluster_id != (mpidr & ~0xffUL)) { if (cluster_id != MPIDR_TO_SGI_CLUSTER_ID(mpidr)) {
cpu--; cpu--;
goto out; goto out;
} }
...@@ -643,6 +670,7 @@ static void gic_send_sgi(u64 cluster_id, u16 tlist, unsigned int irq) ...@@ -643,6 +670,7 @@ static void gic_send_sgi(u64 cluster_id, u16 tlist, unsigned int irq)
MPIDR_TO_SGI_AFFINITY(cluster_id, 2) | MPIDR_TO_SGI_AFFINITY(cluster_id, 2) |
irq << ICC_SGI1R_SGI_ID_SHIFT | irq << ICC_SGI1R_SGI_ID_SHIFT |
MPIDR_TO_SGI_AFFINITY(cluster_id, 1) | MPIDR_TO_SGI_AFFINITY(cluster_id, 1) |
MPIDR_TO_SGI_RS(cluster_id) |
tlist << ICC_SGI1R_TARGET_LIST_SHIFT); tlist << ICC_SGI1R_TARGET_LIST_SHIFT);
pr_debug("CPU%d: ICC_SGI1R_EL1 %llx\n", smp_processor_id(), val); pr_debug("CPU%d: ICC_SGI1R_EL1 %llx\n", smp_processor_id(), val);
...@@ -663,7 +691,7 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) ...@@ -663,7 +691,7 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
smp_wmb(); smp_wmb();
for_each_cpu(cpu, mask) { for_each_cpu(cpu, mask) {
unsigned long cluster_id = cpu_logical_map(cpu) & ~0xffUL; u64 cluster_id = MPIDR_TO_SGI_CLUSTER_ID(cpu_logical_map(cpu));
u16 tlist; u16 tlist;
tlist = gic_compute_target_list(&cpu, mask, cluster_id); tlist = gic_compute_target_list(&cpu, mask, cluster_id);
...@@ -1007,6 +1035,10 @@ static int __init gic_init_bases(void __iomem *dist_base, ...@@ -1007,6 +1035,10 @@ static int __init gic_init_bases(void __iomem *dist_base,
goto out_free; goto out_free;
} }
gic_data.has_rss = !!(typer & GICD_TYPER_RSS);
pr_info("Distributor has %sRange Selector support\n",
gic_data.has_rss ? "" : "no ");
set_handle_irq(gic_handle_irq); set_handle_irq(gic_handle_irq);
gic_update_vlpi_properties(); gic_update_vlpi_properties();
......
...@@ -1256,6 +1256,19 @@ static void gic_teardown(struct gic_chip_data *gic) ...@@ -1256,6 +1256,19 @@ static void gic_teardown(struct gic_chip_data *gic)
#ifdef CONFIG_OF #ifdef CONFIG_OF
static int gic_cnt __initdata; static int gic_cnt __initdata;
static bool gicv2_force_probe;
static int __init gicv2_force_probe_cfg(char *buf)
{
return strtobool(buf, &gicv2_force_probe);
}
early_param("irqchip.gicv2_force_probe", gicv2_force_probe_cfg);
static bool gic_check_gicv2(void __iomem *base)
{
u32 val = readl_relaxed(base + GIC_CPU_IDENT);
return (val & 0xff0fff) == 0x02043B;
}
static bool gic_check_eoimode(struct device_node *node, void __iomem **base) static bool gic_check_eoimode(struct device_node *node, void __iomem **base)
{ {
...@@ -1265,20 +1278,60 @@ static bool gic_check_eoimode(struct device_node *node, void __iomem **base) ...@@ -1265,20 +1278,60 @@ static bool gic_check_eoimode(struct device_node *node, void __iomem **base)
if (!is_hyp_mode_available()) if (!is_hyp_mode_available())
return false; return false;
if (resource_size(&cpuif_res) < SZ_8K) if (resource_size(&cpuif_res) < SZ_8K) {
return false; void __iomem *alt;
if (resource_size(&cpuif_res) == SZ_128K) { /*
u32 val_low, val_high; * Check for a stupid firmware that only exposes the
* first page of a GICv2.
*/
if (!gic_check_gicv2(*base))
return false;
if (!gicv2_force_probe) {
pr_warn("GIC: GICv2 detected, but range too small and irqchip.gicv2_force_probe not set\n");
return false;
}
alt = ioremap(cpuif_res.start, SZ_8K);
if (!alt)
return false;
if (!gic_check_gicv2(alt + SZ_4K)) {
/*
* The first page was that of a GICv2, and
* the second was *something*. Let's trust it
* to be a GICv2, and update the mapping.
*/
pr_warn("GIC: GICv2 at %pa, but range is too small (broken DT?), assuming 8kB\n",
&cpuif_res.start);
iounmap(*base);
*base = alt;
return true;
}
/*
* We detected *two* initial GICv2 pages in a
* row. Could be a GICv2 aliased over two 64kB
* pages. Update the resource, map the iospace, and
* pray.
*/
iounmap(alt);
alt = ioremap(cpuif_res.start, SZ_128K);
if (!alt)
return false;
pr_warn("GIC: Aliased GICv2 at %pa, trying to find the canonical range over 128kB\n",
&cpuif_res.start);
cpuif_res.end = cpuif_res.start + SZ_128K -1;
iounmap(*base);
*base = alt;
}
if (resource_size(&cpuif_res) == SZ_128K) {
/* /*
* Verify that we have the first 4kB of a GIC400 * Verify that we have the first 4kB of a GICv2
* aliased over the first 64kB by checking the * aliased over the first 64kB by checking the
* GICC_IIDR register on both ends. * GICC_IIDR register on both ends.
*/ */
val_low = readl_relaxed(*base + GIC_CPU_IDENT); if (!gic_check_gicv2(*base) ||
val_high = readl_relaxed(*base + GIC_CPU_IDENT + 0xf000); !gic_check_gicv2(*base + 0xf000))
if ((val_low & 0xffff0fff) != 0x0202043B ||
val_low != val_high)
return false; return false;
/* /*
......
This diff is collapsed.
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
*/ */
#include <linux/bitmap.h> #include <linux/bitmap.h>
#include <linux/clocksource.h> #include <linux/clocksource.h>
#include <linux/cpuhotplug.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irq.h> #include <linux/irq.h>
...@@ -48,12 +49,16 @@ static DEFINE_SPINLOCK(gic_lock); ...@@ -48,12 +49,16 @@ static DEFINE_SPINLOCK(gic_lock);
static struct irq_domain *gic_irq_domain; static struct irq_domain *gic_irq_domain;
static struct irq_domain *gic_ipi_domain; static struct irq_domain *gic_ipi_domain;
static int gic_shared_intrs; static int gic_shared_intrs;
static int gic_vpes;
static unsigned int gic_cpu_pin; static unsigned int gic_cpu_pin;
static unsigned int timer_cpu_pin; static unsigned int timer_cpu_pin;
static struct irq_chip gic_level_irq_controller, gic_edge_irq_controller; static struct irq_chip gic_level_irq_controller, gic_edge_irq_controller;
DECLARE_BITMAP(ipi_resrv, GIC_MAX_INTRS); static DECLARE_BITMAP(ipi_resrv, GIC_MAX_INTRS);
DECLARE_BITMAP(ipi_available, GIC_MAX_INTRS); static DECLARE_BITMAP(ipi_available, GIC_MAX_INTRS);
static struct gic_all_vpes_chip_data {
u32 map;
bool mask;
} gic_all_vpes_chip_data[GIC_NUM_LOCAL_INTRS];
static void gic_clear_pcpu_masks(unsigned int intr) static void gic_clear_pcpu_masks(unsigned int intr)
{ {
...@@ -194,46 +199,46 @@ static void gic_ack_irq(struct irq_data *d) ...@@ -194,46 +199,46 @@ static void gic_ack_irq(struct irq_data *d)
static int gic_set_type(struct irq_data *d, unsigned int type) static int gic_set_type(struct irq_data *d, unsigned int type)
{ {
unsigned int irq = GIC_HWIRQ_TO_SHARED(d->hwirq); unsigned int irq, pol, trig, dual;
unsigned long flags; unsigned long flags;
bool is_edge;
irq = GIC_HWIRQ_TO_SHARED(d->hwirq);
spin_lock_irqsave(&gic_lock, flags); spin_lock_irqsave(&gic_lock, flags);
switch (type & IRQ_TYPE_SENSE_MASK) { switch (type & IRQ_TYPE_SENSE_MASK) {
case IRQ_TYPE_EDGE_FALLING: case IRQ_TYPE_EDGE_FALLING:
change_gic_pol(irq, GIC_POL_FALLING_EDGE); pol = GIC_POL_FALLING_EDGE;
change_gic_trig(irq, GIC_TRIG_EDGE); trig = GIC_TRIG_EDGE;
change_gic_dual(irq, GIC_DUAL_SINGLE); dual = GIC_DUAL_SINGLE;
is_edge = true;
break; break;
case IRQ_TYPE_EDGE_RISING: case IRQ_TYPE_EDGE_RISING:
change_gic_pol(irq, GIC_POL_RISING_EDGE); pol = GIC_POL_RISING_EDGE;
change_gic_trig(irq, GIC_TRIG_EDGE); trig = GIC_TRIG_EDGE;
change_gic_dual(irq, GIC_DUAL_SINGLE); dual = GIC_DUAL_SINGLE;
is_edge = true;
break; break;
case IRQ_TYPE_EDGE_BOTH: case IRQ_TYPE_EDGE_BOTH:
/* polarity is irrelevant in this case */ pol = 0; /* Doesn't matter */
change_gic_trig(irq, GIC_TRIG_EDGE); trig = GIC_TRIG_EDGE;
change_gic_dual(irq, GIC_DUAL_DUAL); dual = GIC_DUAL_DUAL;
is_edge = true;
break; break;
case IRQ_TYPE_LEVEL_LOW: case IRQ_TYPE_LEVEL_LOW:
change_gic_pol(irq, GIC_POL_ACTIVE_LOW); pol = GIC_POL_ACTIVE_LOW;
change_gic_trig(irq, GIC_TRIG_LEVEL); trig = GIC_TRIG_LEVEL;
change_gic_dual(irq, GIC_DUAL_SINGLE); dual = GIC_DUAL_SINGLE;
is_edge = false;
break; break;
case IRQ_TYPE_LEVEL_HIGH: case IRQ_TYPE_LEVEL_HIGH:
default: default:
change_gic_pol(irq, GIC_POL_ACTIVE_HIGH); pol = GIC_POL_ACTIVE_HIGH;
change_gic_trig(irq, GIC_TRIG_LEVEL); trig = GIC_TRIG_LEVEL;
change_gic_dual(irq, GIC_DUAL_SINGLE); dual = GIC_DUAL_SINGLE;
is_edge = false;
break; break;
} }
if (is_edge) change_gic_pol(irq, pol);
change_gic_trig(irq, trig);
change_gic_dual(irq, dual);
if (trig == GIC_TRIG_EDGE)
irq_set_chip_handler_name_locked(d, &gic_edge_irq_controller, irq_set_chip_handler_name_locked(d, &gic_edge_irq_controller,
handle_edge_irq, NULL); handle_edge_irq, NULL);
else else
...@@ -338,13 +343,17 @@ static struct irq_chip gic_local_irq_controller = { ...@@ -338,13 +343,17 @@ static struct irq_chip gic_local_irq_controller = {
static void gic_mask_local_irq_all_vpes(struct irq_data *d) static void gic_mask_local_irq_all_vpes(struct irq_data *d)
{ {
int intr = GIC_HWIRQ_TO_LOCAL(d->hwirq); struct gic_all_vpes_chip_data *cd;
int i;
unsigned long flags; unsigned long flags;
int intr, cpu;
intr = GIC_HWIRQ_TO_LOCAL(d->hwirq);
cd = irq_data_get_irq_chip_data(d);
cd->mask = false;
spin_lock_irqsave(&gic_lock, flags); spin_lock_irqsave(&gic_lock, flags);
for (i = 0; i < gic_vpes; i++) { for_each_online_cpu(cpu) {
write_gic_vl_other(mips_cm_vp_id(i)); write_gic_vl_other(mips_cm_vp_id(cpu));
write_gic_vo_rmask(BIT(intr)); write_gic_vo_rmask(BIT(intr));
} }
spin_unlock_irqrestore(&gic_lock, flags); spin_unlock_irqrestore(&gic_lock, flags);
...@@ -352,22 +361,40 @@ static void gic_mask_local_irq_all_vpes(struct irq_data *d) ...@@ -352,22 +361,40 @@ static void gic_mask_local_irq_all_vpes(struct irq_data *d)
static void gic_unmask_local_irq_all_vpes(struct irq_data *d) static void gic_unmask_local_irq_all_vpes(struct irq_data *d)
{ {
int intr = GIC_HWIRQ_TO_LOCAL(d->hwirq); struct gic_all_vpes_chip_data *cd;
int i;
unsigned long flags; unsigned long flags;
int intr, cpu;
intr = GIC_HWIRQ_TO_LOCAL(d->hwirq);
cd = irq_data_get_irq_chip_data(d);
cd->mask = true;
spin_lock_irqsave(&gic_lock, flags); spin_lock_irqsave(&gic_lock, flags);
for (i = 0; i < gic_vpes; i++) { for_each_online_cpu(cpu) {
write_gic_vl_other(mips_cm_vp_id(i)); write_gic_vl_other(mips_cm_vp_id(cpu));
write_gic_vo_smask(BIT(intr)); write_gic_vo_smask(BIT(intr));
} }
spin_unlock_irqrestore(&gic_lock, flags); spin_unlock_irqrestore(&gic_lock, flags);
} }
static void gic_all_vpes_irq_cpu_online(struct irq_data *d)
{
struct gic_all_vpes_chip_data *cd;
unsigned int intr;
intr = GIC_HWIRQ_TO_LOCAL(d->hwirq);
cd = irq_data_get_irq_chip_data(d);
write_gic_vl_map(intr, cd->map);
if (cd->mask)
write_gic_vl_smask(BIT(intr));
}
static struct irq_chip gic_all_vpes_local_irq_controller = { static struct irq_chip gic_all_vpes_local_irq_controller = {
.name = "MIPS GIC Local", .name = "MIPS GIC Local",
.irq_mask = gic_mask_local_irq_all_vpes, .irq_mask = gic_mask_local_irq_all_vpes,
.irq_unmask = gic_unmask_local_irq_all_vpes, .irq_unmask = gic_unmask_local_irq_all_vpes,
.irq_cpu_online = gic_all_vpes_irq_cpu_online,
}; };
static void __gic_irq_dispatch(void) static void __gic_irq_dispatch(void)
...@@ -382,39 +409,6 @@ static void gic_irq_dispatch(struct irq_desc *desc) ...@@ -382,39 +409,6 @@ static void gic_irq_dispatch(struct irq_desc *desc)
gic_handle_shared_int(true); gic_handle_shared_int(true);
} }
static int gic_local_irq_domain_map(struct irq_domain *d, unsigned int virq,
irq_hw_number_t hw)
{
int intr = GIC_HWIRQ_TO_LOCAL(hw);
int i;
unsigned long flags;
u32 val;
if (!gic_local_irq_is_routable(intr))
return -EPERM;
if (intr > GIC_LOCAL_INT_FDC) {
pr_err("Invalid local IRQ %d\n", intr);
return -EINVAL;
}
if (intr == GIC_LOCAL_INT_TIMER) {
/* CONFIG_MIPS_CMP workaround (see __gic_init) */
val = GIC_MAP_PIN_MAP_TO_PIN | timer_cpu_pin;
} else {
val = GIC_MAP_PIN_MAP_TO_PIN | gic_cpu_pin;
}
spin_lock_irqsave(&gic_lock, flags);
for (i = 0; i < gic_vpes; i++) {
write_gic_vl_other(mips_cm_vp_id(i));
write_gic_vo_map(intr, val);
}
spin_unlock_irqrestore(&gic_lock, flags);
return 0;
}
static int gic_shared_irq_domain_map(struct irq_domain *d, unsigned int virq, static int gic_shared_irq_domain_map(struct irq_domain *d, unsigned int virq,
irq_hw_number_t hw, unsigned int cpu) irq_hw_number_t hw, unsigned int cpu)
{ {
...@@ -457,7 +451,11 @@ static int gic_irq_domain_xlate(struct irq_domain *d, struct device_node *ctrlr, ...@@ -457,7 +451,11 @@ static int gic_irq_domain_xlate(struct irq_domain *d, struct device_node *ctrlr,
static int gic_irq_domain_map(struct irq_domain *d, unsigned int virq, static int gic_irq_domain_map(struct irq_domain *d, unsigned int virq,
irq_hw_number_t hwirq) irq_hw_number_t hwirq)
{ {
int err; struct gic_all_vpes_chip_data *cd;
unsigned long flags;
unsigned int intr;
int err, cpu;
u32 map;
if (hwirq >= GIC_SHARED_HWIRQ_BASE) { if (hwirq >= GIC_SHARED_HWIRQ_BASE) {
/* verify that shared irqs don't conflict with an IPI irq */ /* verify that shared irqs don't conflict with an IPI irq */
...@@ -474,8 +472,14 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int virq, ...@@ -474,8 +472,14 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int virq,
return gic_shared_irq_domain_map(d, virq, hwirq, 0); return gic_shared_irq_domain_map(d, virq, hwirq, 0);
} }
switch (GIC_HWIRQ_TO_LOCAL(hwirq)) { intr = GIC_HWIRQ_TO_LOCAL(hwirq);
map = GIC_MAP_PIN_MAP_TO_PIN | gic_cpu_pin;
switch (intr) {
case GIC_LOCAL_INT_TIMER: case GIC_LOCAL_INT_TIMER:
/* CONFIG_MIPS_CMP workaround (see __gic_init) */
map = GIC_MAP_PIN_MAP_TO_PIN | timer_cpu_pin;
/* fall-through */
case GIC_LOCAL_INT_PERFCTR: case GIC_LOCAL_INT_PERFCTR:
case GIC_LOCAL_INT_FDC: case GIC_LOCAL_INT_FDC:
/* /*
...@@ -483,9 +487,11 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int virq, ...@@ -483,9 +487,11 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int virq,
* the rest of the MIPS kernel code does not use the * the rest of the MIPS kernel code does not use the
* percpu IRQ API for them. * percpu IRQ API for them.
*/ */
cd = &gic_all_vpes_chip_data[intr];
cd->map = map;
err = irq_domain_set_hwirq_and_chip(d, virq, hwirq, err = irq_domain_set_hwirq_and_chip(d, virq, hwirq,
&gic_all_vpes_local_irq_controller, &gic_all_vpes_local_irq_controller,
NULL); cd);
if (err) if (err)
return err; return err;
...@@ -504,7 +510,17 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int virq, ...@@ -504,7 +510,17 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int virq,
break; break;
} }
return gic_local_irq_domain_map(d, virq, hwirq); if (!gic_local_irq_is_routable(intr))
return -EPERM;
spin_lock_irqsave(&gic_lock, flags);
for_each_online_cpu(cpu) {
write_gic_vl_other(mips_cm_vp_id(cpu));
write_gic_vo_map(intr, map);
}
spin_unlock_irqrestore(&gic_lock, flags);
return 0;
} }
static int gic_irq_domain_alloc(struct irq_domain *d, unsigned int virq, static int gic_irq_domain_alloc(struct irq_domain *d, unsigned int virq,
...@@ -636,11 +652,25 @@ static const struct irq_domain_ops gic_ipi_domain_ops = { ...@@ -636,11 +652,25 @@ static const struct irq_domain_ops gic_ipi_domain_ops = {
.match = gic_ipi_domain_match, .match = gic_ipi_domain_match,
}; };
static int gic_cpu_startup(unsigned int cpu)
{
/* Enable or disable EIC */
change_gic_vl_ctl(GIC_VX_CTL_EIC,
cpu_has_veic ? GIC_VX_CTL_EIC : 0);
/* Clear all local IRQ masks (ie. disable all local interrupts) */
write_gic_vl_rmask(~0);
/* Invoke irq_cpu_online callbacks to enable desired interrupts */
irq_cpu_online();
return 0;
}
static int __init gic_of_init(struct device_node *node, static int __init gic_of_init(struct device_node *node,
struct device_node *parent) struct device_node *parent)
{ {
unsigned int cpu_vec, i, j, gicconfig, cpu, v[2]; unsigned int cpu_vec, i, gicconfig, v[2], num_ipis;
unsigned long reserved; unsigned long reserved;
phys_addr_t gic_base; phys_addr_t gic_base;
struct resource res; struct resource res;
...@@ -690,17 +720,7 @@ static int __init gic_of_init(struct device_node *node, ...@@ -690,17 +720,7 @@ static int __init gic_of_init(struct device_node *node,
gic_shared_intrs >>= __ffs(GIC_CONFIG_NUMINTERRUPTS); gic_shared_intrs >>= __ffs(GIC_CONFIG_NUMINTERRUPTS);
gic_shared_intrs = (gic_shared_intrs + 1) * 8; gic_shared_intrs = (gic_shared_intrs + 1) * 8;
gic_vpes = gicconfig & GIC_CONFIG_PVPS;
gic_vpes >>= __ffs(GIC_CONFIG_PVPS);
gic_vpes = gic_vpes + 1;
if (cpu_has_veic) { if (cpu_has_veic) {
/* Set EIC mode for all VPEs */
for_each_present_cpu(cpu) {
write_gic_vl_other(mips_cm_vp_id(cpu));
write_gic_vo_ctl(GIC_VX_CTL_EIC);
}
/* Always use vector 1 in EIC mode */ /* Always use vector 1 in EIC mode */
gic_cpu_pin = 0; gic_cpu_pin = 0;
timer_cpu_pin = gic_cpu_pin; timer_cpu_pin = gic_cpu_pin;
...@@ -756,10 +776,12 @@ static int __init gic_of_init(struct device_node *node, ...@@ -756,10 +776,12 @@ static int __init gic_of_init(struct device_node *node,
!of_property_read_u32_array(node, "mti,reserved-ipi-vectors", v, 2)) { !of_property_read_u32_array(node, "mti,reserved-ipi-vectors", v, 2)) {
bitmap_set(ipi_resrv, v[0], v[1]); bitmap_set(ipi_resrv, v[0], v[1]);
} else { } else {
/* Make the last 2 * gic_vpes available for IPIs */ /*
bitmap_set(ipi_resrv, * Reserve 2 interrupts per possible CPU/VP for use as IPIs,
gic_shared_intrs - 2 * gic_vpes, * meeting the requirements of arch/mips SMP.
2 * gic_vpes); */
num_ipis = 2 * num_possible_cpus();
bitmap_set(ipi_resrv, gic_shared_intrs - num_ipis, num_ipis);
} }
bitmap_copy(ipi_available, ipi_resrv, GIC_MAX_INTRS); bitmap_copy(ipi_available, ipi_resrv, GIC_MAX_INTRS);
...@@ -773,15 +795,8 @@ static int __init gic_of_init(struct device_node *node, ...@@ -773,15 +795,8 @@ static int __init gic_of_init(struct device_node *node,
write_gic_rmask(i); write_gic_rmask(i);
} }
for (i = 0; i < gic_vpes; i++) { return cpuhp_setup_state(CPUHP_AP_IRQ_MIPS_GIC_STARTING,
write_gic_vl_other(mips_cm_vp_id(i)); "irqchip/mips/gic:starting",
for (j = 0; j < GIC_NUM_LOCAL_INTRS; j++) { gic_cpu_startup, NULL);
if (!gic_local_irq_is_routable(j))
continue;
write_gic_vo_rmask(BIT(j));
}
}
return 0;
} }
IRQCHIP_DECLARE(mips_gic, "mti,gic", gic_of_init); IRQCHIP_DECLARE(mips_gic, "mti,gic", gic_of_init);
...@@ -25,10 +25,6 @@ ...@@ -25,10 +25,6 @@
#include <linux/irqchip/irq-omap-intc.h> #include <linux/irqchip/irq-omap-intc.h>
/* Define these here for now until we drop all board-files */
#define OMAP24XX_IC_BASE 0x480fe000
#define OMAP34XX_IC_BASE 0x48200000
/* selected INTC register offsets */ /* selected INTC register offsets */
#define INTC_REVISION 0x0000 #define INTC_REVISION 0x0000
...@@ -70,8 +66,8 @@ static struct omap_intc_regs intc_context; ...@@ -70,8 +66,8 @@ static struct omap_intc_regs intc_context;
static struct irq_domain *domain; static struct irq_domain *domain;
static void __iomem *omap_irq_base; static void __iomem *omap_irq_base;
static int omap_nr_pending = 3; static int omap_nr_pending;
static int omap_nr_irqs = 96; static int omap_nr_irqs;
static void intc_writel(u32 reg, u32 val) static void intc_writel(u32 reg, u32 val)
{ {
...@@ -364,14 +360,6 @@ omap_intc_handle_irq(struct pt_regs *regs) ...@@ -364,14 +360,6 @@ omap_intc_handle_irq(struct pt_regs *regs)
handle_domain_irq(domain, irqnr, regs); handle_domain_irq(domain, irqnr, regs);
} }
void __init omap3_init_irq(void)
{
omap_nr_irqs = 96;
omap_nr_pending = 3;
omap_init_irq(OMAP34XX_IC_BASE, NULL);
set_handle_irq(omap_intc_handle_irq);
}
static int __init intc_of_init(struct device_node *node, static int __init intc_of_init(struct device_node *node,
struct device_node *parent) struct device_node *parent)
{ {
......
...@@ -389,9 +389,8 @@ MODULE_DEVICE_TABLE(of, intc_irqpin_dt_ids); ...@@ -389,9 +389,8 @@ MODULE_DEVICE_TABLE(of, intc_irqpin_dt_ids);
static int intc_irqpin_probe(struct platform_device *pdev) static int intc_irqpin_probe(struct platform_device *pdev)
{ {
const struct intc_irqpin_config *config = NULL; const struct intc_irqpin_config *config;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
const struct of_device_id *of_id;
struct intc_irqpin_priv *p; struct intc_irqpin_priv *p;
struct intc_irqpin_iomem *i; struct intc_irqpin_iomem *i;
struct resource *io[INTC_IRQPIN_REG_NR]; struct resource *io[INTC_IRQPIN_REG_NR];
...@@ -422,11 +421,9 @@ static int intc_irqpin_probe(struct platform_device *pdev) ...@@ -422,11 +421,9 @@ static int intc_irqpin_probe(struct platform_device *pdev)
p->pdev = pdev; p->pdev = pdev;
platform_set_drvdata(pdev, p); platform_set_drvdata(pdev, p);
of_id = of_match_device(intc_irqpin_dt_ids, dev); config = of_device_get_match_data(dev);
if (of_id && of_id->data) { if (config)
config = of_id->data;
p->needs_clk = config->needs_clk; p->needs_clk = config->needs_clk;
}
p->clk = devm_clk_get(dev, NULL); p->clk = devm_clk_get(dev, NULL);
if (IS_ERR(p->clk)) { if (IS_ERR(p->clk)) {
......
...@@ -289,13 +289,14 @@ static int stm32_gpio_domain_translate(struct irq_domain *d, ...@@ -289,13 +289,14 @@ static int stm32_gpio_domain_translate(struct irq_domain *d,
return 0; return 0;
} }
static void stm32_gpio_domain_activate(struct irq_domain *d, static int stm32_gpio_domain_activate(struct irq_domain *d,
struct irq_data *irq_data) struct irq_data *irq_data, bool early)
{ {
struct stm32_gpio_bank *bank = d->host_data; struct stm32_gpio_bank *bank = d->host_data;
struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent); struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent);
regmap_field_write(pctl->irqmux[irq_data->hwirq], bank->bank_nr); regmap_field_write(pctl->irqmux[irq_data->hwirq], bank->bank_nr);
return 0;
} }
static int stm32_gpio_domain_alloc(struct irq_domain *d, static int stm32_gpio_domain_alloc(struct irq_domain *d,
......
...@@ -98,6 +98,7 @@ enum cpuhp_state { ...@@ -98,6 +98,7 @@ enum cpuhp_state {
CPUHP_AP_IRQ_HIP04_STARTING, CPUHP_AP_IRQ_HIP04_STARTING,
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_ARM_MVEBU_COHERENCY, CPUHP_AP_ARM_MVEBU_COHERENCY,
CPUHP_AP_PERF_X86_AMD_UNCORE_STARTING, CPUHP_AP_PERF_X86_AMD_UNCORE_STARTING,
CPUHP_AP_PERF_X86_STARTING, CPUHP_AP_PERF_X86_STARTING,
......
...@@ -1113,6 +1113,28 @@ static inline u32 irq_reg_readl(struct irq_chip_generic *gc, ...@@ -1113,6 +1113,28 @@ static inline u32 irq_reg_readl(struct irq_chip_generic *gc,
return readl(gc->reg_base + reg_offset); return readl(gc->reg_base + reg_offset);
} }
struct irq_matrix;
struct irq_matrix *irq_alloc_matrix(unsigned int matrix_bits,
unsigned int alloc_start,
unsigned int alloc_end);
void irq_matrix_online(struct irq_matrix *m);
void irq_matrix_offline(struct irq_matrix *m);
void irq_matrix_assign_system(struct irq_matrix *m, unsigned int bit, bool replace);
int irq_matrix_reserve_managed(struct irq_matrix *m, const struct cpumask *msk);
void irq_matrix_remove_managed(struct irq_matrix *m, const struct cpumask *msk);
int irq_matrix_alloc_managed(struct irq_matrix *m, unsigned int cpu);
void irq_matrix_reserve(struct irq_matrix *m);
void irq_matrix_remove_reserved(struct irq_matrix *m);
int irq_matrix_alloc(struct irq_matrix *m, const struct cpumask *msk,
bool reserved, unsigned int *mapped_cpu);
void irq_matrix_free(struct irq_matrix *m, unsigned int cpu,
unsigned int bit, bool managed);
void irq_matrix_assign(struct irq_matrix *m, unsigned int bit);
unsigned int irq_matrix_available(struct irq_matrix *m, bool cpudown);
unsigned int irq_matrix_allocated(struct irq_matrix *m);
unsigned int irq_matrix_reserved(struct irq_matrix *m);
void irq_matrix_debug_show(struct seq_file *sf, struct irq_matrix *m, int ind);
/* Contrary to Linux irqs, for hardware irqs the irq number 0 is valid */ /* Contrary to Linux irqs, for hardware irqs the irq number 0 is valid */
#define INVALID_HWIRQ (~0UL) #define INVALID_HWIRQ (~0UL)
irq_hw_number_t ipi_get_hwirq(unsigned int irq, unsigned int cpu); irq_hw_number_t ipi_get_hwirq(unsigned int irq, unsigned int cpu);
......
...@@ -68,6 +68,7 @@ ...@@ -68,6 +68,7 @@
#define GICD_CTLR_ENABLE_SS_G1 (1U << 1) #define GICD_CTLR_ENABLE_SS_G1 (1U << 1)
#define GICD_CTLR_ENABLE_SS_G0 (1U << 0) #define GICD_CTLR_ENABLE_SS_G0 (1U << 0)
#define GICD_TYPER_RSS (1U << 26)
#define GICD_TYPER_LPIS (1U << 17) #define GICD_TYPER_LPIS (1U << 17)
#define GICD_TYPER_MBIS (1U << 16) #define GICD_TYPER_MBIS (1U << 16)
...@@ -459,6 +460,7 @@ ...@@ -459,6 +460,7 @@
#define ICC_CTLR_EL1_SEIS_MASK (0x1 << ICC_CTLR_EL1_SEIS_SHIFT) #define ICC_CTLR_EL1_SEIS_MASK (0x1 << ICC_CTLR_EL1_SEIS_SHIFT)
#define ICC_CTLR_EL1_A3V_SHIFT 15 #define ICC_CTLR_EL1_A3V_SHIFT 15
#define ICC_CTLR_EL1_A3V_MASK (0x1 << ICC_CTLR_EL1_A3V_SHIFT) #define ICC_CTLR_EL1_A3V_MASK (0x1 << ICC_CTLR_EL1_A3V_SHIFT)
#define ICC_CTLR_EL1_RSS (0x1 << 18)
#define ICC_PMR_EL1_SHIFT 0 #define ICC_PMR_EL1_SHIFT 0
#define ICC_PMR_EL1_MASK (0xff << ICC_PMR_EL1_SHIFT) #define ICC_PMR_EL1_MASK (0xff << ICC_PMR_EL1_SHIFT)
#define ICC_BPR0_EL1_SHIFT 0 #define ICC_BPR0_EL1_SHIFT 0
...@@ -547,6 +549,8 @@ ...@@ -547,6 +549,8 @@
#define ICC_SGI1R_AFFINITY_2_SHIFT 32 #define ICC_SGI1R_AFFINITY_2_SHIFT 32
#define ICC_SGI1R_AFFINITY_2_MASK (0xffULL << ICC_SGI1R_AFFINITY_2_SHIFT) #define ICC_SGI1R_AFFINITY_2_MASK (0xffULL << ICC_SGI1R_AFFINITY_2_SHIFT)
#define ICC_SGI1R_IRQ_ROUTING_MODE_BIT 40 #define ICC_SGI1R_IRQ_ROUTING_MODE_BIT 40
#define ICC_SGI1R_RS_SHIFT 44
#define ICC_SGI1R_RS_MASK (0xfULL << ICC_SGI1R_RS_SHIFT)
#define ICC_SGI1R_AFFINITY_3_SHIFT 48 #define ICC_SGI1R_AFFINITY_3_SHIFT 48
#define ICC_SGI1R_AFFINITY_3_MASK (0xffULL << ICC_SGI1R_AFFINITY_3_SHIFT) #define ICC_SGI1R_AFFINITY_3_MASK (0xffULL << ICC_SGI1R_AFFINITY_3_SHIFT)
......
...@@ -20,6 +20,12 @@ ...@@ -20,6 +20,12 @@
struct its_vpe; struct its_vpe;
/*
* Maximum number of ITTs when GITS_TYPER.VMOVP == 0, using the
* ITSList mechanism to perform inter-ITS synchronization.
*/
#define GICv4_ITS_LIST_MAX 16
/* Embedded in kvm.arch */ /* Embedded in kvm.arch */
struct its_vm { struct its_vm {
struct fwnode_handle *fwnode; struct fwnode_handle *fwnode;
...@@ -30,6 +36,7 @@ struct its_vm { ...@@ -30,6 +36,7 @@ struct its_vm {
irq_hw_number_t db_lpi_base; irq_hw_number_t db_lpi_base;
unsigned long *db_bitmap; unsigned long *db_bitmap;
int nr_db_lpis; int nr_db_lpis;
u32 vlpi_count[GICv4_ITS_LIST_MAX];
}; };
/* Embedded in kvm_vcpu.arch */ /* Embedded in kvm_vcpu.arch */
...@@ -64,12 +71,14 @@ struct its_vpe { ...@@ -64,12 +71,14 @@ struct its_vpe {
* @vm: Pointer to the GICv4 notion of a VM * @vm: Pointer to the GICv4 notion of a VM
* @vpe: Pointer to the GICv4 notion of a virtual CPU (VPE) * @vpe: Pointer to the GICv4 notion of a virtual CPU (VPE)
* @vintid: Virtual LPI number * @vintid: Virtual LPI number
* @properties: Priority and enable bits (as written in the prop table)
* @db_enabled: Is the VPE doorbell to be generated? * @db_enabled: Is the VPE doorbell to be generated?
*/ */
struct its_vlpi_map { struct its_vlpi_map {
struct its_vm *vm; struct its_vm *vm;
struct its_vpe *vpe; struct its_vpe *vpe;
u32 vintid; u32 vintid;
u8 properties;
bool db_enabled; bool db_enabled;
}; };
......
...@@ -18,8 +18,6 @@ ...@@ -18,8 +18,6 @@
#ifndef __INCLUDE_LINUX_IRQCHIP_IRQ_OMAP_INTC_H #ifndef __INCLUDE_LINUX_IRQCHIP_IRQ_OMAP_INTC_H
#define __INCLUDE_LINUX_IRQCHIP_IRQ_OMAP_INTC_H #define __INCLUDE_LINUX_IRQCHIP_IRQ_OMAP_INTC_H
void omap3_init_irq(void);
int omap_irq_pending(void); int omap_irq_pending(void);
void omap_intc_save_context(void); void omap_intc_save_context(void);
void omap_intc_restore_context(void); void omap_intc_restore_context(void);
......
...@@ -93,6 +93,7 @@ struct irq_desc { ...@@ -93,6 +93,7 @@ struct irq_desc {
#endif #endif
#ifdef CONFIG_GENERIC_IRQ_DEBUGFS #ifdef CONFIG_GENERIC_IRQ_DEBUGFS
struct dentry *debugfs_file; struct dentry *debugfs_file;
const char *dev_name;
#endif #endif
#ifdef CONFIG_SPARSE_IRQ #ifdef CONFIG_SPARSE_IRQ
struct rcu_head rcu; struct rcu_head rcu;
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/irqhandler.h> #include <linux/irqhandler.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/mutex.h>
#include <linux/radix-tree.h> #include <linux/radix-tree.h>
struct device_node; struct device_node;
...@@ -40,6 +41,7 @@ struct of_device_id; ...@@ -40,6 +41,7 @@ struct of_device_id;
struct irq_chip; struct irq_chip;
struct irq_data; struct irq_data;
struct cpumask; struct cpumask;
struct seq_file;
/* Number of irqs reserved for a legacy isa controller */ /* Number of irqs reserved for a legacy isa controller */
#define NUM_ISA_INTERRUPTS 16 #define NUM_ISA_INTERRUPTS 16
...@@ -104,18 +106,21 @@ struct irq_domain_ops { ...@@ -104,18 +106,21 @@ struct irq_domain_ops {
int (*xlate)(struct irq_domain *d, struct device_node *node, int (*xlate)(struct irq_domain *d, struct device_node *node,
const u32 *intspec, unsigned int intsize, const u32 *intspec, unsigned int intsize,
unsigned long *out_hwirq, unsigned int *out_type); unsigned long *out_hwirq, unsigned int *out_type);
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
/* extended V2 interfaces to support hierarchy irq_domains */ /* extended V2 interfaces to support hierarchy irq_domains */
int (*alloc)(struct irq_domain *d, unsigned int virq, int (*alloc)(struct irq_domain *d, unsigned int virq,
unsigned int nr_irqs, void *arg); unsigned int nr_irqs, void *arg);
void (*free)(struct irq_domain *d, unsigned int virq, void (*free)(struct irq_domain *d, unsigned int virq,
unsigned int nr_irqs); unsigned int nr_irqs);
void (*activate)(struct irq_domain *d, struct irq_data *irq_data); int (*activate)(struct irq_domain *d, struct irq_data *irqd, bool early);
void (*deactivate)(struct irq_domain *d, struct irq_data *irq_data); void (*deactivate)(struct irq_domain *d, struct irq_data *irq_data);
int (*translate)(struct irq_domain *d, struct irq_fwspec *fwspec, int (*translate)(struct irq_domain *d, struct irq_fwspec *fwspec,
unsigned long *out_hwirq, unsigned int *out_type); unsigned long *out_hwirq, unsigned int *out_type);
#endif #endif
#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
void (*debug_show)(struct seq_file *m, struct irq_domain *d,
struct irq_data *irqd, int ind);
#endif
}; };
extern struct irq_domain_ops irq_generic_chip_ops; extern struct irq_domain_ops irq_generic_chip_ops;
...@@ -133,8 +138,8 @@ struct irq_domain_chip_generic; ...@@ -133,8 +138,8 @@ struct irq_domain_chip_generic;
* @mapcount: The number of mapped interrupts * @mapcount: The number of mapped interrupts
* *
* Optional elements * Optional elements
* @of_node: Pointer to device tree nodes associated with the irq_domain. Used * @fwnode: Pointer to firmware node associated with the irq_domain. Pretty easy
* when decoding device tree interrupt specifiers. * to swap it for the of_node via the irq_domain_get_of_node accessor
* @gc: Pointer to a list of generic chips. There is a helper function for * @gc: Pointer to a list of generic chips. There is a helper function for
* setting up one or more generic chips for interrupt controllers * setting up one or more generic chips for interrupt controllers
* drivers using the generic chip library which uses this pointer. * drivers using the generic chip library which uses this pointer.
...@@ -172,6 +177,7 @@ struct irq_domain { ...@@ -172,6 +177,7 @@ struct irq_domain {
unsigned int revmap_direct_max_irq; unsigned int revmap_direct_max_irq;
unsigned int revmap_size; unsigned int revmap_size;
struct radix_tree_root revmap_tree; struct radix_tree_root revmap_tree;
struct mutex revmap_tree_mutex;
unsigned int linear_revmap[]; unsigned int linear_revmap[];
}; };
...@@ -437,7 +443,7 @@ extern int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base, ...@@ -437,7 +443,7 @@ extern int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
unsigned int nr_irqs, int node, void *arg, unsigned int nr_irqs, int node, void *arg,
bool realloc, const struct cpumask *affinity); bool realloc, const struct cpumask *affinity);
extern void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs); extern void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs);
extern void irq_domain_activate_irq(struct irq_data *irq_data); extern int irq_domain_activate_irq(struct irq_data *irq_data, bool early);
extern void irq_domain_deactivate_irq(struct irq_data *irq_data); extern void irq_domain_deactivate_irq(struct irq_data *irq_data);
static inline int irq_domain_alloc_irqs(struct irq_domain *domain, static inline int irq_domain_alloc_irqs(struct irq_domain *domain,
...@@ -507,8 +513,6 @@ static inline bool irq_domain_is_msi_remap(struct irq_domain *domain) ...@@ -507,8 +513,6 @@ static inline bool irq_domain_is_msi_remap(struct irq_domain *domain)
extern bool irq_domain_hierarchical_is_msi_remap(struct irq_domain *domain); extern bool irq_domain_hierarchical_is_msi_remap(struct irq_domain *domain);
#else /* CONFIG_IRQ_DOMAIN_HIERARCHY */ #else /* CONFIG_IRQ_DOMAIN_HIERARCHY */
static inline void irq_domain_activate_irq(struct irq_data *data) { }
static inline void irq_domain_deactivate_irq(struct irq_data *data) { }
static inline int irq_domain_alloc_irqs(struct irq_domain *domain, static inline int irq_domain_alloc_irqs(struct irq_domain *domain,
unsigned int nr_irqs, int node, void *arg) unsigned int nr_irqs, int node, void *arg)
{ {
...@@ -557,8 +561,6 @@ irq_domain_hierarchical_is_msi_remap(struct irq_domain *domain) ...@@ -557,8 +561,6 @@ irq_domain_hierarchical_is_msi_remap(struct irq_domain *domain)
#else /* CONFIG_IRQ_DOMAIN */ #else /* CONFIG_IRQ_DOMAIN */
static inline void irq_dispose_mapping(unsigned int virq) { } static inline void irq_dispose_mapping(unsigned int virq) { }
static inline void irq_domain_activate_irq(struct irq_data *data) { }
static inline void irq_domain_deactivate_irq(struct irq_data *data) { }
static inline struct irq_domain *irq_find_matching_fwnode( static inline struct irq_domain *irq_find_matching_fwnode(
struct fwnode_handle *fwnode, enum irq_domain_bus_token bus_token) struct fwnode_handle *fwnode, enum irq_domain_bus_token bus_token)
{ {
......
...@@ -283,6 +283,11 @@ enum { ...@@ -283,6 +283,11 @@ enum {
MSI_FLAG_PCI_MSIX = (1 << 3), MSI_FLAG_PCI_MSIX = (1 << 3),
/* Needs early activate, required for PCI */ /* Needs early activate, required for PCI */
MSI_FLAG_ACTIVATE_EARLY = (1 << 4), MSI_FLAG_ACTIVATE_EARLY = (1 << 4),
/*
* Must reactivate when irq is started even when
* MSI_FLAG_ACTIVATE_EARLY has been set.
*/
MSI_FLAG_MUST_REACTIVATE = (1 << 5),
}; };
int msi_domain_set_affinity(struct irq_data *data, const struct cpumask *mask, int msi_domain_set_affinity(struct irq_data *data, const struct cpumask *mask,
......
#undef TRACE_SYSTEM
#define TRACE_SYSTEM irq_matrix
#if !defined(_TRACE_IRQ_MATRIX_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_IRQ_MATRIX_H
#include <linux/tracepoint.h>
struct irq_matrix;
struct cpumap;
DECLARE_EVENT_CLASS(irq_matrix_global,
TP_PROTO(struct irq_matrix *matrix),
TP_ARGS(matrix),
TP_STRUCT__entry(
__field( unsigned int, online_maps )
__field( unsigned int, global_available )
__field( unsigned int, global_reserved )
__field( unsigned int, total_allocated )
),
TP_fast_assign(
__entry->online_maps = matrix->online_maps;
__entry->global_available = matrix->global_available;
__entry->global_reserved = matrix->global_reserved;
__entry->total_allocated = matrix->total_allocated;
),
TP_printk("online_maps=%d global_avl=%u, global_rsvd=%u, total_alloc=%u",
__entry->online_maps, __entry->global_available,
__entry->global_reserved, __entry->total_allocated)
);
DECLARE_EVENT_CLASS(irq_matrix_global_update,
TP_PROTO(int bit, struct irq_matrix *matrix),
TP_ARGS(bit, matrix),
TP_STRUCT__entry(
__field( int, bit )
__field( unsigned int, online_maps )
__field( unsigned int, global_available )
__field( unsigned int, global_reserved )
__field( unsigned int, total_allocated )
),
TP_fast_assign(
__entry->bit = bit;
__entry->online_maps = matrix->online_maps;
__entry->global_available = matrix->global_available;
__entry->global_reserved = matrix->global_reserved;
__entry->total_allocated = matrix->total_allocated;
),
TP_printk("bit=%d online_maps=%d global_avl=%u, global_rsvd=%u, total_alloc=%u",
__entry->bit, __entry->online_maps,
__entry->global_available, __entry->global_reserved,
__entry->total_allocated)
);
DECLARE_EVENT_CLASS(irq_matrix_cpu,
TP_PROTO(int bit, unsigned int cpu, struct irq_matrix *matrix,
struct cpumap *cmap),
TP_ARGS(bit, cpu, matrix, cmap),
TP_STRUCT__entry(
__field( int, bit )
__field( unsigned int, cpu )
__field( bool, online )
__field( unsigned int, available )
__field( unsigned int, allocated )
__field( unsigned int, managed )
__field( unsigned int, online_maps )
__field( unsigned int, global_available )
__field( unsigned int, global_reserved )
__field( unsigned int, total_allocated )
),
TP_fast_assign(
__entry->bit = bit;
__entry->cpu = cpu;
__entry->online = cmap->online;
__entry->available = cmap->available;
__entry->allocated = cmap->allocated;
__entry->managed = cmap->managed;
__entry->online_maps = matrix->online_maps;
__entry->global_available = matrix->global_available;
__entry->global_reserved = matrix->global_reserved;
__entry->total_allocated = matrix->total_allocated;
),
TP_printk("bit=%d cpu=%u online=%d avl=%u alloc=%u managed=%u online_maps=%u global_avl=%u, global_rsvd=%u, total_alloc=%u",
__entry->bit, __entry->cpu, __entry->online,
__entry->available, __entry->allocated,
__entry->managed, __entry->online_maps,
__entry->global_available, __entry->global_reserved,
__entry->total_allocated)
);
DEFINE_EVENT(irq_matrix_global, irq_matrix_online,
TP_PROTO(struct irq_matrix *matrix),
TP_ARGS(matrix)
);
DEFINE_EVENT(irq_matrix_global, irq_matrix_offline,
TP_PROTO(struct irq_matrix *matrix),
TP_ARGS(matrix)
);
DEFINE_EVENT(irq_matrix_global, irq_matrix_reserve,
TP_PROTO(struct irq_matrix *matrix),
TP_ARGS(matrix)
);
DEFINE_EVENT(irq_matrix_global, irq_matrix_remove_reserved,
TP_PROTO(struct irq_matrix *matrix),
TP_ARGS(matrix)
);
DEFINE_EVENT(irq_matrix_global_update, irq_matrix_assign_system,
TP_PROTO(int bit, struct irq_matrix *matrix),
TP_ARGS(bit, matrix)
);
DEFINE_EVENT(irq_matrix_cpu, irq_matrix_alloc_reserved,
TP_PROTO(int bit, unsigned int cpu,
struct irq_matrix *matrix, struct cpumap *cmap),
TP_ARGS(bit, cpu, matrix, cmap)
);
DEFINE_EVENT(irq_matrix_cpu, irq_matrix_reserve_managed,
TP_PROTO(int bit, unsigned int cpu,
struct irq_matrix *matrix, struct cpumap *cmap),
TP_ARGS(bit, cpu, matrix, cmap)
);
DEFINE_EVENT(irq_matrix_cpu, irq_matrix_remove_managed,
TP_PROTO(int bit, unsigned int cpu,
struct irq_matrix *matrix, struct cpumap *cmap),
TP_ARGS(bit, cpu, matrix, cmap)
);
DEFINE_EVENT(irq_matrix_cpu, irq_matrix_alloc_managed,
TP_PROTO(int bit, unsigned int cpu,
struct irq_matrix *matrix, struct cpumap *cmap),
TP_ARGS(bit, cpu, matrix, cmap)
);
DEFINE_EVENT(irq_matrix_cpu, irq_matrix_assign,
TP_PROTO(int bit, unsigned int cpu,
struct irq_matrix *matrix, struct cpumap *cmap),
TP_ARGS(bit, cpu, matrix, cmap)
);
DEFINE_EVENT(irq_matrix_cpu, irq_matrix_alloc,
TP_PROTO(int bit, unsigned int cpu,
struct irq_matrix *matrix, struct cpumap *cmap),
TP_ARGS(bit, cpu, matrix, cmap)
);
DEFINE_EVENT(irq_matrix_cpu, irq_matrix_free,
TP_PROTO(int bit, unsigned int cpu,
struct irq_matrix *matrix, struct cpumap *cmap),
TP_ARGS(bit, cpu, matrix, cmap)
);
#endif /* _TRACE_IRQ_H */
/* This part must be outside protection */
#include <trace/define_trace.h>
...@@ -97,6 +97,9 @@ config HANDLE_DOMAIN_IRQ ...@@ -97,6 +97,9 @@ config HANDLE_DOMAIN_IRQ
config IRQ_TIMINGS config IRQ_TIMINGS
bool bool
config GENERIC_IRQ_MATRIX_ALLOCATOR
bool
config IRQ_DOMAIN_DEBUG config IRQ_DOMAIN_DEBUG
bool "Expose hardware/virtual IRQ mapping via debugfs" bool "Expose hardware/virtual IRQ mapping via debugfs"
depends on IRQ_DOMAIN && DEBUG_FS depends on IRQ_DOMAIN && DEBUG_FS
......
...@@ -13,3 +13,4 @@ obj-$(CONFIG_GENERIC_MSI_IRQ) += msi.o ...@@ -13,3 +13,4 @@ obj-$(CONFIG_GENERIC_MSI_IRQ) += msi.o
obj-$(CONFIG_GENERIC_IRQ_IPI) += ipi.o obj-$(CONFIG_GENERIC_IRQ_IPI) += ipi.o
obj-$(CONFIG_SMP) += affinity.o obj-$(CONFIG_SMP) += affinity.o
obj-$(CONFIG_GENERIC_IRQ_DEBUGFS) += debugfs.o obj-$(CONFIG_GENERIC_IRQ_DEBUGFS) += debugfs.o
obj-$(CONFIG_GENERIC_IRQ_MATRIX_ALLOCATOR) += matrix.o
...@@ -53,7 +53,7 @@ unsigned long probe_irq_on(void) ...@@ -53,7 +53,7 @@ unsigned long probe_irq_on(void)
if (desc->irq_data.chip->irq_set_type) if (desc->irq_data.chip->irq_set_type)
desc->irq_data.chip->irq_set_type(&desc->irq_data, desc->irq_data.chip->irq_set_type(&desc->irq_data,
IRQ_TYPE_PROBE); IRQ_TYPE_PROBE);
irq_startup(desc, IRQ_NORESEND, IRQ_START_FORCE); irq_activate_and_startup(desc, IRQ_NORESEND);
} }
raw_spin_unlock_irq(&desc->lock); raw_spin_unlock_irq(&desc->lock);
} }
......
...@@ -207,20 +207,24 @@ __irq_startup_managed(struct irq_desc *desc, struct cpumask *aff, bool force) ...@@ -207,20 +207,24 @@ __irq_startup_managed(struct irq_desc *desc, struct cpumask *aff, bool force)
* Catch code which fiddles with enable_irq() on a managed * Catch code which fiddles with enable_irq() on a managed
* and potentially shutdown IRQ. Chained interrupt * and potentially shutdown IRQ. Chained interrupt
* installment or irq auto probing should not happen on * installment or irq auto probing should not happen on
* managed irqs either. Emit a warning, break the affinity * managed irqs either.
* and start it up as a normal interrupt.
*/ */
if (WARN_ON_ONCE(force)) if (WARN_ON_ONCE(force))
return IRQ_STARTUP_NORMAL; return IRQ_STARTUP_ABORT;
/* /*
* The interrupt was requested, but there is no online CPU * The interrupt was requested, but there is no online CPU
* in it's affinity mask. Put it into managed shutdown * in it's affinity mask. Put it into managed shutdown
* state and let the cpu hotplug mechanism start it up once * state and let the cpu hotplug mechanism start it up once
* a CPU in the mask becomes available. * a CPU in the mask becomes available.
*/ */
irqd_set_managed_shutdown(d);
return IRQ_STARTUP_ABORT; return IRQ_STARTUP_ABORT;
} }
/*
* Managed interrupts have reserved resources, so this should not
* happen.
*/
if (WARN_ON(irq_domain_activate_irq(d, false)))
return IRQ_STARTUP_ABORT;
return IRQ_STARTUP_MANAGED; return IRQ_STARTUP_MANAGED;
} }
#else #else
...@@ -236,7 +240,9 @@ static int __irq_startup(struct irq_desc *desc) ...@@ -236,7 +240,9 @@ static int __irq_startup(struct irq_desc *desc)
struct irq_data *d = irq_desc_get_irq_data(desc); struct irq_data *d = irq_desc_get_irq_data(desc);
int ret = 0; int ret = 0;
irq_domain_activate_irq(d); /* Warn if this interrupt is not activated but try nevertheless */
WARN_ON_ONCE(!irqd_is_activated(d));
if (d->chip->irq_startup) { if (d->chip->irq_startup) {
ret = d->chip->irq_startup(d); ret = d->chip->irq_startup(d);
irq_state_clr_disabled(desc); irq_state_clr_disabled(desc);
...@@ -269,6 +275,7 @@ int irq_startup(struct irq_desc *desc, bool resend, bool force) ...@@ -269,6 +275,7 @@ int irq_startup(struct irq_desc *desc, bool resend, bool force)
irq_set_affinity_locked(d, aff, false); irq_set_affinity_locked(d, aff, false);
break; break;
case IRQ_STARTUP_ABORT: case IRQ_STARTUP_ABORT:
irqd_set_managed_shutdown(d);
return 0; return 0;
} }
} }
...@@ -278,6 +285,22 @@ int irq_startup(struct irq_desc *desc, bool resend, bool force) ...@@ -278,6 +285,22 @@ int irq_startup(struct irq_desc *desc, bool resend, bool force)
return ret; return ret;
} }
int irq_activate(struct irq_desc *desc)
{
struct irq_data *d = irq_desc_get_irq_data(desc);
if (!irqd_affinity_is_managed(d))
return irq_domain_activate_irq(d, false);
return 0;
}
void irq_activate_and_startup(struct irq_desc *desc, bool resend)
{
if (WARN_ON(irq_activate(desc)))
return;
irq_startup(desc, resend, IRQ_START_FORCE);
}
static void __irq_disable(struct irq_desc *desc, bool mask); static void __irq_disable(struct irq_desc *desc, bool mask);
void irq_shutdown(struct irq_desc *desc) void irq_shutdown(struct irq_desc *desc)
...@@ -953,7 +976,7 @@ __irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle, ...@@ -953,7 +976,7 @@ __irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle,
irq_settings_set_norequest(desc); irq_settings_set_norequest(desc);
irq_settings_set_nothread(desc); irq_settings_set_nothread(desc);
desc->action = &chained_action; desc->action = &chained_action;
irq_startup(desc, IRQ_RESEND, IRQ_START_FORCE); irq_activate_and_startup(desc, IRQ_RESEND);
} }
} }
......
...@@ -81,6 +81,8 @@ irq_debug_show_data(struct seq_file *m, struct irq_data *data, int ind) ...@@ -81,6 +81,8 @@ irq_debug_show_data(struct seq_file *m, struct irq_data *data, int ind)
data->domain ? data->domain->name : ""); data->domain ? data->domain->name : "");
seq_printf(m, "%*shwirq: 0x%lx\n", ind + 1, "", data->hwirq); seq_printf(m, "%*shwirq: 0x%lx\n", ind + 1, "", data->hwirq);
irq_debug_show_chip(m, data, ind + 1); irq_debug_show_chip(m, data, ind + 1);
if (data->domain && data->domain->ops && data->domain->ops->debug_show)
data->domain->ops->debug_show(m, NULL, data, ind + 1);
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
if (!data->parent_data) if (!data->parent_data)
return; return;
...@@ -149,6 +151,7 @@ static int irq_debug_show(struct seq_file *m, void *p) ...@@ -149,6 +151,7 @@ static int irq_debug_show(struct seq_file *m, void *p)
raw_spin_lock_irq(&desc->lock); raw_spin_lock_irq(&desc->lock);
data = irq_desc_get_irq_data(desc); data = irq_desc_get_irq_data(desc);
seq_printf(m, "handler: %pf\n", desc->handle_irq); seq_printf(m, "handler: %pf\n", desc->handle_irq);
seq_printf(m, "device: %s\n", desc->dev_name);
seq_printf(m, "status: 0x%08x\n", desc->status_use_accessors); seq_printf(m, "status: 0x%08x\n", desc->status_use_accessors);
irq_debug_show_bits(m, 0, desc->status_use_accessors, irqdesc_states, irq_debug_show_bits(m, 0, desc->status_use_accessors, irqdesc_states,
ARRAY_SIZE(irqdesc_states)); ARRAY_SIZE(irqdesc_states));
...@@ -226,6 +229,15 @@ static const struct file_operations dfs_irq_ops = { ...@@ -226,6 +229,15 @@ static const struct file_operations dfs_irq_ops = {
.release = single_release, .release = single_release,
}; };
void irq_debugfs_copy_devname(int irq, struct device *dev)
{
struct irq_desc *desc = irq_to_desc(irq);
const char *name = dev_name(dev);
if (name)
desc->dev_name = kstrdup(name, GFP_KERNEL);
}
void irq_add_debugfs_entry(unsigned int irq, struct irq_desc *desc) void irq_add_debugfs_entry(unsigned int irq, struct irq_desc *desc)
{ {
char name [10]; char name [10];
......
...@@ -74,6 +74,8 @@ extern void __enable_irq(struct irq_desc *desc); ...@@ -74,6 +74,8 @@ extern void __enable_irq(struct irq_desc *desc);
#define IRQ_START_FORCE true #define IRQ_START_FORCE true
#define IRQ_START_COND false #define IRQ_START_COND false
extern int irq_activate(struct irq_desc *desc);
extern void irq_activate_and_startup(struct irq_desc *desc, bool resend);
extern int irq_startup(struct irq_desc *desc, bool resend, bool force); extern int irq_startup(struct irq_desc *desc, bool resend, bool force);
extern void irq_shutdown(struct irq_desc *desc); extern void irq_shutdown(struct irq_desc *desc);
...@@ -436,6 +438,18 @@ static inline bool irq_fixup_move_pending(struct irq_desc *desc, bool fclear) ...@@ -436,6 +438,18 @@ static inline bool irq_fixup_move_pending(struct irq_desc *desc, bool fclear)
} }
#endif /* !CONFIG_GENERIC_PENDING_IRQ */ #endif /* !CONFIG_GENERIC_PENDING_IRQ */
#if !defined(CONFIG_IRQ_DOMAIN) || !defined(CONFIG_IRQ_DOMAIN_HIERARCHY)
static inline int irq_domain_activate_irq(struct irq_data *data, bool early)
{
irqd_set_activated(data);
return 0;
}
static inline void irq_domain_deactivate_irq(struct irq_data *data)
{
irqd_clr_activated(data);
}
#endif
#ifdef CONFIG_GENERIC_IRQ_DEBUGFS #ifdef CONFIG_GENERIC_IRQ_DEBUGFS
#include <linux/debugfs.h> #include <linux/debugfs.h>
...@@ -443,7 +457,9 @@ void irq_add_debugfs_entry(unsigned int irq, struct irq_desc *desc); ...@@ -443,7 +457,9 @@ void irq_add_debugfs_entry(unsigned int irq, struct irq_desc *desc);
static inline void irq_remove_debugfs_entry(struct irq_desc *desc) static inline void irq_remove_debugfs_entry(struct irq_desc *desc)
{ {
debugfs_remove(desc->debugfs_file); debugfs_remove(desc->debugfs_file);
kfree(desc->dev_name);
} }
void irq_debugfs_copy_devname(int irq, struct device *dev);
# ifdef CONFIG_IRQ_DOMAIN # ifdef CONFIG_IRQ_DOMAIN
void irq_domain_debugfs_init(struct dentry *root); void irq_domain_debugfs_init(struct dentry *root);
# else # else
...@@ -458,4 +474,7 @@ static inline void irq_add_debugfs_entry(unsigned int irq, struct irq_desc *d) ...@@ -458,4 +474,7 @@ static inline void irq_add_debugfs_entry(unsigned int irq, struct irq_desc *d)
static inline void irq_remove_debugfs_entry(struct irq_desc *d) static inline void irq_remove_debugfs_entry(struct irq_desc *d)
{ {
} }
static inline void irq_debugfs_copy_devname(int irq, struct device *dev)
{
}
#endif /* CONFIG_GENERIC_IRQ_DEBUGFS */ #endif /* CONFIG_GENERIC_IRQ_DEBUGFS */
...@@ -27,7 +27,7 @@ static struct lock_class_key irq_desc_lock_class; ...@@ -27,7 +27,7 @@ static struct lock_class_key irq_desc_lock_class;
#if defined(CONFIG_SMP) #if defined(CONFIG_SMP)
static int __init irq_affinity_setup(char *str) static int __init irq_affinity_setup(char *str)
{ {
zalloc_cpumask_var(&irq_default_affinity, GFP_NOWAIT); alloc_bootmem_cpumask_var(&irq_default_affinity);
cpulist_parse(str, irq_default_affinity); cpulist_parse(str, irq_default_affinity);
/* /*
* Set at least the boot cpu. We don't want to end up with * Set at least the boot cpu. We don't want to end up with
...@@ -40,10 +40,8 @@ __setup("irqaffinity=", irq_affinity_setup); ...@@ -40,10 +40,8 @@ __setup("irqaffinity=", irq_affinity_setup);
static void __init init_irq_default_affinity(void) static void __init init_irq_default_affinity(void)
{ {
#ifdef CONFIG_CPUMASK_OFFSTACK if (!cpumask_available(irq_default_affinity))
if (!irq_default_affinity)
zalloc_cpumask_var(&irq_default_affinity, GFP_NOWAIT); zalloc_cpumask_var(&irq_default_affinity, GFP_NOWAIT);
#endif
if (cpumask_empty(irq_default_affinity)) if (cpumask_empty(irq_default_affinity))
cpumask_setall(irq_default_affinity); cpumask_setall(irq_default_affinity);
} }
...@@ -448,7 +446,7 @@ static int alloc_descs(unsigned int start, unsigned int cnt, int node, ...@@ -448,7 +446,7 @@ static int alloc_descs(unsigned int start, unsigned int cnt, int node,
} }
} }
flags = affinity ? IRQD_AFFINITY_MANAGED : 0; flags = affinity ? IRQD_AFFINITY_MANAGED | IRQD_MANAGED_SHUTDOWN : 0;
mask = NULL; mask = NULL;
for (i = 0; i < cnt; i++) { for (i = 0; i < cnt; i++) {
...@@ -462,6 +460,7 @@ static int alloc_descs(unsigned int start, unsigned int cnt, int node, ...@@ -462,6 +460,7 @@ static int alloc_descs(unsigned int start, unsigned int cnt, int node,
goto err; goto err;
irq_insert_desc(start + i, desc); irq_insert_desc(start + i, desc);
irq_sysfs_add(start + i, desc); irq_sysfs_add(start + i, desc);
irq_add_debugfs_entry(start + i, desc);
} }
bitmap_set(allocated_irqs, start, cnt); bitmap_set(allocated_irqs, start, cnt);
return start; return start;
......
...@@ -21,7 +21,6 @@ ...@@ -21,7 +21,6 @@
static LIST_HEAD(irq_domain_list); static LIST_HEAD(irq_domain_list);
static DEFINE_MUTEX(irq_domain_mutex); static DEFINE_MUTEX(irq_domain_mutex);
static DEFINE_MUTEX(revmap_trees_mutex);
static struct irq_domain *irq_default_domain; static struct irq_domain *irq_default_domain;
static void irq_domain_check_hierarchy(struct irq_domain *domain); static void irq_domain_check_hierarchy(struct irq_domain *domain);
...@@ -211,6 +210,7 @@ struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size, ...@@ -211,6 +210,7 @@ struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size,
/* Fill structure */ /* Fill structure */
INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL); INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
mutex_init(&domain->revmap_tree_mutex);
domain->ops = ops; domain->ops = ops;
domain->host_data = host_data; domain->host_data = host_data;
domain->hwirq_max = hwirq_max; domain->hwirq_max = hwirq_max;
...@@ -462,9 +462,9 @@ static void irq_domain_clear_mapping(struct irq_domain *domain, ...@@ -462,9 +462,9 @@ static void irq_domain_clear_mapping(struct irq_domain *domain,
if (hwirq < domain->revmap_size) { if (hwirq < domain->revmap_size) {
domain->linear_revmap[hwirq] = 0; domain->linear_revmap[hwirq] = 0;
} else { } else {
mutex_lock(&revmap_trees_mutex); mutex_lock(&domain->revmap_tree_mutex);
radix_tree_delete(&domain->revmap_tree, hwirq); radix_tree_delete(&domain->revmap_tree, hwirq);
mutex_unlock(&revmap_trees_mutex); mutex_unlock(&domain->revmap_tree_mutex);
} }
} }
...@@ -475,9 +475,9 @@ static void irq_domain_set_mapping(struct irq_domain *domain, ...@@ -475,9 +475,9 @@ static void irq_domain_set_mapping(struct irq_domain *domain,
if (hwirq < domain->revmap_size) { if (hwirq < domain->revmap_size) {
domain->linear_revmap[hwirq] = irq_data->irq; domain->linear_revmap[hwirq] = irq_data->irq;
} else { } else {
mutex_lock(&revmap_trees_mutex); mutex_lock(&domain->revmap_tree_mutex);
radix_tree_insert(&domain->revmap_tree, hwirq, irq_data); radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);
mutex_unlock(&revmap_trees_mutex); mutex_unlock(&domain->revmap_tree_mutex);
} }
} }
...@@ -1459,11 +1459,11 @@ static void irq_domain_fix_revmap(struct irq_data *d) ...@@ -1459,11 +1459,11 @@ static void irq_domain_fix_revmap(struct irq_data *d)
return; /* Not using radix tree. */ return; /* Not using radix tree. */
/* Fix up the revmap. */ /* Fix up the revmap. */
mutex_lock(&revmap_trees_mutex); mutex_lock(&d->domain->revmap_tree_mutex);
slot = radix_tree_lookup_slot(&d->domain->revmap_tree, d->hwirq); slot = radix_tree_lookup_slot(&d->domain->revmap_tree, d->hwirq);
if (slot) if (slot)
radix_tree_replace_slot(&d->domain->revmap_tree, slot, d); radix_tree_replace_slot(&d->domain->revmap_tree, slot, d);
mutex_unlock(&revmap_trees_mutex); mutex_unlock(&d->domain->revmap_tree_mutex);
} }
/** /**
...@@ -1682,28 +1682,36 @@ void irq_domain_free_irqs_parent(struct irq_domain *domain, ...@@ -1682,28 +1682,36 @@ void irq_domain_free_irqs_parent(struct irq_domain *domain,
} }
EXPORT_SYMBOL_GPL(irq_domain_free_irqs_parent); EXPORT_SYMBOL_GPL(irq_domain_free_irqs_parent);
static void __irq_domain_activate_irq(struct irq_data *irq_data) static void __irq_domain_deactivate_irq(struct irq_data *irq_data)
{ {
if (irq_data && irq_data->domain) { if (irq_data && irq_data->domain) {
struct irq_domain *domain = irq_data->domain; struct irq_domain *domain = irq_data->domain;
if (domain->ops->deactivate)
domain->ops->deactivate(domain, irq_data);
if (irq_data->parent_data) if (irq_data->parent_data)
__irq_domain_activate_irq(irq_data->parent_data); __irq_domain_deactivate_irq(irq_data->parent_data);
if (domain->ops->activate)
domain->ops->activate(domain, irq_data);
} }
} }
static void __irq_domain_deactivate_irq(struct irq_data *irq_data) static int __irq_domain_activate_irq(struct irq_data *irqd, bool early)
{ {
if (irq_data && irq_data->domain) { int ret = 0;
struct irq_domain *domain = irq_data->domain;
if (domain->ops->deactivate) if (irqd && irqd->domain) {
domain->ops->deactivate(domain, irq_data); struct irq_domain *domain = irqd->domain;
if (irq_data->parent_data)
__irq_domain_deactivate_irq(irq_data->parent_data); if (irqd->parent_data)
ret = __irq_domain_activate_irq(irqd->parent_data,
early);
if (!ret && domain->ops->activate) {
ret = domain->ops->activate(domain, irqd, early);
/* Rollback in case of error */
if (ret && irqd->parent_data)
__irq_domain_deactivate_irq(irqd->parent_data);
}
} }
return ret;
} }
/** /**
...@@ -1714,12 +1722,15 @@ static void __irq_domain_deactivate_irq(struct irq_data *irq_data) ...@@ -1714,12 +1722,15 @@ static void __irq_domain_deactivate_irq(struct irq_data *irq_data)
* This is the second step to call domain_ops->activate to program interrupt * This is the second step to call domain_ops->activate to program interrupt
* controllers, so the interrupt could actually get delivered. * controllers, so the interrupt could actually get delivered.
*/ */
void irq_domain_activate_irq(struct irq_data *irq_data) int irq_domain_activate_irq(struct irq_data *irq_data, bool early)
{ {
if (!irqd_is_activated(irq_data)) { int ret = 0;
__irq_domain_activate_irq(irq_data);
if (!irqd_is_activated(irq_data))
ret = __irq_domain_activate_irq(irq_data, early);
if (!ret)
irqd_set_activated(irq_data); irqd_set_activated(irq_data);
} return ret;
} }
/** /**
...@@ -1810,6 +1821,8 @@ irq_domain_debug_show_one(struct seq_file *m, struct irq_domain *d, int ind) ...@@ -1810,6 +1821,8 @@ irq_domain_debug_show_one(struct seq_file *m, struct irq_domain *d, int ind)
d->revmap_size + d->revmap_direct_max_irq); d->revmap_size + d->revmap_direct_max_irq);
seq_printf(m, "%*smapped: %u\n", ind + 1, "", d->mapcount); seq_printf(m, "%*smapped: %u\n", ind + 1, "", d->mapcount);
seq_printf(m, "%*sflags: 0x%08x\n", ind +1 , "", d->flags); seq_printf(m, "%*sflags: 0x%08x\n", ind +1 , "", d->flags);
if (d->ops && d->ops->debug_show)
d->ops->debug_show(m, d, NULL, ind + 1);
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
if (!d->parent) if (!d->parent)
return; return;
......
...@@ -381,7 +381,8 @@ int irq_select_affinity_usr(unsigned int irq) ...@@ -381,7 +381,8 @@ int irq_select_affinity_usr(unsigned int irq)
/** /**
* irq_set_vcpu_affinity - Set vcpu affinity for the interrupt * irq_set_vcpu_affinity - Set vcpu affinity for the interrupt
* @irq: interrupt number to set affinity * @irq: interrupt number to set affinity
* @vcpu_info: vCPU specific data * @vcpu_info: vCPU specific data or pointer to a percpu array of vCPU
* specific data for percpu_devid interrupts
* *
* This function uses the vCPU specific data to set the vCPU * This function uses the vCPU specific data to set the vCPU
* affinity for an irq. The vCPU specific data is passed from * affinity for an irq. The vCPU specific data is passed from
...@@ -519,7 +520,7 @@ void __enable_irq(struct irq_desc *desc) ...@@ -519,7 +520,7 @@ void __enable_irq(struct irq_desc *desc)
* time. If it was already started up, then irq_startup() * time. If it was already started up, then irq_startup()
* will invoke irq_enable() under the hood. * will invoke irq_enable() under the hood.
*/ */
irq_startup(desc, IRQ_RESEND, IRQ_START_COND); irq_startup(desc, IRQ_RESEND, IRQ_START_FORCE);
break; break;
} }
default: default:
...@@ -1325,6 +1326,21 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) ...@@ -1325,6 +1326,21 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
goto out_unlock; goto out_unlock;
} }
/*
* Activate the interrupt. That activation must happen
* independently of IRQ_NOAUTOEN. request_irq() can fail
* and the callers are supposed to handle
* that. enable_irq() of an interrupt requested with
* IRQ_NOAUTOEN is not supposed to fail. The activation
* keeps it in shutdown mode, it merily associates
* resources if necessary and if that's not possible it
* fails. Interrupts which are in managed shutdown mode
* will simply ignore that activation request.
*/
ret = irq_activate(desc);
if (ret)
goto out_unlock;
desc->istate &= ~(IRQS_AUTODETECT | IRQS_SPURIOUS_DISABLED | \ desc->istate &= ~(IRQS_AUTODETECT | IRQS_SPURIOUS_DISABLED | \
IRQS_ONESHOT | IRQS_WAITING); IRQS_ONESHOT | IRQS_WAITING);
irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS); irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
...@@ -1400,7 +1416,6 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) ...@@ -1400,7 +1416,6 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
wake_up_process(new->secondary->thread); wake_up_process(new->secondary->thread);
register_irq_proc(irq, desc); register_irq_proc(irq, desc);
irq_add_debugfs_entry(irq, desc);
new->dir = NULL; new->dir = NULL;
register_handler_proc(irq, new); register_handler_proc(irq, new);
return 0; return 0;
......
This diff is collapsed.
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
#include <linux/msi.h> #include <linux/msi.h>
#include <linux/slab.h> #include <linux/slab.h>
#include "internals.h"
/** /**
* alloc_msi_entry - Allocate an initialize msi_entry * alloc_msi_entry - Allocate an initialize msi_entry
* @dev: Pointer to the device for which this is allocated * @dev: Pointer to the device for which this is allocated
...@@ -100,13 +102,14 @@ int msi_domain_set_affinity(struct irq_data *irq_data, ...@@ -100,13 +102,14 @@ int msi_domain_set_affinity(struct irq_data *irq_data,
return ret; return ret;
} }
static void msi_domain_activate(struct irq_domain *domain, static int msi_domain_activate(struct irq_domain *domain,
struct irq_data *irq_data) struct irq_data *irq_data, bool early)
{ {
struct msi_msg msg; struct msi_msg msg;
BUG_ON(irq_chip_compose_msi_msg(irq_data, &msg)); BUG_ON(irq_chip_compose_msi_msg(irq_data, &msg));
irq_chip_write_msi_msg(irq_data, &msg); irq_chip_write_msi_msg(irq_data, &msg);
return 0;
} }
static void msi_domain_deactivate(struct irq_domain *domain, static void msi_domain_deactivate(struct irq_domain *domain,
...@@ -373,8 +376,10 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, ...@@ -373,8 +376,10 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
return ret; return ret;
} }
for (i = 0; i < desc->nvec_used; i++) for (i = 0; i < desc->nvec_used; i++) {
irq_set_msi_desc_off(virq, i, desc); irq_set_msi_desc_off(virq, i, desc);
irq_debugfs_copy_devname(virq + i, dev);
}
} }
if (ops->msi_finish) if (ops->msi_finish)
...@@ -396,11 +401,28 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, ...@@ -396,11 +401,28 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
struct irq_data *irq_data; struct irq_data *irq_data;
irq_data = irq_domain_get_irq_data(domain, desc->irq); irq_data = irq_domain_get_irq_data(domain, desc->irq);
irq_domain_activate_irq(irq_data); ret = irq_domain_activate_irq(irq_data, true);
if (ret)
goto cleanup;
if (info->flags & MSI_FLAG_MUST_REACTIVATE)
irqd_clr_activated(irq_data);
} }
} }
return 0; return 0;
cleanup:
for_each_msi_entry(desc, dev) {
struct irq_data *irqd;
if (desc->irq == virq)
break;
irqd = irq_domain_get_irq_data(domain, desc->irq);
if (irqd_is_activated(irqd))
irq_domain_deactivate_irq(irqd);
}
msi_domain_free_irqs(domain, dev);
return ret;
} }
/** /**
......
...@@ -138,11 +138,7 @@ static void irq_work_run_list(struct llist_head *list) ...@@ -138,11 +138,7 @@ static void irq_work_run_list(struct llist_head *list)
return; return;
llnode = llist_del_all(list); llnode = llist_del_all(list);
while (llnode != NULL) { llist_for_each_entry(work, llnode, llnode) {
work = llist_entry(llnode, struct irq_work, llnode);
llnode = llist_next(llnode);
/* /*
* Clear the PENDING bit, after this point the @work * Clear the PENDING bit, after this point the @work
* can be re-used. * can be re-used.
......
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