MIPS: smp.c: Introduce mechanism for freeing and allocating IPIs
[cascardo/linux.git] / arch / mips / kernel / smp.c
index cb02df2..0e131c9 100644 (file)
@@ -231,7 +231,7 @@ static struct irqaction irq_call = {
        .name           = "IPI call"
 };
 
-static __init void smp_ipi_init_one(unsigned int virq,
+static void smp_ipi_init_one(unsigned int virq,
                                    struct irqaction *action)
 {
        int ret;
@@ -241,9 +241,11 @@ static __init void smp_ipi_init_one(unsigned int virq,
        BUG_ON(ret);
 }
 
-static int __init mips_smp_ipi_init(void)
+static unsigned int call_virq, sched_virq;
+
+int mips_smp_ipi_allocate(const struct cpumask *mask)
 {
-       unsigned int call_virq, sched_virq;
+       int virq;
        struct irq_domain *ipidomain;
        struct device_node *node;
 
@@ -270,16 +272,20 @@ static int __init mips_smp_ipi_init(void)
        if (!ipidomain)
                return 0;
 
-       call_virq = irq_reserve_ipi(ipidomain, cpu_possible_mask);
-       BUG_ON(!call_virq);
+       virq = irq_reserve_ipi(ipidomain, mask);
+       BUG_ON(!virq);
+       if (!call_virq)
+               call_virq = virq;
 
-       sched_virq = irq_reserve_ipi(ipidomain, cpu_possible_mask);
-       BUG_ON(!sched_virq);
+       virq = irq_reserve_ipi(ipidomain, mask);
+       BUG_ON(!virq);
+       if (!sched_virq)
+               sched_virq = virq;
 
        if (irq_domain_is_ipi_per_cpu(ipidomain)) {
                int cpu;
 
-               for_each_cpu(cpu, cpu_possible_mask) {
+               for_each_cpu(cpu, mask) {
                        smp_ipi_init_one(call_virq + cpu, &irq_call);
                        smp_ipi_init_one(sched_virq + cpu, &irq_resched);
                }
@@ -288,6 +294,45 @@ static int __init mips_smp_ipi_init(void)
                smp_ipi_init_one(sched_virq, &irq_resched);
        }
 
+       return 0;
+}
+
+int mips_smp_ipi_free(const struct cpumask *mask)
+{
+       struct irq_domain *ipidomain;
+       struct device_node *node;
+
+       node = of_irq_find_parent(of_root);
+       ipidomain = irq_find_matching_host(node, DOMAIN_BUS_IPI);
+
+       /*
+        * Some platforms have half DT setup. So if we found irq node but
+        * didn't find an ipidomain, try to search for one that is not in the
+        * DT.
+        */
+       if (node && !ipidomain)
+               ipidomain = irq_find_matching_host(NULL, DOMAIN_BUS_IPI);
+
+       BUG_ON(!ipidomain);
+
+       if (irq_domain_is_ipi_per_cpu(ipidomain)) {
+               int cpu;
+
+               for_each_cpu(cpu, mask) {
+                       remove_irq(call_virq + cpu, &irq_call);
+                       remove_irq(sched_virq + cpu, &irq_resched);
+               }
+       }
+       irq_destroy_ipi(call_virq, mask);
+       irq_destroy_ipi(sched_virq, mask);
+       return 0;
+}
+
+
+static int __init mips_smp_ipi_init(void)
+{
+       mips_smp_ipi_allocate(cpu_possible_mask);
+
        call_desc = irq_to_desc(call_virq);
        sched_desc = irq_to_desc(sched_virq);