mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-05 00:20:32 +00:00
irqchip/gic-v3: Fix selection of partition domain for EPPIs
commit5f51f80382
("irqchip/gic-v3: Add EPPI range support") added GIC_IRQ_TYPE_PARTITION support for EPPI to gic_irq_domain_translate(), and commit52085d3f20
("irqchip/gic-v3: Dynamically allocate PPI partition descriptors") made the gic_data.ppi_descs array big enough for EPPI, but neither gic_irq_domain_select() nor partition_domain_translate() were updated. This means partitions are created by partition_create_desc() for the EPPI range, but can't be registered as they will always match the root domain and map to the summary interrupt. Update gic_irq_domain_select() to match PPI and EPPI. The fwspec for PPI and EPPI both start from 0. Use gic_irq_domain_translate() to find the hwirq from the fwspec, then convert this to a ppi index. Reported-by: Valentin Schneider <valentin.schneider@arm.com> Signed-off-by: James Morse <james.morse@arm.com> Reviewed-by: Valentin Schneider <valentin.schneider@arm.com> Signed-off-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20210729172748.28841-3-james.morse@arm.com
This commit is contained in:
parent
bfa80ee9ce
commit
d753f849bf
1 changed files with 41 additions and 7 deletions
|
@ -1472,10 +1472,34 @@ static void gic_irq_domain_free(struct irq_domain *domain, unsigned int virq,
|
|||
}
|
||||
}
|
||||
|
||||
static bool fwspec_is_partitioned_ppi(struct irq_fwspec *fwspec,
|
||||
irq_hw_number_t hwirq)
|
||||
{
|
||||
enum gic_intid_range range;
|
||||
|
||||
if (!gic_data.ppi_descs)
|
||||
return false;
|
||||
|
||||
if (!is_of_node(fwspec->fwnode))
|
||||
return false;
|
||||
|
||||
if (fwspec->param_count < 4 || !fwspec->param[3])
|
||||
return false;
|
||||
|
||||
range = __get_intid_range(hwirq);
|
||||
if (range != PPI_RANGE && range != EPPI_RANGE)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int gic_irq_domain_select(struct irq_domain *d,
|
||||
struct irq_fwspec *fwspec,
|
||||
enum irq_domain_bus_token bus_token)
|
||||
{
|
||||
unsigned int type, ret, ppi_idx;
|
||||
irq_hw_number_t hwirq;
|
||||
|
||||
/* Not for us */
|
||||
if (fwspec->fwnode != d->fwnode)
|
||||
return 0;
|
||||
|
@ -1484,16 +1508,19 @@ static int gic_irq_domain_select(struct irq_domain *d,
|
|||
if (!is_of_node(fwspec->fwnode))
|
||||
return 1;
|
||||
|
||||
ret = gic_irq_domain_translate(d, fwspec, &hwirq, &type);
|
||||
if (WARN_ON_ONCE(ret))
|
||||
return 0;
|
||||
|
||||
if (!fwspec_is_partitioned_ppi(fwspec, hwirq))
|
||||
return d == gic_data.domain;
|
||||
|
||||
/*
|
||||
* 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 &&
|
||||
gic_data.ppi_descs)
|
||||
return d == partition_get_domain(gic_data.ppi_descs[fwspec->param[1]]);
|
||||
|
||||
return d == gic_data.domain;
|
||||
ppi_idx = __gic_get_ppi_index(hwirq);
|
||||
return d == partition_get_domain(gic_data.ppi_descs[ppi_idx]);
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops gic_irq_domain_ops = {
|
||||
|
@ -1508,7 +1535,9 @@ static int partition_domain_translate(struct irq_domain *d,
|
|||
unsigned long *hwirq,
|
||||
unsigned int *type)
|
||||
{
|
||||
unsigned long ppi_intid;
|
||||
struct device_node *np;
|
||||
unsigned int ppi_idx;
|
||||
int ret;
|
||||
|
||||
if (!gic_data.ppi_descs)
|
||||
|
@ -1518,7 +1547,12 @@ static int partition_domain_translate(struct irq_domain *d,
|
|||
if (WARN_ON(!np))
|
||||
return -EINVAL;
|
||||
|
||||
ret = partition_translate_id(gic_data.ppi_descs[fwspec->param[1]],
|
||||
ret = gic_irq_domain_translate(d, fwspec, &ppi_intid, type);
|
||||
if (WARN_ON_ONCE(ret))
|
||||
return 0;
|
||||
|
||||
ppi_idx = __gic_get_ppi_index(ppi_intid);
|
||||
ret = partition_translate_id(gic_data.ppi_descs[ppi_idx],
|
||||
of_node_to_fwnode(np));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
Loading…
Reference in a new issue