Commit 670310df authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull irq core updates from Thomas Gleixner:
 "A rather large update for the interrupt core code and the irq chip drivers:

   - Add a new bitmap matrix allocator and supporting changes, which is
     used to replace the x86 vector allocator which comes with separate
     pull request. This allows to replace the convoluted nested loop
     allocation function in x86 with a facility which supports the
     recently added property of managed interrupts proper and allows to
     switch to a best effort vector reservation scheme, which addresses
     problems with vector exhaustion.

   - A large update to the ARM GIC-V3-ITS driver adding support for
     range selectors.

   - New interrupt controllers:
       - Meson and Meson8 GPIO
       - BCM7271 L2
       - Socionext EXIU

     If you expected that this will stop at some point, I have to
     disappoint you. There are new ones posted already. Sigh!

   - STM32 interrupt controller support for new platforms.

   - A pile of fixes, cleanups and updates to the MIPS GIC driver

   - The usual small fixes, cleanups and updates all over the place.
     Most visible one is to move the irq chip drivers Kconfig switches
     into a separate Kconfig menu"

* 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (70 commits)
  genirq: Fix type of shifting literal 1 in __setup_irq()
  irqdomain: Drop pointless NULL check in virq_debug_show_one
  genirq/proc: Return proper error code when irq_set_affinity() fails
  irq/work: Use llist_for_each_entry_safe
  irqchip: mips-gic: Print warning if inherited GIC base is used
  irqchip/mips-gic: Add pr_fmt and reword pr_* messages
  irqchip/stm32: Move the wakeup on interrupt mask
  irqchip/stm32: Fix initial values
  irqchip/stm32: Add stm32h7 support
  dt-bindings/interrupt-controllers: Add compatible string for stm32h7
  irqchip/stm32: Add multi-bank management
  irqchip/stm32: Select GENERIC_IRQ_CHIP
  irqchip/exiu: Add support for Socionext Synquacer EXIU controller
  dt-bindings: Add description of Socionext EXIU interrupt controller
  irqchip/gic-v3-its: Fix VPE activate callback return value
  irqchip: mips-gic: Make IPI bitmaps static
  irqchip: mips-gic: Share register writes in gic_set_type()
  irqchip: mips-gic: Remove gic_vpes variable
  irqchip: mips-gic: Use num_possible_cpus() to reserve IPIs
  irqchip: mips-gic: Configure EIC when CPUs come online
  ...
parents 43ff2f4d ffc661c9
...@@ -1716,6 +1716,13 @@ ...@@ -1716,6 +1716,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.
......
Socionext SynQuacer External Interrupt Unit (EXIU)
The Socionext Synquacer SoC has an external interrupt unit (EXIU)
that forwards a block of 32 configurable input lines to 32 adjacent
level-high type GICv3 SPIs.
Required properties:
- compatible : Should be "socionext,synquacer-exiu".
- reg : Specifies base physical address and size of the
control 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 3.
- interrupt-parent : phandle of the GIC these interrupts are routed to.
- socionext,spi-base : The SPI number of the first SPI of the 32 adjacent
ones the EXIU forwards its interrups to.
Notes:
- Only SPIs can use the EXIU as an interrupt parent.
Example:
exiu: interrupt-controller@510c0000 {
compatible = "socionext,synquacer-exiu";
reg = <0x0 0x510c0000 0x0 0x20>;
interrupt-controller;
interrupt-parent = <&gic>;
#interrupt-cells = <3>;
socionext,spi-base = <112>;
};
...@@ -2,7 +2,9 @@ STM32 External Interrupt Controller ...@@ -2,7 +2,9 @@ STM32 External Interrupt Controller
Required properties: Required properties:
- compatible: Should be "st,stm32-exti" - compatible: Should be:
"st,stm32-exti"
"st,stm32h7-exti"
- reg: Specifies base physical address and size of the registers - reg: Specifies base physical address and size of the registers
- interrupt-controller: Indentifies the node as an interrupt controller - interrupt-controller: Indentifies the node as an interrupt controller
- #interrupt-cells: Specifies the number of cells to encode an interrupt - #interrupt-cells: Specifies the number of cells to encode an interrupt
......
...@@ -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);
......
...@@ -556,6 +556,25 @@ config QCOM_QDF2400_ERRATUM_0065 ...@@ -556,6 +556,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
......
...@@ -161,6 +161,9 @@ config ARCH_SEATTLE ...@@ -161,6 +161,9 @@ config ARCH_SEATTLE
config ARCH_SHMOBILE config ARCH_SHMOBILE
bool bool
config ARCH_SYNQUACER
bool "Socionext SynQuacer SoC Family"
config ARCH_RENESAS config ARCH_RENESAS
bool "Renesas SoC Platforms" bool "Renesas SoC Platforms"
select ARCH_SHMOBILE select ARCH_SHMOBILE
......
...@@ -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);
......
...@@ -42,8 +42,8 @@ extern int mp_irqdomain_alloc(struct irq_domain *domain, unsigned int virq, ...@@ -42,8 +42,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,
......
...@@ -2097,7 +2097,7 @@ static inline void __init check_timer(void) ...@@ -2097,7 +2097,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);
...@@ -2119,7 +2119,7 @@ static inline void __init check_timer(void) ...@@ -2119,7 +2119,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");
...@@ -2978,8 +2978,8 @@ void mp_irqdomain_free(struct irq_domain *domain, unsigned int virq, ...@@ -2978,8 +2978,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;
...@@ -2989,6 +2989,7 @@ void mp_irqdomain_activate(struct irq_domain *domain, ...@@ -2989,6 +2989,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,
......
...@@ -4173,8 +4173,8 @@ static void irq_remapping_free(struct irq_domain *domain, unsigned int virq, ...@@ -4173,8 +4173,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;
...@@ -4183,6 +4183,7 @@ static void irq_remapping_activate(struct irq_domain *domain, ...@@ -4183,6 +4183,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,
......
...@@ -1390,12 +1390,13 @@ static void intel_irq_remapping_free(struct irq_domain *domain, ...@@ -1390,12 +1390,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
...@@ -307,6 +309,7 @@ config EZNPS_GIC ...@@ -307,6 +309,7 @@ config EZNPS_GIC
config STM32_EXTI config STM32_EXTI
bool bool
select IRQ_DOMAIN select IRQ_DOMAIN
select GENERIC_IRQ_CHIP
config QCOM_IRQ_COMBINER config QCOM_IRQ_COMBINER
bool "QCOM IRQ combiner support" bool "QCOM IRQ combiner support"
...@@ -324,3 +327,13 @@ config IRQ_UNIPHIER_AIDET ...@@ -324,3 +327,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
...@@ -81,3 +81,5 @@ obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o irq-aspeed-i2c-ic.o ...@@ -81,3 +81,5 @@ 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_ARCH_SYNQUACER) += irq-sni-exiu.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);
if (ct->chip.irq_ack) {
/* Clear unmasked non-wakeup interrupts */ /* Clear unmasked non-wakeup interrupts */
irq_reg_writel(gc, ~b->saved_mask & ~gc->wake_active, CPU_CLEAR); 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 = 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_ack = irq_gc_ack_set_bit;
ct->regs.ack = CPU_CLEAR; 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) {
void __iomem *alt;
/*
* Check for a stupid firmware that only exposes the
* first page of a GICv2.
*/
if (!gic_check_gicv2(*base))
return false; return false;
if (resource_size(&cpuif_res) == SZ_128K) {
u32 val_low, val_high;
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.
This diff is collapsed.
...@@ -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)) {
......
/*
* Driver for Socionext External Interrupt Unit (EXIU)
*
* Copyright (c) 2017 Linaro, Ltd. <ard.biesheuvel@linaro.org>
*
* Based on irq-tegra.c:
* Copyright (C) 2011 Google, Inc.
* Copyright (C) 2010,2013, NVIDIA Corporation
*
* 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
* published by the Free Software Foundation.
*/
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/irqchip.h>
#include <linux/irqdomain.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#define NUM_IRQS 32
#define EIMASK 0x00
#define EISRCSEL 0x04
#define EIREQSTA 0x08
#define EIRAWREQSTA 0x0C
#define EIREQCLR 0x10
#define EILVL 0x14
#define EIEDG 0x18
#define EISIR 0x1C
struct exiu_irq_data {
void __iomem *base;
u32 spi_base;
};
static void exiu_irq_eoi(struct irq_data *d)
{
struct exiu_irq_data *data = irq_data_get_irq_chip_data(d);
writel(BIT(d->hwirq), data->base + EIREQCLR);
irq_chip_eoi_parent(d);
}
static void exiu_irq_mask(struct irq_data *d)
{
struct exiu_irq_data *data = irq_data_get_irq_chip_data(d);
u32 val;
val = readl_relaxed(data->base + EIMASK) | BIT(d->hwirq);
writel_relaxed(val, data->base + EIMASK);
irq_chip_mask_parent(d);
}
static void exiu_irq_unmask(struct irq_data *d)
{
struct exiu_irq_data *data = irq_data_get_irq_chip_data(d);
u32 val;
val = readl_relaxed(data->base + EIMASK) & ~BIT(d->hwirq);
writel_relaxed(val, data->base + EIMASK);
irq_chip_unmask_parent(d);
}
static void exiu_irq_enable(struct irq_data *d)
{
struct exiu_irq_data *data = irq_data_get_irq_chip_data(d);
u32 val;
/* clear interrupts that were latched while disabled */
writel_relaxed(BIT(d->hwirq), data->base + EIREQCLR);
val = readl_relaxed(data->base + EIMASK) & ~BIT(d->hwirq);
writel_relaxed(val, data->base + EIMASK);
irq_chip_enable_parent(d);
}
static int exiu_irq_set_type(struct irq_data *d, unsigned int type)
{
struct exiu_irq_data *data = irq_data_get_irq_chip_data(d);
u32 val;
val = readl_relaxed(data->base + EILVL);
if (type == IRQ_TYPE_EDGE_RISING || type == IRQ_TYPE_LEVEL_HIGH)
val |= BIT(d->hwirq);
else
val &= ~BIT(d->hwirq);
writel_relaxed(val, data->base + EILVL);
val = readl_relaxed(data->base + EIEDG);
if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH)
val &= ~BIT(d->hwirq);
else
val |= BIT(d->hwirq);
writel_relaxed(val, data->base + EIEDG);
writel_relaxed(BIT(d->hwirq), data->base + EIREQCLR);
return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH);
}
static struct irq_chip exiu_irq_chip = {
.name = "EXIU",
.irq_eoi = exiu_irq_eoi,
.irq_enable = exiu_irq_enable,
.irq_mask = exiu_irq_mask,
.irq_unmask = exiu_irq_unmask,
.irq_set_type = exiu_irq_set_type,
.irq_set_affinity = irq_chip_set_affinity_parent,
.flags = IRQCHIP_SET_TYPE_MASKED |
IRQCHIP_SKIP_SET_WAKE |
IRQCHIP_EOI_THREADED |
IRQCHIP_MASK_ON_SUSPEND,
};
static int exiu_domain_translate(struct irq_domain *domain,
struct irq_fwspec *fwspec,
unsigned long *hwirq,
unsigned int *type)
{
struct exiu_irq_data *info = domain->host_data;
if (is_of_node(fwspec->fwnode)) {
if (fwspec->param_count != 3)
return -EINVAL;
if (fwspec->param[0] != GIC_SPI)
return -EINVAL; /* No PPI should point to this domain */
*hwirq = fwspec->param[1] - info->spi_base;
*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
return 0;
}
return -EINVAL;
}
static int exiu_domain_alloc(struct irq_domain *dom, unsigned int virq,
unsigned int nr_irqs, void *data)
{
struct irq_fwspec *fwspec = data;
struct irq_fwspec parent_fwspec;
struct exiu_irq_data *info = dom->host_data;
irq_hw_number_t hwirq;
if (fwspec->param_count != 3)
return -EINVAL; /* Not GIC compliant */
if (fwspec->param[0] != GIC_SPI)
return -EINVAL; /* No PPI should point to this domain */
WARN_ON(nr_irqs != 1);
hwirq = fwspec->param[1] - info->spi_base;
irq_domain_set_hwirq_and_chip(dom, virq, hwirq, &exiu_irq_chip, info);
parent_fwspec = *fwspec;
parent_fwspec.fwnode = dom->parent->fwnode;
return irq_domain_alloc_irqs_parent(dom, virq, nr_irqs, &parent_fwspec);
}
static const struct irq_domain_ops exiu_domain_ops = {
.translate = exiu_domain_translate,
.alloc = exiu_domain_alloc,
.free = irq_domain_free_irqs_common,
};
static int __init exiu_init(struct device_node *node,
struct device_node *parent)
{
struct irq_domain *parent_domain, *domain;
struct exiu_irq_data *data;
int err;
if (!parent) {
pr_err("%pOF: no parent, giving up\n", node);
return -ENODEV;
}
parent_domain = irq_find_host(parent);
if (!parent_domain) {
pr_err("%pOF: unable to obtain parent domain\n", node);
return -ENXIO;
}
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
if (of_property_read_u32(node, "socionext,spi-base", &data->spi_base)) {
pr_err("%pOF: failed to parse 'spi-base' property\n", node);
err = -ENODEV;
goto out_free;
}
data->base = of_iomap(node, 0);
if (IS_ERR(data->base)) {
err = PTR_ERR(data->base);
goto out_free;
}
/* clear and mask all interrupts */
writel_relaxed(0xFFFFFFFF, data->base + EIREQCLR);
writel_relaxed(0xFFFFFFFF, data->base + EIMASK);
domain = irq_domain_add_hierarchy(parent_domain, 0, NUM_IRQS, node,
&exiu_domain_ops, data);
if (!domain) {
pr_err("%pOF: failed to allocate domain\n", node);
err = -ENOMEM;
goto out_unmap;
}
pr_info("%pOF: %d interrupts forwarded to %pOF\n", node, NUM_IRQS,
parent);
return 0;
out_unmap:
iounmap(data->base);
out_free:
kfree(data);
return err;
}
IRQCHIP_DECLARE(exiu, "socionext,synquacer-exiu", exiu_init);
...@@ -14,27 +14,99 @@ ...@@ -14,27 +14,99 @@
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
#define EXTI_IMR 0x0 #define IRQS_PER_BANK 32
#define EXTI_EMR 0x4
#define EXTI_RTSR 0x8 struct stm32_exti_bank {
#define EXTI_FTSR 0xc u32 imr_ofst;
#define EXTI_SWIER 0x10 u32 emr_ofst;
#define EXTI_PR 0x14 u32 rtsr_ofst;
u32 ftsr_ofst;
u32 swier_ofst;
u32 pr_ofst;
};
static const struct stm32_exti_bank stm32f4xx_exti_b1 = {
.imr_ofst = 0x00,
.emr_ofst = 0x04,
.rtsr_ofst = 0x08,
.ftsr_ofst = 0x0C,
.swier_ofst = 0x10,
.pr_ofst = 0x14,
};
static const struct stm32_exti_bank *stm32f4xx_exti_banks[] = {
&stm32f4xx_exti_b1,
};
static const struct stm32_exti_bank stm32h7xx_exti_b1 = {
.imr_ofst = 0x80,
.emr_ofst = 0x84,
.rtsr_ofst = 0x00,
.ftsr_ofst = 0x04,
.swier_ofst = 0x08,
.pr_ofst = 0x88,
};
static const struct stm32_exti_bank stm32h7xx_exti_b2 = {
.imr_ofst = 0x90,
.emr_ofst = 0x94,
.rtsr_ofst = 0x20,
.ftsr_ofst = 0x24,
.swier_ofst = 0x28,
.pr_ofst = 0x98,
};
static const struct stm32_exti_bank stm32h7xx_exti_b3 = {
.imr_ofst = 0xA0,
.emr_ofst = 0xA4,
.rtsr_ofst = 0x40,
.ftsr_ofst = 0x44,
.swier_ofst = 0x48,
.pr_ofst = 0xA8,
};
static const struct stm32_exti_bank *stm32h7xx_exti_banks[] = {
&stm32h7xx_exti_b1,
&stm32h7xx_exti_b2,
&stm32h7xx_exti_b3,
};
static unsigned long stm32_exti_pending(struct irq_chip_generic *gc)
{
const struct stm32_exti_bank *stm32_bank = gc->private;
return irq_reg_readl(gc, stm32_bank->pr_ofst);
}
static void stm32_exti_irq_ack(struct irq_chip_generic *gc, u32 mask)
{
const struct stm32_exti_bank *stm32_bank = gc->private;
irq_reg_writel(gc, mask, stm32_bank->pr_ofst);
}
static void stm32_irq_handler(struct irq_desc *desc) static void stm32_irq_handler(struct irq_desc *desc)
{ {
struct irq_domain *domain = irq_desc_get_handler_data(desc); struct irq_domain *domain = irq_desc_get_handler_data(desc);
struct irq_chip_generic *gc = domain->gc->gc[0];
struct irq_chip *chip = irq_desc_get_chip(desc); struct irq_chip *chip = irq_desc_get_chip(desc);
unsigned int virq, nbanks = domain->gc->num_chips;
struct irq_chip_generic *gc;
const struct stm32_exti_bank *stm32_bank;
unsigned long pending; unsigned long pending;
int n; int n, i, irq_base = 0;
chained_irq_enter(chip, desc); chained_irq_enter(chip, desc);
while ((pending = irq_reg_readl(gc, EXTI_PR))) { for (i = 0; i < nbanks; i++, irq_base += IRQS_PER_BANK) {
for_each_set_bit(n, &pending, BITS_PER_LONG) { gc = irq_get_domain_generic_chip(domain, irq_base);
generic_handle_irq(irq_find_mapping(domain, n)); stm32_bank = gc->private;
irq_reg_writel(gc, BIT(n), EXTI_PR);
while ((pending = stm32_exti_pending(gc))) {
for_each_set_bit(n, &pending, IRQS_PER_BANK) {
virq = irq_find_mapping(domain, irq_base + n);
generic_handle_irq(virq);
stm32_exti_irq_ack(gc, BIT(n));
}
} }
} }
...@@ -44,13 +116,14 @@ static void stm32_irq_handler(struct irq_desc *desc) ...@@ -44,13 +116,14 @@ static void stm32_irq_handler(struct irq_desc *desc)
static int stm32_irq_set_type(struct irq_data *data, unsigned int type) static int stm32_irq_set_type(struct irq_data *data, unsigned int type)
{ {
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
int pin = data->hwirq; const struct stm32_exti_bank *stm32_bank = gc->private;
int pin = data->hwirq % IRQS_PER_BANK;
u32 rtsr, ftsr; u32 rtsr, ftsr;
irq_gc_lock(gc); irq_gc_lock(gc);
rtsr = irq_reg_readl(gc, EXTI_RTSR); rtsr = irq_reg_readl(gc, stm32_bank->rtsr_ofst);
ftsr = irq_reg_readl(gc, EXTI_FTSR); ftsr = irq_reg_readl(gc, stm32_bank->ftsr_ofst);
switch (type) { switch (type) {
case IRQ_TYPE_EDGE_RISING: case IRQ_TYPE_EDGE_RISING:
...@@ -70,8 +143,8 @@ static int stm32_irq_set_type(struct irq_data *data, unsigned int type) ...@@ -70,8 +143,8 @@ static int stm32_irq_set_type(struct irq_data *data, unsigned int type)
return -EINVAL; return -EINVAL;
} }
irq_reg_writel(gc, rtsr, EXTI_RTSR); irq_reg_writel(gc, rtsr, stm32_bank->rtsr_ofst);
irq_reg_writel(gc, ftsr, EXTI_FTSR); irq_reg_writel(gc, ftsr, stm32_bank->ftsr_ofst);
irq_gc_unlock(gc); irq_gc_unlock(gc);
...@@ -81,17 +154,18 @@ static int stm32_irq_set_type(struct irq_data *data, unsigned int type) ...@@ -81,17 +154,18 @@ static int stm32_irq_set_type(struct irq_data *data, unsigned int type)
static int stm32_irq_set_wake(struct irq_data *data, unsigned int on) static int stm32_irq_set_wake(struct irq_data *data, unsigned int on)
{ {
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
int pin = data->hwirq; const struct stm32_exti_bank *stm32_bank = gc->private;
u32 emr; int pin = data->hwirq % IRQS_PER_BANK;
u32 imr;
irq_gc_lock(gc); irq_gc_lock(gc);
emr = irq_reg_readl(gc, EXTI_EMR); imr = irq_reg_readl(gc, stm32_bank->imr_ofst);
if (on) if (on)
emr |= BIT(pin); imr |= BIT(pin);
else else
emr &= ~BIT(pin); imr &= ~BIT(pin);
irq_reg_writel(gc, emr, EXTI_EMR); irq_reg_writel(gc, imr, stm32_bank->imr_ofst);
irq_gc_unlock(gc); irq_gc_unlock(gc);
...@@ -101,11 +175,12 @@ static int stm32_irq_set_wake(struct irq_data *data, unsigned int on) ...@@ -101,11 +175,12 @@ static int stm32_irq_set_wake(struct irq_data *data, unsigned int on)
static int stm32_exti_alloc(struct irq_domain *d, unsigned int virq, static int stm32_exti_alloc(struct irq_domain *d, unsigned int virq,
unsigned int nr_irqs, void *data) unsigned int nr_irqs, void *data)
{ {
struct irq_chip_generic *gc = d->gc->gc[0]; struct irq_chip_generic *gc;
struct irq_fwspec *fwspec = data; struct irq_fwspec *fwspec = data;
irq_hw_number_t hwirq; irq_hw_number_t hwirq;
hwirq = fwspec->param[0]; hwirq = fwspec->param[0];
gc = irq_get_domain_generic_chip(d, hwirq);
irq_map_generic_chip(d, virq, hwirq); irq_map_generic_chip(d, virq, hwirq);
irq_domain_set_info(d, virq, hwirq, &gc->chip_types->chip, gc, irq_domain_set_info(d, virq, hwirq, &gc->chip_types->chip, gc,
...@@ -129,8 +204,9 @@ struct irq_domain_ops irq_exti_domain_ops = { ...@@ -129,8 +204,9 @@ struct irq_domain_ops irq_exti_domain_ops = {
.free = stm32_exti_free, .free = stm32_exti_free,
}; };
static int __init stm32_exti_init(struct device_node *node, static int
struct device_node *parent) __init stm32_exti_init(const struct stm32_exti_bank **stm32_exti_banks,
int bank_nr, struct device_node *node)
{ {
unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
int nr_irqs, nr_exti, ret, i; int nr_irqs, nr_exti, ret, i;
...@@ -144,14 +220,7 @@ static int __init stm32_exti_init(struct device_node *node, ...@@ -144,14 +220,7 @@ static int __init stm32_exti_init(struct device_node *node,
return -ENOMEM; return -ENOMEM;
} }
/* Determine number of irqs supported */ domain = irq_domain_add_linear(node, bank_nr * IRQS_PER_BANK,
writel_relaxed(~0UL, base + EXTI_RTSR);
nr_exti = fls(readl_relaxed(base + EXTI_RTSR));
writel_relaxed(0, base + EXTI_RTSR);
pr_info("%pOF: %d External IRQs detected\n", node, nr_exti);
domain = irq_domain_add_linear(node, nr_exti,
&irq_exti_domain_ops, NULL); &irq_exti_domain_ops, NULL);
if (!domain) { if (!domain) {
pr_err("%s: Could not register interrupt domain.\n", pr_err("%s: Could not register interrupt domain.\n",
...@@ -160,7 +229,7 @@ static int __init stm32_exti_init(struct device_node *node, ...@@ -160,7 +229,7 @@ static int __init stm32_exti_init(struct device_node *node,
goto out_unmap; goto out_unmap;
} }
ret = irq_alloc_domain_generic_chips(domain, nr_exti, 1, "exti", ret = irq_alloc_domain_generic_chips(domain, IRQS_PER_BANK, 1, "exti",
handle_edge_irq, clr, 0, 0); handle_edge_irq, clr, 0, 0);
if (ret) { if (ret) {
pr_err("%pOF: Could not allocate generic interrupt chip.\n", pr_err("%pOF: Could not allocate generic interrupt chip.\n",
...@@ -168,18 +237,41 @@ static int __init stm32_exti_init(struct device_node *node, ...@@ -168,18 +237,41 @@ static int __init stm32_exti_init(struct device_node *node,
goto out_free_domain; goto out_free_domain;
} }
gc = domain->gc->gc[0]; for (i = 0; i < bank_nr; i++) {
const struct stm32_exti_bank *stm32_bank = stm32_exti_banks[i];
u32 irqs_mask;
gc = irq_get_domain_generic_chip(domain, i * IRQS_PER_BANK);
gc->reg_base = base; gc->reg_base = base;
gc->chip_types->type = IRQ_TYPE_EDGE_BOTH; gc->chip_types->type = IRQ_TYPE_EDGE_BOTH;
gc->chip_types->chip.name = gc->chip_types[0].chip.name;
gc->chip_types->chip.irq_ack = irq_gc_ack_set_bit; gc->chip_types->chip.irq_ack = irq_gc_ack_set_bit;
gc->chip_types->chip.irq_mask = irq_gc_mask_clr_bit; gc->chip_types->chip.irq_mask = irq_gc_mask_clr_bit;
gc->chip_types->chip.irq_unmask = irq_gc_mask_set_bit; gc->chip_types->chip.irq_unmask = irq_gc_mask_set_bit;
gc->chip_types->chip.irq_set_type = stm32_irq_set_type; gc->chip_types->chip.irq_set_type = stm32_irq_set_type;
gc->chip_types->chip.irq_set_wake = stm32_irq_set_wake; gc->chip_types->chip.irq_set_wake = stm32_irq_set_wake;
gc->chip_types->regs.ack = EXTI_PR; gc->chip_types->regs.ack = stm32_bank->pr_ofst;
gc->chip_types->regs.mask = EXTI_IMR; gc->chip_types->regs.mask = stm32_bank->imr_ofst;
gc->chip_types->handler = handle_edge_irq; gc->private = (void *)stm32_bank;
/* Determine number of irqs supported */
writel_relaxed(~0UL, base + stm32_bank->rtsr_ofst);
irqs_mask = readl_relaxed(base + stm32_bank->rtsr_ofst);
nr_exti = fls(readl_relaxed(base + stm32_bank->rtsr_ofst));
/*
* This IP has no reset, so after hot reboot we should
* clear registers to avoid residue
*/
writel_relaxed(0, base + stm32_bank->imr_ofst);
writel_relaxed(0, base + stm32_bank->emr_ofst);
writel_relaxed(0, base + stm32_bank->rtsr_ofst);
writel_relaxed(0, base + stm32_bank->ftsr_ofst);
writel_relaxed(~0UL, base + stm32_bank->pr_ofst);
pr_info("%s: bank%d, External IRQs available:%#x\n",
node->full_name, i, irqs_mask);
}
nr_irqs = of_irq_count(node); nr_irqs = of_irq_count(node);
for (i = 0; i < nr_irqs; i++) { for (i = 0; i < nr_irqs; i++) {
...@@ -198,4 +290,20 @@ static int __init stm32_exti_init(struct device_node *node, ...@@ -198,4 +290,20 @@ static int __init stm32_exti_init(struct device_node *node,
return ret; return ret;
} }
IRQCHIP_DECLARE(stm32_exti, "st,stm32-exti", stm32_exti_init); static int __init stm32f4_exti_of_init(struct device_node *np,
struct device_node *parent)
{
return stm32_exti_init(stm32f4xx_exti_banks,
ARRAY_SIZE(stm32f4xx_exti_banks), np);
}
IRQCHIP_DECLARE(stm32f4_exti, "st,stm32-exti", stm32f4_exti_of_init);
static int __init stm32h7_exti_of_init(struct device_node *np,
struct device_node *parent)
{
return stm32_exti_init(stm32h7xx_exti_banks,
ARRAY_SIZE(stm32h7xx_exti_banks), np);
}
IRQCHIP_DECLARE(stm32h7_exti, "st,stm32h7-exti", stm32h7_exti_of_init);
...@@ -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,
......
...@@ -99,6 +99,7 @@ enum cpuhp_state { ...@@ -99,6 +99,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,
......
...@@ -1114,6 +1114,28 @@ static inline u32 irq_reg_readl(struct irq_chip_generic *gc, ...@@ -1114,6 +1114,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)
...@@ -461,6 +462,7 @@ ...@@ -461,6 +462,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
...@@ -549,6 +551,8 @@ ...@@ -549,6 +551,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);
......
...@@ -94,6 +94,7 @@ struct irq_desc { ...@@ -94,6 +94,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;
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,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;
...@@ -41,6 +42,7 @@ struct of_device_id; ...@@ -41,6 +42,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
...@@ -105,18 +107,21 @@ struct irq_domain_ops { ...@@ -105,18 +107,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;
...@@ -134,8 +139,8 @@ struct irq_domain_chip_generic; ...@@ -134,8 +139,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.
...@@ -173,6 +178,7 @@ struct irq_domain { ...@@ -173,6 +178,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[];
}; };
...@@ -438,7 +444,7 @@ extern int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base, ...@@ -438,7 +444,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,
...@@ -508,8 +514,6 @@ static inline bool irq_domain_is_msi_remap(struct irq_domain *domain) ...@@ -508,8 +514,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)
{ {
...@@ -558,8 +562,6 @@ irq_domain_hierarchical_is_msi_remap(struct irq_domain *domain) ...@@ -558,8 +562,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)
{ {
......
...@@ -284,6 +284,11 @@ enum { ...@@ -284,6 +284,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
......
...@@ -14,3 +14,4 @@ obj-$(CONFIG_GENERIC_MSI_IRQ) += msi.o ...@@ -14,3 +14,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
...@@ -54,7 +54,7 @@ unsigned long probe_irq_on(void) ...@@ -54,7 +54,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)
ret = __irq_startup(desc); ret = __irq_startup(desc);
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];
......
...@@ -75,6 +75,8 @@ extern void __enable_irq(struct irq_desc *desc); ...@@ -75,6 +75,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);
...@@ -437,6 +439,18 @@ static inline bool irq_fixup_move_pending(struct irq_desc *desc, bool fclear) ...@@ -437,6 +439,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>
...@@ -444,7 +458,9 @@ void irq_add_debugfs_entry(unsigned int irq, struct irq_desc *desc); ...@@ -444,7 +458,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
...@@ -459,4 +475,7 @@ static inline void irq_add_debugfs_entry(unsigned int irq, struct irq_desc *d) ...@@ -459,4 +475,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);
} }
} }
...@@ -921,8 +921,7 @@ static void virq_debug_show_one(struct seq_file *m, struct irq_desc *desc) ...@@ -921,8 +921,7 @@ static void virq_debug_show_one(struct seq_file *m, struct irq_desc *desc)
chip = irq_data_get_irq_chip(data); chip = irq_data_get_irq_chip(data);
seq_printf(m, "%-15s ", (chip && chip->name) ? chip->name : "none"); seq_printf(m, "%-15s ", (chip && chip->name) ? chip->name : "none");
seq_printf(m, data ? "0x%p " : " %p ", seq_printf(m, "0x%p ", irq_data_get_irq_chip_data(data));
irq_data_get_irq_chip_data(data));
seq_printf(m, " %c ", (desc->action && desc->action->handler) ? '*' : ' '); seq_printf(m, " %c ", (desc->action && desc->action->handler) ? '*' : ' ');
direct = (irq == hwirq) && (irq < domain->revmap_direct_max_irq); direct = (irq == hwirq) && (irq < domain->revmap_direct_max_irq);
...@@ -1459,11 +1458,11 @@ static void irq_domain_fix_revmap(struct irq_data *d) ...@@ -1459,11 +1458,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 +1681,36 @@ void irq_domain_free_irqs_parent(struct irq_domain *domain, ...@@ -1682,28 +1681,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 +1721,15 @@ static void __irq_domain_deactivate_irq(struct irq_data *irq_data) ...@@ -1714,12 +1721,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 +1820,8 @@ irq_domain_debug_show_one(struct seq_file *m, struct irq_domain *d, int ind) ...@@ -1810,6 +1820,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;
......
...@@ -398,7 +398,8 @@ int irq_select_affinity_usr(unsigned int irq) ...@@ -398,7 +398,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
...@@ -536,7 +537,7 @@ void __enable_irq(struct irq_desc *desc) ...@@ -536,7 +537,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:
...@@ -1305,7 +1306,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) ...@@ -1305,7 +1306,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
* thread_mask assigned. See the loop above which or's * thread_mask assigned. See the loop above which or's
* all existing action->thread_mask bits. * all existing action->thread_mask bits.
*/ */
new->thread_mask = 1 << ffz(thread_mask); new->thread_mask = 1UL << ffz(thread_mask);
} else if (new->handler == irq_default_primary_handler && } else if (new->handler == irq_default_primary_handler &&
!(desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)) { !(desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)) {
...@@ -1342,6 +1343,21 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) ...@@ -1342,6 +1343,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);
...@@ -1417,7 +1433,6 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) ...@@ -1417,7 +1433,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;
} }
/** /**
......
...@@ -155,7 +155,8 @@ static ssize_t write_irq_affinity(int type, struct file *file, ...@@ -155,7 +155,8 @@ static ssize_t write_irq_affinity(int type, struct file *file,
*/ */
err = irq_select_affinity_usr(irq) ? -EINVAL : count; err = irq_select_affinity_usr(irq) ? -EINVAL : count;
} else { } else {
irq_set_affinity(irq, new_value); err = irq_set_affinity(irq, new_value);
if (!err)
err = count; err = count;
} }
......
...@@ -131,9 +131,9 @@ bool irq_work_needs_cpu(void) ...@@ -131,9 +131,9 @@ bool irq_work_needs_cpu(void)
static void irq_work_run_list(struct llist_head *list) static void irq_work_run_list(struct llist_head *list)
{ {
unsigned long flags; struct irq_work *work, *tmp;
struct irq_work *work;
struct llist_node *llnode; struct llist_node *llnode;
unsigned long flags;
BUG_ON(!irqs_disabled()); BUG_ON(!irqs_disabled());
...@@ -141,11 +141,7 @@ static void irq_work_run_list(struct llist_head *list) ...@@ -141,11 +141,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_safe(work, tmp, 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