Skip to content

Commit

Permalink
irqchip/gic: Work around broken Renesas integration
Browse files Browse the repository at this point in the history
[ Upstream commit b78f269 ]

Geert reported that the GIC driver locks up on a Renesas system
since 005c34a ("irqchip/gic: Atomically update affinity")
fixed the driver to use writeb_relaxed() instead of writel_relaxed().

As it turns out, the interconnect used on this system mandates
32bit wide accesses for all MMIO transactions, even if the GIC
architecture specifically mandates for some registers to be byte
accessible. Gahhh...

Work around the issue by crudly detecting the offending system,
and falling back to an inefficient RMW+lock implementation.

Reported-by: Geert Uytterhoeven <geert@linux-m68k.org>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/CAMuHMdV+Ev47K5NO8XHsanSq5YRMCHn2gWAQyV-q2LpJVy9HiQ@mail.gmail.com
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
Marc Zyngier authored and gregkh committed Oct 9, 2021
1 parent 8724a2a commit a7be240
Showing 1 changed file with 51 additions and 1 deletion.
52 changes: 51 additions & 1 deletion drivers/irqchip/irq-gic.c
Expand Up @@ -107,6 +107,8 @@ static DEFINE_RAW_SPINLOCK(cpu_map_lock);

#endif

static DEFINE_STATIC_KEY_FALSE(needs_rmw_access);

/*
* The GIC mapping of CPU interfaces does not necessarily match
* the logical CPU numbering. Let's use a mapping as returned
Expand Down Expand Up @@ -777,6 +779,25 @@ static int gic_pm_init(struct gic_chip_data *gic)
#endif

#ifdef CONFIG_SMP
static void rmw_writeb(u8 bval, void __iomem *addr)
{
static DEFINE_RAW_SPINLOCK(rmw_lock);
unsigned long offset = (unsigned long)addr & 3UL;
unsigned long shift = offset * 8;
unsigned long flags;
u32 val;

raw_spin_lock_irqsave(&rmw_lock, flags);

addr -= offset;
val = readl_relaxed(addr);
val &= ~GENMASK(shift + 7, shift);
val |= bval << shift;
writel_relaxed(val, addr);

raw_spin_unlock_irqrestore(&rmw_lock, flags);
}

static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
bool force)
{
Expand All @@ -791,7 +812,10 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
if (cpu >= NR_GIC_CPU_IF || cpu >= nr_cpu_ids)
return -EINVAL;

writeb_relaxed(gic_cpu_map[cpu], reg);
if (static_branch_unlikely(&needs_rmw_access))
rmw_writeb(gic_cpu_map[cpu], reg);
else
writeb_relaxed(gic_cpu_map[cpu], reg);
irq_data_update_effective_affinity(d, cpumask_of(cpu));

return IRQ_SET_MASK_OK_DONE;
Expand Down Expand Up @@ -1384,6 +1408,30 @@ static bool gic_check_eoimode(struct device_node *node, void __iomem **base)
return true;
}

static bool gic_enable_rmw_access(void *data)
{
/*
* The EMEV2 class of machines has a broken interconnect, and
* locks up on accesses that are less than 32bit. So far, only
* the affinity setting requires it.
*/
if (of_machine_is_compatible("renesas,emev2")) {
static_branch_enable(&needs_rmw_access);
return true;
}

return false;
}

static const struct gic_quirk gic_quirks[] = {
{
.desc = "broken byte access",
.compatible = "arm,pl390",
.init = gic_enable_rmw_access,
},
{ },
};

static int gic_of_setup(struct gic_chip_data *gic, struct device_node *node)
{
if (!gic || !node)
Expand All @@ -1400,6 +1448,8 @@ static int gic_of_setup(struct gic_chip_data *gic, struct device_node *node)
if (of_property_read_u32(node, "cpu-offset", &gic->percpu_offset))
gic->percpu_offset = 0;

gic_enable_of_quirks(node, gic_quirks, gic);

return 0;

error:
Expand Down

0 comments on commit a7be240

Please sign in to comment.