|
@@ -29,6 +29,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>
|
|
@@ -44,6 +45,7 @@ struct redist_region {
|
|
|
};
|
|
|
|
|
|
struct gic_chip_data {
|
|
|
+ struct fwnode_handle *fwnode;
|
|
|
void __iomem *dist_base;
|
|
|
struct redist_region *redist_regions;
|
|
|
struct rdists rdists;
|
|
@@ -51,6 +53,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;
|
|
@@ -812,10 +815,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)
|
|
@@ -843,6 +898,7 @@ static int __init gic_init_bases(void __iomem *dist_base,
|
|
|
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;
|
|
@@ -901,6 +957,119 @@ static int __init gic_validate_dist_version(void __iomem *dist_base)
|
|
|
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;
|
|
@@ -952,8 +1121,11 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
|
|
|
|
|
|
err = gic_init_bases(dist_base, rdist_regs, nr_redist_regions,
|
|
|
redist_stride, &node->fwnode);
|
|
|
- if (!err)
|
|
|
- return 0;
|
|
|
+ if (err)
|
|
|
+ goto out_unmap_rdist;
|
|
|
+
|
|
|
+ gic_populate_ppi_partitions(node);
|
|
|
+ return 0;
|
|
|
|
|
|
out_unmap_rdist:
|
|
|
for (i = 0; i < nr_redist_regions; i++)
|