Merge branch 'linux-linaro-lsk-v4.4-android' of git://git.linaro.org/kernel/linux...
[firefly-linux-kernel-4.4.55.git] / drivers / irqchip / irq-gic-v3.c
index e33c729b9f48d58b89727103f3a0de1be61cdf77..dff17aef68a24026193f9cfa9e43cc158f184faa 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <linux/irqchip.h>
 #include <linux/irqchip/arm-gic-v3.h>
+#include <linux/irqchip/irq-partition-percpu.h>
 
 #include <asm/cputype.h>
 #include <asm/exception.h>
@@ -41,6 +42,7 @@ struct redist_region {
 };
 
 struct gic_chip_data {
+       struct fwnode_handle    *fwnode;
        void __iomem            *dist_base;
        struct redist_region    *redist_regions;
        struct rdists           rdists;
@@ -48,6 +50,7 @@ struct gic_chip_data {
        u64                     redist_stride;
        u32                     nr_redist_regions;
        unsigned int            irq_nr;
+       struct partition_desc   *ppi_descs[16];
 };
 
 static struct gic_chip_data gic_data __read_mostly;
@@ -817,10 +820,62 @@ static void gic_irq_domain_free(struct irq_domain *domain, unsigned int virq,
        }
 }
 
+static int gic_irq_domain_select(struct irq_domain *d,
+                                struct irq_fwspec *fwspec,
+                                enum irq_domain_bus_token bus_token)
+{
+       /* Not for us */
+        if (fwspec->fwnode != d->fwnode)
+               return 0;
+
+       /* If this is not DT, then we have a single domain */
+       if (!is_of_node(fwspec->fwnode))
+               return 1;
+
+       /*
+        * If this is a PPI and we have a 4th (non-null) parameter,
+        * then we need to match the partition domain.
+        */
+       if (fwspec->param_count >= 4 &&
+           fwspec->param[0] == 1 && fwspec->param[3] != 0)
+               return d == partition_get_domain(gic_data.ppi_descs[fwspec->param[1]]);
+
+       return d == gic_data.domain;
+}
+
 static const struct irq_domain_ops gic_irq_domain_ops = {
        .translate = gic_irq_domain_translate,
        .alloc = gic_irq_domain_alloc,
        .free = gic_irq_domain_free,
+       .select = gic_irq_domain_select,
+};
+
+static int partition_domain_translate(struct irq_domain *d,
+                                     struct irq_fwspec *fwspec,
+                                     unsigned long *hwirq,
+                                     unsigned int *type)
+{
+       struct device_node *np;
+       int ret;
+
+       np = of_find_node_by_phandle(fwspec->param[3]);
+       if (WARN_ON(!np))
+               return -EINVAL;
+
+       ret = partition_translate_id(gic_data.ppi_descs[fwspec->param[1]],
+                                    of_node_to_fwnode(np));
+       if (ret < 0)
+               return ret;
+
+       *hwirq = ret;
+       *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
+
+       return 0;
+}
+
+static const struct irq_domain_ops partition_domain_ops = {
+       .translate = partition_domain_translate,
+       .select = gic_irq_domain_select,
 };
 
 static void gicv3_enable_quirks(void)
@@ -831,59 +886,16 @@ static void gicv3_enable_quirks(void)
 #endif
 }
 
-static int __init gic_of_init(struct device_node *node, struct device_node *parent)
+static int __init gic_init_bases(void __iomem *dist_base,
+                                struct redist_region *rdist_regs,
+                                u32 nr_redist_regions,
+                                u64 redist_stride,
+                                struct fwnode_handle *handle)
 {
-       void __iomem *dist_base;
-       struct redist_region *rdist_regs;
-       u64 redist_stride;
-       u32 nr_redist_regions;
+       struct device_node *node;
        u32 typer;
-       u32 reg;
        int gic_irqs;
        int err;
-       int i;
-
-       dist_base = of_iomap(node, 0);
-       if (!dist_base) {
-               pr_err("%s: unable to map gic dist registers\n",
-                       node->full_name);
-               return -ENXIO;
-       }
-
-       reg = readl_relaxed(dist_base + GICD_PIDR2) & GIC_PIDR2_ARCH_MASK;
-       if (reg != GIC_PIDR2_ARCH_GICv3 && reg != GIC_PIDR2_ARCH_GICv4) {
-               pr_err("%s: no distributor detected, giving up\n",
-                       node->full_name);
-               err = -ENODEV;
-               goto out_unmap_dist;
-       }
-
-       if (of_property_read_u32(node, "#redistributor-regions", &nr_redist_regions))
-               nr_redist_regions = 1;
-
-       rdist_regs = kzalloc(sizeof(*rdist_regs) * nr_redist_regions, GFP_KERNEL);
-       if (!rdist_regs) {
-               err = -ENOMEM;
-               goto out_unmap_dist;
-       }
-
-       for (i = 0; i < nr_redist_regions; i++) {
-               struct resource res;
-               int ret;
-
-               ret = of_address_to_resource(node, 1 + i, &res);
-               rdist_regs[i].redist_base = of_iomap(node, 1 + i);
-               if (ret || !rdist_regs[i].redist_base) {
-                       pr_err("%s: couldn't map region %d\n",
-                              node->full_name, i);
-                       err = -ENODEV;
-                       goto out_unmap_rdist;
-               }
-               rdist_regs[i].phys_base = res.start;
-       }
-
-       if (of_property_read_u64(node, "redistributor-stride", &redist_stride))
-               redist_stride = 0;
 
        if (!is_hyp_mode_available())
                static_key_slow_dec(&supports_deactivate);
@@ -891,6 +903,7 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
        if (static_key_true(&supports_deactivate))
                pr_info("GIC: Using split EOI/Deactivate mode\n");
 
+       gic_data.fwnode = handle;
        gic_data.dist_base = dist_base;
        gic_data.redist_regions = rdist_regs;
        gic_data.nr_redist_regions = nr_redist_regions;
@@ -909,8 +922,8 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
                gic_irqs = 1020;
        gic_data.irq_nr = gic_irqs;
 
-       gic_data.domain = irq_domain_add_tree(node, &gic_irq_domain_ops,
-                                             &gic_data);
+       gic_data.domain = irq_domain_create_tree(handle, &gic_irq_domain_ops,
+                                                &gic_data);
        gic_data.rdists.rdist = alloc_percpu(typeof(*gic_data.rdists.rdist));
 
        if (WARN_ON(!gic_data.domain) || WARN_ON(!gic_data.rdists.rdist)) {
@@ -920,7 +933,9 @@ 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())
+       node = to_of_node(handle);
+       if (IS_ENABLED(CONFIG_ARM_GIC_V3_ITS) && gic_dist_supports_lpis() &&
+           node) /* Temp hack to prevent ITS init for ACPI */
                its_init(node, &gic_data.rdists, gic_data.domain);
 
        gic_smp_init();
@@ -934,6 +949,189 @@ out_free:
        if (gic_data.domain)
                irq_domain_remove(gic_data.domain);
        free_percpu(gic_data.rdists.rdist);
+       return err;
+}
+
+static int __init gic_validate_dist_version(void __iomem *dist_base)
+{
+       u32 reg = readl_relaxed(dist_base + GICD_PIDR2) & GIC_PIDR2_ARCH_MASK;
+
+       if (reg != GIC_PIDR2_ARCH_GICv3 && reg != GIC_PIDR2_ARCH_GICv4)
+               return -ENODEV;
+
+       return 0;
+}
+
+static int get_cpu_number(struct device_node *dn)
+{
+       const __be32 *cell;
+       u64 hwid;
+       int i;
+
+       cell = of_get_property(dn, "reg", NULL);
+       if (!cell)
+               return -1;
+
+       hwid = of_read_number(cell, of_n_addr_cells(dn));
+
+       /*
+        * Non affinity bits must be set to 0 in the DT
+        */
+       if (hwid & ~MPIDR_HWID_BITMASK)
+               return -1;
+
+       for (i = 0; i < num_possible_cpus(); i++)
+               if (cpu_logical_map(i) == hwid)
+                       return i;
+
+       return -1;
+}
+
+/* Create all possible partitions at boot time */
+static void gic_populate_ppi_partitions(struct device_node *gic_node)
+{
+       struct device_node *parts_node, *child_part;
+       int part_idx = 0, i;
+       int nr_parts;
+       struct partition_affinity *parts;
+
+       parts_node = of_find_node_by_name(gic_node, "ppi-partitions");
+       if (!parts_node)
+               return;
+
+       nr_parts = of_get_child_count(parts_node);
+
+       if (!nr_parts)
+               return;
+
+       parts = kzalloc(sizeof(*parts) * nr_parts, GFP_KERNEL);
+       if (WARN_ON(!parts))
+               return;
+
+       for_each_child_of_node(parts_node, child_part) {
+               struct partition_affinity *part;
+               int n;
+
+               part = &parts[part_idx];
+
+               part->partition_id = of_node_to_fwnode(child_part);
+
+               pr_info("GIC: PPI partition %s[%d] { ",
+                       child_part->name, part_idx);
+
+               n = of_property_count_elems_of_size(child_part, "affinity",
+                                                   sizeof(u32));
+               WARN_ON(n <= 0);
+
+               for (i = 0; i < n; i++) {
+                       int err, cpu;
+                       u32 cpu_phandle;
+                       struct device_node *cpu_node;
+
+                       err = of_property_read_u32_index(child_part, "affinity",
+                                                        i, &cpu_phandle);
+                       if (WARN_ON(err))
+                               continue;
+
+                       cpu_node = of_find_node_by_phandle(cpu_phandle);
+                       if (WARN_ON(!cpu_node))
+                               continue;
+
+                       cpu = get_cpu_number(cpu_node);
+                       if (WARN_ON(cpu == -1))
+                               continue;
+
+                       pr_cont("%s[%d] ", cpu_node->full_name, cpu);
+
+                       cpumask_set_cpu(cpu, &part->mask);
+               }
+
+               pr_cont("}\n");
+               part_idx++;
+       }
+
+       for (i = 0; i < 16; i++) {
+               unsigned int irq;
+               struct partition_desc *desc;
+               struct irq_fwspec ppi_fwspec = {
+                       .fwnode         = gic_data.fwnode,
+                       .param_count    = 3,
+                       .param          = {
+                               [0]     = 1,
+                               [1]     = i,
+                               [2]     = IRQ_TYPE_NONE,
+                       },
+               };
+
+               irq = irq_create_fwspec_mapping(&ppi_fwspec);
+               if (WARN_ON(!irq))
+                       continue;
+               desc = partition_create_desc(gic_data.fwnode, parts, nr_parts,
+                                            irq, &partition_domain_ops);
+               if (WARN_ON(!desc))
+                       continue;
+
+               gic_data.ppi_descs[i] = desc;
+       }
+}
+
+static int __init gic_of_init(struct device_node *node, struct device_node *parent)
+{
+       void __iomem *dist_base;
+       struct redist_region *rdist_regs;
+       u64 redist_stride;
+       u32 nr_redist_regions;
+       int err, i;
+
+       dist_base = of_iomap(node, 0);
+       if (!dist_base) {
+               pr_err("%s: unable to map gic dist registers\n",
+                       node->full_name);
+               return -ENXIO;
+       }
+
+       err = gic_validate_dist_version(dist_base);
+       if (err) {
+               pr_err("%s: no distributor detected, giving up\n",
+                       node->full_name);
+               goto out_unmap_dist;
+       }
+
+       if (of_property_read_u32(node, "#redistributor-regions", &nr_redist_regions))
+               nr_redist_regions = 1;
+
+       rdist_regs = kzalloc(sizeof(*rdist_regs) * nr_redist_regions, GFP_KERNEL);
+       if (!rdist_regs) {
+               err = -ENOMEM;
+               goto out_unmap_dist;
+       }
+
+       for (i = 0; i < nr_redist_regions; i++) {
+               struct resource res;
+               int ret;
+
+               ret = of_address_to_resource(node, 1 + i, &res);
+               rdist_regs[i].redist_base = of_iomap(node, 1 + i);
+               if (ret || !rdist_regs[i].redist_base) {
+                       pr_err("%s: couldn't map region %d\n",
+                              node->full_name, i);
+                       err = -ENODEV;
+                       goto out_unmap_rdist;
+               }
+               rdist_regs[i].phys_base = res.start;
+       }
+
+       if (of_property_read_u64(node, "redistributor-stride", &redist_stride))
+               redist_stride = 0;
+
+       err = gic_init_bases(dist_base, rdist_regs, nr_redist_regions,
+                            redist_stride, &node->fwnode);
+       if (err)
+               goto out_unmap_rdist;
+
+       gic_populate_ppi_partitions(node);
+       return 0;
+
 out_unmap_rdist:
        for (i = 0; i < nr_redist_regions; i++)
                if (rdist_regs[i].redist_base)