diff options
Diffstat (limited to 'drivers/irqchip/irq-gic.c')
-rw-r--r-- | drivers/irqchip/irq-gic.c | 157 |
1 files changed, 100 insertions, 57 deletions
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 |