irqchip/gic-v3: Add support for partitioned PPIs
Plug the partitioning layer into the GICv3 PPI code, parsing the DT and building the partition affinities and providing the generic code with partition data and callbacks. Signed-off-by: Marc Zyngier <marc.zyngier@arm.com> Cc: Mark Rutland <mark.rutland@arm.com> Cc: devicetree@vger.kernel.org Cc: Jason Cooper <jason@lakedaemon.net> Cc: Will Deacon <will.deacon@arm.com> Cc: Rob Herring <robh+dt@kernel.org> Link: http://lkml.kernel.org/r/1460365075-7316-5-git-send-email-marc.zyngier@arm.com Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
This commit is contained in:
parent
9e2c986cb4
commit
e3825ba1af
@ -27,6 +27,7 @@ config ARM_GIC_V3
|
||||
select IRQ_DOMAIN
|
||||
select MULTI_IRQ_HANDLER
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
select PARTITION_PERCPU
|
||||
|
||||
config ARM_GIC_V3_ITS
|
||||
bool
|
||||
|
@ -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++)
|
||||
|
Loading…
Reference in New Issue
Block a user