diff options
Diffstat (limited to 'drivers/irqchip')
-rw-r--r-- | drivers/irqchip/Kconfig | 6 | ||||
-rw-r--r-- | drivers/irqchip/Makefile | 2 | ||||
-rw-r--r-- | drivers/irqchip/exynos-combiner.c | 14 | ||||
-rw-r--r-- | drivers/irqchip/irq-armada-370-xp.c | 46 | ||||
-rw-r--r-- | drivers/irqchip/irq-aspeed-vic.c | 230 | ||||
-rw-r--r-- | drivers/irqchip/irq-bcm2835.c | 3 | ||||
-rw-r--r-- | drivers/irqchip/irq-bcm2836.c | 40 | ||||
-rw-r--r-- | drivers/irqchip/irq-bcm7120-l2.c | 10 | ||||
-rw-r--r-- | drivers/irqchip/irq-brcmstb-l2.c | 4 | ||||
-rw-r--r-- | drivers/irqchip/irq-clps711x.c | 2 | ||||
-rw-r--r-- | drivers/irqchip/irq-gic-common.c | 4 | ||||
-rw-r--r-- | drivers/irqchip/irq-gic-pm.c | 184 | ||||
-rw-r--r-- | drivers/irqchip/irq-gic-v2m.c | 1 | ||||
-rw-r--r-- | drivers/irqchip/irq-gic-v3-its.c | 404 | ||||
-rw-r--r-- | drivers/irqchip/irq-gic-v3.c | 22 | ||||
-rw-r--r-- | drivers/irqchip/irq-gic.c | 157 | ||||
-rw-r--r-- | drivers/irqchip/irq-hip04.c | 25 | ||||
-rw-r--r-- | drivers/irqchip/irq-mips-gic.c | 7 | ||||
-rw-r--r-- | drivers/irqchip/irq-omap-intc.c | 2 | ||||
-rw-r--r-- | drivers/irqchip/irq-s3c24xx.c | 36 | ||||
-rw-r--r-- | drivers/irqchip/irq-sirfsoc.c | 11 | ||||
-rw-r--r-- | drivers/irqchip/irq-tegra.c | 4 | ||||
-rw-r--r-- | drivers/irqchip/irq-vic.c | 5 |
23 files changed, 876 insertions, 343 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 11ecb6cfb2e5..7f8728984f44 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -8,6 +8,12 @@ config ARM_GIC select IRQ_DOMAIN_HIERARCHY select MULTI_IRQ_HANDLER +config ARM_GIC_PM + bool + depends on PM + select ARM_GIC + select PM_CLK + config ARM_GIC_MAX_NR int default 2 if ARCH_REALVIEW diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 38853a187607..4c203b6b8163 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o +obj-$(CONFIG_ARM_GIC_PM) += irq-gic-pm.o obj-$(CONFIG_REALVIEW_DT) += irq-gic-realview.o obj-$(CONFIG_ARM_GIC_V2M) += irq-gic-v2m.o obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o @@ -69,3 +70,4 @@ obj-$(CONFIG_PIC32_EVIC) += irq-pic32-evic.o obj-$(CONFIG_MVEBU_ODMI) += irq-mvebu-odmi.o obj-$(CONFIG_LS_SCFG_MSI) += irq-ls-scfg-msi.o obj-$(CONFIG_EZNPS_GIC) += irq-eznps.o +obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c index ead15be2d20a..b78a169c9c83 100644 --- a/drivers/irqchip/exynos-combiner.c +++ b/drivers/irqchip/exynos-combiner.c @@ -55,14 +55,14 @@ static void combiner_mask_irq(struct irq_data *data) { u32 mask = 1 << (data->hwirq % 32); - __raw_writel(mask, combiner_base(data) + COMBINER_ENABLE_CLEAR); + writel_relaxed(mask, combiner_base(data) + COMBINER_ENABLE_CLEAR); } static void combiner_unmask_irq(struct irq_data *data) { u32 mask = 1 << (data->hwirq % 32); - __raw_writel(mask, combiner_base(data) + COMBINER_ENABLE_SET); + writel_relaxed(mask, combiner_base(data) + COMBINER_ENABLE_SET); } static void combiner_handle_cascade_irq(struct irq_desc *desc) @@ -75,7 +75,7 @@ static void combiner_handle_cascade_irq(struct irq_desc *desc) chained_irq_enter(chip, desc); spin_lock(&irq_controller_lock); - status = __raw_readl(chip_data->base + COMBINER_INT_STATUS); + status = readl_relaxed(chip_data->base + COMBINER_INT_STATUS); spin_unlock(&irq_controller_lock); status &= chip_data->irq_mask; @@ -135,7 +135,7 @@ static void __init combiner_init_one(struct combiner_chip_data *combiner_data, combiner_data->parent_irq = irq; /* Disable all interrupts */ - __raw_writel(combiner_data->irq_mask, base + COMBINER_ENABLE_CLEAR); + writel_relaxed(combiner_data->irq_mask, base + COMBINER_ENABLE_CLEAR); } static int combiner_irq_domain_xlate(struct irq_domain *d, @@ -218,7 +218,7 @@ static int combiner_suspend(void) for (i = 0; i < max_nr; i++) combiner_data[i].pm_save = - __raw_readl(combiner_data[i].base + COMBINER_ENABLE_SET); + readl_relaxed(combiner_data[i].base + COMBINER_ENABLE_SET); return 0; } @@ -235,9 +235,9 @@ static void combiner_resume(void) int i; for (i = 0; i < max_nr; i++) { - __raw_writel(combiner_data[i].irq_mask, + writel_relaxed(combiner_data[i].irq_mask, combiner_data[i].base + COMBINER_ENABLE_CLEAR); - __raw_writel(combiner_data[i].pm_save, + writel_relaxed(combiner_data[i].pm_save, combiner_data[i].base + COMBINER_ENABLE_SET); } } diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index e7dc6cbda2a1..8bcee65a0b8c 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c @@ -345,38 +345,20 @@ static void armada_mpic_send_doorbell(const struct cpumask *mask, ARMADA_370_XP_SW_TRIG_INT_OFFS); } -static int armada_xp_mpic_secondary_init(struct notifier_block *nfb, - unsigned long action, void *hcpu) +static int armada_xp_mpic_starting_cpu(unsigned int cpu) { - if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) { - armada_xp_mpic_perf_init(); - armada_xp_mpic_smp_cpu_init(); - } - - return NOTIFY_OK; + armada_xp_mpic_perf_init(); + armada_xp_mpic_smp_cpu_init(); + return 0; } -static struct notifier_block armada_370_xp_mpic_cpu_notifier = { - .notifier_call = armada_xp_mpic_secondary_init, - .priority = 100, -}; - -static int mpic_cascaded_secondary_init(struct notifier_block *nfb, - unsigned long action, void *hcpu) +static int mpic_cascaded_starting_cpu(unsigned int cpu) { - if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) { - armada_xp_mpic_perf_init(); - enable_percpu_irq(parent_irq, IRQ_TYPE_NONE); - } - - return NOTIFY_OK; + armada_xp_mpic_perf_init(); + enable_percpu_irq(parent_irq, IRQ_TYPE_NONE); + return 0; } - -static struct notifier_block mpic_cascaded_cpu_notifier = { - .notifier_call = mpic_cascaded_secondary_init, - .priority = 100, -}; -#endif /* CONFIG_SMP */ +#endif static const struct irq_domain_ops armada_370_xp_mpic_irq_ops = { .map = armada_370_xp_mpic_irq_map, @@ -541,7 +523,7 @@ static void armada_370_xp_mpic_resume(void) writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); } -struct syscore_ops armada_370_xp_mpic_syscore_ops = { +static struct syscore_ops armada_370_xp_mpic_syscore_ops = { .suspend = armada_370_xp_mpic_suspend, .resume = armada_370_xp_mpic_resume, }; @@ -595,11 +577,15 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, set_handle_irq(armada_370_xp_handle_irq); #ifdef CONFIG_SMP set_smp_cross_call(armada_mpic_send_doorbell); - register_cpu_notifier(&armada_370_xp_mpic_cpu_notifier); + cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_ARMADA_XP_STARTING, + "AP_IRQ_ARMADA_XP_STARTING", + armada_xp_mpic_starting_cpu, NULL); #endif } else { #ifdef CONFIG_SMP - register_cpu_notifier(&mpic_cascaded_cpu_notifier); + cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_ARMADA_CASC_STARTING, + "AP_IRQ_ARMADA_CASC_STARTING", + mpic_cascaded_starting_cpu, NULL); #endif irq_set_chained_handler(parent_irq, armada_370_xp_mpic_handle_cascade_irq); diff --git a/drivers/irqchip/irq-aspeed-vic.c b/drivers/irqchip/irq-aspeed-vic.c new file mode 100644 index 000000000000..d24451d5bf8a --- /dev/null +++ b/drivers/irqchip/irq-aspeed-vic.c @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2015 - Ben Herrenschmidt, IBM Corp. + * + * Driver for Aspeed "new" VIC as found in SoC generation 3 and later + * + * Based on irq-vic.c: + * + * Copyright (C) 1999 - 2003 ARM Limited + * Copyright (C) 2000 Deep Blue Solutions Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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/export.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/irqchip.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/irqdomain.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/syscore_ops.h> +#include <linux/device.h> +#include <linux/slab.h> + +#include <asm/exception.h> +#include <asm/irq.h> + +/* These definitions correspond to the "new mapping" of the + * register set that interleaves "high" and "low". The offsets + * below are for the "low" register, add 4 to get to the high one + */ +#define AVIC_IRQ_STATUS 0x00 +#define AVIC_FIQ_STATUS 0x08 +#define AVIC_RAW_STATUS 0x10 +#define AVIC_INT_SELECT 0x18 +#define AVIC_INT_ENABLE 0x20 +#define AVIC_INT_ENABLE_CLR 0x28 +#define AVIC_INT_TRIGGER 0x30 +#define AVIC_INT_TRIGGER_CLR 0x38 +#define AVIC_INT_SENSE 0x40 +#define AVIC_INT_DUAL_EDGE 0x48 +#define AVIC_INT_EVENT 0x50 +#define AVIC_EDGE_CLR 0x58 +#define AVIC_EDGE_STATUS 0x60 + +#define NUM_IRQS 64 + +struct aspeed_vic { + void __iomem *base; + u32 edge_sources[2]; + struct irq_domain *dom; +}; +static struct aspeed_vic *system_avic; + +static void vic_init_hw(struct aspeed_vic *vic) +{ + u32 sense; + + /* Disable all interrupts */ + writel(0xffffffff, vic->base + AVIC_INT_ENABLE_CLR); + writel(0xffffffff, vic->base + AVIC_INT_ENABLE_CLR + 4); + + /* Make sure no soft trigger is on */ + writel(0xffffffff, vic->base + AVIC_INT_TRIGGER_CLR); + writel(0xffffffff, vic->base + AVIC_INT_TRIGGER_CLR + 4); + + /* Set everything to be IRQ */ + writel(0, vic->base + AVIC_INT_SELECT); + writel(0, vic->base + AVIC_INT_SELECT + 4); + + /* Some interrupts have a programable high/low level trigger + * (4 GPIO direct inputs), for now we assume this was configured + * by firmware. We read which ones are edge now. + */ + sense = readl(vic->base + AVIC_INT_SENSE); + vic->edge_sources[0] = ~sense; + sense = readl(vic->base + AVIC_INT_SENSE + 4); + vic->edge_sources[1] = ~sense; + + /* Clear edge detection latches */ + writel(0xffffffff, vic->base + AVIC_EDGE_CLR); + writel(0xffffffff, vic->base + AVIC_EDGE_CLR + 4); +} + +static void __exception_irq_entry avic_handle_irq(struct pt_regs *regs) +{ + struct aspeed_vic *vic = system_avic; + u32 stat, irq; + + for (;;) { + irq = 0; + stat = readl_relaxed(vic->base + AVIC_IRQ_STATUS); + if (!stat) { + stat = readl_relaxed(vic->base + AVIC_IRQ_STATUS + 4); + irq = 32; + } + if (stat == 0) + break; + irq += ffs(stat) - 1; + handle_domain_irq(vic->dom, irq, regs); + } +} + +static void avic_ack_irq(struct irq_data *d) +{ + struct aspeed_vic *vic = irq_data_get_irq_chip_data(d); + unsigned int sidx = d->hwirq >> 5; + unsigned int sbit = 1u << (d->hwirq & 0x1f); + + /* Clear edge latch for edge interrupts, nop for level */ + if (vic->edge_sources[sidx] & sbit) + writel(sbit, vic->base + AVIC_EDGE_CLR + sidx * 4); +} + +static void avic_mask_irq(struct irq_data *d) +{ + struct aspeed_vic *vic = irq_data_get_irq_chip_data(d); + unsigned int sidx = d->hwirq >> 5; + unsigned int sbit = 1u << (d->hwirq & 0x1f); + + writel(sbit, vic->base + AVIC_INT_ENABLE_CLR + sidx * 4); +} + +static void avic_unmask_irq(struct irq_data *d) +{ + struct aspeed_vic *vic = irq_data_get_irq_chip_data(d); + unsigned int sidx = d->hwirq >> 5; + unsigned int sbit = 1u << (d->hwirq & 0x1f); + + writel(sbit, vic->base + AVIC_INT_ENABLE + sidx * 4); +} + +/* For level irq, faster than going through a nop "ack" and mask */ +static void avic_mask_ack_irq(struct irq_data *d) +{ + struct aspeed_vic *vic = irq_data_get_irq_chip_data(d); + unsigned int sidx = d->hwirq >> 5; + unsigned int sbit = 1u << (d->hwirq & 0x1f); + + /* First mask */ + writel(sbit, vic->base + AVIC_INT_ENABLE_CLR + sidx * 4); + + /* Then clear edge latch for edge interrupts */ + if (vic->edge_sources[sidx] & sbit) + writel(sbit, vic->base + AVIC_EDGE_CLR + sidx * 4); +} + +static struct irq_chip avic_chip = { + .name = "AVIC", + .irq_ack = avic_ack_irq, + .irq_mask = avic_mask_irq, + .irq_unmask = avic_unmask_irq, + .irq_mask_ack = avic_mask_ack_irq, +}; + +static int avic_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct aspeed_vic *vic = d->host_data; + unsigned int sidx = hwirq >> 5; + unsigned int sbit = 1u << (hwirq & 0x1f); + + /* Check if interrupt exists */ + if (sidx > 1) + return -EPERM; + + if (vic->edge_sources[sidx] & sbit) + irq_set_chip_and_handler(irq, &avic_chip, handle_edge_irq); + else + irq_set_chip_and_handler(irq, &avic_chip, handle_level_irq); + irq_set_chip_data(irq, vic); + irq_set_probe(irq); + return 0; +} + +static struct irq_domain_ops avic_dom_ops = { + .map = avic_map, + .xlate = irq_domain_xlate_onetwocell, +}; + +static int __init avic_of_init(struct device_node *node, + struct device_node *parent) +{ + void __iomem *regs; + struct aspeed_vic *vic; + + if (WARN(parent, "non-root Aspeed VIC not supported")) + return -EINVAL; + if (WARN(system_avic, "duplicate Aspeed VIC not supported")) + return -EINVAL; + + regs = of_iomap(node, 0); + if (WARN_ON(!regs)) + return -EIO; + + vic = kzalloc(sizeof(struct aspeed_vic), GFP_KERNEL); + if (WARN_ON(!vic)) { + iounmap(regs); + return -ENOMEM; + } + vic->base = regs; + + /* Initialize soures, all masked */ + vic_init_hw(vic); + + /* Ready to receive interrupts */ + system_avic = vic; + set_handle_irq(avic_handle_irq); + + /* Register our domain */ + vic->dom = irq_domain_add_simple(node, NUM_IRQS, 0, + &avic_dom_ops, vic); + + return 0; +} + +IRQCHIP_DECLARE(aspeed_new_vic, "aspeed,ast2400-vic", avic_of_init); diff --git a/drivers/irqchip/irq-bcm2835.c b/drivers/irqchip/irq-bcm2835.c index bf9cc5f2e839..44d7c38dde47 100644 --- a/drivers/irqchip/irq-bcm2835.c +++ b/drivers/irqchip/irq-bcm2835.c @@ -52,7 +52,6 @@ #include <linux/irqdomain.h> #include <asm/exception.h> -#include <asm/mach/irq.h> /* Put the bank and irq (32 bits) into the hwirq */ #define MAKE_HWIRQ(b, n) ((b << 5) | (n)) @@ -242,7 +241,7 @@ static void __exception_irq_entry bcm2835_handle_irq( u32 hwirq; while ((hwirq = get_next_armctrl_hwirq()) != ~0) - handle_IRQ(irq_linear_revmap(intc.domain, hwirq), regs); + handle_domain_irq(intc.domain, hwirq, regs); } static void bcm2836_chained_handle_irq(struct irq_desc *desc) diff --git a/drivers/irqchip/irq-bcm2836.c b/drivers/irqchip/irq-bcm2836.c index 72ff1d5c5de6..d96b2c947e74 100644 --- a/drivers/irqchip/irq-bcm2836.c +++ b/drivers/irqchip/irq-bcm2836.c @@ -180,7 +180,7 @@ __exception_irq_entry bcm2836_arm_irqchip_handle_irq(struct pt_regs *regs) } else if (stat) { u32 hwirq = ffs(stat) - 1; - handle_IRQ(irq_linear_revmap(intc.domain, hwirq), regs); + handle_domain_irq(intc.domain, hwirq, regs); } } @@ -202,30 +202,23 @@ static void bcm2836_arm_irqchip_send_ipi(const struct cpumask *mask, } } -/* Unmasks the IPI on the CPU when it's online. */ -static int bcm2836_arm_irqchip_cpu_notify(struct notifier_block *nfb, - unsigned long action, void *hcpu) +static int bcm2836_cpu_starting(unsigned int cpu) { - unsigned int cpu = (unsigned long)hcpu; - unsigned int int_reg = LOCAL_MAILBOX_INT_CONTROL0; - unsigned int mailbox = 0; - - if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) - bcm2836_arm_irqchip_unmask_per_cpu_irq(int_reg, mailbox, cpu); - else if (action == CPU_DYING) - bcm2836_arm_irqchip_mask_per_cpu_irq(int_reg, mailbox, cpu); - - return NOTIFY_OK; + bcm2836_arm_irqchip_unmask_per_cpu_irq(LOCAL_MAILBOX_INT_CONTROL0, 0, + cpu); + return 0; } -static struct notifier_block bcm2836_arm_irqchip_cpu_notifier = { - .notifier_call = bcm2836_arm_irqchip_cpu_notify, - .priority = 100, -}; +static int bcm2836_cpu_dying(unsigned int cpu) +{ + bcm2836_arm_irqchip_mask_per_cpu_irq(LOCAL_MAILBOX_INT_CONTROL0, 0, + cpu); + return 0; +} #ifdef CONFIG_ARM -int __init bcm2836_smp_boot_secondary(unsigned int cpu, - struct task_struct *idle) +static int __init bcm2836_smp_boot_secondary(unsigned int cpu, + struct task_struct *idle) { unsigned long secondary_startup_phys = (unsigned long)virt_to_phys((void *)secondary_startup); @@ -251,10 +244,9 @@ bcm2836_arm_irqchip_smp_init(void) { #ifdef CONFIG_SMP /* Unmask IPIs to the boot CPU. */ - bcm2836_arm_irqchip_cpu_notify(&bcm2836_arm_irqchip_cpu_notifier, - CPU_STARTING, - (void *)(uintptr_t)smp_processor_id()); - register_cpu_notifier(&bcm2836_arm_irqchip_cpu_notifier); + cpuhp_setup_state(CPUHP_AP_IRQ_BCM2836_STARTING, + "AP_IRQ_BCM2836_STARTING", bcm2836_cpu_starting, + bcm2836_cpu_dying); set_smp_cross_call(bcm2836_arm_irqchip_send_ipi); diff --git a/drivers/irqchip/irq-bcm7120-l2.c b/drivers/irqchip/irq-bcm7120-l2.c index 61b18ab33ad9..0ec92631e23c 100644 --- a/drivers/irqchip/irq-bcm7120-l2.c +++ b/drivers/irqchip/irq-bcm7120-l2.c @@ -215,7 +215,7 @@ static int __init bcm7120_l2_intc_iomap_3380(struct device_node *dn, return 0; } -int __init bcm7120_l2_intc_probe(struct device_node *dn, +static int __init bcm7120_l2_intc_probe(struct device_node *dn, struct device_node *parent, int (*iomap_regs_fn)(struct device_node *, struct bcm7120_l2_intc_data *), @@ -339,15 +339,15 @@ out_unmap: return ret; } -int __init bcm7120_l2_intc_probe_7120(struct device_node *dn, - struct device_node *parent) +static int __init bcm7120_l2_intc_probe_7120(struct device_node *dn, + struct device_node *parent) { return bcm7120_l2_intc_probe(dn, parent, bcm7120_l2_intc_iomap_7120, "BCM7120 L2"); } -int __init bcm7120_l2_intc_probe_3380(struct device_node *dn, - struct device_node *parent) +static int __init bcm7120_l2_intc_probe_3380(struct device_node *dn, + struct device_node *parent) { return bcm7120_l2_intc_probe(dn, parent, bcm7120_l2_intc_iomap_3380, "BCM3380 L2"); diff --git a/drivers/irqchip/irq-brcmstb-l2.c b/drivers/irqchip/irq-brcmstb-l2.c index 65cd341f331a..1d4a5b46d9ae 100644 --- a/drivers/irqchip/irq-brcmstb-l2.c +++ b/drivers/irqchip/irq-brcmstb-l2.c @@ -112,8 +112,8 @@ static void brcmstb_l2_intc_resume(struct irq_data *d) irq_gc_unlock(gc); } -int __init brcmstb_l2_intc_of_init(struct device_node *np, - struct device_node *parent) +static int __init brcmstb_l2_intc_of_init(struct device_node *np, + struct device_node *parent) { unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; struct brcmstb_l2_intc_data *data; diff --git a/drivers/irqchip/irq-clps711x.c b/drivers/irqchip/irq-clps711x.c index 2223b3f15d68..f913f4db7ae1 100644 --- a/drivers/irqchip/irq-clps711x.c +++ b/drivers/irqchip/irq-clps711x.c @@ -234,5 +234,5 @@ static int __init clps711x_intc_init_dt(struct device_node *np, return _clps711x_intc_init(np, res.start, resource_size(&res)); } -IRQCHIP_DECLARE(clps711x, "cirrus,clps711x-intc", clps711x_intc_init_dt); +IRQCHIP_DECLARE(clps711x, "cirrus,ep7209-intc", clps711x_intc_init_dt); #endif diff --git a/drivers/irqchip/irq-gic-common.c b/drivers/irqchip/irq-gic-common.c index 89e7423f0ebb..9ae71804b5dd 100644 --- a/drivers/irqchip/irq-gic-common.c +++ b/drivers/irqchip/irq-gic-common.c @@ -90,8 +90,8 @@ int gic_configure_irq(unsigned int irq, unsigned int type, return ret; } -void __init gic_dist_config(void __iomem *base, int gic_irqs, - void (*sync_access)(void)) +void gic_dist_config(void __iomem *base, int gic_irqs, + void (*sync_access)(void)) { unsigned int i; diff --git a/drivers/irqchip/irq-gic-pm.c b/drivers/irqchip/irq-gic-pm.c new file mode 100644 index 000000000000..4cbffba3ff13 --- /dev/null +++ b/drivers/irqchip/irq-gic-pm.c @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2016 NVIDIA CORPORATION, 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 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/of_device.h> +#include <linux/of_irq.h> +#include <linux/irqchip/arm-gic.h> +#include <linux/platform_device.h> +#include <linux/pm_clock.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> + +struct gic_clk_data { + unsigned int num_clocks; + const char *const *clocks; +}; + +static int gic_runtime_resume(struct device *dev) +{ + struct gic_chip_data *gic = dev_get_drvdata(dev); + int ret; + + ret = pm_clk_resume(dev); + if (ret) + return ret; + + /* + * On the very first resume, the pointer to the driver data + * will be NULL and this is intentional, because we do not + * want to restore the GIC on the very first resume. So if + * the pointer is not valid just return. + */ + if (!gic) + return 0; + + gic_dist_restore(gic); + gic_cpu_restore(gic); + + return 0; +} + +static int gic_runtime_suspend(struct device *dev) +{ + struct gic_chip_data *gic = dev_get_drvdata(dev); + + gic_dist_save(gic); + gic_cpu_save(gic); + + return pm_clk_suspend(dev); +} + +static int gic_get_clocks(struct device *dev, const struct gic_clk_data *data) +{ + struct clk *clk; + unsigned int i; + int ret; + + if (!dev || !data) + return -EINVAL; + + ret = pm_clk_create(dev); + if (ret) + return ret; + + for (i = 0; i < data->num_clocks; i++) { + clk = of_clk_get_by_name(dev->of_node, data->clocks[i]); + if (IS_ERR(clk)) { + dev_err(dev, "failed to get clock %s\n", + data->clocks[i]); + ret = PTR_ERR(clk); + goto error; + } + + ret = pm_clk_add_clk(dev, clk); + if (ret) { + dev_err(dev, "failed to add clock at index %d\n", i); + clk_put(clk); + goto error; + } + } + + return 0; + +error: + pm_clk_destroy(dev); + + return ret; +} + +static int gic_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct gic_clk_data *data; + struct gic_chip_data *gic; + int ret, irq; + + data = of_device_get_match_data(&pdev->dev); + if (!data) { + dev_err(&pdev->dev, "no device match found\n"); + return -ENODEV; + } + + irq = irq_of_parse_and_map(dev->of_node, 0); + if (!irq) { + dev_err(dev, "no parent interrupt found!\n"); + return -EINVAL; + } + + ret = gic_get_clocks(dev, data); + if (ret) + goto irq_dispose; + + pm_runtime_enable(dev); + + ret = pm_runtime_get_sync(dev); + if (ret < 0) + goto rpm_disable; + + ret = gic_of_init_child(dev, &gic, irq); + if (ret) + goto rpm_put; + + platform_set_drvdata(pdev, gic); + + pm_runtime_put(dev); + + dev_info(dev, "GIC IRQ controller registered\n"); + + return 0; + +rpm_put: + pm_runtime_put_sync(dev); +rpm_disable: + pm_runtime_disable(dev); + pm_clk_destroy(dev); +irq_dispose: + irq_dispose_mapping(irq); + + return ret; +} + +static const struct dev_pm_ops gic_pm_ops = { + SET_RUNTIME_PM_OPS(gic_runtime_suspend, + gic_runtime_resume, NULL) +}; + +static const char * const gic400_clocks[] = { + "clk", +}; + +static const struct gic_clk_data gic400_data = { + .num_clocks = ARRAY_SIZE(gic400_clocks), + .clocks = gic400_clocks, +}; + +static const struct of_device_id gic_match[] = { + { .compatible = "nvidia,tegra210-agic", .data = &gic400_data }, + {}, +}; +MODULE_DEVICE_TABLE(of, gic_match); + +static struct platform_driver gic_driver = { + .probe = gic_probe, + .driver = { + .name = "gic", + .of_match_table = gic_match, + .pm = &gic_pm_ops, + } +}; + +builtin_platform_driver(gic_driver); diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index ad0d2960b664..35eb7ac5d21f 100644 --- a/drivers/irqchip/irq-gic-v2m.c +++ b/drivers/irqchip/irq-gic-v2m.c @@ -24,6 +24,7 @@ #include <linux/of_pci.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/irqchip/arm-gic.h> /* * MSI_TYPER: diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 5eb1f9e17a98..7ceaba81efb4 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -56,13 +56,14 @@ struct its_collection { }; /* - * The ITS_BASER structure - contains memory information and cached - * value of BASER register configuration. + * The ITS_BASER structure - contains memory information, cached + * value of BASER register configuration and ITS page size. */ struct its_baser { void *base; u64 val; u32 order; + u32 psz; }; /* @@ -824,180 +825,241 @@ static const char *its_base_type_string[] = { [GITS_BASER_TYPE_RESERVED7] = "Reserved (7)", }; -static void its_free_tables(struct its_node *its) +static u64 its_read_baser(struct its_node *its, struct its_baser *baser) { - int i; + u32 idx = baser - its->tables; - for (i = 0; i < GITS_BASER_NR_REGS; i++) { - if (its->tables[i].base) { - free_pages((unsigned long)its->tables[i].base, - its->tables[i].order); - its->tables[i].base = NULL; - } - } + return readq_relaxed(its->base + GITS_BASER + (idx << 3)); } -static int its_alloc_tables(const char *node_name, struct its_node *its) +static void its_write_baser(struct its_node *its, struct its_baser *baser, + u64 val) { - int err; - int i; - int psz = SZ_64K; - u64 shr = GITS_BASER_InnerShareable; - u64 cache; - u64 typer; - u32 ids; + u32 idx = baser - its->tables; - if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_22375) { - /* - * erratum 22375: only alloc 8MB table size - * erratum 24313: ignore memory access type - */ - cache = 0; - ids = 0x14; /* 20 bits, 8MB */ - } else { - cache = GITS_BASER_WaWb; - typer = readq_relaxed(its->base + GITS_TYPER); - ids = GITS_TYPER_DEVBITS(typer); + writeq_relaxed(val, its->base + GITS_BASER + (idx << 3)); + baser->val = its_read_baser(its, baser); +} + +static int its_setup_baser(struct its_node *its, struct its_baser *baser, + u64 cache, u64 shr, u32 psz, u32 order, + bool indirect) +{ + u64 val = its_read_baser(its, baser); + u64 esz = GITS_BASER_ENTRY_SIZE(val); + u64 type = GITS_BASER_TYPE(val); + u32 alloc_pages; + void *base; + u64 tmp; + +retry_alloc_baser: + alloc_pages = (PAGE_ORDER_TO_SIZE(order) / psz); + if (alloc_pages > GITS_BASER_PAGES_MAX) { + pr_warn("ITS@%pa: %s too large, reduce ITS pages %u->%u\n", + &its->phys_base, its_base_type_string[type], + alloc_pages, GITS_BASER_PAGES_MAX); + alloc_pages = GITS_BASER_PAGES_MAX; + order = get_order(GITS_BASER_PAGES_MAX * psz); } - its->device_ids = ids; + base = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, order); + if (!base) + return -ENOMEM; - for (i = 0; i < GITS_BASER_NR_REGS; i++) { - u64 val = readq_relaxed(its->base + GITS_BASER + i * 8); - u64 type = GITS_BASER_TYPE(val); - u64 entry_size = GITS_BASER_ENTRY_SIZE(val); - int order = get_order(psz); - int alloc_pages; - u64 tmp; - void *base; +retry_baser: + val = (virt_to_phys(base) | + (type << GITS_BASER_TYPE_SHIFT) | + ((esz - 1) << GITS_BASER_ENTRY_SIZE_SHIFT) | + ((alloc_pages - 1) << GITS_BASER_PAGES_SHIFT) | + cache | + shr | + GITS_BASER_VALID); + + val |= indirect ? GITS_BASER_INDIRECT : 0x0; + + switch (psz) { + case SZ_4K: + val |= GITS_BASER_PAGE_SIZE_4K; + break; + case SZ_16K: + val |= GITS_BASER_PAGE_SIZE_16K; + break; + case SZ_64K: + val |= GITS_BASER_PAGE_SIZE_64K; + break; + } - if (type == GITS_BASER_TYPE_NONE) - continue; + its_write_baser(its, baser, val); + tmp = baser->val; + if ((val ^ tmp) & GITS_BASER_SHAREABILITY_MASK) { /* - * Allocate as many entries as required to fit the - * range of device IDs that the ITS can grok... The ID - * space being incredibly sparse, this results in a - * massive waste of memory. - * - * For other tables, only allocate a single page. + * Shareability didn't stick. Just use + * whatever the read reported, which is likely + * to be the only thing this redistributor + * supports. If that's zero, make it + * non-cacheable as well. */ - if (type == GITS_BASER_TYPE_DEVICE) { - /* - * 'order' was initialized earlier to the default page - * granule of the the ITS. We can't have an allocation - * smaller than that. If the requested allocation - * is smaller, round up to the default page granule. - */ - order = max(get_order((1UL << ids) * entry_size), - order); - if (order >= MAX_ORDER) { - order = MAX_ORDER - 1; - pr_warn("%s: Device Table too large, reduce its page order to %u\n", - node_name, order); - } - } - -retry_alloc_baser: - alloc_pages = (PAGE_ORDER_TO_SIZE(order) / psz); - if (alloc_pages > GITS_BASER_PAGES_MAX) { - alloc_pages = GITS_BASER_PAGES_MAX; - order = get_order(GITS_BASER_PAGES_MAX * psz); - pr_warn("%s: Device Table too large, reduce its page order to %u (%u pages)\n", - node_name, order, alloc_pages); + shr = tmp & GITS_BASER_SHAREABILITY_MASK; + if (!shr) { + cache = GITS_BASER_nC; + __flush_dcache_area(base, PAGE_ORDER_TO_SIZE(order)); } + goto retry_baser; + } - base = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, order); - if (!base) { - err = -ENOMEM; - goto out_free; - } - - its->tables[i].base = base; - its->tables[i].order = order; - -retry_baser: - val = (virt_to_phys(base) | - (type << GITS_BASER_TYPE_SHIFT) | - ((entry_size - 1) << GITS_BASER_ENTRY_SIZE_SHIFT) | - cache | - shr | - GITS_BASER_VALID); + if ((val ^ tmp) & GITS_BASER_PAGE_SIZE_MASK) { + /* + * Page size didn't stick. Let's try a smaller + * size and retry. If we reach 4K, then + * something is horribly wrong... + */ + free_pages((unsigned long)base, order); + baser->base = NULL; switch (psz) { - case SZ_4K: - val |= GITS_BASER_PAGE_SIZE_4K; - break; case SZ_16K: - val |= GITS_BASER_PAGE_SIZE_16K; - break; + psz = SZ_4K; + goto retry_alloc_baser; case SZ_64K: - val |= GITS_BASER_PAGE_SIZE_64K; - break; + psz = SZ_16K; + goto retry_alloc_baser; } + } + + if (val != tmp) { + pr_err("ITS@%pa: %s doesn't stick: %lx %lx\n", + &its->phys_base, its_base_type_string[type], + (unsigned long) val, (unsigned long) tmp); + free_pages((unsigned long)base, order); + return -ENXIO; + } - val |= alloc_pages - 1; - its->tables[i].val = val; + baser->order = order; + baser->base = base; + baser->psz = psz; + tmp = indirect ? GITS_LVL1_ENTRY_SIZE : esz; - writeq_relaxed(val, its->base + GITS_BASER + i * 8); - tmp = readq_relaxed(its->base + GITS_BASER + i * 8); + pr_info("ITS@%pa: allocated %d %s @%lx (%s, esz %d, psz %dK, shr %d)\n", + &its->phys_base, (int)(PAGE_ORDER_TO_SIZE(order) / tmp), + its_base_type_string[type], + (unsigned long)virt_to_phys(base), + indirect ? "indirect" : "flat", (int)esz, + psz / SZ_1K, (int)shr >> GITS_BASER_SHAREABILITY_SHIFT); - if ((val ^ tmp) & GITS_BASER_SHAREABILITY_MASK) { + return 0; +} + +static bool its_parse_baser_device(struct its_node *its, struct its_baser *baser, + u32 psz, u32 *order) +{ + u64 esz = GITS_BASER_ENTRY_SIZE(its_read_baser(its, baser)); + u64 val = GITS_BASER_InnerShareable | GITS_BASER_WaWb; + u32 ids = its->device_ids; + u32 new_order = *order; + bool indirect = false; + + /* No need to enable Indirection if memory requirement < (psz*2)bytes */ + if ((esz << ids) > (psz * 2)) { + /* + * Find out whether hw supports a single or two-level table by + * table by reading bit at offset '62' after writing '1' to it. + */ + its_write_baser(its, baser, val | GITS_BASER_INDIRECT); + indirect = !!(baser->val & GITS_BASER_INDIRECT); + + if (indirect) { /* - * Shareability didn't stick. Just use - * whatever the read reported, which is likely - * to be the only thing this redistributor - * supports. If that's zero, make it - * non-cacheable as well. + * The size of the lvl2 table is equal to ITS page size + * which is 'psz'. For computing lvl1 table size, + * subtract ID bits that sparse lvl2 table from 'ids' + * which is reported by ITS hardware times lvl1 table + * entry size. */ - shr = tmp & GITS_BASER_SHAREABILITY_MASK; - if (!shr) { - cache = GITS_BASER_nC; - __flush_dcache_area(base, PAGE_ORDER_TO_SIZE(order)); - } - goto retry_baser; + ids -= ilog2(psz / esz); + esz = GITS_LVL1_ENTRY_SIZE; } + } - if ((val ^ tmp) & GITS_BASER_PAGE_SIZE_MASK) { - /* - * Page size didn't stick. Let's try a smaller - * size and retry. If we reach 4K, then - * something is horribly wrong... - */ - free_pages((unsigned long)base, order); - its->tables[i].base = NULL; + /* + * Allocate as many entries as required to fit the + * range of device IDs that the ITS can grok... The ID + * space being incredibly sparse, this results in a + * massive waste of memory if two-level device table + * feature is not supported by hardware. + */ + new_order = max_t(u32, get_order(esz << ids), new_order); + if (new_order >= MAX_ORDER) { + new_order = MAX_ORDER - 1; + ids = ilog2(PAGE_ORDER_TO_SIZE(new_order) / esz); + pr_warn("ITS@%pa: Device Table too large, reduce ids %u->%u\n", + &its->phys_base, its->device_ids, ids); + } - switch (psz) { - case SZ_16K: - psz = SZ_4K; - goto retry_alloc_baser; - case SZ_64K: - psz = SZ_16K; - goto retry_alloc_baser; - } - } + *order = new_order; + + return indirect; +} - if (val != tmp) { - pr_err("ITS: %s: GITS_BASER%d doesn't stick: %lx %lx\n", - node_name, i, - (unsigned long) val, (unsigned long) tmp); - err = -ENXIO; - goto out_free; +static void its_free_tables(struct its_node *its) +{ + int i; + + for (i = 0; i < GITS_BASER_NR_REGS; i++) { + if (its->tables[i].base) { + free_pages((unsigned long)its->tables[i].base, + its->tables[i].order); + its->tables[i].base = NULL; } + } +} + +static int its_alloc_tables(struct its_node *its) +{ + u64 typer = readq_relaxed(its->base + GITS_TYPER); + u32 ids = GITS_TYPER_DEVBITS(typer); + u64 shr = GITS_BASER_InnerShareable; + u64 cache = GITS_BASER_WaWb; + u32 psz = SZ_64K; + int err, i; - pr_info("ITS: allocated %d %s @%lx (psz %dK, shr %d)\n", - (int)(PAGE_ORDER_TO_SIZE(order) / entry_size), - its_base_type_string[type], - (unsigned long)virt_to_phys(base), - psz / SZ_1K, (int)shr >> GITS_BASER_SHAREABILITY_SHIFT); + if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_22375) { + /* + * erratum 22375: only alloc 8MB table size + * erratum 24313: ignore memory access type + */ + cache = GITS_BASER_nCnB; + ids = 0x14; /* 20 bits, 8MB */ } - return 0; + its->device_ids = ids; -out_free: - its_free_tables(its); + for (i = 0; i < GITS_BASER_NR_REGS; i++) { + struct its_baser *baser = its->tables + i; + u64 val = its_read_baser(its, baser); + u64 type = GITS_BASER_TYPE(val); + u32 order = get_order(psz); + bool indirect = false; - return err; + if (type == GITS_BASER_TYPE_NONE) + continue; + + if (type == GITS_BASER_TYPE_DEVICE) + indirect = its_parse_baser_device(its, baser, psz, &order); + + err = its_setup_baser(its, baser, cache, shr, psz, order, indirect); + if (err < 0) { + its_free_tables(its); + return err; + } + + /* Update settings which will be used for next BASERn */ + psz = baser->psz; + cache = baser->val & GITS_BASER_CACHEABILITY_MASK; + shr = baser->val & GITS_BASER_SHAREABILITY_MASK; + } + + return 0; } static int its_alloc_collections(struct its_node *its) @@ -1185,10 +1247,57 @@ static struct its_baser *its_get_baser(struct its_node *its, u32 type) return NULL; } +static bool its_alloc_device_table(struct its_node *its, u32 dev_id) +{ + struct its_baser *baser; + struct page *page; + u32 esz, idx; + __le64 *table; + + baser = its_get_baser(its, GITS_BASER_TYPE_DEVICE); + + /* Don't allow device id that exceeds ITS hardware limit */ + if (!baser) + return (ilog2(dev_id) < its->device_ids); + + /* Don't allow device id that exceeds single, flat table limit */ + esz = GITS_BASER_ENTRY_SIZE(baser->val); + if (!(baser->val & GITS_BASER_INDIRECT)) + return (dev_id < (PAGE_ORDER_TO_SIZE(baser->order) / esz)); + + /* Compute 1st level table index & check if that exceeds table limit */ + idx = dev_id >> ilog2(baser->psz / esz); + if (idx >= (PAGE_ORDER_TO_SIZE(baser->order) / GITS_LVL1_ENTRY_SIZE)) + return false; + + table = baser->base; + + /* Allocate memory for 2nd level table */ + if (!table[idx]) { + page = alloc_pages(GFP_KERNEL | __GFP_ZERO, get_order(baser->psz)); + if (!page) + return false; + + /* Flush Lvl2 table to PoC if hw doesn't support coherency */ + if (!(baser->val & GITS_BASER_SHAREABILITY_MASK)) + __flush_dcache_area(page_address(page), baser->psz); + + table[idx] = cpu_to_le64(page_to_phys(page) | GITS_BASER_VALID); + + /* Flush Lvl1 entry to PoC if hw doesn't support coherency */ + if (!(baser->val & GITS_BASER_SHAREABILITY_MASK)) + __flush_dcache_area(table + idx, GITS_LVL1_ENTRY_SIZE); + + /* Ensure updated table contents are visible to ITS hardware */ + dsb(sy); + } + + return true; +} + static struct its_device *its_create_device(struct its_node *its, u32 dev_id, int nvecs) { - struct its_baser *baser; struct its_device *dev; unsigned long *lpi_map; unsigned long flags; @@ -1199,14 +1308,7 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id, int nr_ites; int sz; - baser = its_get_baser(its, GITS_BASER_TYPE_DEVICE); - - /* Don't allow 'dev_id' that exceeds single, flat table limit */ - if (baser) { - if (dev_id >= (PAGE_ORDER_TO_SIZE(baser->order) / - GITS_BASER_ENTRY_SIZE(baser->val))) - return NULL; - } else if (ilog2(dev_id) >= its->device_ids) + if (!its_alloc_device_table(its, dev_id)) return NULL; dev = kzalloc(sizeof(*dev), GFP_KERNEL); @@ -1569,7 +1671,7 @@ static int __init its_probe(struct device_node *node, its_enable_quirks(its); - err = its_alloc_tables(node->full_name, its); + err = its_alloc_tables(its); if (err) goto out_free_cmd; diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 2c5ba0e704bf..6fc56c3466b0 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -538,23 +538,13 @@ static void gic_cpu_init(void) } #ifdef CONFIG_SMP -static int gic_secondary_init(struct notifier_block *nfb, - unsigned long action, void *hcpu) + +static int gic_starting_cpu(unsigned int cpu) { - if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) - gic_cpu_init(); - return NOTIFY_OK; + gic_cpu_init(); + return 0; } -/* - * Notifier for enabling the GIC CPU interface. Set an arbitrarily high - * priority because the GIC needs to be up before the ARM generic timers. - */ -static struct notifier_block gic_cpu_notifier = { - .notifier_call = gic_secondary_init, - .priority = 100, -}; - static u16 gic_compute_target_list(int *base_cpu, const struct cpumask *mask, unsigned long cluster_id) { @@ -634,7 +624,9 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) static void gic_smp_init(void) { set_smp_cross_call(gic_raise_softirq); - register_cpu_notifier(&gic_cpu_notifier); + cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_GICV3_STARTING, + "AP_IRQ_GICV3_STARTING", gic_starting_cpu, + NULL); } static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index fbc4ae2afd29..c2cab572c511 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -75,7 +75,7 @@ struct gic_chip_data { void __iomem *raw_dist_base; void __iomem *raw_cpu_base; u32 percpu_offset; -#ifdef CONFIG_CPU_PM +#if defined(CONFIG_CPU_PM) || defined(CONFIG_ARM_GIC_PM) u32 saved_spi_enable[DIV_ROUND_UP(1020, 32)]; u32 saved_spi_active[DIV_ROUND_UP(1020, 32)]; u32 saved_spi_conf[DIV_ROUND_UP(1020, 16)]; @@ -449,7 +449,7 @@ static void gic_cpu_if_up(struct gic_chip_data *gic) } -static void __init gic_dist_init(struct gic_chip_data *gic) +static void gic_dist_init(struct gic_chip_data *gic) { unsigned int i; u32 cpumask; @@ -528,14 +528,14 @@ int gic_cpu_if_down(unsigned int gic_nr) return 0; } -#ifdef CONFIG_CPU_PM +#if defined(CONFIG_CPU_PM) || defined(CONFIG_ARM_GIC_PM) /* * Saves the GIC distributor registers during suspend or idle. Must be called * with interrupts disabled but before powering down the GIC. After calling * this function, no interrupts will be delivered by the GIC, and another * platform-specific wakeup source must be enabled. */ -static void gic_dist_save(struct gic_chip_data *gic) +void gic_dist_save(struct gic_chip_data *gic) { unsigned int gic_irqs; void __iomem *dist_base; @@ -574,7 +574,7 @@ static void gic_dist_save(struct gic_chip_data *gic) * handled normally, but any edge interrupts that occured will not be seen by * the GIC and need to be handled by the platform-specific wakeup source. */ -static void gic_dist_restore(struct gic_chip_data *gic) +void gic_dist_restore(struct gic_chip_data *gic) { unsigned int gic_irqs; unsigned int i; @@ -620,7 +620,7 @@ static void gic_dist_restore(struct gic_chip_data *gic) writel_relaxed(GICD_ENABLE, dist_base + GIC_DIST_CTRL); } -static void gic_cpu_save(struct gic_chip_data *gic) +void gic_cpu_save(struct gic_chip_data *gic) { int i; u32 *ptr; @@ -650,7 +650,7 @@ static void gic_cpu_save(struct gic_chip_data *gic) } -static void gic_cpu_restore(struct gic_chip_data *gic) +void gic_cpu_restore(struct gic_chip_data *gic) { int i; u32 *ptr; @@ -727,7 +727,7 @@ static struct notifier_block gic_notifier_block = { .notifier_call = gic_notifier, }; -static int __init gic_pm_init(struct gic_chip_data *gic) +static int gic_pm_init(struct gic_chip_data *gic) { gic->saved_ppi_enable = __alloc_percpu(DIV_ROUND_UP(32, 32) * 4, sizeof(u32)); @@ -757,7 +757,7 @@ free_ppi_enable: return -ENOMEM; } #else -static int __init gic_pm_init(struct gic_chip_data *gic) +static int gic_pm_init(struct gic_chip_data *gic) { return 0; } @@ -984,25 +984,12 @@ static int gic_irq_domain_translate(struct irq_domain *d, return -EINVAL; } -#ifdef CONFIG_SMP -static int gic_secondary_init(struct notifier_block *nfb, unsigned long action, - void *hcpu) +static int gic_starting_cpu(unsigned int cpu) { - if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) - gic_cpu_init(&gic_data[0]); - return NOTIFY_OK; + gic_cpu_init(&gic_data[0]); + return 0; } -/* - * Notifier for enabling the GIC CPU interface. Set an arbitrarily high - * priority because the GIC needs to be up before the ARM generic timers. - */ -static struct notifier_block gic_cpu_notifier = { - .notifier_call = gic_secondary_init, - .priority = 100, -}; -#endif - static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, void *arg) { @@ -1032,32 +1019,31 @@ static const struct irq_domain_ops gic_irq_domain_ops = { .unmap = gic_irq_domain_unmap, }; -static int __init __gic_init_bases(struct gic_chip_data *gic, int irq_start, - struct fwnode_handle *handle) +static void gic_init_chip(struct gic_chip_data *gic, struct device *dev, + const char *name, bool use_eoimode1) { - irq_hw_number_t hwirq_base; - int gic_irqs, irq_base, i, ret; - - if (WARN_ON(!gic || gic->domain)) - return -EINVAL; - /* Initialize irq_chip */ gic->chip = gic_chip; + gic->chip.name = name; + gic->chip.parent_device = dev; - if (static_key_true(&supports_deactivate) && gic == &gic_data[0]) { + if (use_eoimode1) { gic->chip.irq_mask = gic_eoimode1_mask_irq; gic->chip.irq_eoi = gic_eoimode1_eoi_irq; gic->chip.irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity; - gic->chip.name = kasprintf(GFP_KERNEL, "GICv2"); - } else { - gic->chip.name = kasprintf(GFP_KERNEL, "GIC-%d", - (int)(gic - &gic_data[0])); } #ifdef CONFIG_SMP if (gic == &gic_data[0]) gic->chip.irq_set_affinity = gic_set_affinity; #endif +} + +static int gic_init_bases(struct gic_chip_data *gic, int irq_start, + struct fwnode_handle *handle) +{ + irq_hw_number_t hwirq_base; + int gic_irqs, irq_base, ret; if (IS_ENABLED(CONFIG_GIC_NON_BANKED) && gic->percpu_offset) { /* Frankein-GIC without banked registers... */ @@ -1138,6 +1124,36 @@ static int __init __gic_init_bases(struct gic_chip_data *gic, int irq_start, goto error; } + gic_dist_init(gic); + ret = gic_cpu_init(gic); + if (ret) + goto error; + + ret = gic_pm_init(gic); + if (ret) + goto error; + + return 0; + +error: + if (IS_ENABLED(CONFIG_GIC_NON_BANKED) && gic->percpu_offset) { + free_percpu(gic->dist_base.percpu_base); + free_percpu(gic->cpu_base.percpu_base); + } + + return ret; +} + +static int __init __gic_init_bases(struct gic_chip_data *gic, + int irq_start, + struct fwnode_handle *handle) +{ + char *name; + int i, ret; + + if (WARN_ON(!gic || gic->domain)) + return -EINVAL; + if (gic == &gic_data[0]) { /* * Initialize the CPU interface map to all CPUs. @@ -1148,31 +1164,26 @@ static int __init __gic_init_bases(struct gic_chip_data *gic, int irq_start, gic_cpu_map[i] = 0xff; #ifdef CONFIG_SMP set_smp_cross_call(gic_raise_softirq); - register_cpu_notifier(&gic_cpu_notifier); #endif + cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_GIC_STARTING, + "AP_IRQ_GIC_STARTING", + gic_starting_cpu, NULL); set_handle_irq(gic_handle_irq); if (static_key_true(&supports_deactivate)) pr_info("GIC: Using split EOI/Deactivate mode\n"); } - gic_dist_init(gic); - ret = gic_cpu_init(gic); - if (ret) - goto error; - - ret = gic_pm_init(gic); - if (ret) - goto error; - - return 0; - -error: - if (IS_ENABLED(CONFIG_GIC_NON_BANKED) && gic->percpu_offset) { - free_percpu(gic->dist_base.percpu_base); - free_percpu(gic->cpu_base.percpu_base); + if (static_key_true(&supports_deactivate) && gic == &gic_data[0]) { + name = kasprintf(GFP_KERNEL, "GICv2"); + gic_init_chip(gic, NULL, name, true); + } else { + name = kasprintf(GFP_KERNEL, "GIC-%d", (int)(gic-&gic_data[0])); + gic_init_chip(gic, NULL, name, false); } - kfree(gic->chip.name); + ret = gic_init_bases(gic, irq_start, handle); + if (ret) + kfree(name); return ret; } @@ -1250,7 +1261,7 @@ static bool gic_check_eoimode(struct device_node *node, void __iomem **base) return true; } -static int __init gic_of_setup(struct gic_chip_data *gic, struct device_node *node) +static int gic_of_setup(struct gic_chip_data *gic, struct device_node *node) { if (!gic || !node) return -EINVAL; @@ -1274,6 +1285,34 @@ error: return -ENOMEM; } +int gic_of_init_child(struct device *dev, struct gic_chip_data **gic, int irq) +{ + int ret; + + if (!dev || !dev->of_node || !gic || !irq) + return -EINVAL; + + *gic = devm_kzalloc(dev, sizeof(**gic), GFP_KERNEL); + if (!*gic) + return -ENOMEM; + + gic_init_chip(*gic, dev, dev->of_node->name, false); + + ret = gic_of_setup(*gic, dev->of_node); + if (ret) + return ret; + + ret = gic_init_bases(*gic, -1, &dev->of_node->fwnode); + if (ret) { + gic_teardown(*gic); + return ret; + } + + irq_set_chained_handler_and_data(irq, gic_handle_cascade_irq, *gic); + + return 0; +} + static void __init gic_of_setup_kvm_info(struct device_node *node) { int ret; @@ -1353,7 +1392,11 @@ IRQCHIP_DECLARE(cortex_a7_gic, "arm,cortex-a7-gic", gic_of_init); IRQCHIP_DECLARE(msm_8660_qgic, "qcom,msm-8660-qgic", gic_of_init); IRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", gic_of_init); IRQCHIP_DECLARE(pl390, "arm,pl390", gic_of_init); - +#else +int gic_of_init_child(struct device *dev, struct gic_chip_data **gic, int irq) +{ + return -ENOTSUPP; +} #endif #ifdef CONFIG_ACPI diff --git a/drivers/irqchip/irq-hip04.c b/drivers/irqchip/irq-hip04.c index 9e25d8ce08e5..021b0e0833c1 100644 --- a/drivers/irqchip/irq-hip04.c +++ b/drivers/irqchip/irq-hip04.c @@ -342,26 +342,12 @@ static int hip04_irq_domain_xlate(struct irq_domain *d, return ret; } -#ifdef CONFIG_SMP -static int hip04_irq_secondary_init(struct notifier_block *nfb, - unsigned long action, - void *hcpu) +static int hip04_irq_starting_cpu(unsigned int cpu) { - if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) - hip04_irq_cpu_init(&hip04_data); - return NOTIFY_OK; + hip04_irq_cpu_init(&hip04_data); + return 0; } -/* - * Notifier for enabling the INTC CPU interface. Set an arbitrarily high - * priority because the GIC needs to be up before the ARM generic timers. - */ -static struct notifier_block hip04_irq_cpu_notifier = { - .notifier_call = hip04_irq_secondary_init, - .priority = 100, -}; -#endif - static const struct irq_domain_ops hip04_irq_domain_ops = { .map = hip04_irq_domain_map, .xlate = hip04_irq_domain_xlate, @@ -417,13 +403,12 @@ hip04_of_init(struct device_node *node, struct device_node *parent) #ifdef CONFIG_SMP set_smp_cross_call(hip04_raise_softirq); - register_cpu_notifier(&hip04_irq_cpu_notifier); #endif set_handle_irq(hip04_handle_irq); hip04_irq_dist_init(&hip04_data); - hip04_irq_cpu_init(&hip04_data); - + cpuhp_setup_state(CPUHP_AP_IRQ_HIP04_STARTING, "AP_IRQ_HIP04_STARTING", + hip04_irq_starting_cpu, NULL); return 0; } IRQCHIP_DECLARE(hip04_intc, "hisilicon,hip04-intc", hip04_of_init); diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c index 8a4adbeb2b8c..3786d0f21972 100644 --- a/drivers/irqchip/irq-mips-gic.c +++ b/drivers/irqchip/irq-mips-gic.c @@ -718,7 +718,7 @@ static int gic_shared_irq_domain_map(struct irq_domain *d, unsigned int virq, spin_lock_irqsave(&gic_lock, flags); gic_map_to_pin(intr, gic_cpu_pin); - gic_map_to_vpe(intr, vpe); + gic_map_to_vpe(intr, mips_cm_vp_id(vpe)); for (i = 0; i < min(gic_vpes, NR_CPUS); i++) clear_bit(intr, pcpu_masks[i].pcpu_mask); set_bit(intr, pcpu_masks[vpe].pcpu_mask); @@ -959,7 +959,7 @@ int gic_ipi_domain_match(struct irq_domain *d, struct device_node *node, switch (bus_token) { case DOMAIN_BUS_IPI: is_ipi = d->bus_token == bus_token; - return to_of_node(d->fwnode) == node && is_ipi; + return (!node || to_of_node(d->fwnode) == node) && is_ipi; break; default: return 0; @@ -1042,12 +1042,14 @@ static void __init __gic_init(unsigned long gic_base_addr, &gic_irq_domain_ops, NULL); if (!gic_irq_domain) panic("Failed to add GIC IRQ domain"); + gic_irq_domain->name = "mips-gic-irq"; gic_dev_domain = irq_domain_add_hierarchy(gic_irq_domain, 0, GIC_NUM_LOCAL_INTRS + gic_shared_intrs, node, &gic_dev_domain_ops, NULL); if (!gic_dev_domain) panic("Failed to add GIC DEV domain"); + gic_dev_domain->name = "mips-gic-dev"; gic_ipi_domain = irq_domain_add_hierarchy(gic_irq_domain, IRQ_DOMAIN_FLAG_IPI_PER_CPU, @@ -1056,6 +1058,7 @@ static void __init __gic_init(unsigned long gic_base_addr, if (!gic_ipi_domain) panic("Failed to add GIC IPI domain"); + gic_ipi_domain->name = "mips-gic-ipi"; gic_ipi_domain->bus_token = DOMAIN_BUS_IPI; if (node && diff --git a/drivers/irqchip/irq-omap-intc.c b/drivers/irqchip/irq-omap-intc.c index 9d1bcfc33e4c..b04a8ac6e744 100644 --- a/drivers/irqchip/irq-omap-intc.c +++ b/drivers/irqchip/irq-omap-intc.c @@ -23,6 +23,8 @@ #include <linux/of_address.h> #include <linux/of_irq.h> +#include <linux/irqchip/irq-omap-intc.h> + /* Define these here for now until we drop all board-files */ #define OMAP24XX_IC_BASE 0x480fe000 #define OMAP34XX_IC_BASE 0x48200000 diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c index 5dc5a760c723..c25ce5af091a 100644 --- a/drivers/irqchip/irq-s3c24xx.c +++ b/drivers/irqchip/irq-s3c24xx.c @@ -92,9 +92,9 @@ static void s3c_irq_mask(struct irq_data *data) unsigned long mask; unsigned int irqno; - mask = __raw_readl(intc->reg_mask); + mask = readl_relaxed(intc->reg_mask); mask |= (1UL << irq_data->offset); - __raw_writel(mask, intc->reg_mask); + writel_relaxed(mask, intc->reg_mask); if (parent_intc) { parent_data = &parent_intc->irqs[irq_data->parent_irq]; @@ -119,9 +119,9 @@ static void s3c_irq_unmask(struct irq_data *data) unsigned long mask; unsigned int irqno; - mask = __raw_readl(intc->reg_mask); + mask = readl_relaxed(intc->reg_mask); mask &= ~(1UL << irq_data->offset); - __raw_writel(mask, intc->reg_mask); + writel_relaxed(mask, intc->reg_mask); if (parent_intc) { irqno = irq_find_mapping(parent_intc->domain, @@ -136,9 +136,9 @@ static inline void s3c_irq_ack(struct irq_data *data) struct s3c_irq_intc *intc = irq_data->intc; unsigned long bitval = 1UL << irq_data->offset; - __raw_writel(bitval, intc->reg_pending); + writel_relaxed(bitval, intc->reg_pending); if (intc->reg_intpnd) - __raw_writel(bitval, intc->reg_intpnd); + writel_relaxed(bitval, intc->reg_intpnd); } static int s3c_irq_type(struct irq_data *data, unsigned int type) @@ -172,9 +172,9 @@ static int s3c_irqext_type_set(void __iomem *gpcon_reg, unsigned long newvalue = 0, value; /* Set the GPIO to external interrupt mode */ - value = __raw_readl(gpcon_reg); + value = readl_relaxed(gpcon_reg); value = (value & ~(3 << gpcon_offset)) | (0x02 << gpcon_offset); - __raw_writel(value, gpcon_reg); + writel_relaxed(value, gpcon_reg); /* Set the external interrupt to pointed trigger type */ switch (type) @@ -208,9 +208,9 @@ static int s3c_irqext_type_set(void __iomem *gpcon_reg, return -EINVAL; } - value = __raw_readl(extint_reg); + value = readl_relaxed(extint_reg); value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset); - __raw_writel(value, extint_reg); + writel_relaxed(value, extint_reg); return 0; } @@ -315,8 +315,8 @@ static void s3c_irq_demux(struct irq_desc *desc) chained_irq_enter(chip, desc); - src = __raw_readl(sub_intc->reg_pending); - msk = __raw_readl(sub_intc->reg_mask); + src = readl_relaxed(sub_intc->reg_pending); + msk = readl_relaxed(sub_intc->reg_mask); src &= ~msk; src &= irq_data->sub_bits; @@ -337,7 +337,7 @@ static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc, int pnd; int offset; - pnd = __raw_readl(intc->reg_intpnd); + pnd = readl_relaxed(intc->reg_intpnd); if (!pnd) return false; @@ -352,7 +352,7 @@ static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc, * * Thanks to Klaus, Shannon, et al for helping to debug this problem */ - offset = __raw_readl(intc->reg_intpnd + 4); + offset = readl_relaxed(intc->reg_intpnd + 4); /* Find the bit manually, when the offset is wrong. * The pending register only ever contains the one bit of the next @@ -406,7 +406,7 @@ int s3c24xx_set_fiq(unsigned int irq, bool on) intmod = 0; } - __raw_writel(intmod, S3C2410_INTMOD); + writel_relaxed(intmod, S3C2410_INTMOD); return 0; } @@ -508,14 +508,14 @@ static void s3c24xx_clear_intc(struct s3c_irq_intc *intc) last = 0; for (i = 0; i < 4; i++) { - pend = __raw_readl(reg_source); + pend = readl_relaxed(reg_source); if (pend == 0 || pend == last) break; - __raw_writel(pend, intc->reg_pending); + writel_relaxed(pend, intc->reg_pending); if (intc->reg_intpnd) - __raw_writel(pend, intc->reg_intpnd); + writel_relaxed(pend, intc->reg_intpnd); pr_info("irq: clearing pending status %08x\n", (int)pend); last = pend; diff --git a/drivers/irqchip/irq-sirfsoc.c b/drivers/irqchip/irq-sirfsoc.c index 10cb21b9ba3d..e1336848affa 100644 --- a/drivers/irqchip/irq-sirfsoc.c +++ b/drivers/irqchip/irq-sirfsoc.c @@ -29,6 +29,11 @@ static struct irq_domain *sirfsoc_irqdomain; +static void __iomem *sirfsoc_irq_get_regbase(void) +{ + return (void __iomem __force *)sirfsoc_irqdomain->host_data; +} + static __init void sirfsoc_alloc_gc(void __iomem *base) { unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; @@ -53,7 +58,7 @@ static __init void sirfsoc_alloc_gc(void __iomem *base) static void __exception_irq_entry sirfsoc_handle_irq(struct pt_regs *regs) { - void __iomem *base = sirfsoc_irqdomain->host_data; + void __iomem *base = sirfsoc_irq_get_regbase(); u32 irqstat; irqstat = readl_relaxed(base + SIRFSOC_INIT_IRQ_ID); @@ -94,7 +99,7 @@ static struct sirfsoc_irq_status sirfsoc_irq_st; static int sirfsoc_irq_suspend(void) { - void __iomem *base = sirfsoc_irqdomain->host_data; + void __iomem *base = sirfsoc_irq_get_regbase(); sirfsoc_irq_st.mask0 = readl_relaxed(base + SIRFSOC_INT_RISC_MASK0); sirfsoc_irq_st.mask1 = readl_relaxed(base + SIRFSOC_INT_RISC_MASK1); @@ -106,7 +111,7 @@ static int sirfsoc_irq_suspend(void) static void sirfsoc_irq_resume(void) { - void __iomem *base = sirfsoc_irqdomain->host_data; + void __iomem *base = sirfsoc_irq_get_regbase(); writel_relaxed(sirfsoc_irq_st.mask0, base + SIRFSOC_INT_RISC_MASK0); writel_relaxed(sirfsoc_irq_st.mask1, base + SIRFSOC_INT_RISC_MASK1); diff --git a/drivers/irqchip/irq-tegra.c b/drivers/irqchip/irq-tegra.c index e902f081e16c..3973a14bb15b 100644 --- a/drivers/irqchip/irq-tegra.c +++ b/drivers/irqchip/irq-tegra.c @@ -90,7 +90,7 @@ static struct tegra_ictlr_info *lic; static inline void tegra_ictlr_write_mask(struct irq_data *d, unsigned long reg) { - void __iomem *base = d->chip_data; + void __iomem *base = (void __iomem __force *)d->chip_data; u32 mask; mask = BIT(d->hwirq % 32); @@ -266,7 +266,7 @@ static int tegra_ictlr_domain_alloc(struct irq_domain *domain, irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, &tegra_ictlr_chip, - info->base[ictlr]); + (void __force *)info->base[ictlr]); } parent_fwspec = *fwspec; diff --git a/drivers/irqchip/irq-vic.c b/drivers/irqchip/irq-vic.c index b956dfffe78c..f811a7de5857 100644 --- a/drivers/irqchip/irq-vic.c +++ b/drivers/irqchip/irq-vic.c @@ -167,7 +167,7 @@ static int vic_suspend(void) return 0; } -struct syscore_ops vic_syscore_ops = { +static struct syscore_ops vic_syscore_ops = { .suspend = vic_suspend, .resume = vic_resume, }; @@ -517,7 +517,8 @@ int __init vic_init_cascaded(void __iomem *base, unsigned int parent_irq, EXPORT_SYMBOL_GPL(vic_init_cascaded); #ifdef CONFIG_OF -int __init vic_of_init(struct device_node *node, struct device_node *parent) +static int __init vic_of_init(struct device_node *node, + struct device_node *parent) { void __iomem *regs; u32 interrupt_mask = ~0; |