diff options
Diffstat (limited to 'drivers/irqchip')
| -rw-r--r-- | drivers/irqchip/Kconfig | 34 | ||||
| -rw-r--r-- | drivers/irqchip/Makefile | 1 | ||||
| -rw-r--r-- | drivers/irqchip/irq-gic-common.c | 13 | ||||
| -rw-r--r-- | drivers/irqchip/irq-gic-common.h | 3 | ||||
| -rw-r--r-- | drivers/irqchip/irq-gic-v3.c | 236 | ||||
| -rw-r--r-- | drivers/irqchip/irq-gic.c | 211 | ||||
| -rw-r--r-- | drivers/irqchip/msm_show_resume_irq.c | 22 |
7 files changed, 503 insertions, 17 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 9ce6fa46f00e..57063279729f 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -7,6 +7,7 @@ config ARM_GIC select IRQ_DOMAIN select IRQ_DOMAIN_HIERARCHY select MULTI_IRQ_HANDLER + select QCOM_SHOW_RESUME_IRQ config ARM_GIC_V2M bool @@ -27,6 +28,25 @@ config ARM_GIC_V3_ITS bool select PCI_MSI_IRQ_DOMAIN +config ARM_GIC_V3_ACL + bool "GICv3 Access control" + depends on ARM_GIC_V3 + help + Access to GIC ITS address space is controlled by EL2. + Kernel has no permission to access ITS + +config ARM_GIC_V3_NO_ACCESS_CONTROL + bool "GICv3 No Access Control Configuration" + depends on ARM_GIC_V3 + help + On some SOCs with the access control configurations it is + not allowed to access certain set of the GIC registers + from non-secure world. Provide a common flag to protect + those functionalities and compile them out for such + configurations, so that specific registers are not touched. + + For production kernels, you should say 'N' here. + config ARM_NVIC bool select IRQ_DOMAIN @@ -80,6 +100,16 @@ config BRCMSTB_L2_IRQ select GENERIC_IRQ_CHIP select IRQ_DOMAIN +config QCOM_SHOW_RESUME_IRQ + bool "Enable logging of interrupts that could have caused resume" + depends on ARM_GIC + default n + help + This option logs wake up interrupts that have triggered just before + the resume loop unrolls. It helps to debug to know any unnecessary + wake up interrupts that causes system to come out of low power modes. + Say Y if you want to debug why the system resumed. + config DW_APB_ICTL bool select GENERIC_IRQ_CHIP @@ -194,6 +224,10 @@ config IRQ_MXS select IRQ_DOMAIN select STMP_DEVICE +config MSM_IRQ + bool + select IRQ_DOMAIN + config GOLDFISH_PIC bool "Goldfish programmable interrupt controller" depends on MIPS && (GOLDFISH || COMPILE_TEST) diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 616d052b65c0..67aedf02e991 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -55,4 +55,5 @@ obj-$(CONFIG_RENESAS_H8S_INTC) += irq-renesas-h8s.o obj-$(CONFIG_ARCH_SA1100) += irq-sa11x0.o obj-$(CONFIG_INGENIC_IRQ) += irq-ingenic.o obj-$(CONFIG_IMX_GPCV2) += irq-imx-gpcv2.o +obj-$(CONFIG_QCOM_SHOW_RESUME_IRQ) += msm_show_resume_irq.o obj-$(CONFIG_GOLDFISH_PIC) += irq-goldfish-pic.o diff --git a/drivers/irqchip/irq-gic-common.c b/drivers/irqchip/irq-gic-common.c index f174ce0ca361..5f8b8759672a 100644 --- a/drivers/irqchip/irq-gic-common.c +++ b/drivers/irqchip/irq-gic-common.c @@ -32,6 +32,19 @@ void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks, } } +/* + * Supported arch specific GIC irq extension. + * Default make them NULL. + */ +struct irq_chip gic_arch_extn = { + .irq_eoi = NULL, + .irq_mask = NULL, + .irq_unmask = NULL, + .irq_retrigger = NULL, + .irq_set_type = NULL, + .irq_set_wake = NULL, +}; + int gic_configure_irq(unsigned int irq, unsigned int type, void __iomem *base, void (*sync_access)(void)) { diff --git a/drivers/irqchip/irq-gic-common.h b/drivers/irqchip/irq-gic-common.h index fff697db8e22..9fd675ddf229 100644 --- a/drivers/irqchip/irq-gic-common.h +++ b/drivers/irqchip/irq-gic-common.h @@ -26,6 +26,9 @@ struct gic_quirk { u32 iidr; u32 mask; }; +extern bool from_suspend; +extern struct irq_chip gic_arch_extn; +extern int msm_show_resume_irq_mask; int gic_configure_irq(unsigned int irq, unsigned int type, void __iomem *base, void (*sync_access)(void)); diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 9ab424b9b281..058b65cbfdd0 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -24,9 +24,13 @@ #include <linux/of_irq.h> #include <linux/percpu.h> #include <linux/slab.h> +#include <linux/module.h> +#include <linux/wakeup_reason.h> #include <linux/irqchip.h> #include <linux/irqchip/arm-gic-v3.h> +#include <linux/syscore_ops.h> +#include <linux/irqchip/msm-mpm-irq.h> #include <asm/cputype.h> #include <asm/exception.h> @@ -48,6 +52,14 @@ struct gic_chip_data { u64 redist_stride; u32 nr_redist_regions; unsigned int irq_nr; +#ifdef CONFIG_PM + unsigned int wakeup_irqs[32]; + unsigned int enabled_irqs[32]; +#endif +#ifdef CONFIG_ARM_GIC_PANIC_HANDLER + u32 saved_dist_regs[0x400]; + u32 saved_router_regs[0x800]; +#endif }; static struct gic_chip_data gic_data __read_mostly; @@ -85,7 +97,7 @@ static void gic_do_wait_for_rwp(void __iomem *base) { u32 count = 1000000; /* 1s! */ - while (readl_relaxed(base + GICD_CTLR) & GICD_CTLR_RWP) { + while (readl_relaxed_no_log(base + GICD_CTLR) & GICD_CTLR_RWP) { count--; if (!count) { pr_err_ratelimited("RWP timeout, gone fishing\n"); @@ -120,6 +132,7 @@ static u64 __maybe_unused gic_read_iar(void) } #endif +#ifdef CONFIG_ARM_GIC_V3_NO_ACCESS_CONTROL static void gic_enable_redist(bool enable) { void __iomem *rbase; @@ -153,6 +166,9 @@ static void gic_enable_redist(bool enable) pr_err_ratelimited("redistributor failed to %s...\n", enable ? "wakeup" : "sleep"); } +#else +static void gic_enable_redist(bool enable) { } +#endif /* * Routines to disable, enable, EOI and route interrupts @@ -167,7 +183,7 @@ static int gic_peek_irq(struct irq_data *d, u32 offset) else base = gic_data.dist_base; - return !!(readl_relaxed(base + offset + (gic_irq(d) / 32) * 4) & mask); + return !!(readl_relaxed_no_log(base + offset + (gic_irq(d) / 32) * 4) & mask); } static void gic_poke_irq(struct irq_data *d, u32 offset) @@ -184,12 +200,15 @@ static void gic_poke_irq(struct irq_data *d, u32 offset) rwp_wait = gic_dist_wait_for_rwp; } - writel_relaxed(mask, base + offset + (gic_irq(d) / 32) * 4); + writel_relaxed_no_log(mask, base + offset + (gic_irq(d) / 32) * 4); rwp_wait(); } static void gic_mask_irq(struct irq_data *d) { + if (gic_arch_extn.irq_mask) + gic_arch_extn.irq_mask(d); + gic_poke_irq(d, GICD_ICENABLER); } @@ -210,6 +229,8 @@ static void gic_eoimode1_mask_irq(struct irq_data *d) static void gic_unmask_irq(struct irq_data *d) { + if (gic_arch_extn.irq_unmask) + gic_arch_extn.irq_unmask(d); gic_poke_irq(d, GICD_ISENABLER); } @@ -267,9 +288,20 @@ static int gic_irq_get_irqchip_state(struct irq_data *d, return 0; } +static void gic_disable_irq(struct irq_data *d) +{ + /* don't lazy-disable PPIs */ + if (gic_irq(d) < 32) + gic_mask_irq(d); + if (gic_arch_extn.irq_disable) + gic_arch_extn.irq_disable(d); +} static void gic_eoi_irq(struct irq_data *d) { + if (gic_arch_extn.irq_eoi) + gic_arch_extn.irq_eoi(d); + gic_write_eoir(gic_irq(d)); } @@ -307,6 +339,9 @@ static int gic_set_type(struct irq_data *d, unsigned int type) rwp_wait = gic_dist_wait_for_rwp; } + if (gic_arch_extn.irq_set_type) + gic_arch_extn.irq_set_type(d, type); + return gic_configure_irq(irq, type, base, rwp_wait); } @@ -319,6 +354,135 @@ static int gic_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu) return 0; } +static int gic_retrigger(struct irq_data *d) +{ + if (gic_arch_extn.irq_retrigger) + return gic_arch_extn.irq_retrigger(d); + + /* the genirq layer expects 0 if we can't retrigger in hardware */ + return 0; +} + +static inline void __iomem *gic_data_dist_base(struct gic_chip_data *data) +{ + return data->dist_base; +} + +#ifdef CONFIG_ARM_GIC_PANIC_HANDLER +static int gic_panic_handler(struct notifier_block *this, + unsigned long event, void *ptr) +{ + int i; + void __iomem *base; + + base = gic_data.dist_base; + for (i = 0; i < 0x400; i += 1) + gic_data.saved_dist_regs[i] = readl_relaxed(base + 4 * i); + + base = gic_data.dist_base + GICD_IROUTER; + for (i = 0; i < 0x800; i += 1) + gic_data.saved_router_regs[i] = readl_relaxed(base + 4 * i); + + return NOTIFY_DONE; +} + +static struct notifier_block gic_panic_blk = { + .notifier_call = gic_panic_handler, +}; +#endif + +#ifdef CONFIG_PM +static int gic_suspend_one(struct gic_chip_data *gic) +{ + unsigned int i; + void __iomem *base = gic_data_dist_base(gic); + + for (i = 0; i * 32 < gic->irq_nr; i++) { + gic->enabled_irqs[i] + = readl_relaxed(base + GICD_ISENABLER + i * 4); + /* disable all of them */ + writel_relaxed(0xffffffff, base + GICD_ICENABLER + i * 4); + /* enable the wakeup set */ + writel_relaxed(gic->wakeup_irqs[i], + base + GICD_ISENABLER + i * 4); + } + return 0; +} + +static int gic_suspend(void) +{ + gic_suspend_one(&gic_data); + return 0; +} + +static void gic_show_resume_irq(struct gic_chip_data *gic) +{ + unsigned int i; + u32 enabled; + u32 pending[32]; + void __iomem *base = gic_data_dist_base(gic); + + if (!msm_show_resume_irq_mask) + return; + + for (i = 0; i * 32 < gic->irq_nr; i++) { + enabled = readl_relaxed(base + GICD_ICENABLER + i * 4); + pending[i] = readl_relaxed(base + GICD_ISPENDR + i * 4); + pending[i] &= enabled; + } + + for (i = find_first_bit((unsigned long *)pending, gic->irq_nr); + i < gic->irq_nr; + i = find_next_bit((unsigned long *)pending, gic->irq_nr, i+1)) { + unsigned int irq = irq_find_mapping(gic->domain, i); + struct irq_desc *desc = irq_to_desc(irq); + const char *name = "null"; + + if (desc == NULL) + name = "stray irq"; + else if (desc->action && desc->action->name) + name = desc->action->name; + + log_base_wakeup_reason(irq); + pr_warn("%s: %d triggered %s\n", __func__, irq, name); + } +} + +static void gic_resume_one(struct gic_chip_data *gic) +{ + unsigned int i; + void __iomem *base = gic_data_dist_base(gic); + + gic_show_resume_irq(gic); + + for (i = 0; i * 32 < gic->irq_nr; i++) { + /* disable all of them */ + writel_relaxed(0xffffffff, base + GICD_ICENABLER + i * 4); + /* enable the enabled set */ + writel_relaxed(gic->enabled_irqs[i], + base + GICD_ISENABLER + i * 4); + } +} + +static void gic_resume(void) +{ + gic_resume_one(&gic_data); +} + +static struct syscore_ops gic_syscore_ops = { + .suspend = gic_suspend, + .resume = gic_resume, +}; + +static int __init gic_init_sys(void) +{ + register_syscore_ops(&gic_syscore_ops); + return 0; +} +arch_initcall(gic_init_sys); + +#endif + static u64 gic_mpidr_to_affinity(unsigned long mpidr) { u64 aff; @@ -340,7 +504,7 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs if (likely(irqnr > 15 && irqnr < 1020) || irqnr >= 8192) { int err; - + uncached_logk(LOGK_IRQ, (void *)(uintptr_t)irqnr); if (static_key_true(&supports_deactivate)) gic_write_eoir(irqnr); @@ -357,6 +521,7 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs continue; } if (irqnr < 16) { + uncached_logk(LOGK_IRQ, (void *)(uintptr_t)irqnr); gic_write_eoir(irqnr); if (static_key_true(&supports_deactivate)) gic_write_dir(irqnr); @@ -444,9 +609,6 @@ static int gic_populate_rdist(void) u64 offset = ptr - gic_data.redist_regions[i].redist_base; gic_data_rdist_rd_base() = ptr; gic_data_rdist()->phys_base = gic_data.redist_regions[i].phys_base + offset; - pr_info("CPU%d: found redistributor %lx region %d:%pa\n", - smp_processor_id(), mpidr, i, - &gic_data_rdist()->phys_base); return 0; } @@ -516,7 +678,8 @@ static void gic_cpu_init(void) gic_cpu_config(rbase, gic_redist_wait_for_rwp); /* Give LPIs a spin */ - if (IS_ENABLED(CONFIG_ARM_GIC_V3_ITS) && gic_dist_supports_lpis()) + if (IS_ENABLED(CONFIG_ARM_GIC_V3_ITS) && gic_dist_supports_lpis() && + !IS_ENABLED(CONFIG_ARM_GIC_V3_ACL)) its_cpu_init(); /* initialise system registers */ @@ -649,6 +812,14 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, gic_write_irouter(val, reg); /* + * It is possible that irq is disabled from SW perspective only, + * because kernel takes lazy disable approach. Therefore check irq + * descriptor if it should kept disabled. + */ + if (irqd_irq_disabled(d)) + enabled = 0; + + /* * If the interrupt was enabled, enabled it again. Otherwise, * just wait for the distributor to have digested our changes. */ @@ -664,10 +835,44 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, #define gic_smp_init() do { } while(0) #endif +#ifdef CONFIG_PM +int gic_set_wake(struct irq_data *d, unsigned int on) +{ + int ret = -ENXIO; + unsigned int reg_offset, bit_offset; + unsigned int gicirq = gic_irq(d); + struct gic_chip_data *gic_data = irq_data_get_irq_chip_data(d); + + /* per-cpu interrupts cannot be wakeup interrupts */ + WARN_ON(gicirq < 32); + + reg_offset = gicirq / 32; + bit_offset = gicirq % 32; + + if (on) + gic_data->wakeup_irqs[reg_offset] |= 1 << bit_offset; + else + gic_data->wakeup_irqs[reg_offset] &= ~(1 << bit_offset); + + if (gic_arch_extn.irq_set_wake) + ret = gic_arch_extn.irq_set_wake(d, on); + else + pr_err("mpm: set wake is null\n"); + + return ret; +} + +#else +#define gic_set_wake NULL +#endif + #ifdef CONFIG_CPU_PM static int gic_cpu_pm_notifier(struct notifier_block *self, unsigned long cmd, void *v) { + if (from_suspend) + return NOTIFY_OK; + if (cmd == CPU_PM_EXIT) { gic_enable_redist(true); gic_cpu_sys_reg_init(); @@ -691,13 +896,16 @@ static void gic_cpu_pm_init(void) static inline void gic_cpu_pm_init(void) { } #endif /* CONFIG_CPU_PM */ -static struct irq_chip gic_chip = { +struct irq_chip gic_chip = { .name = "GICv3", .irq_mask = gic_mask_irq, .irq_unmask = gic_unmask_irq, .irq_eoi = gic_eoi_irq, .irq_set_type = gic_set_type, + .irq_retrigger = gic_retrigger, .irq_set_affinity = gic_set_affinity, + .irq_disable = gic_disable_irq, + .irq_set_wake = gic_set_wake, .irq_get_irqchip_state = gic_irq_get_irqchip_state, .irq_set_irqchip_state = gic_irq_set_irqchip_state, .flags = IRQCHIP_SET_TYPE_MASKED, @@ -714,6 +922,7 @@ static struct irq_chip gic_eoimode1_chip = { .irq_set_irqchip_state = gic_irq_set_irqchip_state, .irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity, .flags = IRQCHIP_SET_TYPE_MASKED, + .irq_set_wake = gic_set_wake, }; #define GIC_ID_NR (1U << gic_data.rdists.id_bits) @@ -923,13 +1132,20 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare set_handle_irq(gic_handle_irq); - if (IS_ENABLED(CONFIG_ARM_GIC_V3_ITS) && gic_dist_supports_lpis()) + if (IS_ENABLED(CONFIG_ARM_GIC_V3_ITS) && gic_dist_supports_lpis() && + !IS_ENABLED(CONFIG_ARM_GIC_V3_ACL)) its_init(node, &gic_data.rdists, gic_data.domain); + gic_chip.flags |= gic_arch_extn.flags; gic_smp_init(); gic_dist_init(); gic_cpu_init(); gic_cpu_pm_init(); + of_mpm_init(); + +#ifdef CONFIG_ARM_GIC_PANIC_HANDLER + atomic_notifier_chain_register(&panic_notifier_list, &gic_panic_blk); +#endif return 0; diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 5fe968a4338a..db27aa9c7e8d 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -41,7 +41,8 @@ #include <linux/irqchip.h> #include <linux/irqchip/chained_irq.h> #include <linux/irqchip/arm-gic.h> - +#include <linux/syscore_ops.h> +#include <linux/wakeup_reason.h> #include <asm/cputype.h> #include <asm/irq.h> #include <asm/exception.h> @@ -69,6 +70,7 @@ union gic_base { }; struct gic_chip_data { + unsigned int irq_offset; union gic_base dist_base; union gic_base cpu_base; #ifdef CONFIG_CPU_PM @@ -85,6 +87,10 @@ struct gic_chip_data { #ifdef CONFIG_GIC_NON_BANKED void __iomem *(*get_base)(union gic_base *); #endif +#ifdef CONFIG_PM + unsigned int wakeup_irqs[32]; + unsigned int enabled_irqs[32]; +#endif }; static DEFINE_RAW_SPINLOCK(irq_controller_lock); @@ -98,6 +104,11 @@ static DEFINE_RAW_SPINLOCK(irq_controller_lock); static u8 gic_cpu_map[NR_GIC_CPU_IF] __read_mostly; static struct static_key supports_deactivate = STATIC_KEY_INIT_TRUE; +/* + * Supported arch specific GIC irq extension. + * Default make them NULL. + */ +extern struct irq_chip gic_arch_extn; #ifndef MAX_GIC_NR #define MAX_GIC_NR 1 @@ -182,7 +193,13 @@ static int gic_peek_irq(struct irq_data *d, u32 offset) static void gic_mask_irq(struct irq_data *d) { + unsigned long flags; + + raw_spin_lock_irqsave(&irq_controller_lock, flags); gic_poke_irq(d, GIC_DIST_ENABLE_CLEAR); + if (gic_arch_extn.irq_mask) + gic_arch_extn.irq_mask(d); + raw_spin_unlock_irqrestore(&irq_controller_lock, flags); } static void gic_eoimode1_mask_irq(struct irq_data *d) @@ -202,11 +219,126 @@ static void gic_eoimode1_mask_irq(struct irq_data *d) static void gic_unmask_irq(struct irq_data *d) { + unsigned long flags; + + raw_spin_lock_irqsave(&irq_controller_lock, flags); + if (gic_arch_extn.irq_unmask) + gic_arch_extn.irq_unmask(d); gic_poke_irq(d, GIC_DIST_ENABLE_SET); + raw_spin_unlock_irqrestore(&irq_controller_lock, flags); +} + +#ifdef CONFIG_PM +static int gic_suspend_one(struct gic_chip_data *gic) +{ + unsigned int i; + void __iomem *base = gic_data_dist_base(gic); + + for (i = 0; i * 32 < gic->gic_irqs; i++) { + gic->enabled_irqs[i] + = readl_relaxed(base + GIC_DIST_ENABLE_SET + i * 4); + /* disable all of them */ + writel_relaxed(0xffffffff, + base + GIC_DIST_ENABLE_CLEAR + i * 4); + /* enable the wakeup set */ + writel_relaxed(gic->wakeup_irqs[i], + base + GIC_DIST_ENABLE_SET + i * 4); + } + /* make sure all gic setting finished */ + mb(); + return 0; +} + +static int gic_suspend(void) +{ + int i; + + for (i = 0; i < MAX_GIC_NR; i++) + gic_suspend_one(&gic_data[i]); + return 0; +} + +static void gic_show_resume_irq(struct gic_chip_data *gic) +{ + unsigned int i; + u32 enabled; + u32 pending[32]; + void __iomem *base = gic_data_dist_base(gic); + + raw_spin_lock(&irq_controller_lock); + for (i = 0; i * 32 < gic->gic_irqs; i++) { + enabled = readl_relaxed(base + GIC_DIST_ENABLE_CLEAR + i * 4); + pending[i] = readl_relaxed(base + GIC_DIST_PENDING_SET + i * 4); + pending[i] &= enabled; + } + raw_spin_unlock(&irq_controller_lock); + + for (i = find_first_bit((unsigned long *)pending, gic->gic_irqs); + i < gic->gic_irqs; + i = find_next_bit((unsigned long *)pending, + gic->gic_irqs, i+1)) { + unsigned int irq = irq_find_mapping(gic->domain, + i + gic->irq_offset); + struct irq_desc *desc = irq_to_desc(irq); + const char *name = "null"; + + if (desc == NULL) + name = "stray irq"; + else if (desc->action && desc->action->name) + name = desc->action->name; + + pr_warn("%s: %d triggered %s\n", __func__, + i + gic->irq_offset, name); + } +} + +static void gic_resume_one(struct gic_chip_data *gic) +{ + unsigned int i; + void __iomem *base = gic_data_dist_base(gic); + + gic_show_resume_irq(gic); + for (i = 0; i * 32 < gic->gic_irqs; i++) { + /* disable all of them */ + writel_relaxed(0xffffffff, + base + GIC_DIST_ENABLE_CLEAR + i * 4); + /* enable the enabled set */ + writel_relaxed(gic->enabled_irqs[i], + base + GIC_DIST_ENABLE_SET + i * 4); + } + /* make sure all gic setting finished */ + mb(); } +static void gic_resume(void) +{ + int i; + + for (i = 0; i < MAX_GIC_NR; i++) + gic_resume_one(&gic_data[i]); +} + +static struct syscore_ops gic_syscore_ops = { + .suspend = gic_suspend, + .resume = gic_resume, +}; + +static int __init gic_init_sys(void) +{ + register_syscore_ops(&gic_syscore_ops); + return 0; +} +arch_initcall(gic_init_sys); +#endif + static void gic_eoi_irq(struct irq_data *d) { + if (gic_arch_extn.irq_eoi) { + raw_spin_lock(&irq_controller_lock); + gic_arch_extn.irq_eoi(d); + raw_spin_unlock(&irq_controller_lock); + } + writel_relaxed(gic_irq(d), gic_cpu_base(d) + GIC_CPU_EOI); } @@ -272,6 +404,8 @@ static int gic_set_type(struct irq_data *d, unsigned int type) { void __iomem *base = gic_dist_base(d); unsigned int gicirq = gic_irq(d); + unsigned long flags; + int ret; /* Interrupt configuration for SGIs can't be changed */ if (gicirq < 16) @@ -282,7 +416,25 @@ static int gic_set_type(struct irq_data *d, unsigned int type) type != IRQ_TYPE_EDGE_RISING) return -EINVAL; - return gic_configure_irq(gicirq, type, base, NULL); + raw_spin_lock_irqsave(&irq_controller_lock, flags); + + if (gic_arch_extn.irq_set_type) + gic_arch_extn.irq_set_type(d, type); + + ret = gic_configure_irq(gicirq, type, base, NULL); + + raw_spin_unlock_irqrestore(&irq_controller_lock, flags); + + return ret; +} + +static int gic_retrigger(struct irq_data *d) +{ + if (gic_arch_extn.irq_retrigger) + return gic_arch_extn.irq_retrigger(d); + + /* the genirq layer expects 0 if we can't retrigger in hardware */ + return 0; } static int gic_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu) @@ -326,6 +478,35 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, } #endif +#ifdef CONFIG_PM +static int gic_set_wake(struct irq_data *d, unsigned int on) +{ + int ret = -ENXIO; + unsigned int reg_offset, bit_offset; + unsigned int gicirq = gic_irq(d); + struct gic_chip_data *gic_data = irq_data_get_irq_chip_data(d); + + /* per-cpu interrupts cannot be wakeup interrupts */ + WARN_ON(gicirq < 32); + + reg_offset = gicirq / 32; + bit_offset = gicirq % 32; + + if (on) + gic_data->wakeup_irqs[reg_offset] |= 1 << bit_offset; + else + gic_data->wakeup_irqs[reg_offset] &= ~(1 << bit_offset); + + if (gic_arch_extn.irq_set_wake) + ret = gic_arch_extn.irq_set_wake(d, on); + + return ret; +} + +#else +#define gic_set_wake NULL +#endif + static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) { u32 irqstat, irqnr; @@ -363,12 +544,13 @@ static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) } while (1); } -static void gic_handle_cascade_irq(struct irq_desc *desc) +static bool gic_handle_cascade_irq(struct irq_desc *desc) { struct gic_chip_data *chip_data = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); unsigned int cascade_irq, gic_irq; unsigned long status; + int handled = false; chained_irq_enter(chip, desc); @@ -384,10 +566,12 @@ static void gic_handle_cascade_irq(struct irq_desc *desc) if (unlikely(gic_irq < 32 || gic_irq > 1020)) handle_bad_irq(desc); else - generic_handle_irq(cascade_irq); + handled = generic_handle_irq(cascade_irq); + out: chained_irq_exit(chip, desc); + return handled == true; } static struct irq_chip gic_chip = { @@ -396,9 +580,11 @@ static struct irq_chip gic_chip = { .irq_unmask = gic_unmask_irq, .irq_eoi = gic_eoi_irq, .irq_set_type = gic_set_type, + .irq_retrigger = gic_retrigger, #ifdef CONFIG_SMP .irq_set_affinity = gic_set_affinity, #endif + .irq_set_wake = gic_set_wake, .irq_get_irqchip_state = gic_irq_get_irqchip_state, .irq_set_irqchip_state = gic_irq_set_irqchip_state, .flags = IRQCHIP_SET_TYPE_MASKED | @@ -707,7 +893,8 @@ static void gic_cpu_restore(unsigned int gic_nr) gic_cpu_if_up(&gic_data[gic_nr]); } -static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v) +static int gic_notifier(struct notifier_block *self, unsigned long cmd, + void *aff_level) { int i; @@ -726,11 +913,20 @@ static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v) gic_cpu_restore(i); break; case CPU_CLUSTER_PM_ENTER: - gic_dist_save(i); + /* + * Affinity level of the node + * eg: + * cpu level = 0 + * l2 level = 1 + * cci level = 2 + */ + if (!(unsigned long)aff_level) + gic_dist_save(i); break; case CPU_CLUSTER_PM_ENTER_FAILED: case CPU_CLUSTER_PM_EXIT: - gic_dist_restore(i); + if (!(unsigned long)aff_level) + gic_dist_restore(i); break; } } @@ -1147,6 +1343,7 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start, pr_info("GIC: Using split EOI/Deactivate mode\n"); } + gic_chip.flags |= gic_arch_extn.flags; gic_dist_init(gic); gic_cpu_init(gic); gic_pm_init(gic); diff --git a/drivers/irqchip/msm_show_resume_irq.c b/drivers/irqchip/msm_show_resume_irq.c new file mode 100644 index 000000000000..cfadb7c2643d --- /dev/null +++ b/drivers/irqchip/msm_show_resume_irq.c @@ -0,0 +1,22 @@ +/* Copyright (c) 2011, 2014-2016 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> + +int msm_show_resume_irq_mask = 1; + +module_param_named( + debug_mask, msm_show_resume_irq_mask, int, S_IRUGO | S_IWUSR | S_IWGRP +); |
