Merge branch 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull genirq updates from Thomas Gleixner: "The irq departement provides: - A synchronization fix for free_irq() to synchronize just the removed interrupt thread on shared interrupt lines. - Consolidate the multi low level interrupt entry handling and mvoe it to the generic code instead of adding yet another copy for RISC-V - Refactoring of the ARM LPI allocator and LPI exposure to the hypervisor - Yet another interrupt chip driver for the JZ4725B SoC - Speed up for /proc/interrupts as people seem to love reading this file with high frequency - Miscellaneous fixes and updates" * 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (23 commits) irqchip/gic-v3-its: Make its_lock a raw_spin_lock_t genirq/irqchip: Remove MULTI_IRQ_HANDLER as it's now obselete openrisc: Use the new GENERIC_IRQ_MULTI_HANDLER arm64: Use the new GENERIC_IRQ_MULTI_HANDLER ARM: Convert to GENERIC_IRQ_MULTI_HANDLER irqchip: Port the ARM IRQ drivers to GENERIC_IRQ_MULTI_HANDLER irqchip/gic-v3-its: Reduce minimum LPI allocation to 1 for PCI devices dt-bindings: irqchip: renesas-irqc: Document r8a77980 support dt-bindings: irqchip: renesas-irqc: Document r8a77470 support irqchip/ingenic: Add support for the JZ4725B SoC irqchip/stm32: Add exti0 translation for stm32mp1 genirq: Remove redundant NULL pointer check in __free_irq() irqchip/gic-v3-its: Honor hypervisor enforced LPI range irqchip/gic-v3: Expose GICD_TYPER in the rdist structure irqchip/gic-v3-its: Drop chunk allocation compatibility irqchip/gic-v3-its: Move minimum LPI requirements to individual busses irqchip/gic-v3-its: Use full range of LPIs irqchip/gic-v3-its: Refactor LPI allocator genirq: Synchronize only with single thread on free_irq() genirq: Update code comments wrt recycled thread_mask ...hifive-unleashed-5.1
commit
d0daaeaf60
|
@ -4,6 +4,7 @@ Required properties:
|
||||||
|
|
||||||
- compatible : should be "ingenic,<socname>-intc". Valid strings are:
|
- compatible : should be "ingenic,<socname>-intc". Valid strings are:
|
||||||
ingenic,jz4740-intc
|
ingenic,jz4740-intc
|
||||||
|
ingenic,jz4725b-intc
|
||||||
ingenic,jz4770-intc
|
ingenic,jz4770-intc
|
||||||
ingenic,jz4775-intc
|
ingenic,jz4775-intc
|
||||||
ingenic,jz4780-intc
|
ingenic,jz4780-intc
|
||||||
|
|
|
@ -7,6 +7,7 @@ Required properties:
|
||||||
- "renesas,irqc-r8a73a4" (R-Mobile APE6)
|
- "renesas,irqc-r8a73a4" (R-Mobile APE6)
|
||||||
- "renesas,irqc-r8a7743" (RZ/G1M)
|
- "renesas,irqc-r8a7743" (RZ/G1M)
|
||||||
- "renesas,irqc-r8a7745" (RZ/G1E)
|
- "renesas,irqc-r8a7745" (RZ/G1E)
|
||||||
|
- "renesas,irqc-r8a77470" (RZ/G1C)
|
||||||
- "renesas,irqc-r8a7790" (R-Car H2)
|
- "renesas,irqc-r8a7790" (R-Car H2)
|
||||||
- "renesas,irqc-r8a7791" (R-Car M2-W)
|
- "renesas,irqc-r8a7791" (R-Car M2-W)
|
||||||
- "renesas,irqc-r8a7792" (R-Car V2H)
|
- "renesas,irqc-r8a7792" (R-Car V2H)
|
||||||
|
@ -16,6 +17,7 @@ Required properties:
|
||||||
- "renesas,intc-ex-r8a7796" (R-Car M3-W)
|
- "renesas,intc-ex-r8a7796" (R-Car M3-W)
|
||||||
- "renesas,intc-ex-r8a77965" (R-Car M3-N)
|
- "renesas,intc-ex-r8a77965" (R-Car M3-N)
|
||||||
- "renesas,intc-ex-r8a77970" (R-Car V3M)
|
- "renesas,intc-ex-r8a77970" (R-Car V3M)
|
||||||
|
- "renesas,intc-ex-r8a77980" (R-Car V3H)
|
||||||
- "renesas,intc-ex-r8a77995" (R-Car D3)
|
- "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
|
||||||
|
|
|
@ -337,8 +337,8 @@ config ARCH_MULTIPLATFORM
|
||||||
select TIMER_OF
|
select TIMER_OF
|
||||||
select COMMON_CLK
|
select COMMON_CLK
|
||||||
select GENERIC_CLOCKEVENTS
|
select GENERIC_CLOCKEVENTS
|
||||||
|
select GENERIC_IRQ_MULTI_HANDLER
|
||||||
select MIGHT_HAVE_PCI
|
select MIGHT_HAVE_PCI
|
||||||
select MULTI_IRQ_HANDLER
|
|
||||||
select PCI_DOMAINS if PCI
|
select PCI_DOMAINS if PCI
|
||||||
select SPARSE_IRQ
|
select SPARSE_IRQ
|
||||||
select USE_OF
|
select USE_OF
|
||||||
|
@ -465,9 +465,9 @@ config ARCH_DOVE
|
||||||
bool "Marvell Dove"
|
bool "Marvell Dove"
|
||||||
select CPU_PJ4
|
select CPU_PJ4
|
||||||
select GENERIC_CLOCKEVENTS
|
select GENERIC_CLOCKEVENTS
|
||||||
|
select GENERIC_IRQ_MULTI_HANDLER
|
||||||
select GPIOLIB
|
select GPIOLIB
|
||||||
select MIGHT_HAVE_PCI
|
select MIGHT_HAVE_PCI
|
||||||
select MULTI_IRQ_HANDLER
|
|
||||||
select MVEBU_MBUS
|
select MVEBU_MBUS
|
||||||
select PINCTRL
|
select PINCTRL
|
||||||
select PINCTRL_DOVE
|
select PINCTRL_DOVE
|
||||||
|
@ -512,8 +512,8 @@ config ARCH_LPC32XX
|
||||||
select COMMON_CLK
|
select COMMON_CLK
|
||||||
select CPU_ARM926T
|
select CPU_ARM926T
|
||||||
select GENERIC_CLOCKEVENTS
|
select GENERIC_CLOCKEVENTS
|
||||||
|
select GENERIC_IRQ_MULTI_HANDLER
|
||||||
select GPIOLIB
|
select GPIOLIB
|
||||||
select MULTI_IRQ_HANDLER
|
|
||||||
select SPARSE_IRQ
|
select SPARSE_IRQ
|
||||||
select USE_OF
|
select USE_OF
|
||||||
help
|
help
|
||||||
|
@ -532,11 +532,11 @@ config ARCH_PXA
|
||||||
select TIMER_OF
|
select TIMER_OF
|
||||||
select CPU_XSCALE if !CPU_XSC3
|
select CPU_XSCALE if !CPU_XSC3
|
||||||
select GENERIC_CLOCKEVENTS
|
select GENERIC_CLOCKEVENTS
|
||||||
|
select GENERIC_IRQ_MULTI_HANDLER
|
||||||
select GPIO_PXA
|
select GPIO_PXA
|
||||||
select GPIOLIB
|
select GPIOLIB
|
||||||
select HAVE_IDE
|
select HAVE_IDE
|
||||||
select IRQ_DOMAIN
|
select IRQ_DOMAIN
|
||||||
select MULTI_IRQ_HANDLER
|
|
||||||
select PLAT_PXA
|
select PLAT_PXA
|
||||||
select SPARSE_IRQ
|
select SPARSE_IRQ
|
||||||
help
|
help
|
||||||
|
@ -572,11 +572,11 @@ config ARCH_SA1100
|
||||||
select CPU_FREQ
|
select CPU_FREQ
|
||||||
select CPU_SA1100
|
select CPU_SA1100
|
||||||
select GENERIC_CLOCKEVENTS
|
select GENERIC_CLOCKEVENTS
|
||||||
|
select GENERIC_IRQ_MULTI_HANDLER
|
||||||
select GPIOLIB
|
select GPIOLIB
|
||||||
select HAVE_IDE
|
select HAVE_IDE
|
||||||
select IRQ_DOMAIN
|
select IRQ_DOMAIN
|
||||||
select ISA
|
select ISA
|
||||||
select MULTI_IRQ_HANDLER
|
|
||||||
select NEED_MACH_MEMORY_H
|
select NEED_MACH_MEMORY_H
|
||||||
select SPARSE_IRQ
|
select SPARSE_IRQ
|
||||||
help
|
help
|
||||||
|
@ -590,10 +590,10 @@ config ARCH_S3C24XX
|
||||||
select GENERIC_CLOCKEVENTS
|
select GENERIC_CLOCKEVENTS
|
||||||
select GPIO_SAMSUNG
|
select GPIO_SAMSUNG
|
||||||
select GPIOLIB
|
select GPIOLIB
|
||||||
|
select GENERIC_IRQ_MULTI_HANDLER
|
||||||
select HAVE_S3C2410_I2C if I2C
|
select HAVE_S3C2410_I2C if I2C
|
||||||
select HAVE_S3C2410_WATCHDOG if WATCHDOG
|
select HAVE_S3C2410_WATCHDOG if WATCHDOG
|
||||||
select HAVE_S3C_RTC if RTC_CLASS
|
select HAVE_S3C_RTC if RTC_CLASS
|
||||||
select MULTI_IRQ_HANDLER
|
|
||||||
select NEED_MACH_IO_H
|
select NEED_MACH_IO_H
|
||||||
select SAMSUNG_ATAGS
|
select SAMSUNG_ATAGS
|
||||||
select USE_OF
|
select USE_OF
|
||||||
|
@ -627,10 +627,10 @@ config ARCH_OMAP1
|
||||||
select CLKSRC_MMIO
|
select CLKSRC_MMIO
|
||||||
select GENERIC_CLOCKEVENTS
|
select GENERIC_CLOCKEVENTS
|
||||||
select GENERIC_IRQ_CHIP
|
select GENERIC_IRQ_CHIP
|
||||||
|
select GENERIC_IRQ_MULTI_HANDLER
|
||||||
select GPIOLIB
|
select GPIOLIB
|
||||||
select HAVE_IDE
|
select HAVE_IDE
|
||||||
select IRQ_DOMAIN
|
select IRQ_DOMAIN
|
||||||
select MULTI_IRQ_HANDLER
|
|
||||||
select NEED_MACH_IO_H if PCCARD
|
select NEED_MACH_IO_H if PCCARD
|
||||||
select NEED_MACH_MEMORY_H
|
select NEED_MACH_MEMORY_H
|
||||||
select SPARSE_IRQ
|
select SPARSE_IRQ
|
||||||
|
@ -921,11 +921,6 @@ config IWMMXT
|
||||||
Enable support for iWMMXt context switching at run time if
|
Enable support for iWMMXt context switching at run time if
|
||||||
running on a CPU that supports it.
|
running on a CPU that supports it.
|
||||||
|
|
||||||
config MULTI_IRQ_HANDLER
|
|
||||||
bool
|
|
||||||
help
|
|
||||||
Allow each machine to specify it's own IRQ handler at run time.
|
|
||||||
|
|
||||||
if !MMU
|
if !MMU
|
||||||
source "arch/arm/Kconfig-nommu"
|
source "arch/arm/Kconfig-nommu"
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -31,11 +31,6 @@ extern void asm_do_IRQ(unsigned int, struct pt_regs *);
|
||||||
void handle_IRQ(unsigned int, struct pt_regs *);
|
void handle_IRQ(unsigned int, struct pt_regs *);
|
||||||
void init_IRQ(void);
|
void init_IRQ(void);
|
||||||
|
|
||||||
#ifdef CONFIG_MULTI_IRQ_HANDLER
|
|
||||||
extern void (*handle_arch_irq)(struct pt_regs *);
|
|
||||||
extern void set_handle_irq(void (*handle_irq)(struct pt_regs *));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_SMP
|
#ifdef CONFIG_SMP
|
||||||
extern void arch_trigger_cpumask_backtrace(const cpumask_t *mask,
|
extern void arch_trigger_cpumask_backtrace(const cpumask_t *mask,
|
||||||
bool exclude_self);
|
bool exclude_self);
|
||||||
|
|
|
@ -59,7 +59,7 @@ struct machine_desc {
|
||||||
void (*init_time)(void);
|
void (*init_time)(void);
|
||||||
void (*init_machine)(void);
|
void (*init_machine)(void);
|
||||||
void (*init_late)(void);
|
void (*init_late)(void);
|
||||||
#ifdef CONFIG_MULTI_IRQ_HANDLER
|
#ifdef CONFIG_GENERIC_IRQ_MULTI_HANDLER
|
||||||
void (*handle_irq)(struct pt_regs *);
|
void (*handle_irq)(struct pt_regs *);
|
||||||
#endif
|
#endif
|
||||||
void (*restart)(enum reboot_mode, const char *);
|
void (*restart)(enum reboot_mode, const char *);
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
#include <asm/glue-df.h>
|
#include <asm/glue-df.h>
|
||||||
#include <asm/glue-pf.h>
|
#include <asm/glue-pf.h>
|
||||||
#include <asm/vfpmacros.h>
|
#include <asm/vfpmacros.h>
|
||||||
#ifndef CONFIG_MULTI_IRQ_HANDLER
|
#ifndef CONFIG_GENERIC_IRQ_MULTI_HANDLER
|
||||||
#include <mach/entry-macro.S>
|
#include <mach/entry-macro.S>
|
||||||
#endif
|
#endif
|
||||||
#include <asm/thread_notify.h>
|
#include <asm/thread_notify.h>
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
* Interrupt handling.
|
* Interrupt handling.
|
||||||
*/
|
*/
|
||||||
.macro irq_handler
|
.macro irq_handler
|
||||||
#ifdef CONFIG_MULTI_IRQ_HANDLER
|
#ifdef CONFIG_GENERIC_IRQ_MULTI_HANDLER
|
||||||
ldr r1, =handle_arch_irq
|
ldr r1, =handle_arch_irq
|
||||||
mov r0, sp
|
mov r0, sp
|
||||||
badr lr, 9997f
|
badr lr, 9997f
|
||||||
|
@ -1226,9 +1226,3 @@ vector_addrexcptn:
|
||||||
.globl cr_alignment
|
.globl cr_alignment
|
||||||
cr_alignment:
|
cr_alignment:
|
||||||
.space 4
|
.space 4
|
||||||
|
|
||||||
#ifdef CONFIG_MULTI_IRQ_HANDLER
|
|
||||||
.globl handle_arch_irq
|
|
||||||
handle_arch_irq:
|
|
||||||
.space 4
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -102,16 +102,6 @@ void __init init_IRQ(void)
|
||||||
uniphier_cache_init();
|
uniphier_cache_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_MULTI_IRQ_HANDLER
|
|
||||||
void __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
|
|
||||||
{
|
|
||||||
if (handle_arch_irq)
|
|
||||||
return;
|
|
||||||
|
|
||||||
handle_arch_irq = handle_irq;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_SPARSE_IRQ
|
#ifdef CONFIG_SPARSE_IRQ
|
||||||
int __init arch_probe_nr_irqs(void)
|
int __init arch_probe_nr_irqs(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1145,7 +1145,7 @@ void __init setup_arch(char **cmdline_p)
|
||||||
|
|
||||||
reserve_crashkernel();
|
reserve_crashkernel();
|
||||||
|
|
||||||
#ifdef CONFIG_MULTI_IRQ_HANDLER
|
#ifdef CONFIG_GENERIC_IRQ_MULTI_HANDLER
|
||||||
handle_arch_irq = mdesc->handle_irq;
|
handle_arch_irq = mdesc->handle_irq;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,7 @@ config ARM64
|
||||||
select GENERIC_CPU_AUTOPROBE
|
select GENERIC_CPU_AUTOPROBE
|
||||||
select GENERIC_EARLY_IOREMAP
|
select GENERIC_EARLY_IOREMAP
|
||||||
select GENERIC_IDLE_POLL_SETUP
|
select GENERIC_IDLE_POLL_SETUP
|
||||||
|
select GENERIC_IRQ_MULTI_HANDLER
|
||||||
select GENERIC_IRQ_PROBE
|
select GENERIC_IRQ_PROBE
|
||||||
select GENERIC_IRQ_SHOW
|
select GENERIC_IRQ_SHOW
|
||||||
select GENERIC_IRQ_SHOW_LEVEL
|
select GENERIC_IRQ_SHOW_LEVEL
|
||||||
|
@ -264,9 +265,6 @@ config ARCH_SUPPORTS_UPROBES
|
||||||
config ARCH_PROC_KCORE_TEXT
|
config ARCH_PROC_KCORE_TEXT
|
||||||
def_bool y
|
def_bool y
|
||||||
|
|
||||||
config MULTI_IRQ_HANDLER
|
|
||||||
def_bool y
|
|
||||||
|
|
||||||
source "init/Kconfig"
|
source "init/Kconfig"
|
||||||
|
|
||||||
source "kernel/Kconfig.freezer"
|
source "kernel/Kconfig.freezer"
|
||||||
|
|
|
@ -8,8 +8,6 @@
|
||||||
|
|
||||||
struct pt_regs;
|
struct pt_regs;
|
||||||
|
|
||||||
extern void set_handle_irq(void (*handle_irq)(struct pt_regs *));
|
|
||||||
|
|
||||||
static inline int nr_legacy_irqs(void)
|
static inline int nr_legacy_irqs(void)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -42,16 +42,6 @@ int arch_show_interrupts(struct seq_file *p, int prec)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void (*handle_arch_irq)(struct pt_regs *) = NULL;
|
|
||||||
|
|
||||||
void __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
|
|
||||||
{
|
|
||||||
if (handle_arch_irq)
|
|
||||||
return;
|
|
||||||
|
|
||||||
handle_arch_irq = handle_irq;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_VMAP_STACK
|
#ifdef CONFIG_VMAP_STACK
|
||||||
static void init_irq_stacks(void)
|
static void init_irq_stacks(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -27,7 +27,6 @@ config OPENRISC
|
||||||
select GENERIC_STRNLEN_USER
|
select GENERIC_STRNLEN_USER
|
||||||
select GENERIC_SMP_IDLE_THREAD
|
select GENERIC_SMP_IDLE_THREAD
|
||||||
select MODULES_USE_ELF_RELA
|
select MODULES_USE_ELF_RELA
|
||||||
select MULTI_IRQ_HANDLER
|
|
||||||
select HAVE_DEBUG_STACKOVERFLOW
|
select HAVE_DEBUG_STACKOVERFLOW
|
||||||
select OR1K_PIC
|
select OR1K_PIC
|
||||||
select CPU_NO_EFFICIENT_FFS if !OPENRISC_HAVE_INST_FF1
|
select CPU_NO_EFFICIENT_FFS if !OPENRISC_HAVE_INST_FF1
|
||||||
|
@ -36,6 +35,7 @@ config OPENRISC
|
||||||
select ARCH_USE_QUEUED_RWLOCKS
|
select ARCH_USE_QUEUED_RWLOCKS
|
||||||
select OMPIC if SMP
|
select OMPIC if SMP
|
||||||
select ARCH_WANT_FRAME_POINTERS
|
select ARCH_WANT_FRAME_POINTERS
|
||||||
|
select GENERIC_IRQ_MULTI_HANDLER
|
||||||
|
|
||||||
config CPU_BIG_ENDIAN
|
config CPU_BIG_ENDIAN
|
||||||
def_bool y
|
def_bool y
|
||||||
|
@ -69,9 +69,6 @@ config STACKTRACE_SUPPORT
|
||||||
config LOCKDEP_SUPPORT
|
config LOCKDEP_SUPPORT
|
||||||
def_bool y
|
def_bool y
|
||||||
|
|
||||||
config MULTI_IRQ_HANDLER
|
|
||||||
def_bool y
|
|
||||||
|
|
||||||
source "init/Kconfig"
|
source "init/Kconfig"
|
||||||
|
|
||||||
source "kernel/Kconfig.freezer"
|
source "kernel/Kconfig.freezer"
|
||||||
|
|
|
@ -24,6 +24,4 @@
|
||||||
|
|
||||||
#define NO_IRQ (-1)
|
#define NO_IRQ (-1)
|
||||||
|
|
||||||
extern void set_handle_irq(void (*handle_irq)(struct pt_regs *));
|
|
||||||
|
|
||||||
#endif /* __ASM_OPENRISC_IRQ_H__ */
|
#endif /* __ASM_OPENRISC_IRQ_H__ */
|
||||||
|
|
|
@ -41,13 +41,6 @@ void __init init_IRQ(void)
|
||||||
irqchip_init();
|
irqchip_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void (*handle_arch_irq)(struct pt_regs *);
|
|
||||||
|
|
||||||
void __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
|
|
||||||
{
|
|
||||||
handle_arch_irq = handle_irq;
|
|
||||||
}
|
|
||||||
|
|
||||||
void __irq_entry do_IRQ(struct pt_regs *regs)
|
void __irq_entry do_IRQ(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
handle_arch_irq(regs);
|
handle_arch_irq(regs);
|
||||||
|
|
|
@ -8,7 +8,7 @@ config ARM_GIC
|
||||||
bool
|
bool
|
||||||
select IRQ_DOMAIN
|
select IRQ_DOMAIN
|
||||||
select IRQ_DOMAIN_HIERARCHY
|
select IRQ_DOMAIN_HIERARCHY
|
||||||
select MULTI_IRQ_HANDLER
|
select GENERIC_IRQ_MULTI_HANDLER
|
||||||
select GENERIC_IRQ_EFFECTIVE_AFF_MASK
|
select GENERIC_IRQ_EFFECTIVE_AFF_MASK
|
||||||
|
|
||||||
config ARM_GIC_PM
|
config ARM_GIC_PM
|
||||||
|
@ -34,7 +34,7 @@ config GIC_NON_BANKED
|
||||||
config ARM_GIC_V3
|
config ARM_GIC_V3
|
||||||
bool
|
bool
|
||||||
select IRQ_DOMAIN
|
select IRQ_DOMAIN
|
||||||
select MULTI_IRQ_HANDLER
|
select GENERIC_IRQ_MULTI_HANDLER
|
||||||
select IRQ_DOMAIN_HIERARCHY
|
select IRQ_DOMAIN_HIERARCHY
|
||||||
select PARTITION_PERCPU
|
select PARTITION_PERCPU
|
||||||
select GENERIC_IRQ_EFFECTIVE_AFF_MASK
|
select GENERIC_IRQ_EFFECTIVE_AFF_MASK
|
||||||
|
@ -66,7 +66,7 @@ config ARM_NVIC
|
||||||
config ARM_VIC
|
config ARM_VIC
|
||||||
bool
|
bool
|
||||||
select IRQ_DOMAIN
|
select IRQ_DOMAIN
|
||||||
select MULTI_IRQ_HANDLER
|
select GENERIC_IRQ_MULTI_HANDLER
|
||||||
|
|
||||||
config ARM_VIC_NR
|
config ARM_VIC_NR
|
||||||
int
|
int
|
||||||
|
@ -93,14 +93,14 @@ config ATMEL_AIC_IRQ
|
||||||
bool
|
bool
|
||||||
select GENERIC_IRQ_CHIP
|
select GENERIC_IRQ_CHIP
|
||||||
select IRQ_DOMAIN
|
select IRQ_DOMAIN
|
||||||
select MULTI_IRQ_HANDLER
|
select GENERIC_IRQ_MULTI_HANDLER
|
||||||
select SPARSE_IRQ
|
select SPARSE_IRQ
|
||||||
|
|
||||||
config ATMEL_AIC5_IRQ
|
config ATMEL_AIC5_IRQ
|
||||||
bool
|
bool
|
||||||
select GENERIC_IRQ_CHIP
|
select GENERIC_IRQ_CHIP
|
||||||
select IRQ_DOMAIN
|
select IRQ_DOMAIN
|
||||||
select MULTI_IRQ_HANDLER
|
select GENERIC_IRQ_MULTI_HANDLER
|
||||||
select SPARSE_IRQ
|
select SPARSE_IRQ
|
||||||
|
|
||||||
config I8259
|
config I8259
|
||||||
|
@ -137,7 +137,7 @@ config DW_APB_ICTL
|
||||||
config FARADAY_FTINTC010
|
config FARADAY_FTINTC010
|
||||||
bool
|
bool
|
||||||
select IRQ_DOMAIN
|
select IRQ_DOMAIN
|
||||||
select MULTI_IRQ_HANDLER
|
select GENERIC_IRQ_MULTI_HANDLER
|
||||||
select SPARSE_IRQ
|
select SPARSE_IRQ
|
||||||
|
|
||||||
config HISILICON_IRQ_MBIGEN
|
config HISILICON_IRQ_MBIGEN
|
||||||
|
@ -162,7 +162,7 @@ config CLPS711X_IRQCHIP
|
||||||
bool
|
bool
|
||||||
depends on ARCH_CLPS711X
|
depends on ARCH_CLPS711X
|
||||||
select IRQ_DOMAIN
|
select IRQ_DOMAIN
|
||||||
select MULTI_IRQ_HANDLER
|
select GENERIC_IRQ_MULTI_HANDLER
|
||||||
select SPARSE_IRQ
|
select SPARSE_IRQ
|
||||||
default y
|
default y
|
||||||
|
|
||||||
|
@ -181,7 +181,7 @@ config OMAP_IRQCHIP
|
||||||
config ORION_IRQCHIP
|
config ORION_IRQCHIP
|
||||||
bool
|
bool
|
||||||
select IRQ_DOMAIN
|
select IRQ_DOMAIN
|
||||||
select MULTI_IRQ_HANDLER
|
select GENERIC_IRQ_MULTI_HANDLER
|
||||||
|
|
||||||
config PIC32_EVIC
|
config PIC32_EVIC
|
||||||
bool
|
bool
|
||||||
|
|
|
@ -45,6 +45,9 @@ static int its_fsl_mc_msi_prepare(struct irq_domain *msi_domain,
|
||||||
*/
|
*/
|
||||||
info->scratchpad[0].ul = mc_bus_dev->icid;
|
info->scratchpad[0].ul = mc_bus_dev->icid;
|
||||||
msi_info = msi_get_domain_info(msi_domain->parent);
|
msi_info = msi_get_domain_info(msi_domain->parent);
|
||||||
|
|
||||||
|
/* Allocate at least 32 MSIs, and always as a power of 2 */
|
||||||
|
nvec = max_t(int, 32, roundup_pow_of_two(nvec));
|
||||||
return msi_info->ops->msi_prepare(msi_domain->parent, dev, nvec, info);
|
return msi_info->ops->msi_prepare(msi_domain->parent, dev, nvec, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,7 @@ static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
|
||||||
{
|
{
|
||||||
struct pci_dev *pdev, *alias_dev;
|
struct pci_dev *pdev, *alias_dev;
|
||||||
struct msi_domain_info *msi_info;
|
struct msi_domain_info *msi_info;
|
||||||
int alias_count = 0;
|
int alias_count = 0, minnvec = 1;
|
||||||
|
|
||||||
if (!dev_is_pci(dev))
|
if (!dev_is_pci(dev))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -86,8 +86,18 @@ static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
|
||||||
/* ITS specific DeviceID, as the core ITS ignores dev. */
|
/* ITS specific DeviceID, as the core ITS ignores dev. */
|
||||||
info->scratchpad[0].ul = pci_msi_domain_get_msi_rid(domain, pdev);
|
info->scratchpad[0].ul = pci_msi_domain_get_msi_rid(domain, pdev);
|
||||||
|
|
||||||
return msi_info->ops->msi_prepare(domain->parent,
|
/*
|
||||||
dev, max(nvec, alias_count), info);
|
* Always allocate a power of 2, and special case device 0 for
|
||||||
|
* broken systems where the DevID is not wired (and all devices
|
||||||
|
* appear as DevID 0). For that reason, we generously allocate a
|
||||||
|
* minimum of 32 MSIs for DevID 0. If you want more because all
|
||||||
|
* your devices are aliasing to DevID 0, consider fixing your HW.
|
||||||
|
*/
|
||||||
|
nvec = max(nvec, alias_count);
|
||||||
|
if (!info->scratchpad[0].ul)
|
||||||
|
minnvec = 32;
|
||||||
|
nvec = max_t(int, minnvec, roundup_pow_of_two(nvec));
|
||||||
|
return msi_info->ops->msi_prepare(domain->parent, dev, nvec, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct msi_domain_ops its_pci_msi_ops = {
|
static struct msi_domain_ops its_pci_msi_ops = {
|
||||||
|
|
|
@ -73,6 +73,8 @@ static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
|
||||||
/* ITS specific DeviceID, as the core ITS ignores dev. */
|
/* ITS specific DeviceID, as the core ITS ignores dev. */
|
||||||
info->scratchpad[0].ul = dev_id;
|
info->scratchpad[0].ul = dev_id;
|
||||||
|
|
||||||
|
/* Allocate at least 32 MSIs, and always as a power of 2 */
|
||||||
|
nvec = max_t(int, 32, roundup_pow_of_two(nvec));
|
||||||
return msi_info->ops->msi_prepare(domain->parent,
|
return msi_info->ops->msi_prepare(domain->parent,
|
||||||
dev, nvec, info);
|
dev, nvec, info);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
#include <linux/dma-iommu.h>
|
#include <linux/dma-iommu.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/irqdomain.h>
|
#include <linux/irqdomain.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/list_sort.h>
|
||||||
#include <linux/log2.h>
|
#include <linux/log2.h>
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
#include <linux/msi.h>
|
#include <linux/msi.h>
|
||||||
|
@ -160,7 +162,7 @@ static struct {
|
||||||
} vpe_proxy;
|
} vpe_proxy;
|
||||||
|
|
||||||
static LIST_HEAD(its_nodes);
|
static LIST_HEAD(its_nodes);
|
||||||
static DEFINE_SPINLOCK(its_lock);
|
static DEFINE_RAW_SPINLOCK(its_lock);
|
||||||
static struct rdists *gic_rdists;
|
static struct rdists *gic_rdists;
|
||||||
static struct irq_domain *its_parent;
|
static struct irq_domain *its_parent;
|
||||||
|
|
||||||
|
@ -1421,112 +1423,176 @@ static struct irq_chip its_irq_chip = {
|
||||||
.irq_set_vcpu_affinity = its_irq_set_vcpu_affinity,
|
.irq_set_vcpu_affinity = its_irq_set_vcpu_affinity,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* How we allocate LPIs:
|
* How we allocate LPIs:
|
||||||
*
|
*
|
||||||
* The GIC has id_bits bits for interrupt identifiers. From there, we
|
* lpi_range_list contains ranges of LPIs that are to available to
|
||||||
* must subtract 8192 which are reserved for SGIs/PPIs/SPIs. Then, as
|
* allocate from. To allocate LPIs, just pick the first range that
|
||||||
* we allocate LPIs by chunks of 32, we can shift the whole thing by 5
|
* fits the required allocation, and reduce it by the required
|
||||||
* bits to the right.
|
* amount. Once empty, remove the range from the list.
|
||||||
*
|
*
|
||||||
* This gives us (((1UL << id_bits) - 8192) >> 5) possible allocations.
|
* To free a range of LPIs, add a free range to the list, sort it and
|
||||||
|
* merge the result if the new range happens to be adjacent to an
|
||||||
|
* already free block.
|
||||||
|
*
|
||||||
|
* The consequence of the above is that allocation is cost is low, but
|
||||||
|
* freeing is expensive. We assumes that freeing rarely occurs.
|
||||||
*/
|
*/
|
||||||
#define IRQS_PER_CHUNK_SHIFT 5
|
|
||||||
#define IRQS_PER_CHUNK (1UL << IRQS_PER_CHUNK_SHIFT)
|
|
||||||
#define ITS_MAX_LPI_NRBITS 16 /* 64K LPIs */
|
|
||||||
|
|
||||||
static unsigned long *lpi_bitmap;
|
static DEFINE_MUTEX(lpi_range_lock);
|
||||||
static u32 lpi_chunks;
|
static LIST_HEAD(lpi_range_list);
|
||||||
static DEFINE_SPINLOCK(lpi_lock);
|
|
||||||
|
|
||||||
static int its_lpi_to_chunk(int lpi)
|
struct lpi_range {
|
||||||
|
struct list_head entry;
|
||||||
|
u32 base_id;
|
||||||
|
u32 span;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct lpi_range *mk_lpi_range(u32 base, u32 span)
|
||||||
{
|
{
|
||||||
return (lpi - 8192) >> IRQS_PER_CHUNK_SHIFT;
|
struct lpi_range *range;
|
||||||
|
|
||||||
|
range = kzalloc(sizeof(*range), GFP_KERNEL);
|
||||||
|
if (range) {
|
||||||
|
INIT_LIST_HEAD(&range->entry);
|
||||||
|
range->base_id = base;
|
||||||
|
range->span = span;
|
||||||
|
}
|
||||||
|
|
||||||
|
return range;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int its_chunk_to_lpi(int chunk)
|
static int lpi_range_cmp(void *priv, struct list_head *a, struct list_head *b)
|
||||||
{
|
{
|
||||||
return (chunk << IRQS_PER_CHUNK_SHIFT) + 8192;
|
struct lpi_range *ra, *rb;
|
||||||
|
|
||||||
|
ra = container_of(a, struct lpi_range, entry);
|
||||||
|
rb = container_of(b, struct lpi_range, entry);
|
||||||
|
|
||||||
|
return rb->base_id - ra->base_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void merge_lpi_ranges(void)
|
||||||
|
{
|
||||||
|
struct lpi_range *range, *tmp;
|
||||||
|
|
||||||
|
list_for_each_entry_safe(range, tmp, &lpi_range_list, entry) {
|
||||||
|
if (!list_is_last(&range->entry, &lpi_range_list) &&
|
||||||
|
(tmp->base_id == (range->base_id + range->span))) {
|
||||||
|
tmp->base_id = range->base_id;
|
||||||
|
tmp->span += range->span;
|
||||||
|
list_del(&range->entry);
|
||||||
|
kfree(range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int alloc_lpi_range(u32 nr_lpis, u32 *base)
|
||||||
|
{
|
||||||
|
struct lpi_range *range, *tmp;
|
||||||
|
int err = -ENOSPC;
|
||||||
|
|
||||||
|
mutex_lock(&lpi_range_lock);
|
||||||
|
|
||||||
|
list_for_each_entry_safe(range, tmp, &lpi_range_list, entry) {
|
||||||
|
if (range->span >= nr_lpis) {
|
||||||
|
*base = range->base_id;
|
||||||
|
range->base_id += nr_lpis;
|
||||||
|
range->span -= nr_lpis;
|
||||||
|
|
||||||
|
if (range->span == 0) {
|
||||||
|
list_del(&range->entry);
|
||||||
|
kfree(range);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&lpi_range_lock);
|
||||||
|
|
||||||
|
pr_debug("ITS: alloc %u:%u\n", *base, nr_lpis);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int free_lpi_range(u32 base, u32 nr_lpis)
|
||||||
|
{
|
||||||
|
struct lpi_range *new;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
mutex_lock(&lpi_range_lock);
|
||||||
|
|
||||||
|
new = mk_lpi_range(base, nr_lpis);
|
||||||
|
if (!new) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_add(&new->entry, &lpi_range_list);
|
||||||
|
list_sort(NULL, &lpi_range_list, lpi_range_cmp);
|
||||||
|
merge_lpi_ranges();
|
||||||
|
out:
|
||||||
|
mutex_unlock(&lpi_range_lock);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init its_lpi_init(u32 id_bits)
|
static int __init its_lpi_init(u32 id_bits)
|
||||||
{
|
{
|
||||||
lpi_chunks = its_lpi_to_chunk(1UL << id_bits);
|
u32 lpis = (1UL << id_bits) - 8192;
|
||||||
|
u32 numlpis;
|
||||||
|
int err;
|
||||||
|
|
||||||
lpi_bitmap = kcalloc(BITS_TO_LONGS(lpi_chunks), sizeof(long),
|
numlpis = 1UL << GICD_TYPER_NUM_LPIS(gic_rdists->gicd_typer);
|
||||||
GFP_KERNEL);
|
|
||||||
if (!lpi_bitmap) {
|
if (numlpis > 2 && !WARN_ON(numlpis > lpis)) {
|
||||||
lpi_chunks = 0;
|
lpis = numlpis;
|
||||||
return -ENOMEM;
|
pr_info("ITS: Using hypervisor restricted LPI range [%u]\n",
|
||||||
|
lpis);
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_info("ITS: Allocated %d chunks for LPIs\n", (int)lpi_chunks);
|
/*
|
||||||
return 0;
|
* Initializing the allocator is just the same as freeing the
|
||||||
|
* full range of LPIs.
|
||||||
|
*/
|
||||||
|
err = free_lpi_range(8192, lpis);
|
||||||
|
pr_debug("ITS: Allocator initialized for %u LPIs\n", lpis);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned long *its_lpi_alloc_chunks(int nr_irqs, int *base, int *nr_ids)
|
static unsigned long *its_lpi_alloc(int nr_irqs, u32 *base, int *nr_ids)
|
||||||
{
|
{
|
||||||
unsigned long *bitmap = NULL;
|
unsigned long *bitmap = NULL;
|
||||||
int chunk_id;
|
int err = 0;
|
||||||
int nr_chunks;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
nr_chunks = DIV_ROUND_UP(nr_irqs, IRQS_PER_CHUNK);
|
|
||||||
|
|
||||||
spin_lock(&lpi_lock);
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
chunk_id = bitmap_find_next_zero_area(lpi_bitmap, lpi_chunks,
|
err = alloc_lpi_range(nr_irqs, base);
|
||||||
0, nr_chunks, 0);
|
if (!err)
|
||||||
if (chunk_id < lpi_chunks)
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
nr_chunks--;
|
nr_irqs /= 2;
|
||||||
} while (nr_chunks > 0);
|
} while (nr_irqs > 0);
|
||||||
|
|
||||||
if (!nr_chunks)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
bitmap = kcalloc(BITS_TO_LONGS(nr_chunks * IRQS_PER_CHUNK),
|
bitmap = kcalloc(BITS_TO_LONGS(nr_irqs), sizeof (long), GFP_ATOMIC);
|
||||||
sizeof(long),
|
|
||||||
GFP_ATOMIC);
|
|
||||||
if (!bitmap)
|
if (!bitmap)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
for (i = 0; i < nr_chunks; i++)
|
*nr_ids = nr_irqs;
|
||||||
set_bit(chunk_id + i, lpi_bitmap);
|
|
||||||
|
|
||||||
*base = its_chunk_to_lpi(chunk_id);
|
|
||||||
*nr_ids = nr_chunks * IRQS_PER_CHUNK;
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
spin_unlock(&lpi_lock);
|
|
||||||
|
|
||||||
if (!bitmap)
|
if (!bitmap)
|
||||||
*base = *nr_ids = 0;
|
*base = *nr_ids = 0;
|
||||||
|
|
||||||
return bitmap;
|
return bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void its_lpi_free_chunks(unsigned long *bitmap, int base, int nr_ids)
|
static void its_lpi_free(unsigned long *bitmap, u32 base, u32 nr_ids)
|
||||||
{
|
{
|
||||||
int lpi;
|
WARN_ON(free_lpi_range(base, nr_ids));
|
||||||
|
|
||||||
spin_lock(&lpi_lock);
|
|
||||||
|
|
||||||
for (lpi = base; lpi < (base + nr_ids); lpi += IRQS_PER_CHUNK) {
|
|
||||||
int chunk = its_lpi_to_chunk(lpi);
|
|
||||||
|
|
||||||
BUG_ON(chunk > lpi_chunks);
|
|
||||||
if (test_bit(chunk, lpi_bitmap)) {
|
|
||||||
clear_bit(chunk, lpi_bitmap);
|
|
||||||
} else {
|
|
||||||
pr_err("Bad LPI chunk %d\n", chunk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock(&lpi_lock);
|
|
||||||
|
|
||||||
kfree(bitmap);
|
kfree(bitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1559,7 +1625,7 @@ static int __init its_alloc_lpi_tables(void)
|
||||||
{
|
{
|
||||||
phys_addr_t paddr;
|
phys_addr_t paddr;
|
||||||
|
|
||||||
lpi_id_bits = min_t(u32, gic_rdists->id_bits, ITS_MAX_LPI_NRBITS);
|
lpi_id_bits = GICD_TYPER_ID_BITS(gic_rdists->gicd_typer);
|
||||||
gic_rdists->prop_page = its_allocate_prop_table(GFP_NOWAIT);
|
gic_rdists->prop_page = its_allocate_prop_table(GFP_NOWAIT);
|
||||||
if (!gic_rdists->prop_page) {
|
if (!gic_rdists->prop_page) {
|
||||||
pr_err("Failed to allocate PROPBASE\n");
|
pr_err("Failed to allocate PROPBASE\n");
|
||||||
|
@ -1997,12 +2063,12 @@ static void its_cpu_init_collections(void)
|
||||||
{
|
{
|
||||||
struct its_node *its;
|
struct its_node *its;
|
||||||
|
|
||||||
spin_lock(&its_lock);
|
raw_spin_lock(&its_lock);
|
||||||
|
|
||||||
list_for_each_entry(its, &its_nodes, entry)
|
list_for_each_entry(its, &its_nodes, entry)
|
||||||
its_cpu_init_collection(its);
|
its_cpu_init_collection(its);
|
||||||
|
|
||||||
spin_unlock(&its_lock);
|
raw_spin_unlock(&its_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct its_device *its_find_device(struct its_node *its, u32 dev_id)
|
static struct its_device *its_find_device(struct its_node *its, u32 dev_id)
|
||||||
|
@ -2134,17 +2200,20 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
|
||||||
if (!its_alloc_device_table(its, dev_id))
|
if (!its_alloc_device_table(its, dev_id))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
if (WARN_ON(!is_power_of_2(nvecs)))
|
||||||
|
nvecs = roundup_pow_of_two(nvecs);
|
||||||
|
|
||||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||||
/*
|
/*
|
||||||
* We allocate at least one chunk worth of LPIs bet device,
|
* Even if the device wants a single LPI, the ITT must be
|
||||||
* and thus that many ITEs. The device may require less though.
|
* sized as a power of two (and you need at least one bit...).
|
||||||
*/
|
*/
|
||||||
nr_ites = max(IRQS_PER_CHUNK, roundup_pow_of_two(nvecs));
|
nr_ites = max(2, nvecs);
|
||||||
sz = nr_ites * its->ite_size;
|
sz = nr_ites * its->ite_size;
|
||||||
sz = max(sz, ITS_ITT_ALIGN) + ITS_ITT_ALIGN - 1;
|
sz = max(sz, ITS_ITT_ALIGN) + ITS_ITT_ALIGN - 1;
|
||||||
itt = kzalloc(sz, GFP_KERNEL);
|
itt = kzalloc(sz, GFP_KERNEL);
|
||||||
if (alloc_lpis) {
|
if (alloc_lpis) {
|
||||||
lpi_map = its_lpi_alloc_chunks(nvecs, &lpi_base, &nr_lpis);
|
lpi_map = its_lpi_alloc(nvecs, &lpi_base, &nr_lpis);
|
||||||
if (lpi_map)
|
if (lpi_map)
|
||||||
col_map = kcalloc(nr_lpis, sizeof(*col_map),
|
col_map = kcalloc(nr_lpis, sizeof(*col_map),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
|
@ -2379,9 +2448,9 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq,
|
||||||
/* If all interrupts have been freed, start mopping the floor */
|
/* If all interrupts have been freed, start mopping the floor */
|
||||||
if (bitmap_empty(its_dev->event_map.lpi_map,
|
if (bitmap_empty(its_dev->event_map.lpi_map,
|
||||||
its_dev->event_map.nr_lpis)) {
|
its_dev->event_map.nr_lpis)) {
|
||||||
its_lpi_free_chunks(its_dev->event_map.lpi_map,
|
its_lpi_free(its_dev->event_map.lpi_map,
|
||||||
its_dev->event_map.lpi_base,
|
its_dev->event_map.lpi_base,
|
||||||
its_dev->event_map.nr_lpis);
|
its_dev->event_map.nr_lpis);
|
||||||
kfree(its_dev->event_map.col_map);
|
kfree(its_dev->event_map.col_map);
|
||||||
|
|
||||||
/* Unmap device/itt */
|
/* Unmap device/itt */
|
||||||
|
@ -2780,7 +2849,7 @@ static void its_vpe_irq_domain_free(struct irq_domain *domain,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bitmap_empty(vm->db_bitmap, vm->nr_db_lpis)) {
|
if (bitmap_empty(vm->db_bitmap, vm->nr_db_lpis)) {
|
||||||
its_lpi_free_chunks(vm->db_bitmap, vm->db_lpi_base, vm->nr_db_lpis);
|
its_lpi_free(vm->db_bitmap, vm->db_lpi_base, vm->nr_db_lpis);
|
||||||
its_free_prop_table(vm->vprop_page);
|
its_free_prop_table(vm->vprop_page);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2795,18 +2864,18 @@ static int its_vpe_irq_domain_alloc(struct irq_domain *domain, unsigned int virq
|
||||||
|
|
||||||
BUG_ON(!vm);
|
BUG_ON(!vm);
|
||||||
|
|
||||||
bitmap = its_lpi_alloc_chunks(nr_irqs, &base, &nr_ids);
|
bitmap = its_lpi_alloc(roundup_pow_of_two(nr_irqs), &base, &nr_ids);
|
||||||
if (!bitmap)
|
if (!bitmap)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
if (nr_ids < nr_irqs) {
|
if (nr_ids < nr_irqs) {
|
||||||
its_lpi_free_chunks(bitmap, base, nr_ids);
|
its_lpi_free(bitmap, base, nr_ids);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
vprop_page = its_allocate_prop_table(GFP_KERNEL);
|
vprop_page = its_allocate_prop_table(GFP_KERNEL);
|
||||||
if (!vprop_page) {
|
if (!vprop_page) {
|
||||||
its_lpi_free_chunks(bitmap, base, nr_ids);
|
its_lpi_free(bitmap, base, nr_ids);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2833,7 +2902,7 @@ static int its_vpe_irq_domain_alloc(struct irq_domain *domain, unsigned int virq
|
||||||
if (i > 0)
|
if (i > 0)
|
||||||
its_vpe_irq_domain_free(domain, virq, i - 1);
|
its_vpe_irq_domain_free(domain, virq, i - 1);
|
||||||
|
|
||||||
its_lpi_free_chunks(bitmap, base, nr_ids);
|
its_lpi_free(bitmap, base, nr_ids);
|
||||||
its_free_prop_table(vprop_page);
|
its_free_prop_table(vprop_page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3070,7 +3139,7 @@ static int its_save_disable(void)
|
||||||
struct its_node *its;
|
struct its_node *its;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
spin_lock(&its_lock);
|
raw_spin_lock(&its_lock);
|
||||||
list_for_each_entry(its, &its_nodes, entry) {
|
list_for_each_entry(its, &its_nodes, entry) {
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
|
|
||||||
|
@ -3102,7 +3171,7 @@ err:
|
||||||
writel_relaxed(its->ctlr_save, base + GITS_CTLR);
|
writel_relaxed(its->ctlr_save, base + GITS_CTLR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
spin_unlock(&its_lock);
|
raw_spin_unlock(&its_lock);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -3112,7 +3181,7 @@ static void its_restore_enable(void)
|
||||||
struct its_node *its;
|
struct its_node *its;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
spin_lock(&its_lock);
|
raw_spin_lock(&its_lock);
|
||||||
list_for_each_entry(its, &its_nodes, entry) {
|
list_for_each_entry(its, &its_nodes, entry) {
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
int i;
|
int i;
|
||||||
|
@ -3164,7 +3233,7 @@ static void its_restore_enable(void)
|
||||||
GITS_TYPER_HCC(gic_read_typer(base + GITS_TYPER)))
|
GITS_TYPER_HCC(gic_read_typer(base + GITS_TYPER)))
|
||||||
its_cpu_init_collection(its);
|
its_cpu_init_collection(its);
|
||||||
}
|
}
|
||||||
spin_unlock(&its_lock);
|
raw_spin_unlock(&its_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct syscore_ops its_syscore_ops = {
|
static struct syscore_ops its_syscore_ops = {
|
||||||
|
@ -3398,9 +3467,9 @@ static int __init its_probe_one(struct resource *res,
|
||||||
if (err)
|
if (err)
|
||||||
goto out_free_tables;
|
goto out_free_tables;
|
||||||
|
|
||||||
spin_lock(&its_lock);
|
raw_spin_lock(&its_lock);
|
||||||
list_add(&its->entry, &its_nodes);
|
list_add(&its->entry, &its_nodes);
|
||||||
spin_unlock(&its_lock);
|
raw_spin_unlock(&its_lock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
|
@ -877,7 +877,7 @@ static struct irq_chip gic_eoimode1_chip = {
|
||||||
.flags = IRQCHIP_SET_TYPE_MASKED,
|
.flags = IRQCHIP_SET_TYPE_MASKED,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define GIC_ID_NR (1U << gic_data.rdists.id_bits)
|
#define GIC_ID_NR (1U << GICD_TYPER_ID_BITS(gic_data.rdists.gicd_typer))
|
||||||
|
|
||||||
static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
|
static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
|
||||||
irq_hw_number_t hw)
|
irq_hw_number_t hw)
|
||||||
|
@ -1091,7 +1091,7 @@ static int __init gic_init_bases(void __iomem *dist_base,
|
||||||
* The GIC only supports up to 1020 interrupt sources (SGI+PPI+SPI)
|
* The GIC only supports up to 1020 interrupt sources (SGI+PPI+SPI)
|
||||||
*/
|
*/
|
||||||
typer = readl_relaxed(gic_data.dist_base + GICD_TYPER);
|
typer = readl_relaxed(gic_data.dist_base + GICD_TYPER);
|
||||||
gic_data.rdists.id_bits = GICD_TYPER_ID_BITS(typer);
|
gic_data.rdists.gicd_typer = typer;
|
||||||
gic_irqs = GICD_TYPER_IRQS(typer);
|
gic_irqs = GICD_TYPER_IRQS(typer);
|
||||||
if (gic_irqs > 1020)
|
if (gic_irqs > 1020)
|
||||||
gic_irqs = 1020;
|
gic_irqs = 1020;
|
||||||
|
|
|
@ -165,6 +165,7 @@ static int __init intc_1chip_of_init(struct device_node *node,
|
||||||
return ingenic_intc_of_init(node, 1);
|
return ingenic_intc_of_init(node, 1);
|
||||||
}
|
}
|
||||||
IRQCHIP_DECLARE(jz4740_intc, "ingenic,jz4740-intc", intc_1chip_of_init);
|
IRQCHIP_DECLARE(jz4740_intc, "ingenic,jz4740-intc", intc_1chip_of_init);
|
||||||
|
IRQCHIP_DECLARE(jz4725b_intc, "ingenic,jz4725b-intc", intc_1chip_of_init);
|
||||||
|
|
||||||
static int __init intc_2chip_of_init(struct device_node *node,
|
static int __init intc_2chip_of_init(struct device_node *node,
|
||||||
struct device_node *parent)
|
struct device_node *parent)
|
||||||
|
|
|
@ -159,6 +159,7 @@ static const struct stm32_exti_bank *stm32mp1_exti_banks[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct stm32_desc_irq stm32mp1_desc_irq[] = {
|
static const struct stm32_desc_irq stm32mp1_desc_irq[] = {
|
||||||
|
{ .exti = 0, .irq_parent = 6 },
|
||||||
{ .exti = 1, .irq_parent = 7 },
|
{ .exti = 1, .irq_parent = 7 },
|
||||||
{ .exti = 2, .irq_parent = 8 },
|
{ .exti = 2, .irq_parent = 8 },
|
||||||
{ .exti = 3, .irq_parent = 9 },
|
{ .exti = 3, .irq_parent = 9 },
|
||||||
|
|
|
@ -73,6 +73,7 @@
|
||||||
#define GICD_TYPER_MBIS (1U << 16)
|
#define GICD_TYPER_MBIS (1U << 16)
|
||||||
|
|
||||||
#define GICD_TYPER_ID_BITS(typer) ((((typer) >> 19) & 0x1f) + 1)
|
#define GICD_TYPER_ID_BITS(typer) ((((typer) >> 19) & 0x1f) + 1)
|
||||||
|
#define GICD_TYPER_NUM_LPIS(typer) ((((typer) >> 11) & 0x1f) + 1)
|
||||||
#define GICD_TYPER_IRQS(typer) ((((typer) & 0x1f) + 1) * 32)
|
#define GICD_TYPER_IRQS(typer) ((((typer) & 0x1f) + 1) * 32)
|
||||||
|
|
||||||
#define GICD_IROUTER_SPI_MODE_ONE (0U << 31)
|
#define GICD_IROUTER_SPI_MODE_ONE (0U << 31)
|
||||||
|
@ -576,8 +577,8 @@ struct rdists {
|
||||||
phys_addr_t phys_base;
|
phys_addr_t phys_base;
|
||||||
} __percpu *rdist;
|
} __percpu *rdist;
|
||||||
struct page *prop_page;
|
struct page *prop_page;
|
||||||
int id_bits;
|
|
||||||
u64 flags;
|
u64 flags;
|
||||||
|
u32 gicd_typer;
|
||||||
bool has_vlpis;
|
bool has_vlpis;
|
||||||
bool has_direct_lpi;
|
bool has_direct_lpi;
|
||||||
};
|
};
|
||||||
|
|
|
@ -134,7 +134,6 @@ config GENERIC_IRQ_DEBUGFS
|
||||||
endmenu
|
endmenu
|
||||||
|
|
||||||
config GENERIC_IRQ_MULTI_HANDLER
|
config GENERIC_IRQ_MULTI_HANDLER
|
||||||
depends on !MULTI_IRQ_HANDLER
|
|
||||||
bool
|
bool
|
||||||
help
|
help
|
||||||
Allow to specify the low level IRQ handler at run time.
|
Allow to specify the low level IRQ handler at run time.
|
||||||
|
|
|
@ -443,6 +443,7 @@ static void free_desc(unsigned int irq)
|
||||||
* We free the descriptor, masks and stat fields via RCU. That
|
* We free the descriptor, masks and stat fields via RCU. That
|
||||||
* allows demultiplex interrupts to do rcu based management of
|
* allows demultiplex interrupts to do rcu based management of
|
||||||
* the child interrupts.
|
* the child interrupts.
|
||||||
|
* This also allows us to use rcu in kstat_irqs_usr().
|
||||||
*/
|
*/
|
||||||
call_rcu(&desc->rcu, delayed_free_desc);
|
call_rcu(&desc->rcu, delayed_free_desc);
|
||||||
}
|
}
|
||||||
|
@ -928,17 +929,17 @@ unsigned int kstat_irqs(unsigned int irq)
|
||||||
* kstat_irqs_usr - Get the statistics for an interrupt
|
* kstat_irqs_usr - Get the statistics for an interrupt
|
||||||
* @irq: The interrupt number
|
* @irq: The interrupt number
|
||||||
*
|
*
|
||||||
* Returns the sum of interrupt counts on all cpus since boot for
|
* Returns the sum of interrupt counts on all cpus since boot for @irq.
|
||||||
* @irq. Contrary to kstat_irqs() this can be called from any
|
* Contrary to kstat_irqs() this can be called from any context.
|
||||||
* preemptible context. It's protected against concurrent removal of
|
* It uses rcu since a concurrent removal of an interrupt descriptor is
|
||||||
* an interrupt descriptor when sparse irqs are enabled.
|
* observing an rcu grace period before delayed_free_desc()/irq_kobj_release().
|
||||||
*/
|
*/
|
||||||
unsigned int kstat_irqs_usr(unsigned int irq)
|
unsigned int kstat_irqs_usr(unsigned int irq)
|
||||||
{
|
{
|
||||||
unsigned int sum;
|
unsigned int sum;
|
||||||
|
|
||||||
irq_lock_sparse();
|
rcu_read_lock();
|
||||||
sum = kstat_irqs(irq);
|
sum = kstat_irqs(irq);
|
||||||
irq_unlock_sparse();
|
rcu_read_unlock();
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
|
@ -790,9 +790,19 @@ static irqreturn_t irq_forced_secondary_handler(int irq, void *dev_id)
|
||||||
|
|
||||||
static int irq_wait_for_interrupt(struct irqaction *action)
|
static int irq_wait_for_interrupt(struct irqaction *action)
|
||||||
{
|
{
|
||||||
set_current_state(TASK_INTERRUPTIBLE);
|
for (;;) {
|
||||||
|
set_current_state(TASK_INTERRUPTIBLE);
|
||||||
|
|
||||||
while (!kthread_should_stop()) {
|
if (kthread_should_stop()) {
|
||||||
|
/* may need to run one last time */
|
||||||
|
if (test_and_clear_bit(IRQTF_RUNTHREAD,
|
||||||
|
&action->thread_flags)) {
|
||||||
|
__set_current_state(TASK_RUNNING);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
__set_current_state(TASK_RUNNING);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (test_and_clear_bit(IRQTF_RUNTHREAD,
|
if (test_and_clear_bit(IRQTF_RUNTHREAD,
|
||||||
&action->thread_flags)) {
|
&action->thread_flags)) {
|
||||||
|
@ -800,10 +810,7 @@ static int irq_wait_for_interrupt(struct irqaction *action)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
schedule();
|
schedule();
|
||||||
set_current_state(TASK_INTERRUPTIBLE);
|
|
||||||
}
|
}
|
||||||
__set_current_state(TASK_RUNNING);
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1024,11 +1031,8 @@ static int irq_thread(void *data)
|
||||||
/*
|
/*
|
||||||
* This is the regular exit path. __free_irq() is stopping the
|
* This is the regular exit path. __free_irq() is stopping the
|
||||||
* thread via kthread_stop() after calling
|
* thread via kthread_stop() after calling
|
||||||
* synchronize_irq(). So neither IRQTF_RUNTHREAD nor the
|
* synchronize_hardirq(). So neither IRQTF_RUNTHREAD nor the
|
||||||
* oneshot mask bit can be set. We cannot verify that as we
|
* oneshot mask bit can be set.
|
||||||
* cannot touch the oneshot mask at this point anymore as
|
|
||||||
* __setup_irq() might have given out currents thread_mask
|
|
||||||
* again.
|
|
||||||
*/
|
*/
|
||||||
task_work_cancel(current, irq_thread_dtor);
|
task_work_cancel(current, irq_thread_dtor);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1251,8 +1255,10 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Protects against a concurrent __free_irq() call which might wait
|
* Protects against a concurrent __free_irq() call which might wait
|
||||||
* for synchronize_irq() to complete without holding the optional
|
* for synchronize_hardirq() to complete without holding the optional
|
||||||
* chip bus lock and desc->lock.
|
* chip bus lock and desc->lock. Also protects against handing out
|
||||||
|
* a recycled oneshot thread_mask bit while it's still in use by
|
||||||
|
* its previous owner.
|
||||||
*/
|
*/
|
||||||
mutex_lock(&desc->request_mutex);
|
mutex_lock(&desc->request_mutex);
|
||||||
|
|
||||||
|
@ -1571,9 +1577,6 @@ static struct irqaction *__free_irq(struct irq_desc *desc, void *dev_id)
|
||||||
|
|
||||||
WARN(in_interrupt(), "Trying to free IRQ %d from IRQ context!\n", irq);
|
WARN(in_interrupt(), "Trying to free IRQ %d from IRQ context!\n", irq);
|
||||||
|
|
||||||
if (!desc)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
mutex_lock(&desc->request_mutex);
|
mutex_lock(&desc->request_mutex);
|
||||||
chip_bus_lock(desc);
|
chip_bus_lock(desc);
|
||||||
raw_spin_lock_irqsave(&desc->lock, flags);
|
raw_spin_lock_irqsave(&desc->lock, flags);
|
||||||
|
@ -1620,11 +1623,11 @@ static struct irqaction *__free_irq(struct irq_desc *desc, void *dev_id)
|
||||||
/*
|
/*
|
||||||
* Drop bus_lock here so the changes which were done in the chip
|
* Drop bus_lock here so the changes which were done in the chip
|
||||||
* callbacks above are synced out to the irq chips which hang
|
* callbacks above are synced out to the irq chips which hang
|
||||||
* behind a slow bus (I2C, SPI) before calling synchronize_irq().
|
* behind a slow bus (I2C, SPI) before calling synchronize_hardirq().
|
||||||
*
|
*
|
||||||
* Aside of that the bus_lock can also be taken from the threaded
|
* Aside of that the bus_lock can also be taken from the threaded
|
||||||
* handler in irq_finalize_oneshot() which results in a deadlock
|
* handler in irq_finalize_oneshot() which results in a deadlock
|
||||||
* because synchronize_irq() would wait forever for the thread to
|
* because kthread_stop() would wait forever for the thread to
|
||||||
* complete, which is blocked on the bus lock.
|
* complete, which is blocked on the bus lock.
|
||||||
*
|
*
|
||||||
* The still held desc->request_mutex() protects against a
|
* The still held desc->request_mutex() protects against a
|
||||||
|
@ -1636,7 +1639,7 @@ static struct irqaction *__free_irq(struct irq_desc *desc, void *dev_id)
|
||||||
unregister_handler_proc(irq, action);
|
unregister_handler_proc(irq, action);
|
||||||
|
|
||||||
/* Make sure it's not being used on another CPU: */
|
/* Make sure it's not being used on another CPU: */
|
||||||
synchronize_irq(irq);
|
synchronize_hardirq(irq);
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG_SHIRQ
|
#ifdef CONFIG_DEBUG_SHIRQ
|
||||||
/*
|
/*
|
||||||
|
@ -1645,7 +1648,7 @@ static struct irqaction *__free_irq(struct irq_desc *desc, void *dev_id)
|
||||||
* is so by doing an extra call to the handler ....
|
* is so by doing an extra call to the handler ....
|
||||||
*
|
*
|
||||||
* ( We do this after actually deregistering it, to make sure that a
|
* ( We do this after actually deregistering it, to make sure that a
|
||||||
* 'real' IRQ doesn't run in * parallel with our fake. )
|
* 'real' IRQ doesn't run in parallel with our fake. )
|
||||||
*/
|
*/
|
||||||
if (action->flags & IRQF_SHARED) {
|
if (action->flags & IRQF_SHARED) {
|
||||||
local_irq_save(flags);
|
local_irq_save(flags);
|
||||||
|
@ -1654,6 +1657,12 @@ static struct irqaction *__free_irq(struct irq_desc *desc, void *dev_id)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The action has already been removed above, but the thread writes
|
||||||
|
* its oneshot mask bit when it completes. Though request_mutex is
|
||||||
|
* held across this which prevents __setup_irq() from handing out
|
||||||
|
* the same bit to a newly requested action.
|
||||||
|
*/
|
||||||
if (action->thread) {
|
if (action->thread) {
|
||||||
kthread_stop(action->thread);
|
kthread_stop(action->thread);
|
||||||
put_task_struct(action->thread);
|
put_task_struct(action->thread);
|
||||||
|
|
|
@ -475,22 +475,24 @@ int show_interrupts(struct seq_file *p, void *v)
|
||||||
seq_putc(p, '\n');
|
seq_putc(p, '\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
irq_lock_sparse();
|
rcu_read_lock();
|
||||||
desc = irq_to_desc(i);
|
desc = irq_to_desc(i);
|
||||||
if (!desc)
|
if (!desc)
|
||||||
goto outsparse;
|
goto outsparse;
|
||||||
|
|
||||||
raw_spin_lock_irqsave(&desc->lock, flags);
|
if (desc->kstat_irqs)
|
||||||
for_each_online_cpu(j)
|
for_each_online_cpu(j)
|
||||||
any_count |= kstat_irqs_cpu(i, j);
|
any_count |= *per_cpu_ptr(desc->kstat_irqs, j);
|
||||||
action = desc->action;
|
|
||||||
if ((!action || irq_desc_is_chained(desc)) && !any_count)
|
if ((!desc->action || irq_desc_is_chained(desc)) && !any_count)
|
||||||
goto out;
|
goto outsparse;
|
||||||
|
|
||||||
seq_printf(p, "%*d: ", prec, i);
|
seq_printf(p, "%*d: ", prec, i);
|
||||||
for_each_online_cpu(j)
|
for_each_online_cpu(j)
|
||||||
seq_printf(p, "%10u ", kstat_irqs_cpu(i, j));
|
seq_printf(p, "%10u ", desc->kstat_irqs ?
|
||||||
|
*per_cpu_ptr(desc->kstat_irqs, j) : 0);
|
||||||
|
|
||||||
|
raw_spin_lock_irqsave(&desc->lock, flags);
|
||||||
if (desc->irq_data.chip) {
|
if (desc->irq_data.chip) {
|
||||||
if (desc->irq_data.chip->irq_print_chip)
|
if (desc->irq_data.chip->irq_print_chip)
|
||||||
desc->irq_data.chip->irq_print_chip(&desc->irq_data, p);
|
desc->irq_data.chip->irq_print_chip(&desc->irq_data, p);
|
||||||
|
@ -511,6 +513,7 @@ int show_interrupts(struct seq_file *p, void *v)
|
||||||
if (desc->name)
|
if (desc->name)
|
||||||
seq_printf(p, "-%-8s", desc->name);
|
seq_printf(p, "-%-8s", desc->name);
|
||||||
|
|
||||||
|
action = desc->action;
|
||||||
if (action) {
|
if (action) {
|
||||||
seq_printf(p, " %s", action->name);
|
seq_printf(p, " %s", action->name);
|
||||||
while ((action = action->next) != NULL)
|
while ((action = action->next) != NULL)
|
||||||
|
@ -518,10 +521,9 @@ int show_interrupts(struct seq_file *p, void *v)
|
||||||
}
|
}
|
||||||
|
|
||||||
seq_putc(p, '\n');
|
seq_putc(p, '\n');
|
||||||
out:
|
|
||||||
raw_spin_unlock_irqrestore(&desc->lock, flags);
|
raw_spin_unlock_irqrestore(&desc->lock, flags);
|
||||||
outsparse:
|
outsparse:
|
||||||
irq_unlock_sparse();
|
rcu_read_unlock();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue