Merge branch irq/gicv3-eppi-partition into irq/irqchip-next

- Add support for partitionned EPPIs, modeled after the existing
  partitioned PPI support

* irq/gicv3-eppi-partition:
  irqchip/gic-v3: Fix selection of partition domain for EPPIs
  irqchip/gic-v3: Add __gic_get_ppi_index() to find the PPI number from hwirq

Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
Marc Zyngier 2021-08-12 08:12:30 +01:00
commit 53b13565fc

View File

@ -446,18 +446,23 @@ static void gic_irq_set_prio(struct irq_data *d, u8 prio)
writeb_relaxed(prio, base + offset + index);
}
static u32 gic_get_ppi_index(struct irq_data *d)
static u32 __gic_get_ppi_index(irq_hw_number_t hwirq)
{
switch (get_intid_range(d)) {
switch (__get_intid_range(hwirq)) {
case PPI_RANGE:
return d->hwirq - 16;
return hwirq - 16;
case EPPI_RANGE:
return d->hwirq - EPPI_BASE_INTID + 16;
return hwirq - EPPI_BASE_INTID + 16;
default:
unreachable();
}
}
static u32 gic_get_ppi_index(struct irq_data *d)
{
return __gic_get_ppi_index(d->hwirq);
}
static int gic_irq_nmi_setup(struct irq_data *d)
{
struct irq_desc *desc = irq_to_desc(d->irq);
@ -1467,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;
@ -1479,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 = {
@ -1503,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)
@ -1513,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;