Commit 863bae1f authored by Thomas Gleixner's avatar Thomas Gleixner

Merge tag 'irqchip-5.10' of...

Merge tag 'irqchip-5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms into irq/core

Pull irqchip updates from Marc Zyngier:

 Core changes:

  - Allow irq retriggering to follow a hierarchy
  - Allow interrupt hierarchies to be trimmed at allocation time
  - Allow interrupts to be hidden from /proc/interrupts (IPIs)
  - Introduce stub for set_handle_irq() when !GENERIC_IRQ_MULTI_HANDLER
  - New per-cpu IPI handling flow

 Architecture changes:
  - Move arm/arm64 IPI handling to the core interrupt code, removing
    the home brewed accounting

 Driver updates:
 - New driver for the MStar (and more recently Mediatek) platforms
 - New driver for the Actions Owl SIRQ controller
 - New driver for the TI PRUSS infrastructure
 - Wake-up support for the Qualcomm PDC controller
 - Primary interrupt controller support for the Designware APB ICTL
 - Convert the IPI code for GIC, GICv3, hip04, armada-270-xp and bcm2836
   to using standard interrupts
 - Improve GICv3 pseudo-NMI support to deal with both non-secure and secure
   priorities on arm64
 - Convert the GIC/GICv3 drivers to using HW-based irq retrigger
 - A sprinkling of dev_err_probe() conversion
 - A set of NVIDIA Tegra fixes for interrupt hierarchy corruption
 - A reset fix for the Loongson HTVEC driver
 - A couple of error handling fixes in the TI SCI drivers
parents cdabce2e 63ea38a4
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/interrupt-controller/actions,owl-sirq.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Actions Semi Owl SoCs SIRQ interrupt controller
maintainers:
- Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
- Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
description: |
This interrupt controller is found in the Actions Semi Owl SoCs (S500, S700
and S900) and provides support for handling up to 3 external interrupt lines.
properties:
compatible:
enum:
- actions,s500-sirq
- actions,s700-sirq
- actions,s900-sirq
reg:
maxItems: 1
interrupt-controller: true
'#interrupt-cells':
const: 2
description:
The first cell is the input IRQ number, between 0 and 2, while the second
cell is the trigger type as defined in interrupt.txt in this directory.
'interrupts':
description: |
Contains the GIC SPI IRQs mapped to the external interrupt lines.
They shall be specified sequentially from output 0 to 2.
minItems: 3
maxItems: 3
required:
- compatible
- reg
- interrupt-controller
- '#interrupt-cells'
- 'interrupts'
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
sirq: interrupt-controller@b01b0200 {
compatible = "actions,s500-sirq";
reg = <0xb01b0200 0x4>;
interrupt-controller;
#interrupt-cells = <2>;
interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>, /* SIRQ0 */
<GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>, /* SIRQ1 */
<GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>; /* SIRQ2 */
};
...
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/interrupt-controller/mstar,mst-intc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: MStar Interrupt Controller
maintainers:
- Mark-PK Tsai <mark-pk.tsai@mediatek.com>
description: |+
MStar, SigmaStar and Mediatek TV SoCs contain multiple legacy
interrupt controllers that routes interrupts to the GIC.
The HW block exposes a number of interrupt controllers, each
can support up to 64 interrupts.
properties:
compatible:
const: mstar,mst-intc
interrupt-controller: true
"#interrupt-cells":
const: 3
description: |
Use the same format as specified by GIC in arm,gic.yaml.
reg:
maxItems: 1
mstar,irqs-map-range:
description: |
The range <start, end> of parent interrupt controller's interrupt
lines that are hardwired to mstar interrupt controller.
$ref: /schemas/types.yaml#/definitions/uint32-matrix
items:
minItems: 2
maxItems: 2
mstar,intc-no-eoi:
description:
Mark this controller has no End Of Interrupt(EOI) implementation.
type: boolean
required:
- compatible
- reg
- mstar,irqs-map-range
additionalProperties: false
examples:
- |
mst_intc0: interrupt-controller@1f2032d0 {
compatible = "mstar,mst-intc";
interrupt-controller;
#interrupt-cells = <3>;
interrupt-parent = <&gic>;
reg = <0x1f2032d0 0x30>;
mstar,irqs-map-range = <0 63>;
};
...
......@@ -2,7 +2,8 @@ Synopsys DesignWare APB interrupt controller (dw_apb_ictl)
Synopsys DesignWare provides interrupt controller IP for APB known as
dw_apb_ictl. The IP is used as secondary interrupt controller in some SoCs with
APB bus, e.g. Marvell Armada 1500.
APB bus, e.g. Marvell Armada 1500. It can also be used as primary interrupt
controller in some SoCs, e.g. Hisilicon SD5203.
Required properties:
- compatible: shall be "snps,dw-apb-ictl"
......@@ -10,6 +11,8 @@ Required properties:
region starting with ENABLE_LOW register
- interrupt-controller: identifies the node as an interrupt controller
- #interrupt-cells: number of cells to encode an interrupt-specifier, shall be 1
Additional required property when it's used as secondary interrupt controller:
- interrupts: interrupt reference to primary interrupt controller
The interrupt sources map to the corresponding bits in the interrupt
......@@ -21,6 +24,7 @@ registers, i.e.
- (optional) fast interrupts start at 64.
Example:
/* dw_apb_ictl is used as secondary interrupt controller */
aic: interrupt-controller@3000 {
compatible = "snps,dw-apb-ictl";
reg = <0x3000 0xc00>;
......@@ -29,3 +33,11 @@ Example:
interrupt-parent = <&gic>;
interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
};
/* dw_apb_ictl is used as primary interrupt controller */
vic: interrupt-controller@10130000 {
compatible = "snps,dw-apb-ictl";
reg = <0x10130000 0x1000>;
interrupt-controller;
#interrupt-cells = <1>;
};
# SPDX-License-Identifier: (GPL-2.0-only or BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/interrupt-controller/ti,pruss-intc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TI PRU-ICSS Local Interrupt Controller
maintainers:
- Suman Anna <s-anna@ti.com>
description: |
Each PRU-ICSS has a single interrupt controller instance that is common
to all the PRU cores. Most interrupt controllers can route 64 input events
which are then mapped to 10 possible output interrupts through two levels
of mapping. The input events can be triggered by either the PRUs and/or
various other PRUSS internal and external peripherals. The first 2 output
interrupts (0, 1) are fed exclusively to the internal PRU cores, with the
remaining 8 (2 through 9) connected to external interrupt controllers
including the MPU and/or other PRUSS instances, DSPs or devices.
The property "ti,irqs-reserved" is used for denoting the connection
differences on the output interrupts 2 through 9. If this property is not
defined, it implies that all the PRUSS INTC output interrupts 2 through 9
(host_intr0 through host_intr7) are connected exclusively to the Arm interrupt
controller.
The K3 family of SoCs can handle 160 input events that can be mapped to 20
different possible output interrupts. The additional output interrupts (10
through 19) are connected to new sub-modules within the ICSSG instances.
This interrupt-controller node should be defined as a child node of the
corresponding PRUSS node. The node should be named "interrupt-controller".
properties:
compatible:
enum:
- ti,pruss-intc
- ti,icssg-intc
description: |
Use "ti,pruss-intc" for OMAP-L13x/AM18x/DA850 SoCs,
AM335x family of SoCs,
AM437x family of SoCs,
AM57xx family of SoCs
66AK2G family of SoCs
Use "ti,icssg-intc" for K3 AM65x & J721E family of SoCs
reg:
maxItems: 1
interrupts:
minItems: 1
maxItems: 8
description: |
All the interrupts generated towards the main host processor in the SoC.
A shared interrupt can be skipped if the desired destination and usage is
by a different processor/device.
interrupt-names:
minItems: 1
maxItems: 8
items:
pattern: host_intr[0-7]
description: |
Should use one of the above names for each valid host event interrupt
connected to Arm interrupt controller, the name should match the
corresponding host event interrupt number.
interrupt-controller: true
"#interrupt-cells":
const: 3
description: |
Client users shall use the PRU System event number (the interrupt source
that the client is interested in) [cell 1], PRU channel [cell 2] and PRU
host_event (target) [cell 3] as the value of the interrupts property in
their node. The system events can be mapped to some output host
interrupts through 2 levels of many-to-one mapping i.e. events to channel
mapping and channels to host interrupts so through this property entire
mapping is provided.
ti,irqs-reserved:
$ref: /schemas/types.yaml#definitions/uint8
description: |
Bitmask of host interrupts between 0 and 7 (corresponding to PRUSS INTC
output interrupts 2 through 9) that are not connected to the Arm interrupt
controller or are shared and used by other devices or processors in the
SoC. Define this property when any of 8 interrupts should not be handled
by Arm interrupt controller.
Eg: - AM437x and 66AK2G SoCs do not have "host_intr5" interrupt
connected to MPU
- AM65x and J721E SoCs have "host_intr5", "host_intr6" and
"host_intr7" interrupts connected to MPU, and other ICSSG
instances.
required:
- compatible
- reg
- interrupts
- interrupt-names
- interrupt-controller
- "#interrupt-cells"
additionalProperties: false
examples:
- |
/* AM33xx PRU-ICSS */
pruss: pruss@0 {
compatible = "ti,am3356-pruss";
reg = <0x0 0x80000>;
#address-cells = <1>;
#size-cells = <1>;
ranges;
pruss_intc: interrupt-controller@20000 {
compatible = "ti,pruss-intc";
reg = <0x20000 0x2000>;
interrupts = <20 21 22 23 24 25 26 27>;
interrupt-names = "host_intr0", "host_intr1",
"host_intr2", "host_intr3",
"host_intr4", "host_intr5",
"host_intr6", "host_intr7";
interrupt-controller;
#interrupt-cells = <3>;
};
};
- |
/* AM4376 PRU-ICSS */
#include <dt-bindings/interrupt-controller/arm-gic.h>
pruss@0 {
compatible = "ti,am4376-pruss";
reg = <0x0 0x40000>;
#address-cells = <1>;
#size-cells = <1>;
ranges;
interrupt-controller@20000 {
compatible = "ti,pruss-intc";
reg = <0x20000 0x2000>;
interrupt-controller;
#interrupt-cells = <3>;
interrupts = <GIC_SPI 20 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 22 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "host_intr0", "host_intr1",
"host_intr2", "host_intr3",
"host_intr4",
"host_intr6", "host_intr7";
ti,irqs-reserved = /bits/ 8 <0x20>; /* BIT(5) */
};
};
......@@ -1525,6 +1525,7 @@ F: Documentation/devicetree/bindings/arm/actions.yaml
F: Documentation/devicetree/bindings/clock/actions,owl-cmu.txt
F: Documentation/devicetree/bindings/dma/owl-dma.txt
F: Documentation/devicetree/bindings/i2c/i2c-owl.txt
F: Documentation/devicetree/bindings/interrupt-controller/actions,owl-sirq.yaml
F: Documentation/devicetree/bindings/mmc/owl-mmc.yaml
F: Documentation/devicetree/bindings/pinctrl/actions,s900-pinctrl.txt
F: Documentation/devicetree/bindings/power/actions,owl-sps.txt
......@@ -1536,6 +1537,7 @@ F: drivers/clk/actions/
F: drivers/clocksource/timer-owl*
F: drivers/dma/owl-dma.c
F: drivers/i2c/busses/i2c-owl.c
F: drivers/irqchip/irq-owl-sirq.c
F: drivers/mmc/host/owl-mmc.c
F: drivers/pinctrl/actions/*
F: drivers/soc/actions/
......@@ -11779,6 +11781,13 @@ Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
F: drivers/media/usb/msi2500/
MSTAR INTERRUPT CONTROLLER DRIVER
M: Mark-PK Tsai <mark-pk.tsai@mediatek.com>
M: Daniel Palmer <daniel@thingy.jp>
S: Maintained
F: Documentation/devicetree/bindings/interrupt-controller/mstar,mst-intc.yaml
F: drivers/irqchip/irq-mst-intc.c
MSYSTEMS DISKONCHIP G3 MTD DRIVER
M: Robert Jarzmik <robert.jarzmik@free.fr>
L: linux-mtd@lists.infradead.org
......
......@@ -49,6 +49,7 @@ config ARM
select GENERIC_ARCH_TOPOLOGY if ARM_CPU_TOPOLOGY
select GENERIC_ATOMIC64 if CPU_V7M || CPU_V6 || !CPU_32v6K || !AEABI
select GENERIC_CLOCKEVENTS_BROADCAST if SMP
select GENERIC_IRQ_IPI if SMP
select GENERIC_CPU_AUTOPROBE
select GENERIC_EARLY_IOREMAP
select GENERIC_IDLE_POLL_SETUP
......
......@@ -6,29 +6,12 @@
#include <linux/threads.h>
#include <asm/irq.h>
/* number of IPIS _not_ including IPI_CPU_BACKTRACE */
#define NR_IPI 7
typedef struct {
unsigned int __softirq_pending;
#ifdef CONFIG_SMP
unsigned int ipi_irqs[NR_IPI];
#endif
} ____cacheline_aligned irq_cpustat_t;
#include <linux/irq_cpustat.h> /* Standard mappings for irq_cpustat_t above */
#define __inc_irq_stat(cpu, member) __IRQ_STAT(cpu, member)++
#define __get_irq_stat(cpu, member) __IRQ_STAT(cpu, member)
#ifdef CONFIG_SMP
u64 smp_irq_stat_cpu(unsigned int cpu);
#else
#define smp_irq_stat_cpu(cpu) 0
#endif
#define arch_irq_stat_cpu smp_irq_stat_cpu
#define __ARCH_IRQ_EXIT_IRQS_DISABLED 1
#endif /* __ASM_HARDIRQ_H */
......@@ -39,11 +39,10 @@ void handle_IPI(int ipinr, struct pt_regs *regs);
*/
extern void smp_init_cpus(void);
/*
* Provide a function to raise an IPI cross call on CPUs in callmap.
* Register IPI interrupts with the arch SMP code
*/
extern void set_smp_cross_call(void (*)(const struct cpumask *, unsigned int));
extern void set_smp_ipi_range(int ipi_base, int nr_ipi);
/*
* Called from platform specific assembly code, this is the
......
......@@ -18,7 +18,6 @@
* IRQ's are in fact implemented a bit like signal handlers for the kernel.
* Naturally it's not a 1:1 relation, but there are similarities.
*/
#include <linux/kernel_stat.h>
#include <linux/signal.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
......
......@@ -26,6 +26,7 @@
#include <linux/completion.h>
#include <linux/cpufreq.h>
#include <linux/irq_work.h>
#include <linux/kernel_stat.h>
#include <linux/atomic.h>
#include <asm/bugs.h>
......@@ -65,18 +66,26 @@ enum ipi_msg_type {
IPI_CPU_STOP,
IPI_IRQ_WORK,
IPI_COMPLETION,
NR_IPI,
/*
* CPU_BACKTRACE is special and not included in NR_IPI
* or tracable with trace_ipi_*
*/
IPI_CPU_BACKTRACE,
IPI_CPU_BACKTRACE = NR_IPI,
/*
* SGI8-15 can be reserved by secure firmware, and thus may
* not be usable by the kernel. Please keep the above limited
* to at most 8 entries.
*/
MAX_IPI
};
static int ipi_irq_base __read_mostly;
static int nr_ipi __read_mostly = NR_IPI;
static struct irq_desc *ipi_desc[MAX_IPI] __read_mostly;
static void ipi_setup(int cpu);
static DECLARE_COMPLETION(cpu_running);
static struct smp_operations smp_ops __ro_after_init;
......@@ -226,6 +235,17 @@ int platform_can_hotplug_cpu(unsigned int cpu)
return cpu != 0;
}
static void ipi_teardown(int cpu)
{
int i;
if (WARN_ON_ONCE(!ipi_irq_base))
return;
for (i = 0; i < nr_ipi; i++)
disable_percpu_irq(ipi_irq_base + i);
}
/*
* __cpu_disable runs on the processor to be shutdown.
*/
......@@ -247,6 +267,7 @@ int __cpu_disable(void)
* and we must not schedule until we're ready to give up the cpu.
*/
set_cpu_online(cpu, false);
ipi_teardown(cpu);
/*
* OK - migrate IRQs away from this CPU
......@@ -422,6 +443,8 @@ asmlinkage void secondary_start_kernel(void)
notify_cpu_starting(cpu);
ipi_setup(cpu);
calibrate_delay();
smp_store_cpu_info(cpu);
......@@ -500,14 +523,6 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
}
}
static void (*__smp_cross_call)(const struct cpumask *, unsigned int);
void __init set_smp_cross_call(void (*fn)(const struct cpumask *, unsigned int))
{
if (!__smp_cross_call)
__smp_cross_call = fn;
}
static const char *ipi_types[NR_IPI] __tracepoint_string = {
#define S(x,s) [x] = s
S(IPI_WAKEUP, "CPU wakeup interrupts"),
......@@ -519,38 +534,28 @@ static const char *ipi_types[NR_IPI] __tracepoint_string = {
S(IPI_COMPLETION, "completion interrupts"),
};
static void smp_cross_call(const struct cpumask *target, unsigned int ipinr)
{
trace_ipi_raise_rcuidle(target, ipi_types[ipinr]);
__smp_cross_call(target, ipinr);
}
static void smp_cross_call(const struct cpumask *target, unsigned int ipinr);
void show_ipi_list(struct seq_file *p, int prec)
{
unsigned int cpu, i;
for (i = 0; i < NR_IPI; i++) {
unsigned int irq;
if (!ipi_desc[i])
continue;
irq = irq_desc_get_irq(ipi_desc[i]);
seq_printf(p, "%*s%u: ", prec - 1, "IPI", i);
for_each_online_cpu(cpu)
seq_printf(p, "%10u ",
__get_irq_stat(cpu, ipi_irqs[i]));
seq_printf(p, "%10u ", kstat_irqs_cpu(irq, cpu));
seq_printf(p, " %s\n", ipi_types[i]);
}
}
u64 smp_irq_stat_cpu(unsigned int cpu)
{
u64 sum = 0;
int i;
for (i = 0; i < NR_IPI; i++)
sum += __get_irq_stat(cpu, ipi_irqs[i]);
return sum;
}
void arch_send_call_function_ipi_mask(const struct cpumask *mask)
{
smp_cross_call(mask, IPI_CALL_FUNC);
......@@ -627,15 +632,12 @@ asmlinkage void __exception_irq_entry do_IPI(int ipinr, struct pt_regs *regs)
handle_IPI(ipinr, regs);
}
void handle_IPI(int ipinr, struct pt_regs *regs)
static void do_handle_IPI(int ipinr)
{
unsigned int cpu = smp_processor_id();
struct pt_regs *old_regs = set_irq_regs(regs);
if ((unsigned)ipinr < NR_IPI) {
if ((unsigned)ipinr < NR_IPI)
trace_ipi_entry_rcuidle(ipi_types[ipinr]);
__inc_irq_stat(cpu, ipi_irqs[ipinr]);
}
switch (ipinr) {
case IPI_WAKEUP:
......@@ -643,9 +645,7 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
case IPI_TIMER:
irq_enter();
tick_receive_broadcast();
irq_exit();
break;
#endif
......@@ -654,36 +654,26 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
break;
case IPI_CALL_FUNC:
irq_enter();
generic_smp_call_function_interrupt();
irq_exit();
break;
case IPI_CPU_STOP:
irq_enter();
ipi_cpu_stop(cpu);
irq_exit();
break;
#ifdef CONFIG_IRQ_WORK
case IPI_IRQ_WORK:
irq_enter();
irq_work_run();
irq_exit();
break;
#endif
case IPI_COMPLETION:
irq_enter();
ipi_complete(cpu);
irq_exit();
break;
case IPI_CPU_BACKTRACE:
printk_nmi_enter();
irq_enter();
nmi_cpu_backtrace(regs);
irq_exit();
nmi_cpu_backtrace(get_irq_regs());
printk_nmi_exit();
break;
......@@ -695,9 +685,67 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
if ((unsigned)ipinr < NR_IPI)
trace_ipi_exit_rcuidle(ipi_types[ipinr]);
}
/* Legacy version, should go away once all irqchips have been converted */
void handle_IPI(int ipinr, struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
irq_enter();
do_handle_IPI(ipinr);
irq_exit();
set_irq_regs(old_regs);
}
static irqreturn_t ipi_handler(int irq, void *data)
{
do_handle_IPI(irq - ipi_irq_base);
return IRQ_HANDLED;
}
static void smp_cross_call(const struct cpumask *target, unsigned int ipinr)
{
trace_ipi_raise_rcuidle(target, ipi_types[ipinr]);
__ipi_send_mask(ipi_desc[ipinr], target);
}
static void ipi_setup(int cpu)
{
int i;
if (WARN_ON_ONCE(!ipi_irq_base))
return;
for (i = 0; i < nr_ipi; i++)
enable_percpu_irq(ipi_irq_base + i, 0);
}
void __init set_smp_ipi_range(int ipi_base, int n)
{
int i;
WARN_ON(n < MAX_IPI);
nr_ipi = min(n, MAX_IPI);
for (i = 0; i < nr_ipi; i++) {
int err;
err = request_percpu_irq(ipi_base + i, ipi_handler,
"IPI", &irq_stat);
WARN_ON(err);
ipi_desc[i] = irq_to_desc(ipi_base + i);
irq_set_status_flags(ipi_base + i, IRQ_HIDDEN);
}
ipi_irq_base = ipi_base;
/* Setup the boot CPU immediately */
ipi_setup(smp_processor_id());
}
void smp_send_reschedule(int cpu)
{
smp_cross_call(cpumask_of(cpu), IPI_RESCHEDULE);
......@@ -805,7 +853,7 @@ core_initcall(register_cpufreq_notifier);
static void raise_nmi(cpumask_t *mask)
{
__smp_cross_call(mask, IPI_CPU_BACKTRACE);
__ipi_send_mask(ipi_desc[IPI_CPU_BACKTRACE], mask);
}
void arch_trigger_cpumask_backtrace(const cpumask_t *mask, bool exclude_self)
......
......@@ -106,6 +106,7 @@ config ARM64
select GENERIC_CPU_VULNERABILITIES
select GENERIC_EARLY_IOREMAP
select GENERIC_IDLE_POLL_SETUP
select GENERIC_IRQ_IPI
select GENERIC_IRQ_MULTI_HANDLER
select GENERIC_IRQ_PROBE
select GENERIC_IRQ_SHOW
......
......@@ -153,7 +153,7 @@ static inline bool gic_prio_masking_enabled(void)
static inline void gic_pmr_mask_irqs(void)
{
BUILD_BUG_ON(GICD_INT_DEF_PRI < (GIC_PRIO_IRQOFF |
BUILD_BUG_ON(GICD_INT_DEF_PRI < (__GIC_PRIO_IRQOFF |
GIC_PRIO_PSR_I_SET));
BUILD_BUG_ON(GICD_INT_DEF_PRI >= GIC_PRIO_IRQON);
/*
......@@ -162,6 +162,12 @@ static inline void gic_pmr_mask_irqs(void)
* are applied to IRQ priorities
*/
BUILD_BUG_ON((0x80 | (GICD_INT_DEF_PRI >> 1)) >= GIC_PRIO_IRQON);
/*
* Same situation as above, but now we make sure that we can mask
* regular interrupts.
*/
BUILD_BUG_ON((0x80 | (GICD_INT_DEF_PRI >> 1)) < (__GIC_PRIO_IRQOFF_NS |
GIC_PRIO_PSR_I_SET));
gic_write_pmr(GIC_PRIO_IRQOFF);
}
......
......@@ -13,21 +13,12 @@
#include <asm/kvm_arm.h>
#include <asm/sysreg.h>
#define NR_IPI 7
typedef struct {
unsigned int __softirq_pending;
unsigned int ipi_irqs[NR_IPI];
} ____cacheline_aligned irq_cpustat_t;
#include <linux/irq_cpustat.h> /* Standard mappings for irq_cpustat_t above */
#define __inc_irq_stat(cpu, member) __IRQ_STAT(cpu, member)++
#define __get_irq_stat(cpu, member) __IRQ_STAT(cpu, member)
u64 smp_irq_stat_cpu(unsigned int cpu);
#define arch_irq_stat_cpu smp_irq_stat_cpu
#define __ARCH_IRQ_EXIT_IRQS_DISABLED 1
struct nmi_ctx {
......
......@@ -2,11 +2,9 @@
#ifndef __ASM_IRQ_WORK_H
#define __ASM_IRQ_WORK_H
#include <asm/smp.h>
static inline bool arch_irq_work_has_interrupt(void)
{
return !!__smp_cross_call;
return true;
}
#endif /* __ASM_IRQ_WORK_H */
......@@ -31,9 +31,21 @@
* interrupt disabling temporarily does not rely on IRQ priorities.
*/
#define GIC_PRIO_IRQON 0xe0
#define GIC_PRIO_IRQOFF (GIC_PRIO_IRQON & ~0x80)
#define __GIC_PRIO_IRQOFF (GIC_PRIO_IRQON & ~0x80)
#define __GIC_PRIO_IRQOFF_NS 0xa0
#define GIC_PRIO_PSR_I_SET (1 << 4)
#define GIC_PRIO_IRQOFF \
({ \
extern struct static_key_false gic_nonsecure_priorities;\
u8 __prio = __GIC_PRIO_IRQOFF; \
\
if (static_branch_unlikely(&gic_nonsecure_priorities)) \
__prio = __GIC_PRIO_IRQOFF_NS; \
\
__prio; \
})
/* Additional SPSR bits not exposed in the UABI */
#define PSR_MODE_THREAD_BIT (1 << 0)
#define PSR_IL_BIT (1 << 20)
......
......@@ -55,16 +55,6 @@ static inline void set_cpu_logical_map(int cpu, u64 hwid)
struct seq_file;
/*
* generate IPI list text
*/
extern void show_ipi_list(struct seq_file *p, int prec);
/*
* Called from C code, this handles an IPI.
*/
extern void handle_IPI(int ipinr, struct pt_regs *regs);
/*
* Discover the set of possible CPUs and determine their
* SMP operations.
......@@ -72,11 +62,9 @@ extern void handle_IPI(int ipinr, struct pt_regs *regs);
extern void smp_init_cpus(void);
/*
* Provide a function to raise an IPI cross call on CPUs in callmap.
* Register IPI interrupts with the arch SMP code
*/
extern void set_smp_cross_call(void (*)(const struct cpumask *, unsigned int));
extern void (*__smp_cross_call)(const struct cpumask *, unsigned int);
extern void set_smp_ipi_range(int ipi_base, int nr_ipi);
/*
* Called from the secondary holding pen, this is the secondary CPU entry point.
......
......@@ -101,6 +101,8 @@ KVM_NVHE_ALIAS(vgic_v3_cpuif_trap);
/* Static key checked in pmr_sync(). */
#ifdef CONFIG_ARM64_PSEUDO_NMI
KVM_NVHE_ALIAS(gic_pmr_sync);
/* Static key checked in GIC_PRIO_IRQOFF. */
KVM_NVHE_ALIAS(gic_nonsecure_priorities);
#endif
/* EL2 exception handling */
......
......@@ -10,10 +10,10 @@
* Copyright (C) 2012 ARM Ltd.
*/
#include <linux/kernel_stat.h>
#include <linux/irq.h>
#include <linux/memory.h>
#include <linux/smp.h>
#include <linux/hardirq.h>
#include <linux/init.h>
#include <linux/irqchip.h>
#include <linux/kprobes.h>
......@@ -22,20 +22,11 @@
#include <asm/daifflags.h>
#include <asm/vmap_stack.h>
unsigned long irq_err_count;
/* Only access this in an NMI enter/exit */
DEFINE_PER_CPU(struct nmi_ctx, nmi_contexts);
DEFINE_PER_CPU(unsigned long *, irq_stack_ptr);
int arch_show_interrupts(struct seq_file *p, int prec)
{
show_ipi_list(p, prec);
seq_printf(p, "%*s: %10lu\n", prec, "Err", irq_err_count);
return 0;
}
#ifdef CONFIG_VMAP_STACK
static void init_irq_stacks(void)
{
......
......@@ -30,6 +30,7 @@
#include <linux/completion.h>
#include <linux/of.h>
#include <linux/irq_work.h>
#include <linux/kernel_stat.h>
#include <linux/kexec.h>
#include <linux/kvm_host.h>
......@@ -72,10 +73,18 @@ enum ipi_msg_type {
IPI_CPU_CRASH_STOP,
IPI_TIMER,
IPI_IRQ_WORK,
IPI_WAKEUP
IPI_WAKEUP,
NR_IPI
};
static int ipi_irq_base __read_mostly;
static int nr_ipi __read_mostly = NR_IPI;
static struct irq_desc *ipi_desc[NR_IPI] __read_mostly;
static void ipi_setup(int cpu);
#ifdef CONFIG_HOTPLUG_CPU
static void ipi_teardown(int cpu);
static int op_cpu_kill(unsigned int cpu);
#else
static inline int op_cpu_kill(unsigned int cpu)
......@@ -237,6 +246,8 @@ asmlinkage notrace void secondary_start_kernel(void)
*/
notify_cpu_starting(cpu);
ipi_setup(cpu);
store_cpu_topology(cpu);
numa_add_cpu(cpu);
......@@ -302,6 +313,7 @@ int __cpu_disable(void)
* and we must not schedule until we're ready to give up the cpu.
*/
set_cpu_online(cpu, false);
ipi_teardown(cpu);
/*
* OK - migrate IRQs away from this CPU
......@@ -772,13 +784,6 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
}
}
void (*__smp_cross_call)(const struct cpumask *, unsigned int);
void __init set_smp_cross_call(void (*fn)(const struct cpumask *, unsigned int))
{
__smp_cross_call = fn;
}
static const char *ipi_types[NR_IPI] __tracepoint_string = {
#define S(x,s) [x] = s
S(IPI_RESCHEDULE, "Rescheduling interrupts"),
......@@ -790,35 +795,25 @@ static const char *ipi_types[NR_IPI] __tracepoint_string = {
S(IPI_WAKEUP, "CPU wake-up interrupts"),
};
static void smp_cross_call(const struct cpumask *target, unsigned int ipinr)
{
trace_ipi_raise(target, ipi_types[ipinr]);
__smp_cross_call(target, ipinr);
}
static void smp_cross_call(const struct cpumask *target, unsigned int ipinr);
void show_ipi_list(struct seq_file *p, int prec)
unsigned long irq_err_count;
int arch_show_interrupts(struct seq_file *p, int prec)
{
unsigned int cpu, i;
for (i = 0; i < NR_IPI; i++) {
unsigned int irq = irq_desc_get_irq(ipi_desc[i]);
seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i,
prec >= 4 ? " " : "");
for_each_online_cpu(cpu)
seq_printf(p, "%10u ",
__get_irq_stat(cpu, ipi_irqs[i]));
seq_printf(p, "%10u ", kstat_irqs_cpu(irq, cpu));
seq_printf(p, " %s\n", ipi_types[i]);
}
}
u64 smp_irq_stat_cpu(unsigned int cpu)
{
u64 sum = 0;
int i;
for (i = 0; i < NR_IPI; i++)
sum += __get_irq_stat(cpu, ipi_irqs[i]);
return sum;
seq_printf(p, "%*s: %10lu\n", prec, "Err", irq_err_count);
return 0;
}
void arch_send_call_function_ipi_mask(const struct cpumask *mask)
......@@ -841,7 +836,6 @@ void arch_send_wakeup_ipi_mask(const struct cpumask *mask)
#ifdef CONFIG_IRQ_WORK
void arch_irq_work_raise(void)
{
if (__smp_cross_call)
smp_cross_call(cpumask_of(smp_processor_id()), IPI_IRQ_WORK);
}
#endif
......@@ -890,15 +884,12 @@ static void ipi_cpu_crash_stop(unsigned int cpu, struct pt_regs *regs)
/*
* Main handler for inter-processor interrupts
*/
void handle_IPI(int ipinr, struct pt_regs *regs)
static void do_handle_IPI(int ipinr)
{
unsigned int cpu = smp_processor_id();
struct pt_regs *old_regs = set_irq_regs(regs);
if ((unsigned)ipinr < NR_IPI) {
if ((unsigned)ipinr < NR_IPI)
trace_ipi_entry_rcuidle(ipi_types[ipinr]);
__inc_irq_stat(cpu, ipi_irqs[ipinr]);
}
switch (ipinr) {
case IPI_RESCHEDULE:
......@@ -906,21 +897,16 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
break;
case IPI_CALL_FUNC:
irq_enter();
generic_smp_call_function_interrupt();
irq_exit();
break;
case IPI_CPU_STOP:
irq_enter();
local_cpu_stop();
irq_exit();
break;
case IPI_CPU_CRASH_STOP:
if (IS_ENABLED(CONFIG_KEXEC_CORE)) {
irq_enter();
ipi_cpu_crash_stop(cpu, regs);
ipi_cpu_crash_stop(cpu, get_irq_regs());
unreachable();
}
......@@ -928,17 +914,13 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
case IPI_TIMER:
irq_enter();
tick_receive_broadcast();
irq_exit();
break;
#endif
#ifdef CONFIG_IRQ_WORK
case IPI_IRQ_WORK:
irq_enter();
irq_work_run();
irq_exit();
break;
#endif
......@@ -957,7 +939,66 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
if ((unsigned)ipinr < NR_IPI)
trace_ipi_exit_rcuidle(ipi_types[ipinr]);
set_irq_regs(old_regs);
}
static irqreturn_t ipi_handler(int irq, void *data)
{
do_handle_IPI(irq - ipi_irq_base);
return IRQ_HANDLED;
}
static void smp_cross_call(const struct cpumask *target, unsigned int ipinr)
{
trace_ipi_raise(target, ipi_types[ipinr]);
__ipi_send_mask(ipi_desc[ipinr], target);
}
static void ipi_setup(int cpu)
{
int i;
if (WARN_ON_ONCE(!ipi_irq_base))
return;
for (i = 0; i < nr_ipi; i++)
enable_percpu_irq(ipi_irq_base + i, 0);
}
#ifdef CONFIG_HOTPLUG_CPU
static void ipi_teardown(int cpu)
{
int i;
if (WARN_ON_ONCE(!ipi_irq_base))
return;
for (i = 0; i < nr_ipi; i++)
disable_percpu_irq(ipi_irq_base + i);
}
#endif
void __init set_smp_ipi_range(int ipi_base, int n)
{
int i;
WARN_ON(n < NR_IPI);
nr_ipi = min(n, NR_IPI);
for (i = 0; i < nr_ipi; i++) {
int err;
err = request_percpu_irq(ipi_base + i, ipi_handler,
"IPI", &cpu_number);
WARN_ON(err);
ipi_desc[i] = irq_to_desc(ipi_base + i);
irq_set_status_flags(ipi_base + i, IRQ_HIDDEN);
}
ipi_irq_base = ipi_base;
/* Setup the boot CPU immediately */
ipi_setup(smp_processor_id());
}
void smp_send_reschedule(int cpu)
......
......@@ -430,7 +430,18 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type)
else
irq_set_handler_locked(data, handle_edge_irq);
if (data->parent_data)
return irq_chip_set_type_parent(data, type);
return 0;
}
static int tegra186_irq_set_wake(struct irq_data *data, unsigned int on)
{
if (data->parent_data)
return irq_chip_set_wake_parent(data, on);
return 0;
}
static void tegra186_gpio_irq(struct irq_desc *desc)
......@@ -678,7 +689,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
gpio->intc.irq_mask = tegra186_irq_mask;
gpio->intc.irq_unmask = tegra186_irq_unmask;
gpio->intc.irq_set_type = tegra186_irq_set_type;
gpio->intc.irq_set_wake = irq_chip_set_wake_parent;
gpio->intc.irq_set_wake = tegra186_irq_set_wake;
irq = &gpio->gpio.irq;
irq->chip = &gpio->intc;
......
......@@ -148,7 +148,7 @@ config DAVINCI_CP_INTC
config DW_APB_ICTL
bool
select GENERIC_IRQ_CHIP
select IRQ_DOMAIN
select IRQ_DOMAIN_HIERARCHY
config FARADAY_FTINTC010
bool
......@@ -232,12 +232,12 @@ config RENESAS_INTC_IRQPIN
interrupt pins, as found on SH/R-Mobile and R-Car Gen1 SoCs.
config RENESAS_IRQC
bool "Renesas R-Mobile APE6 and R-Car IRQC support" if COMPILE_TEST
bool "Renesas R-Mobile APE6, R-Car Gen{2,3} and RZ/G{1,2} IRQC support" if COMPILE_TEST
select GENERIC_IRQ_CHIP
select IRQ_DOMAIN
help
Enable support for the Renesas Interrupt Controller for external
devices, as found on R-Mobile APE6, R-Car Gen2, and R-Car Gen3 SoCs.
devices, as found on R-Mobile APE6, R-Car Gen{2,3} and RZ/G{1,2} SoCs.
config RENESAS_RZA1_IRQC
bool "Renesas RZ/A1 IRQC support" if COMPILE_TEST
......@@ -493,6 +493,16 @@ config TI_SCI_INTA_IRQCHIP
If you wish to use interrupt aggregator irq resources managed by the
TI System Controller, say Y here. Otherwise, say N.
config TI_PRUSS_INTC
tristate "TI PRU-ICSS Interrupt Controller"
depends on ARCH_DAVINCI || SOC_AM33XX || SOC_AM43XX || SOC_DRA7XX || ARCH_KEYSTONE || ARCH_K3
select IRQ_DOMAIN
help
This enables support for the PRU-ICSS Local Interrupt Controller
present within a PRU-ICSS subsystem present on various TI SoCs.
The PRUSS INTC enables various interrupts to be routed to multiple
different processors within the SoC.
config RISCV_INTC
bool "RISC-V Local Interrupt Controller"
depends on RISCV
......@@ -571,4 +581,12 @@ config LOONGSON_PCH_MSI
help
Support for the Loongson PCH MSI Controller.
config MST_IRQ
bool "MStar Interrupt Controller"
default ARCH_MEDIATEK
select IRQ_DOMAIN
select IRQ_DOMAIN_HIERARCHY
help
Support MStar Interrupt Controller.
endmenu
......@@ -7,6 +7,7 @@ obj-$(CONFIG_ATH79) += irq-ath79-cpu.o
obj-$(CONFIG_ATH79) += irq-ath79-misc.o
obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o
obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2836.o
obj-$(CONFIG_ARCH_ACTIONS) += irq-owl-sirq.o
obj-$(CONFIG_DAVINCI_AINTC) += irq-davinci-aintc.o
obj-$(CONFIG_DAVINCI_CP_INTC) += irq-davinci-cp-intc.o
obj-$(CONFIG_EXYNOS_IRQ_COMBINER) += exynos-combiner.o
......@@ -106,8 +107,10 @@ obj-$(CONFIG_MADERA_IRQ) += irq-madera.o
obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o
obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o
obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o
obj-$(CONFIG_TI_PRUSS_INTC) += irq-pruss-intc.o
obj-$(CONFIG_LOONGSON_LIOINTC) += irq-loongson-liointc.o
obj-$(CONFIG_LOONGSON_HTPIC) += irq-loongson-htpic.o
obj-$(CONFIG_LOONGSON_HTVEC) += irq-loongson-htvec.o
obj-$(CONFIG_LOONGSON_PCH_PIC) += irq-loongson-pch-pic.o
obj-$(CONFIG_LOONGSON_PCH_MSI) += irq-loongson-pch-msi.o
obj-$(CONFIG_MST_IRQ) += irq-mst-intc.o
......@@ -310,7 +310,134 @@ static inline int armada_370_xp_msi_init(struct device_node *node,
}
#endif
static void armada_xp_mpic_perf_init(void)
{
unsigned long cpuid = cpu_logical_map(smp_processor_id());
/* Enable Performance Counter Overflow interrupts */
writel(ARMADA_370_XP_INT_CAUSE_PERF(cpuid),
per_cpu_int_base + ARMADA_370_XP_INT_FABRIC_MASK_OFFS);
}
#ifdef CONFIG_SMP
static struct irq_domain *ipi_domain;
static void armada_370_xp_ipi_mask(struct irq_data *d)
{
u32 reg;
reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
reg &= ~BIT(d->hwirq);
writel(reg, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
}
static void armada_370_xp_ipi_unmask(struct irq_data *d)
{
u32 reg;
reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
reg |= BIT(d->hwirq);
writel(reg, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
}
static void armada_370_xp_ipi_send_mask(struct irq_data *d,
const struct cpumask *mask)
{
unsigned long map = 0;
int cpu;
/* Convert our logical CPU mask into a physical one. */
for_each_cpu(cpu, mask)
map |= 1 << cpu_logical_map(cpu);
/*
* Ensure that stores to Normal memory are visible to the
* other CPUs before issuing the IPI.
*/
dsb();
/* submit softirq */
writel((map << 8) | d->hwirq, main_int_base +
ARMADA_370_XP_SW_TRIG_INT_OFFS);
}
static void armada_370_xp_ipi_eoi(struct irq_data *d)
{
writel(~BIT(d->hwirq), per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
}
static struct irq_chip ipi_irqchip = {
.name = "IPI",
.irq_mask = armada_370_xp_ipi_mask,
.irq_unmask = armada_370_xp_ipi_unmask,
.irq_eoi = armada_370_xp_ipi_eoi,
.ipi_send_mask = armada_370_xp_ipi_send_mask,
};
static int armada_370_xp_ipi_alloc(struct irq_domain *d,
unsigned int virq,
unsigned int nr_irqs, void *args)
{
int i;
for (i = 0; i < nr_irqs; i++) {
irq_set_percpu_devid(virq + i);
irq_domain_set_info(d, virq + i, i, &ipi_irqchip,
d->host_data,
handle_percpu_devid_fasteoi_ipi,
NULL, NULL);
}
return 0;
}
static void armada_370_xp_ipi_free(struct irq_domain *d,
unsigned int virq,
unsigned int nr_irqs)
{
/* Not freeing IPIs */
}
static const struct irq_domain_ops ipi_domain_ops = {
.alloc = armada_370_xp_ipi_alloc,
.free = armada_370_xp_ipi_free,
};
static void ipi_resume(void)
{
int i;
for (i = 0; i < IPI_DOORBELL_END; i++) {
int irq;
irq = irq_find_mapping(ipi_domain, i);
if (irq <= 0)
continue;
if (irq_percpu_is_enabled(irq)) {
struct irq_data *d;
d = irq_domain_get_irq_data(ipi_domain, irq);
armada_370_xp_ipi_unmask(d);
}
}
}
static __init void armada_xp_ipi_init(struct device_node *node)
{
int base_ipi;
ipi_domain = irq_domain_create_linear(of_node_to_fwnode(node),
IPI_DOORBELL_END,
&ipi_domain_ops, NULL);
if (WARN_ON(!ipi_domain))
return;
irq_domain_update_bus_token(ipi_domain, DOMAIN_BUS_IPI);
base_ipi = __irq_domain_alloc_irqs(ipi_domain, -1, IPI_DOORBELL_END,
NUMA_NO_NODE, NULL, false, NULL);
if (WARN_ON(!base_ipi))
return;
set_smp_ipi_range(base_ipi, IPI_DOORBELL_END);
}
static DEFINE_RAW_SPINLOCK(irq_controller_lock);
static int armada_xp_set_affinity(struct irq_data *d,
......@@ -334,43 +461,6 @@ static int armada_xp_set_affinity(struct irq_data *d,
return IRQ_SET_MASK_OK;
}
#endif
static struct irq_chip armada_370_xp_irq_chip = {
.name = "MPIC",
.irq_mask = armada_370_xp_irq_mask,
.irq_mask_ack = armada_370_xp_irq_mask,
.irq_unmask = armada_370_xp_irq_unmask,
#ifdef CONFIG_SMP
.irq_set_affinity = armada_xp_set_affinity,
#endif
.flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND,
};
static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
unsigned int virq, irq_hw_number_t hw)
{
armada_370_xp_irq_mask(irq_get_irq_data(virq));
if (!is_percpu_irq(hw))
writel(hw, per_cpu_int_base +
ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
else
writel(hw, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS);
irq_set_status_flags(virq, IRQ_LEVEL);
if (is_percpu_irq(hw)) {
irq_set_percpu_devid(virq);
irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
handle_percpu_devid_irq);
} else {
irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
handle_level_irq);
irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(virq)));
}
irq_set_probe(virq);
return 0;
}
static void armada_xp_mpic_smp_cpu_init(void)
{
......@@ -383,48 +473,16 @@ static void armada_xp_mpic_smp_cpu_init(void)
for (i = 0; i < nr_irqs; i++)
writel(i, per_cpu_int_base + ARMADA_370_XP_INT_SET_MASK_OFFS);
/* Disable all IPIs */
writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
/* Clear pending IPIs */
writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
/* Enable first 8 IPIs */
writel(IPI_DOORBELL_MASK, per_cpu_int_base +
ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
/* Unmask IPI interrupt */
writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
}
static void armada_xp_mpic_perf_init(void)
{
unsigned long cpuid = cpu_logical_map(smp_processor_id());
/* Enable Performance Counter Overflow interrupts */
writel(ARMADA_370_XP_INT_CAUSE_PERF(cpuid),
per_cpu_int_base + ARMADA_370_XP_INT_FABRIC_MASK_OFFS);
}
#ifdef CONFIG_SMP
static void armada_mpic_send_doorbell(const struct cpumask *mask,
unsigned int irq)
{
int cpu;
unsigned long map = 0;
/* Convert our logical CPU mask into a physical one. */
for_each_cpu(cpu, mask)
map |= 1 << cpu_logical_map(cpu);
/*
* Ensure that stores to Normal memory are visible to the
* other CPUs before issuing the IPI.
*/
dsb();
/* submit softirq */
writel((map << 8) | irq, main_int_base +
ARMADA_370_XP_SW_TRIG_INT_OFFS);
}
static void armada_xp_mpic_reenable_percpu(void)
{
unsigned int irq;
......@@ -445,6 +503,8 @@ static void armada_xp_mpic_reenable_percpu(void)
armada_370_xp_irq_unmask(data);
}
ipi_resume();
}
static int armada_xp_mpic_starting_cpu(unsigned int cpu)
......@@ -462,8 +522,47 @@ static int mpic_cascaded_starting_cpu(unsigned int cpu)
enable_percpu_irq(parent_irq, IRQ_TYPE_NONE);
return 0;
}
#else
static void armada_xp_mpic_smp_cpu_init(void) {}
static void ipi_resume(void) {}
#endif
static struct irq_chip armada_370_xp_irq_chip = {
.name = "MPIC",
.irq_mask = armada_370_xp_irq_mask,
.irq_mask_ack = armada_370_xp_irq_mask,
.irq_unmask = armada_370_xp_irq_unmask,
#ifdef CONFIG_SMP
.irq_set_affinity = armada_xp_set_affinity,
#endif
.flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND,
};
static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
unsigned int virq, irq_hw_number_t hw)
{
armada_370_xp_irq_mask(irq_get_irq_data(virq));
if (!is_percpu_irq(hw))
writel(hw, per_cpu_int_base +
ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
else
writel(hw, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS);
irq_set_status_flags(virq, IRQ_LEVEL);
if (is_percpu_irq(hw)) {
irq_set_percpu_devid(virq);
irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
handle_percpu_devid_irq);
} else {
irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
handle_level_irq);
irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(virq)));
}
irq_set_probe(virq);
return 0;
}
static const struct irq_domain_ops armada_370_xp_mpic_irq_ops = {
.map = armada_370_xp_mpic_irq_map,
.xlate = irq_domain_xlate_onecell,
......@@ -562,22 +661,15 @@ armada_370_xp_handle_irq(struct pt_regs *regs)
#ifdef CONFIG_SMP
/* IPI Handling */
if (irqnr == 0) {
u32 ipimask, ipinr;
unsigned long ipimask;
int ipi;
ipimask = readl_relaxed(per_cpu_int_base +
ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS)
& IPI_DOORBELL_MASK;
writel(~ipimask, per_cpu_int_base +
ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
/* Handle all pending doorbells */
for (ipinr = IPI_DOORBELL_START;
ipinr < IPI_DOORBELL_END; ipinr++) {
if (ipimask & (0x1 << ipinr))
handle_IPI(ipinr, regs);
}
continue;
for_each_set_bit(ipi, &ipimask, IPI_DOORBELL_END)
handle_domain_irq(ipi_domain, ipi, regs);
}
#endif
......@@ -636,6 +728,8 @@ static void armada_370_xp_mpic_resume(void)
writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
if (doorbell_mask_reg & PCI_MSI_DOORBELL_MASK)
writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
ipi_resume();
}
static struct syscore_ops armada_370_xp_mpic_syscore_ops = {
......@@ -691,7 +785,7 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
irq_set_default_host(armada_370_xp_mpic_domain);
set_handle_irq(armada_370_xp_handle_irq);
#ifdef CONFIG_SMP
set_smp_cross_call(armada_mpic_send_doorbell);
armada_xp_ipi_init(node);
cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_ARMADA_XP_STARTING,
"irqchip/armada/ipi:starting",
armada_xp_mpic_starting_cpu, NULL);
......
......@@ -10,6 +10,7 @@
#include <linux/of_irq.h>
#include <linux/irqchip.h>
#include <linux/irqdomain.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqchip/irq-bcm2836.h>
#include <asm/exception.h>
......@@ -89,12 +90,24 @@ static struct irq_chip bcm2836_arm_irqchip_gpu = {
.irq_unmask = bcm2836_arm_irqchip_unmask_gpu_irq,
};
static void bcm2836_arm_irqchip_dummy_op(struct irq_data *d)
{
}
static struct irq_chip bcm2836_arm_irqchip_dummy = {
.name = "bcm2836-dummy",
.irq_eoi = bcm2836_arm_irqchip_dummy_op,
};
static int bcm2836_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hw)
{
struct irq_chip *chip;
switch (hw) {
case LOCAL_IRQ_MAILBOX0:
chip = &bcm2836_arm_irqchip_dummy;
break;
case LOCAL_IRQ_CNTPSIRQ:
case LOCAL_IRQ_CNTPNSIRQ:
case LOCAL_IRQ_CNTHPIRQ:
......@@ -127,17 +140,7 @@ __exception_irq_entry bcm2836_arm_irqchip_handle_irq(struct pt_regs *regs)
u32 stat;
stat = readl_relaxed(intc.base + LOCAL_IRQ_PENDING0 + 4 * cpu);
if (stat & BIT(LOCAL_IRQ_MAILBOX0)) {
#ifdef CONFIG_SMP
void __iomem *mailbox0 = (intc.base +
LOCAL_MAILBOX0_CLR0 + 16 * cpu);
u32 mbox_val = readl(mailbox0);
u32 ipi = ffs(mbox_val) - 1;
writel(1 << ipi, mailbox0);
handle_IPI(ipi, regs);
#endif
} else if (stat) {
if (stat) {
u32 hwirq = ffs(stat) - 1;
handle_domain_irq(intc.domain, hwirq, regs);
......@@ -145,8 +148,35 @@ __exception_irq_entry bcm2836_arm_irqchip_handle_irq(struct pt_regs *regs)
}
#ifdef CONFIG_SMP
static void bcm2836_arm_irqchip_send_ipi(const struct cpumask *mask,
unsigned int ipi)
static struct irq_domain *ipi_domain;
static void bcm2836_arm_irqchip_handle_ipi(struct irq_desc *desc)
{
struct irq_chip *chip = irq_desc_get_chip(desc);
int cpu = smp_processor_id();
u32 mbox_val;
chained_irq_enter(chip, desc);
mbox_val = readl_relaxed(intc.base + LOCAL_MAILBOX0_CLR0 + 16 * cpu);
if (mbox_val) {
int hwirq = ffs(mbox_val) - 1;
generic_handle_irq(irq_find_mapping(ipi_domain, hwirq));
}
chained_irq_exit(chip, desc);
}
static void bcm2836_arm_irqchip_ipi_eoi(struct irq_data *d)
{
int cpu = smp_processor_id();
writel_relaxed(BIT(d->hwirq),
intc.base + LOCAL_MAILBOX0_CLR0 + 16 * cpu);
}
static void bcm2836_arm_irqchip_ipi_send_mask(struct irq_data *d,
const struct cpumask *mask)
{
int cpu;
void __iomem *mailbox0_base = intc.base + LOCAL_MAILBOX0_SET0;
......@@ -157,11 +187,47 @@ static void bcm2836_arm_irqchip_send_ipi(const struct cpumask *mask,
*/
smp_wmb();
for_each_cpu(cpu, mask) {
writel(1 << ipi, mailbox0_base + 16 * cpu);
for_each_cpu(cpu, mask)
writel_relaxed(BIT(d->hwirq), mailbox0_base + 16 * cpu);
}
static struct irq_chip bcm2836_arm_irqchip_ipi = {
.name = "IPI",
.irq_mask = bcm2836_arm_irqchip_dummy_op,
.irq_unmask = bcm2836_arm_irqchip_dummy_op,
.irq_eoi = bcm2836_arm_irqchip_ipi_eoi,
.ipi_send_mask = bcm2836_arm_irqchip_ipi_send_mask,
};
static int bcm2836_arm_irqchip_ipi_alloc(struct irq_domain *d,
unsigned int virq,
unsigned int nr_irqs, void *args)
{
int i;
for (i = 0; i < nr_irqs; i++) {
irq_set_percpu_devid(virq + i);
irq_domain_set_info(d, virq + i, i, &bcm2836_arm_irqchip_ipi,
d->host_data,
handle_percpu_devid_fasteoi_ipi,
NULL, NULL);
}
return 0;
}
static void bcm2836_arm_irqchip_ipi_free(struct irq_domain *d,
unsigned int virq,
unsigned int nr_irqs)
{
/* Not freeing IPIs */
}
static const struct irq_domain_ops ipi_domain_ops = {
.alloc = bcm2836_arm_irqchip_ipi_alloc,
.free = bcm2836_arm_irqchip_ipi_free,
};
static int bcm2836_cpu_starting(unsigned int cpu)
{
bcm2836_arm_irqchip_unmask_per_cpu_irq(LOCAL_MAILBOX_INT_CONTROL0, 0,
......@@ -175,25 +241,58 @@ static int bcm2836_cpu_dying(unsigned int cpu)
cpu);
return 0;
}
#endif
static const struct irq_domain_ops bcm2836_arm_irqchip_intc_ops = {
.xlate = irq_domain_xlate_onetwocell,
.map = bcm2836_map,
};
#define BITS_PER_MBOX 32
static void
bcm2836_arm_irqchip_smp_init(void)
static void bcm2836_arm_irqchip_smp_init(void)
{
#ifdef CONFIG_SMP
struct irq_fwspec ipi_fwspec = {
.fwnode = intc.domain->fwnode,
.param_count = 1,
.param = {
[0] = LOCAL_IRQ_MAILBOX0,
},
};
int base_ipi, mux_irq;
mux_irq = irq_create_fwspec_mapping(&ipi_fwspec);
if (WARN_ON(mux_irq <= 0))
return;
ipi_domain = irq_domain_create_linear(intc.domain->fwnode,
BITS_PER_MBOX, &ipi_domain_ops,
NULL);
if (WARN_ON(!ipi_domain))
return;
ipi_domain->flags |= IRQ_DOMAIN_FLAG_IPI_SINGLE;
irq_domain_update_bus_token(ipi_domain, DOMAIN_BUS_IPI);
base_ipi = __irq_domain_alloc_irqs(ipi_domain, -1, BITS_PER_MBOX,
NUMA_NO_NODE, NULL,
false, NULL);
if (WARN_ON(!base_ipi))
return;
set_smp_ipi_range(base_ipi, BITS_PER_MBOX);
irq_set_chained_handler_and_data(mux_irq,
bcm2836_arm_irqchip_handle_ipi, NULL);
/* Unmask IPIs to the boot CPU. */
cpuhp_setup_state(CPUHP_AP_IRQ_BCM2836_STARTING,
"irqchip/bcm2836:starting", bcm2836_cpu_starting,
bcm2836_cpu_dying);
set_smp_cross_call(bcm2836_arm_irqchip_send_ipi);
#endif
}
#else
#define bcm2836_arm_irqchip_smp_init() do { } while(0)
#endif
static const struct irq_domain_ops bcm2836_arm_irqchip_intc_ops = {
.xlate = irq_domain_xlate_onetwocell,
.map = bcm2836_map,
};
/*
* The LOCAL_IRQ_CNT* timer firings are based off of the external
......@@ -232,6 +331,8 @@ static int __init bcm2836_arm_irqchip_l1_intc_of_init(struct device_node *node,
if (!intc.domain)
panic("%pOF: unable to create IRQ domain\n", node);
irq_domain_update_bus_token(intc.domain, DOMAIN_BUS_WIRED);
bcm2836_arm_irqchip_smp_init();
set_handle_irq(bcm2836_arm_irqchip_handle_irq);
......
......@@ -17,6 +17,7 @@
#include <linux/irqchip/chained_irq.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#define APB_INT_ENABLE_L 0x00
#define APB_INT_ENABLE_H 0x04
......@@ -26,7 +27,28 @@
#define APB_INT_FINALSTATUS_H 0x34
#define APB_INT_BASE_OFFSET 0x04
static void dw_apb_ictl_handler(struct irq_desc *desc)
/* irq domain of the primary interrupt controller. */
static struct irq_domain *dw_apb_ictl_irq_domain;
static void __irq_entry dw_apb_ictl_handle_irq(struct pt_regs *regs)
{
struct irq_domain *d = dw_apb_ictl_irq_domain;
int n;
for (n = 0; n < d->revmap_size; n += 32) {
struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, n);
u32 stat = readl_relaxed(gc->reg_base + APB_INT_FINALSTATUS_L);
while (stat) {
u32 hwirq = ffs(stat) - 1;
handle_domain_irq(d, hwirq, regs);
stat &= ~BIT(hwirq);
}
}
}
static void dw_apb_ictl_handle_irq_cascaded(struct irq_desc *desc)
{
struct irq_domain *d = irq_desc_get_handler_data(desc);
struct irq_chip *chip = irq_desc_get_chip(desc);
......@@ -43,13 +65,37 @@ static void dw_apb_ictl_handler(struct irq_desc *desc)
u32 virq = irq_find_mapping(d, gc->irq_base + hwirq);
generic_handle_irq(virq);
stat &= ~(1 << hwirq);
stat &= ~BIT(hwirq);
}
}
chained_irq_exit(chip, desc);
}
static int dw_apb_ictl_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs, void *arg)
{
int i, ret;
irq_hw_number_t hwirq;
unsigned int type = IRQ_TYPE_NONE;
struct irq_fwspec *fwspec = arg;
ret = irq_domain_translate_onecell(domain, fwspec, &hwirq, &type);
if (ret)
return ret;
for (i = 0; i < nr_irqs; i++)
irq_map_generic_chip(domain, virq + i, hwirq + i);
return 0;
}
static const struct irq_domain_ops dw_apb_ictl_irq_domain_ops = {
.translate = irq_domain_translate_onecell,
.alloc = dw_apb_ictl_irq_domain_alloc,
.free = irq_domain_free_irqs_top,
};
#ifdef CONFIG_PM
static void dw_apb_ictl_resume(struct irq_data *d)
{
......@@ -68,20 +114,28 @@ static void dw_apb_ictl_resume(struct irq_data *d)
static int __init dw_apb_ictl_init(struct device_node *np,
struct device_node *parent)
{
const struct irq_domain_ops *domain_ops;
unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
struct resource r;
struct irq_domain *domain;
struct irq_chip_generic *gc;
void __iomem *iobase;
int ret, nrirqs, irq, i;
int ret, nrirqs, parent_irq, i;
u32 reg;
if (!parent) {
/* Used as the primary interrupt controller */
parent_irq = 0;
domain_ops = &dw_apb_ictl_irq_domain_ops;
} else {
/* Map the parent interrupt for the chained handler */
irq = irq_of_parse_and_map(np, 0);
if (irq <= 0) {
parent_irq = irq_of_parse_and_map(np, 0);
if (parent_irq <= 0) {
pr_err("%pOF: unable to parse irq\n", np);
return -EINVAL;
}
domain_ops = &irq_generic_chip_ops;
}
ret = of_address_to_resource(np, 0, &r);
if (ret) {
......@@ -120,8 +174,7 @@ static int __init dw_apb_ictl_init(struct device_node *np,
else
nrirqs = fls(readl_relaxed(iobase + APB_INT_ENABLE_L));
domain = irq_domain_add_linear(np, nrirqs,
&irq_generic_chip_ops, NULL);
domain = irq_domain_add_linear(np, nrirqs, domain_ops, NULL);
if (!domain) {
pr_err("%pOF: unable to add irq domain\n", np);
ret = -ENOMEM;
......@@ -146,7 +199,13 @@ static int __init dw_apb_ictl_init(struct device_node *np,
gc->chip_types[0].chip.irq_resume = dw_apb_ictl_resume;
}
irq_set_chained_handler_and_data(irq, dw_apb_ictl_handler, domain);
if (parent_irq) {
irq_set_chained_handler_and_data(parent_irq,
dw_apb_ictl_handle_irq_cascaded, domain);
} else {
dw_apb_ictl_irq_domain = domain;
set_handle_irq(dw_apb_ictl_handle_irq);
}
return 0;
......
......@@ -152,9 +152,6 @@ void gic_cpu_config(void __iomem *base, int nr, void (*sync_access)(void))
writel_relaxed(GICD_INT_DEF_PRI_X4,
base + GIC_DIST_PRI + i * 4 / 4);
/* Ensure all SGI interrupts are now enabled */
writel_relaxed(GICD_INT_EN_SET_SGI, base + GIC_DIST_ENABLE_SET);
if (sync_access)
sync_access();
}
......@@ -1720,6 +1720,11 @@ static int its_irq_set_irqchip_state(struct irq_data *d,
return 0;
}
static int its_irq_retrigger(struct irq_data *d)
{
return !its_irq_set_irqchip_state(d, IRQCHIP_STATE_PENDING, true);
}
/*
* Two favourable cases:
*
......@@ -1971,6 +1976,7 @@ static struct irq_chip its_irq_chip = {
.irq_set_affinity = its_set_affinity,
.irq_compose_msi_msg = its_irq_compose_msi_msg,
.irq_set_irqchip_state = its_irq_set_irqchip_state,
.irq_retrigger = its_irq_retrigger,
.irq_set_vcpu_affinity = its_irq_set_vcpu_affinity,
};
......
This diff is collapsed.
This diff is collapsed.
......@@ -171,6 +171,29 @@ static int hip04_irq_set_affinity(struct irq_data *d,
return IRQ_SET_MASK_OK;
}
static void hip04_ipi_send_mask(struct irq_data *d, const struct cpumask *mask)
{
int cpu;
unsigned long flags, map = 0;
raw_spin_lock_irqsave(&irq_controller_lock, flags);
/* Convert our logical CPU mask into a physical one. */
for_each_cpu(cpu, mask)
map |= hip04_cpu_map[cpu];
/*
* Ensure that stores to Normal memory are visible to the
* other CPUs before they observe us issuing the IPI.
*/
dmb(ishst);
/* this always happens on GIC0 */
writel_relaxed(map << 8 | d->hwirq, hip04_data.dist_base + GIC_DIST_SOFTINT);
raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
}
#endif
static void __exception_irq_entry hip04_handle_irq(struct pt_regs *regs)
......@@ -182,19 +205,9 @@ static void __exception_irq_entry hip04_handle_irq(struct pt_regs *regs)
irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
irqnr = irqstat & GICC_IAR_INT_ID_MASK;
if (likely(irqnr > 15 && irqnr <= HIP04_MAX_IRQS)) {
if (irqnr <= HIP04_MAX_IRQS)
handle_domain_irq(hip04_data.domain, irqnr, regs);
continue;
}
if (irqnr < 16) {
writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
#ifdef CONFIG_SMP
handle_IPI(irqnr, regs);
#endif
continue;
}
break;
} while (1);
} while (irqnr > HIP04_MAX_IRQS);
}
static struct irq_chip hip04_irq_chip = {
......@@ -205,6 +218,7 @@ static struct irq_chip hip04_irq_chip = {
.irq_set_type = hip04_irq_set_type,
#ifdef CONFIG_SMP
.irq_set_affinity = hip04_irq_set_affinity,
.ipi_send_mask = hip04_ipi_send_mask,
#endif
.flags = IRQCHIP_SET_TYPE_MASKED |
IRQCHIP_SKIP_SET_WAKE |
......@@ -279,39 +293,17 @@ static void hip04_irq_cpu_init(struct hip04_irq_data *intc)
writel_relaxed(1, base + GIC_CPU_CTRL);
}
#ifdef CONFIG_SMP
static void hip04_raise_softirq(const struct cpumask *mask, unsigned int irq)
{
int cpu;
unsigned long flags, map = 0;
raw_spin_lock_irqsave(&irq_controller_lock, flags);
/* Convert our logical CPU mask into a physical one. */
for_each_cpu(cpu, mask)
map |= hip04_cpu_map[cpu];
/*
* Ensure that stores to Normal memory are visible to the
* other CPUs before they observe us issuing the IPI.
*/
dmb(ishst);
/* this always happens on GIC0 */
writel_relaxed(map << 8 | irq, hip04_data.dist_base + GIC_DIST_SOFTINT);
raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
}
#endif
static int hip04_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hw)
{
if (hw < 32) {
if (hw < 16) {
irq_set_percpu_devid(irq);
irq_set_chip_and_handler(irq, &hip04_irq_chip,
handle_percpu_devid_fasteoi_ipi);
} else if (hw < 32) {
irq_set_percpu_devid(irq);
irq_set_chip_and_handler(irq, &hip04_irq_chip,
handle_percpu_devid_irq);
irq_set_status_flags(irq, IRQ_NOAUTOEN);
} else {
irq_set_chip_and_handler(irq, &hip04_irq_chip,
handle_fasteoi_irq);
......@@ -328,10 +320,13 @@ static int hip04_irq_domain_xlate(struct irq_domain *d,
unsigned long *out_hwirq,
unsigned int *out_type)
{
unsigned long ret = 0;
if (irq_domain_get_of_node(d) != controller)
return -EINVAL;
if (intsize == 1 && intspec[0] < 16) {
*out_hwirq = intspec[0];
*out_type = IRQ_TYPE_EDGE_RISING;
return 0;
}
if (intsize < 3)
return -EINVAL;
......@@ -344,7 +339,7 @@ static int hip04_irq_domain_xlate(struct irq_domain *d,
*out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
return ret;
return 0;
}
static int hip04_irq_starting_cpu(unsigned int cpu)
......@@ -361,7 +356,6 @@ static const struct irq_domain_ops hip04_irq_domain_ops = {
static int __init
hip04_of_init(struct device_node *node, struct device_node *parent)
{
irq_hw_number_t hwirq_base = 16;
int nr_irqs, irq_base, i;
if (WARN_ON(!node))
......@@ -390,24 +384,21 @@ hip04_of_init(struct device_node *node, struct device_node *parent)
nr_irqs = HIP04_MAX_IRQS;
hip04_data.nr_irqs = nr_irqs;
nr_irqs -= hwirq_base; /* calculate # of irqs to allocate */
irq_base = irq_alloc_descs(-1, hwirq_base, nr_irqs, numa_node_id());
irq_base = irq_alloc_descs(-1, 0, nr_irqs, numa_node_id());
if (irq_base < 0) {
pr_err("failed to allocate IRQ numbers\n");
return -EINVAL;
}
hip04_data.domain = irq_domain_add_legacy(node, nr_irqs, irq_base,
hwirq_base,
0,
&hip04_irq_domain_ops,
&hip04_data);
if (WARN_ON(!hip04_data.domain))
return -EINVAL;
#ifdef CONFIG_SMP
set_smp_cross_call(hip04_raise_softirq);
set_smp_ipi_range(irq_base, 16);
#endif
set_handle_irq(hip04_handle_irq);
......
......@@ -226,12 +226,9 @@ static int imx_intmux_probe(struct platform_device *pdev)
}
data->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(data->ipg_clk)) {
ret = PTR_ERR(data->ipg_clk);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "failed to get ipg clk: %d\n", ret);
return ret;
}
if (IS_ERR(data->ipg_clk))
return dev_err_probe(&pdev->dev, PTR_ERR(data->ipg_clk),
"failed to get ipg clk\n");
data->channum = channum;
raw_spin_lock_init(&data->lock);
......
......@@ -158,12 +158,9 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
}
data->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(data->ipg_clk)) {
ret = PTR_ERR(data->ipg_clk);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "failed to get ipg clk: %d\n", ret);
return ret;
}
if (IS_ERR(data->ipg_clk))
return dev_err_probe(&pdev->dev, PTR_ERR(data->ipg_clk),
"failed to get ipg clk\n");
raw_spin_lock_init(&data->lock);
......
......@@ -151,7 +151,7 @@ static void htvec_reset(struct htvec *priv)
/* Clear IRQ cause registers, mask all interrupts */
for (idx = 0; idx < priv->num_parents; idx++) {
writel_relaxed(0x0, priv->base + HTVEC_EN_OFF + 4 * idx);
writel_relaxed(0xFFFFFFFF, priv->base);
writel_relaxed(0xFFFFFFFF, priv->base + 4 * idx);
}
}
......@@ -172,7 +172,7 @@ static int htvec_of_init(struct device_node *node,
goto free_priv;
}
/* Interrupt may come from any of the 4 interrupt line */
/* Interrupt may come from any of the 8 interrupt lines */
for (i = 0; i < HTVEC_MAX_PARENT_IRQ; i++) {
parent_irq[i] = irq_of_parse_and_map(node, i);
if (parent_irq[i] <= 0)
......
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
/*
* Copyright (c) 2020 MediaTek Inc.
* Author Mark-PK Tsai <mark-pk.tsai@mediatek.com>
*/
#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 <linux/slab.h>
#include <linux/spinlock.h>
#define INTC_MASK 0x0
#define INTC_EOI 0x20
struct mst_intc_chip_data {
raw_spinlock_t lock;
unsigned int irq_start, nr_irqs;
void __iomem *base;
bool no_eoi;
};
static void mst_set_irq(struct irq_data *d, u32 offset)
{
irq_hw_number_t hwirq = irqd_to_hwirq(d);
struct mst_intc_chip_data *cd = irq_data_get_irq_chip_data(d);
u16 val, mask;
unsigned long flags;
mask = 1 << (hwirq % 16);
offset += (hwirq / 16) * 4;
raw_spin_lock_irqsave(&cd->lock, flags);
val = readw_relaxed(cd->base + offset) | mask;
writew_relaxed(val, cd->base + offset);
raw_spin_unlock_irqrestore(&cd->lock, flags);
}
static void mst_clear_irq(struct irq_data *d, u32 offset)
{
irq_hw_number_t hwirq = irqd_to_hwirq(d);
struct mst_intc_chip_data *cd = irq_data_get_irq_chip_data(d);
u16 val, mask;
unsigned long flags;
mask = 1 << (hwirq % 16);
offset += (hwirq / 16) * 4;
raw_spin_lock_irqsave(&cd->lock, flags);
val = readw_relaxed(cd->base + offset) & ~mask;
writew_relaxed(val, cd->base + offset);
raw_spin_unlock_irqrestore(&cd->lock, flags);
}
static void mst_intc_mask_irq(struct irq_data *d)
{
mst_set_irq(d, INTC_MASK);
irq_chip_mask_parent(d);
}
static void mst_intc_unmask_irq(struct irq_data *d)
{
mst_clear_irq(d, INTC_MASK);
irq_chip_unmask_parent(d);
}
static void mst_intc_eoi_irq(struct irq_data *d)
{
struct mst_intc_chip_data *cd = irq_data_get_irq_chip_data(d);
if (!cd->no_eoi)
mst_set_irq(d, INTC_EOI);
irq_chip_eoi_parent(d);
}
static struct irq_chip mst_intc_chip = {
.name = "mst-intc",
.irq_mask = mst_intc_mask_irq,
.irq_unmask = mst_intc_unmask_irq,
.irq_eoi = mst_intc_eoi_irq,
.irq_get_irqchip_state = irq_chip_get_parent_state,
.irq_set_irqchip_state = irq_chip_set_parent_state,
.irq_set_affinity = irq_chip_set_affinity_parent,
.irq_set_vcpu_affinity = irq_chip_set_vcpu_affinity_parent,
.irq_set_type = irq_chip_set_type_parent,
.irq_retrigger = irq_chip_retrigger_hierarchy,
.flags = IRQCHIP_SET_TYPE_MASKED |
IRQCHIP_SKIP_SET_WAKE |
IRQCHIP_MASK_ON_SUSPEND,
};
static int mst_intc_domain_translate(struct irq_domain *d,
struct irq_fwspec *fwspec,
unsigned long *hwirq,
unsigned int *type)
{
struct mst_intc_chip_data *cd = d->host_data;
if (is_of_node(fwspec->fwnode)) {
if (fwspec->param_count != 3)
return -EINVAL;
/* No PPI should point to this domain */
if (fwspec->param[0] != 0)
return -EINVAL;
if (fwspec->param[1] >= cd->nr_irqs)
return -EINVAL;
*hwirq = fwspec->param[1];
*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
return 0;
}
return -EINVAL;
}
static int mst_intc_domain_alloc(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs, void *data)
{
int i;
irq_hw_number_t hwirq;
struct irq_fwspec parent_fwspec, *fwspec = data;
struct mst_intc_chip_data *cd = domain->host_data;
/* Not GIC compliant */
if (fwspec->param_count != 3)
return -EINVAL;
/* No PPI should point to this domain */
if (fwspec->param[0])
return -EINVAL;
hwirq = fwspec->param[1];
for (i = 0; i < nr_irqs; i++)
irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
&mst_intc_chip,
domain->host_data);
parent_fwspec = *fwspec;
parent_fwspec.fwnode = domain->parent->fwnode;
parent_fwspec.param[1] = cd->irq_start + hwirq;
return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &parent_fwspec);
}
static const struct irq_domain_ops mst_intc_domain_ops = {
.translate = mst_intc_domain_translate,
.alloc = mst_intc_domain_alloc,
.free = irq_domain_free_irqs_common,
};
int __init
mst_intc_of_init(struct device_node *dn, struct device_node *parent)
{
struct irq_domain *domain, *domain_parent;
struct mst_intc_chip_data *cd;
u32 irq_start, irq_end;
domain_parent = irq_find_host(parent);
if (!domain_parent) {
pr_err("mst-intc: interrupt-parent not found\n");
return -EINVAL;
}
if (of_property_read_u32_index(dn, "mstar,irqs-map-range", 0, &irq_start) ||
of_property_read_u32_index(dn, "mstar,irqs-map-range", 1, &irq_end))
return -EINVAL;
cd = kzalloc(sizeof(*cd), GFP_KERNEL);
if (!cd)
return -ENOMEM;
cd->base = of_iomap(dn, 0);
if (!cd->base) {
kfree(cd);
return -ENOMEM;
}
cd->no_eoi = of_property_read_bool(dn, "mstar,intc-no-eoi");
raw_spin_lock_init(&cd->lock);
cd->irq_start = irq_start;
cd->nr_irqs = irq_end - irq_start + 1;
domain = irq_domain_add_hierarchy(domain_parent, 0, cd->nr_irqs, dn,
&mst_intc_domain_ops, cd);
if (!domain) {
iounmap(cd->base);
kfree(cd);
return -ENOMEM;
}
return 0;
}
IRQCHIP_DECLARE(mst_intc, "mstar,mst-intc", mst_intc_of_init);
// SPDX-License-Identifier: GPL-2.0+
/*
* Actions Semi Owl SoCs SIRQ interrupt controller driver
*
* Copyright (C) 2014 Actions Semi Inc.
* David Liu <liuwei@actions-semi.com>
*
* Author: Parthiban Nallathambi <pn@denx.de>
* Author: Saravanan Sekar <sravanhome@gmail.com>
* Author: Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
*/
#include <linux/bitfield.h>
#include <linux/interrupt.h>
#include <linux/irqchip.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#define NUM_SIRQ 3
#define INTC_EXTCTL_PENDING BIT(0)
#define INTC_EXTCTL_CLK_SEL BIT(4)
#define INTC_EXTCTL_EN BIT(5)
#define INTC_EXTCTL_TYPE_MASK GENMASK(7, 6)
#define INTC_EXTCTL_TYPE_HIGH 0
#define INTC_EXTCTL_TYPE_LOW BIT(6)
#define INTC_EXTCTL_TYPE_RISING BIT(7)
#define INTC_EXTCTL_TYPE_FALLING (BIT(6) | BIT(7))
/* S500 & S700 SIRQ control register masks */
#define INTC_EXTCTL_SIRQ0_MASK GENMASK(23, 16)
#define INTC_EXTCTL_SIRQ1_MASK GENMASK(15, 8)
#define INTC_EXTCTL_SIRQ2_MASK GENMASK(7, 0)
/* S900 SIRQ control register offsets, relative to controller base address */
#define INTC_EXTCTL0 0x0000
#define INTC_EXTCTL1 0x0328
#define INTC_EXTCTL2 0x032c
struct owl_sirq_params {
/* INTC_EXTCTL reg shared for all three SIRQ lines */
bool reg_shared;
/* INTC_EXTCTL reg offsets relative to controller base address */
u16 reg_offset[NUM_SIRQ];
};
struct owl_sirq_chip_data {
const struct owl_sirq_params *params;
void __iomem *base;
raw_spinlock_t lock;
u32 ext_irqs[NUM_SIRQ];
};
/* S500 & S700 SoCs */
static const struct owl_sirq_params owl_sirq_s500_params = {
.reg_shared = true,
.reg_offset = { 0, 0, 0 },
};
/* S900 SoC */
static const struct owl_sirq_params owl_sirq_s900_params = {
.reg_shared = false,
.reg_offset = { INTC_EXTCTL0, INTC_EXTCTL1, INTC_EXTCTL2 },
};
static u32 owl_field_get(u32 val, u32 index)
{
switch (index) {
case 0:
return FIELD_GET(INTC_EXTCTL_SIRQ0_MASK, val);
case 1:
return FIELD_GET(INTC_EXTCTL_SIRQ1_MASK, val);
case 2:
default:
return FIELD_GET(INTC_EXTCTL_SIRQ2_MASK, val);
}
}
static u32 owl_field_prep(u32 val, u32 index)
{
switch (index) {
case 0:
return FIELD_PREP(INTC_EXTCTL_SIRQ0_MASK, val);
case 1:
return FIELD_PREP(INTC_EXTCTL_SIRQ1_MASK, val);
case 2:
default:
return FIELD_PREP(INTC_EXTCTL_SIRQ2_MASK, val);
}
}
static u32 owl_sirq_read_extctl(struct owl_sirq_chip_data *data, u32 index)
{
u32 val;
val = readl_relaxed(data->base + data->params->reg_offset[index]);
if (data->params->reg_shared)
val = owl_field_get(val, index);
return val;
}
static void owl_sirq_write_extctl(struct owl_sirq_chip_data *data,
u32 extctl, u32 index)
{
u32 val;
if (data->params->reg_shared) {
val = readl_relaxed(data->base + data->params->reg_offset[index]);
val &= ~owl_field_prep(0xff, index);
extctl = owl_field_prep(extctl, index) | val;
}
writel_relaxed(extctl, data->base + data->params->reg_offset[index]);
}
static void owl_sirq_clear_set_extctl(struct owl_sirq_chip_data *d,
u32 clear, u32 set, u32 index)
{
unsigned long flags;
u32 val;
raw_spin_lock_irqsave(&d->lock, flags);
val = owl_sirq_read_extctl(d, index);
val &= ~clear;
val |= set;
owl_sirq_write_extctl(d, val, index);
raw_spin_unlock_irqrestore(&d->lock, flags);
}
static void owl_sirq_eoi(struct irq_data *data)
{
struct owl_sirq_chip_data *chip_data = irq_data_get_irq_chip_data(data);
/*
* Software must clear external interrupt pending, when interrupt type
* is edge triggered, so we need per SIRQ based clearing.
*/
if (!irqd_is_level_type(data))
owl_sirq_clear_set_extctl(chip_data, 0, INTC_EXTCTL_PENDING,
data->hwirq);
irq_chip_eoi_parent(data);
}
static void owl_sirq_mask(struct irq_data *data)
{
struct owl_sirq_chip_data *chip_data = irq_data_get_irq_chip_data(data);
owl_sirq_clear_set_extctl(chip_data, INTC_EXTCTL_EN, 0, data->hwirq);
irq_chip_mask_parent(data);
}
static void owl_sirq_unmask(struct irq_data *data)
{
struct owl_sirq_chip_data *chip_data = irq_data_get_irq_chip_data(data);
owl_sirq_clear_set_extctl(chip_data, 0, INTC_EXTCTL_EN, data->hwirq);
irq_chip_unmask_parent(data);
}
/*
* GIC does not handle falling edge or active low, hence SIRQ shall be
* programmed to convert falling edge to rising edge signal and active
* low to active high signal.
*/
static int owl_sirq_set_type(struct irq_data *data, unsigned int type)
{
struct owl_sirq_chip_data *chip_data = irq_data_get_irq_chip_data(data);
u32 sirq_type;
switch (type) {
case IRQ_TYPE_LEVEL_LOW:
sirq_type = INTC_EXTCTL_TYPE_LOW;
type = IRQ_TYPE_LEVEL_HIGH;
break;
case IRQ_TYPE_LEVEL_HIGH:
sirq_type = INTC_EXTCTL_TYPE_HIGH;
break;
case IRQ_TYPE_EDGE_FALLING:
sirq_type = INTC_EXTCTL_TYPE_FALLING;
type = IRQ_TYPE_EDGE_RISING;
break;
case IRQ_TYPE_EDGE_RISING:
sirq_type = INTC_EXTCTL_TYPE_RISING;
break;
default:
return -EINVAL;
}
owl_sirq_clear_set_extctl(chip_data, INTC_EXTCTL_TYPE_MASK, sirq_type,
data->hwirq);
return irq_chip_set_type_parent(data, type);
}
static struct irq_chip owl_sirq_chip = {
.name = "owl-sirq",
.irq_mask = owl_sirq_mask,
.irq_unmask = owl_sirq_unmask,
.irq_eoi = owl_sirq_eoi,
.irq_set_type = owl_sirq_set_type,
.irq_retrigger = irq_chip_retrigger_hierarchy,
#ifdef CONFIG_SMP
.irq_set_affinity = irq_chip_set_affinity_parent,
#endif
};
static int owl_sirq_domain_translate(struct irq_domain *d,
struct irq_fwspec *fwspec,
unsigned long *hwirq,
unsigned int *type)
{
if (!is_of_node(fwspec->fwnode))
return -EINVAL;
if (fwspec->param_count != 2 || fwspec->param[0] >= NUM_SIRQ)
return -EINVAL;
*hwirq = fwspec->param[0];
*type = fwspec->param[1];
return 0;
}
static int owl_sirq_domain_alloc(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs, void *data)
{
struct owl_sirq_chip_data *chip_data = domain->host_data;
struct irq_fwspec *fwspec = data;
struct irq_fwspec parent_fwspec;
irq_hw_number_t hwirq;
unsigned int type;
int ret;
if (WARN_ON(nr_irqs != 1))
return -EINVAL;
ret = owl_sirq_domain_translate(domain, fwspec, &hwirq, &type);
if (ret)
return ret;
switch (type) {
case IRQ_TYPE_EDGE_RISING:
case IRQ_TYPE_LEVEL_HIGH:
break;
case IRQ_TYPE_EDGE_FALLING:
type = IRQ_TYPE_EDGE_RISING;
break;
case IRQ_TYPE_LEVEL_LOW:
type = IRQ_TYPE_LEVEL_HIGH;
break;
default:
return -EINVAL;
}
irq_domain_set_hwirq_and_chip(domain, virq, hwirq, &owl_sirq_chip,
chip_data);
parent_fwspec.fwnode = domain->parent->fwnode;
parent_fwspec.param_count = 3;
parent_fwspec.param[0] = GIC_SPI;
parent_fwspec.param[1] = chip_data->ext_irqs[hwirq];
parent_fwspec.param[2] = type;
return irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec);
}
static const struct irq_domain_ops owl_sirq_domain_ops = {
.translate = owl_sirq_domain_translate,
.alloc = owl_sirq_domain_alloc,
.free = irq_domain_free_irqs_common,
};
static int __init owl_sirq_init(const struct owl_sirq_params *params,
struct device_node *node,
struct device_node *parent)
{
struct irq_domain *domain, *parent_domain;
struct owl_sirq_chip_data *chip_data;
int ret, i;
parent_domain = irq_find_host(parent);
if (!parent_domain) {
pr_err("%pOF: failed to find sirq parent domain\n", node);
return -ENXIO;
}
chip_data = kzalloc(sizeof(*chip_data), GFP_KERNEL);
if (!chip_data)
return -ENOMEM;
raw_spin_lock_init(&chip_data->lock);
chip_data->params = params;
chip_data->base = of_iomap(node, 0);
if (!chip_data->base) {
pr_err("%pOF: failed to map sirq registers\n", node);
ret = -ENXIO;
goto out_free;
}
for (i = 0; i < NUM_SIRQ; i++) {
struct of_phandle_args irq;
ret = of_irq_parse_one(node, i, &irq);
if (ret) {
pr_err("%pOF: failed to parse interrupt %d\n", node, i);
goto out_unmap;
}
if (WARN_ON(irq.args_count != 3)) {
ret = -EINVAL;
goto out_unmap;
}
chip_data->ext_irqs[i] = irq.args[1];
/* Set 24MHz external interrupt clock freq */
owl_sirq_clear_set_extctl(chip_data, 0, INTC_EXTCTL_CLK_SEL, i);
}
domain = irq_domain_add_hierarchy(parent_domain, 0, NUM_SIRQ, node,
&owl_sirq_domain_ops, chip_data);
if (!domain) {
pr_err("%pOF: failed to add domain\n", node);
ret = -ENOMEM;
goto out_unmap;
}
return 0;
out_unmap:
iounmap(chip_data->base);
out_free:
kfree(chip_data);
return ret;
}
static int __init owl_sirq_s500_of_init(struct device_node *node,
struct device_node *parent)
{
return owl_sirq_init(&owl_sirq_s500_params, node, parent);
}
IRQCHIP_DECLARE(owl_sirq_s500, "actions,s500-sirq", owl_sirq_s500_of_init);
IRQCHIP_DECLARE(owl_sirq_s700, "actions,s700-sirq", owl_sirq_s500_of_init);
static int __init owl_sirq_s900_of_init(struct device_node *node,
struct device_node *parent)
{
return owl_sirq_init(&owl_sirq_s900_params, node, parent);
}
IRQCHIP_DECLARE(owl_sirq_s900, "actions,s900-sirq", owl_sirq_s900_of_init);
This diff is collapsed.
......@@ -175,8 +175,8 @@ static struct ti_sci_inta_vint_desc *ti_sci_inta_alloc_parent_irq(struct irq_dom
struct irq_fwspec parent_fwspec;
struct device_node *parent_node;
unsigned int parent_virq;
u16 vint_id, p_hwirq;
int ret;
int p_hwirq, ret;
u16 vint_id;
vint_id = ti_sci_get_free_resource(inta->vint);
if (vint_id == TI_SCI_RESOURCE_NULL)
......@@ -600,13 +600,9 @@ static int ti_sci_inta_irq_domain_probe(struct platform_device *pdev)
inta->pdev = pdev;
inta->sci = devm_ti_sci_get_by_phandle(dev, "ti,sci");
if (IS_ERR(inta->sci)) {
ret = PTR_ERR(inta->sci);
if (ret != -EPROBE_DEFER)
dev_err(dev, "ti,sci read fail %d\n", ret);
inta->sci = NULL;
return ret;
}
if (IS_ERR(inta->sci))
return dev_err_probe(dev, PTR_ERR(inta->sci),
"ti,sci read fail\n");
ret = of_property_read_u32(dev->of_node, "ti,sci-dev-id", &inta->ti_sci_id);
if (ret) {
......
......@@ -137,8 +137,8 @@ static int ti_sci_intr_alloc_parent_irq(struct irq_domain *domain,
struct ti_sci_intr_irq_domain *intr = domain->host_data;
struct device_node *parent_node;
struct irq_fwspec fwspec;
u16 out_irq, p_hwirq;
int err = 0;
int p_hwirq, err = 0;
u16 out_irq;
out_irq = ti_sci_get_free_resource(intr->out_irqs);
if (out_irq == TI_SCI_RESOURCE_NULL)
......@@ -254,13 +254,9 @@ static int ti_sci_intr_irq_domain_probe(struct platform_device *pdev)
}
intr->sci = devm_ti_sci_get_by_phandle(dev, "ti,sci");
if (IS_ERR(intr->sci)) {
ret = PTR_ERR(intr->sci);
if (ret != -EPROBE_DEFER)
dev_err(dev, "ti,sci read fail %d\n", ret);
intr->sci = NULL;
return ret;
}
if (IS_ERR(intr->sci))
return dev_err_probe(dev, PTR_ERR(intr->sci),
"ti,sci read fail\n");
ret = of_property_read_u32(dev_of_node(dev), "ti,sci-dev-id",
&intr->ti_sci_id);
......
......@@ -205,7 +205,8 @@ static struct irq_chip qcom_pdc_gic_chip = {
.irq_set_type = qcom_pdc_gic_set_type,
.flags = IRQCHIP_MASK_ON_SUSPEND |
IRQCHIP_SET_TYPE_MASKED |
IRQCHIP_SKIP_SET_WAKE,
IRQCHIP_SKIP_SET_WAKE |
IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND,
.irq_set_vcpu_affinity = irq_chip_set_vcpu_affinity_parent,
.irq_set_affinity = irq_chip_set_affinity_parent,
};
......@@ -340,7 +341,8 @@ static const struct irq_domain_ops qcom_pdc_gpio_ops = {
static int pdc_setup_pin_mapping(struct device_node *np)
{
int ret, n;
int ret, n, i;
u32 irq_index, reg_index, val;
n = of_property_count_elems_of_size(np, "qcom,pdc-ranges", sizeof(u32));
if (n <= 0 || n % 3)
......@@ -369,6 +371,14 @@ static int pdc_setup_pin_mapping(struct device_node *np)
&pdc_region[n].cnt);
if (ret)
return ret;
for (i = 0; i < pdc_region[n].cnt; i++) {
reg_index = (i + pdc_region[n].pin_base) >> 5;
irq_index = (i + pdc_region[n].pin_base) & 0x1f;
val = pdc_reg_read(IRQ_ENABLE_BANK, reg_index);
val &= ~BIT(irq_index);
pdc_reg_write(IRQ_ENABLE_BANK, reg_index, val);
}
}
return 0;
......
......@@ -1077,12 +1077,10 @@ static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
* when TLMM is powered on. To allow that, enable the GPIO
* summary line to be wakeup capable at GIC.
*/
if (d->parent_data)
irq_chip_set_wake_parent(d, on);
irq_set_irq_wake(pctrl->irq, on);
if (d->parent_data && test_bit(d->hwirq, pctrl->skip_wake_irqs))
return irq_chip_set_wake_parent(d, on);
return 0;
return irq_set_irq_wake(pctrl->irq, on);
}
static int msm_gpio_irq_reqres(struct irq_data *d)
......@@ -1243,6 +1241,9 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl)
pctrl->irq_chip.irq_release_resources = msm_gpio_irq_relres;
pctrl->irq_chip.irq_set_affinity = msm_gpio_irq_set_affinity;
pctrl->irq_chip.irq_set_vcpu_affinity = msm_gpio_irq_set_vcpu_affinity;
pctrl->irq_chip.flags = IRQCHIP_MASK_ON_SUSPEND |
IRQCHIP_SET_TYPE_MASKED |
IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND;
np = of_parse_phandle(pctrl->dev->of_node, "wakeup-parent", 0);
if (np) {
......
......@@ -1990,44 +1990,17 @@ static int tegra_pmc_irq_alloc(struct irq_domain *domain, unsigned int virq,
event->id,
&pmc->irq, pmc);
/*
* GPIOs don't have an equivalent interrupt in the
* parent controller (GIC). However some code, such
* as the one in irq_get_irqchip_state(), require a
* valid IRQ chip to be set. Make sure that's the
* case by passing NULL here, which will install a
* dummy IRQ chip for the interrupt in the parent
* domain.
*/
if (domain->parent)
irq_domain_set_hwirq_and_chip(domain->parent,
virq, 0, NULL,
NULL);
/* GPIO hierarchies stop at the PMC level */
if (!err && domain->parent)
err = irq_domain_disconnect_hierarchy(domain->parent,
virq);
break;
}
}
/*
* For interrupts that don't have associated wake events, assign a
* dummy hardware IRQ number. This is used in the ->irq_set_type()
* and ->irq_set_wake() callbacks to return early for these IRQs.
*/
if (i == soc->num_wake_events) {
err = irq_domain_set_hwirq_and_chip(domain, virq, ULONG_MAX,
&pmc->irq, pmc);
/*
* Interrupts without a wake event don't have a corresponding
* interrupt in the parent controller (GIC). Pass NULL for the
* chip here, which causes a dummy IRQ chip to be installed
* for the interrupt in the parent domain, to make this
* explicit.
*/
if (domain->parent)
irq_domain_set_hwirq_and_chip(domain->parent, virq, 0,
NULL, NULL);
}
/* If there is no wake-up event, there is no PMC mapping */
if (i == soc->num_wake_events)
err = irq_domain_disconnect_hierarchy(domain, virq);
return err;
}
......@@ -2043,9 +2016,6 @@ static int tegra210_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
unsigned int offset, bit;
u32 value;
if (data->hwirq == ULONG_MAX)
return 0;
offset = data->hwirq / 32;
bit = data->hwirq % 32;
......@@ -2080,9 +2050,6 @@ static int tegra210_pmc_irq_set_type(struct irq_data *data, unsigned int type)
unsigned int offset, bit;
u32 value;
if (data->hwirq == ULONG_MAX)
return 0;
offset = data->hwirq / 32;
bit = data->hwirq % 32;
......@@ -2123,10 +2090,6 @@ static int tegra186_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
unsigned int offset, bit;
u32 value;
/* nothing to do if there's no associated wake event */
if (WARN_ON(data->hwirq == ULONG_MAX))
return 0;
offset = data->hwirq / 32;
bit = data->hwirq % 32;
......@@ -2154,10 +2117,6 @@ static int tegra186_pmc_irq_set_type(struct irq_data *data, unsigned int type)
struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
u32 value;
/* nothing to do if there's no associated wake event */
if (data->hwirq == ULONG_MAX)
return 0;
value = readl(pmc->wake + WAKE_AOWAKE_CNTRL(data->hwirq));
switch (type) {
......@@ -2184,6 +2143,34 @@ static int tegra186_pmc_irq_set_type(struct irq_data *data, unsigned int type)
return 0;
}
static void tegra_irq_mask_parent(struct irq_data *data)
{
if (data->parent_data)
irq_chip_mask_parent(data);
}
static void tegra_irq_unmask_parent(struct irq_data *data)
{
if (data->parent_data)
irq_chip_unmask_parent(data);
}
static void tegra_irq_eoi_parent(struct irq_data *data)
{
if (data->parent_data)
irq_chip_eoi_parent(data);
}
static int tegra_irq_set_affinity_parent(struct irq_data *data,
const struct cpumask *dest,
bool force)
{
if (data->parent_data)
return irq_chip_set_affinity_parent(data, dest, force);
return -EINVAL;
}
static int tegra_pmc_irq_init(struct tegra_pmc *pmc)
{
struct irq_domain *parent = NULL;
......@@ -2199,10 +2186,10 @@ static int tegra_pmc_irq_init(struct tegra_pmc *pmc)
return 0;
pmc->irq.name = dev_name(pmc->dev);
pmc->irq.irq_mask = irq_chip_mask_parent;
pmc->irq.irq_unmask = irq_chip_unmask_parent;
pmc->irq.irq_eoi = irq_chip_eoi_parent;
pmc->irq.irq_set_affinity = irq_chip_set_affinity_parent;
pmc->irq.irq_mask = tegra_irq_mask_parent;
pmc->irq.irq_unmask = tegra_irq_unmask_parent;
pmc->irq.irq_eoi = tegra_irq_eoi_parent;
pmc->irq.irq_set_affinity = tegra_irq_set_affinity_parent;
pmc->irq.irq_set_type = pmc->soc->irq_set_type;
pmc->irq.irq_set_wake = pmc->soc->irq_set_wake;
......
......@@ -71,6 +71,7 @@ enum irqchip_irq_state;
* it from the spurious interrupt detection
* mechanism and from core side polling.
* IRQ_DISABLE_UNLAZY - Disable lazy irq disable
* IRQ_HIDDEN - Don't show up in /proc/interrupts
*/
enum {
IRQ_TYPE_NONE = 0x00000000,
......@@ -97,13 +98,14 @@ enum {
IRQ_PER_CPU_DEVID = (1 << 17),
IRQ_IS_POLLED = (1 << 18),
IRQ_DISABLE_UNLAZY = (1 << 19),
IRQ_HIDDEN = (1 << 20),
};
#define IRQF_MODIFY_MASK \
(IRQ_TYPE_SENSE_MASK | IRQ_NOPROBE | IRQ_NOREQUEST | \
IRQ_NOAUTOEN | IRQ_MOVE_PCNTXT | IRQ_LEVEL | IRQ_NO_BALANCING | \
IRQ_PER_CPU | IRQ_NESTED_THREAD | IRQ_NOTHREAD | IRQ_PER_CPU_DEVID | \
IRQ_IS_POLLED | IRQ_DISABLE_UNLAZY)
IRQ_IS_POLLED | IRQ_DISABLE_UNLAZY | IRQ_HIDDEN)
#define IRQ_NO_BALANCING_MASK (IRQ_PER_CPU | IRQ_NO_BALANCING)
......@@ -215,6 +217,8 @@ struct irq_data {
* from actual interrupt context.
* IRQD_AFFINITY_ON_ACTIVATE - Affinity is set on activation. Don't call
* irq_chip::irq_set_affinity() when deactivated.
* IRQD_IRQ_ENABLED_ON_SUSPEND - Interrupt is enabled on suspend by irq pm if
* irqchip have flag IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND set.
*/
enum {
IRQD_TRIGGER_MASK = 0xf,
......@@ -240,6 +244,7 @@ enum {
IRQD_MSI_NOMASK_QUIRK = (1 << 27),
IRQD_HANDLE_ENFORCE_IRQCTX = (1 << 28),
IRQD_AFFINITY_ON_ACTIVATE = (1 << 29),
IRQD_IRQ_ENABLED_ON_SUSPEND = (1 << 30),
};
#define __irqd_to_state(d) ACCESS_PRIVATE((d)->common, state_use_accessors)
......@@ -319,6 +324,11 @@ static inline bool irqd_is_handle_enforce_irqctx(struct irq_data *d)
return __irqd_to_state(d) & IRQD_HANDLE_ENFORCE_IRQCTX;
}
static inline bool irqd_is_enabled_on_suspend(struct irq_data *d)
{
return __irqd_to_state(d) & IRQD_IRQ_ENABLED_ON_SUSPEND;
}
static inline bool irqd_is_wakeup_set(struct irq_data *d)
{
return __irqd_to_state(d) & IRQD_WAKEUP_STATE;
......@@ -553,8 +563,10 @@ struct irq_chip {
* IRQCHIP_SKIP_SET_WAKE: Skip chip.irq_set_wake(), for this irq chip
* IRQCHIP_ONESHOT_SAFE: One shot does not require mask/unmask
* IRQCHIP_EOI_THREADED: Chip requires eoi() on unmask in threaded mode
* IRQCHIP_SUPPORTS_LEVEL_MSI Chip can provide two doorbells for Level MSIs
* IRQCHIP_SUPPORTS_LEVEL_MSI: Chip can provide two doorbells for Level MSIs
* IRQCHIP_SUPPORTS_NMI: Chip can deliver NMIs, only for root irqchips
* IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND: Invokes __enable_irq()/__disable_irq() for wake irqs
* in the suspend path if they are in disabled state
*/
enum {
IRQCHIP_SET_TYPE_MASKED = (1 << 0),
......@@ -566,6 +578,7 @@ enum {
IRQCHIP_EOI_THREADED = (1 << 6),
IRQCHIP_SUPPORTS_LEVEL_MSI = (1 << 7),
IRQCHIP_SUPPORTS_NMI = (1 << 8),
IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND = (1 << 9),
};
#include <linux/irqdesc.h>
......@@ -634,6 +647,7 @@ static inline int irq_set_parent(int irq, int parent_irq)
*/
extern void handle_level_irq(struct irq_desc *desc);
extern void handle_fasteoi_irq(struct irq_desc *desc);
extern void handle_percpu_devid_fasteoi_ipi(struct irq_desc *desc);
extern void handle_edge_irq(struct irq_desc *desc);
extern void handle_edge_eoi_irq(struct irq_desc *desc);
extern void handle_simple_irq(struct irq_desc *desc);
......@@ -1252,6 +1266,12 @@ int __init set_handle_irq(void (*handle_irq)(struct pt_regs *));
* top-level IRQ handler.
*/
extern void (*handle_arch_irq)(struct pt_regs *) __ro_after_init;
#else
#define set_handle_irq(handle_irq) \
do { \
(void)handle_irq; \
WARN_ON(1); \
} while (0)
#endif
#endif /* _LINUX_IRQ_H */
......@@ -509,6 +509,9 @@ extern void irq_domain_free_irqs_parent(struct irq_domain *domain,
unsigned int irq_base,
unsigned int nr_irqs);
extern int irq_domain_disconnect_hierarchy(struct irq_domain *domain,
unsigned int virq);
static inline bool irq_domain_is_hierarchy(struct irq_domain *domain)
{
return domain->flags & IRQ_DOMAIN_FLAG_HIERARCHY;
......
......@@ -944,6 +944,33 @@ void handle_percpu_devid_irq(struct irq_desc *desc)
chip->irq_eoi(&desc->irq_data);
}
/**
* handle_percpu_devid_fasteoi_ipi - Per CPU local IPI handler with per cpu
* dev ids
* @desc: the interrupt description structure for this irq
*
* The biggest difference with the IRQ version is that the interrupt is
* EOIed early, as the IPI could result in a context switch, and we need to
* make sure the IPI can fire again. We also assume that the arch code has
* registered an action. If not, we are positively doomed.
*/
void handle_percpu_devid_fasteoi_ipi(struct irq_desc *desc)
{
struct irq_chip *chip = irq_desc_get_chip(desc);
struct irqaction *action = desc->action;
unsigned int irq = irq_desc_get_irq(desc);
irqreturn_t res;
__kstat_incr_irqs_this_cpu(desc);
if (chip->irq_eoi)
chip->irq_eoi(&desc->irq_data);
trace_irq_handler_entry(irq, action);
res = action->handler(irq, raw_cpu_ptr(action->percpu_dev_id));
trace_irq_handler_exit(irq, action, res);
}
/**
* handle_percpu_devid_fasteoi_nmi - Per CPU local NMI handler with per cpu
* dev ids
......
......@@ -57,6 +57,7 @@ static const struct irq_bit_descr irqchip_flags[] = {
BIT_MASK_DESCR(IRQCHIP_EOI_THREADED),
BIT_MASK_DESCR(IRQCHIP_SUPPORTS_LEVEL_MSI),
BIT_MASK_DESCR(IRQCHIP_SUPPORTS_NMI),
BIT_MASK_DESCR(IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND),
};
static void
......@@ -125,6 +126,8 @@ static const struct irq_bit_descr irqdata_states[] = {
BIT_MASK_DESCR(IRQD_DEFAULT_TRIGGER_SET),
BIT_MASK_DESCR(IRQD_HANDLE_ENFORCE_IRQCTX),
BIT_MASK_DESCR(IRQD_IRQ_ENABLED_ON_SUSPEND),
};
static const struct irq_bit_descr irqdesc_states[] = {
......@@ -136,6 +139,7 @@ static const struct irq_bit_descr irqdesc_states[] = {
BIT_MASK_DESCR(_IRQ_PER_CPU_DEVID),
BIT_MASK_DESCR(_IRQ_IS_POLLED),
BIT_MASK_DESCR(_IRQ_DISABLE_UNLAZY),
BIT_MASK_DESCR(_IRQ_HIDDEN),
};
static const struct irq_bit_descr irqdesc_istates[] = {
......
......@@ -1136,6 +1136,17 @@ static struct irq_data *irq_domain_insert_irq_data(struct irq_domain *domain,
return irq_data;
}
static void __irq_domain_free_hierarchy(struct irq_data *irq_data)
{
struct irq_data *tmp;
while (irq_data) {
tmp = irq_data;
irq_data = irq_data->parent_data;
kfree(tmp);
}
}
static void irq_domain_free_irq_data(unsigned int virq, unsigned int nr_irqs)
{
struct irq_data *irq_data, *tmp;
......@@ -1147,12 +1158,83 @@ static void irq_domain_free_irq_data(unsigned int virq, unsigned int nr_irqs)
irq_data->parent_data = NULL;
irq_data->domain = NULL;
while (tmp) {
irq_data = tmp;
tmp = tmp->parent_data;
kfree(irq_data);
__irq_domain_free_hierarchy(tmp);
}
}
/**
* irq_domain_disconnect_hierarchy - Mark the first unused level of a hierarchy
* @domain: IRQ domain from which the hierarchy is to be disconnected
* @virq: IRQ number where the hierarchy is to be trimmed
*
* Marks the @virq level belonging to @domain as disconnected.
* Returns -EINVAL if @virq doesn't have a valid irq_data pointing
* to @domain.
*
* Its only use is to be able to trim levels of hierarchy that do not
* have any real meaning for this interrupt, and that the driver marks
* as such from its .alloc() callback.
*/
int irq_domain_disconnect_hierarchy(struct irq_domain *domain,
unsigned int virq)
{
struct irq_data *irqd;
irqd = irq_domain_get_irq_data(domain, virq);
if (!irqd)
return -EINVAL;
irqd->chip = ERR_PTR(-ENOTCONN);
return 0;
}
static int irq_domain_trim_hierarchy(unsigned int virq)
{
struct irq_data *tail, *irqd, *irq_data;
irq_data = irq_get_irq_data(virq);
tail = NULL;
/* The first entry must have a valid irqchip */
if (!irq_data->chip || IS_ERR(irq_data->chip))
return -EINVAL;
/*
* Validate that the irq_data chain is sane in the presence of
* a hierarchy trimming marker.
*/
for (irqd = irq_data->parent_data; irqd; irq_data = irqd, irqd = irqd->parent_data) {
/* Can't have a valid irqchip after a trim marker */
if (irqd->chip && tail)
return -EINVAL;
/* Can't have an empty irqchip before a trim marker */
if (!irqd->chip && !tail)
return -EINVAL;
if (IS_ERR(irqd->chip)) {
/* Only -ENOTCONN is a valid trim marker */
if (PTR_ERR(irqd->chip) != -ENOTCONN)
return -EINVAL;
tail = irq_data;
}
}
/* No trim marker, nothing to do */
if (!tail)
return 0;
pr_info("IRQ%d: trimming hierarchy from %s\n",
virq, tail->parent_data->domain->name);
/* Sever the inner part of the hierarchy... */
irqd = tail;
tail = tail->parent_data;
irqd->parent_data = NULL;
__irq_domain_free_hierarchy(tail);
return 0;
}
static int irq_domain_alloc_irq_data(struct irq_domain *domain,
......@@ -1362,6 +1444,15 @@ int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
mutex_unlock(&irq_domain_mutex);
goto out_free_irq_data;
}
for (i = 0; i < nr_irqs; i++) {
ret = irq_domain_trim_hierarchy(virq + i);
if (ret) {
mutex_unlock(&irq_domain_mutex);
goto out_free_irq_data;
}
}
for (i = 0; i < nr_irqs; i++)
irq_domain_insert_irq(virq + i);
mutex_unlock(&irq_domain_mutex);
......
......@@ -69,12 +69,26 @@ void irq_pm_remove_action(struct irq_desc *desc, struct irqaction *action)
static bool suspend_device_irq(struct irq_desc *desc)
{
unsigned long chipflags = irq_desc_get_chip(desc)->flags;
struct irq_data *irqd = &desc->irq_data;
if (!desc->action || irq_desc_is_chained(desc) ||
desc->no_suspend_depth)
return false;
if (irqd_is_wakeup_set(&desc->irq_data)) {
irqd_set(&desc->irq_data, IRQD_WAKEUP_ARMED);
if (irqd_is_wakeup_set(irqd)) {
irqd_set(irqd, IRQD_WAKEUP_ARMED);
if ((chipflags & IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND) &&
irqd_irq_disabled(irqd)) {
/*
* Interrupt marked for wakeup is in disabled state.
* Enable interrupt here to unmask/enable in irqchip
* to be able to resume with such interrupts.
*/
__enable_irq(desc);
irqd_set(irqd, IRQD_IRQ_ENABLED_ON_SUSPEND);
}
/*
* We return true here to force the caller to issue
* synchronize_irq(). We need to make sure that the
......@@ -93,7 +107,7 @@ static bool suspend_device_irq(struct irq_desc *desc)
* chip level. The chip implementation indicates that with
* IRQCHIP_MASK_ON_SUSPEND.
*/
if (irq_desc_get_chip(desc)->flags & IRQCHIP_MASK_ON_SUSPEND)
if (chipflags & IRQCHIP_MASK_ON_SUSPEND)
mask_irq(desc);
return true;
}
......@@ -137,7 +151,19 @@ EXPORT_SYMBOL_GPL(suspend_device_irqs);
static void resume_irq(struct irq_desc *desc)
{
irqd_clear(&desc->irq_data, IRQD_WAKEUP_ARMED);
struct irq_data *irqd = &desc->irq_data;
irqd_clear(irqd, IRQD_WAKEUP_ARMED);
if (irqd_is_enabled_on_suspend(irqd)) {
/*
* Interrupt marked for wakeup was enabled during suspend
* entry. Disable such interrupts to restore them back to
* original state.
*/
__disable_irq(desc);
irqd_clear(irqd, IRQD_IRQ_ENABLED_ON_SUSPEND);
}
if (desc->istate & IRQS_SUSPENDED)
goto resume;
......
......@@ -485,7 +485,7 @@ int show_interrupts(struct seq_file *p, void *v)
rcu_read_lock();
desc = irq_to_desc(i);
if (!desc)
if (!desc || irq_settings_is_hidden(desc))
goto outsparse;
if (desc->kstat_irqs)
......
......@@ -86,6 +86,18 @@ static int irq_sw_resend(struct irq_desc *desc)
}
#endif
static int try_retrigger(struct irq_desc *desc)
{
if (desc->irq_data.chip->irq_retrigger)
return desc->irq_data.chip->irq_retrigger(&desc->irq_data);
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
return irq_chip_retrigger_hierarchy(&desc->irq_data);
#else
return 0;
#endif
}
/*
* IRQ resend
*
......@@ -113,8 +125,7 @@ int check_irq_resend(struct irq_desc *desc, bool inject)
desc->istate &= ~IRQS_PENDING;
if (!desc->irq_data.chip->irq_retrigger ||
!desc->irq_data.chip->irq_retrigger(&desc->irq_data))
if (!try_retrigger(desc))
err = irq_sw_resend(desc);
/* If the retrigger was successfull, mark it with the REPLAY bit */
......
......@@ -17,6 +17,7 @@ enum {
_IRQ_PER_CPU_DEVID = IRQ_PER_CPU_DEVID,
_IRQ_IS_POLLED = IRQ_IS_POLLED,
_IRQ_DISABLE_UNLAZY = IRQ_DISABLE_UNLAZY,
_IRQ_HIDDEN = IRQ_HIDDEN,
_IRQF_MODIFY_MASK = IRQF_MODIFY_MASK,
};
......@@ -31,6 +32,7 @@ enum {
#define IRQ_PER_CPU_DEVID GOT_YOU_MORON
#define IRQ_IS_POLLED GOT_YOU_MORON
#define IRQ_DISABLE_UNLAZY GOT_YOU_MORON
#define IRQ_HIDDEN GOT_YOU_MORON
#undef IRQF_MODIFY_MASK
#define IRQF_MODIFY_MASK GOT_YOU_MORON
......@@ -167,3 +169,8 @@ static inline void irq_settings_clr_disable_unlazy(struct irq_desc *desc)
{
desc->status_use_accessors &= ~_IRQ_DISABLE_UNLAZY;
}
static inline bool irq_settings_is_hidden(struct irq_desc *desc)
{
return desc->status_use_accessors & _IRQ_HIDDEN;
}
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