Skip to content

Commit b78f269

Browse files
author
Marc Zyngier
committed
irqchip/gic: Work around broken Renesas integration
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 <[email protected]> Signed-off-by: Marc Zyngier <[email protected]> Link: https://lore.kernel.org/r/CAMuHMdV+Ev47K5NO8XHsanSq5YRMCHn2gWAQyV-q2LpJVy9HiQ@mail.gmail.com
1 parent 3ce8c70 commit b78f269

File tree

1 file changed

+51
-1
lines changed

1 file changed

+51
-1
lines changed

drivers/irqchip/irq-gic.c

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ static DEFINE_RAW_SPINLOCK(cpu_map_lock);
107107

108108
#endif
109109

110+
static DEFINE_STATIC_KEY_FALSE(needs_rmw_access);
111+
110112
/*
111113
* The GIC mapping of CPU interfaces does not necessarily match
112114
* the logical CPU numbering. Let's use a mapping as returned
@@ -774,6 +776,25 @@ static int gic_pm_init(struct gic_chip_data *gic)
774776
#endif
775777

776778
#ifdef CONFIG_SMP
779+
static void rmw_writeb(u8 bval, void __iomem *addr)
780+
{
781+
static DEFINE_RAW_SPINLOCK(rmw_lock);
782+
unsigned long offset = (unsigned long)addr & 3UL;
783+
unsigned long shift = offset * 8;
784+
unsigned long flags;
785+
u32 val;
786+
787+
raw_spin_lock_irqsave(&rmw_lock, flags);
788+
789+
addr -= offset;
790+
val = readl_relaxed(addr);
791+
val &= ~GENMASK(shift + 7, shift);
792+
val |= bval << shift;
793+
writel_relaxed(val, addr);
794+
795+
raw_spin_unlock_irqrestore(&rmw_lock, flags);
796+
}
797+
777798
static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
778799
bool force)
779800
{
@@ -788,7 +809,10 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
788809
if (cpu >= NR_GIC_CPU_IF || cpu >= nr_cpu_ids)
789810
return -EINVAL;
790811

791-
writeb_relaxed(gic_cpu_map[cpu], reg);
812+
if (static_branch_unlikely(&needs_rmw_access))
813+
rmw_writeb(gic_cpu_map[cpu], reg);
814+
else
815+
writeb_relaxed(gic_cpu_map[cpu], reg);
792816
irq_data_update_effective_affinity(d, cpumask_of(cpu));
793817

794818
return IRQ_SET_MASK_OK_DONE;
@@ -1375,6 +1399,30 @@ static bool gic_check_eoimode(struct device_node *node, void __iomem **base)
13751399
return true;
13761400
}
13771401

1402+
static bool gic_enable_rmw_access(void *data)
1403+
{
1404+
/*
1405+
* The EMEV2 class of machines has a broken interconnect, and
1406+
* locks up on accesses that are less than 32bit. So far, only
1407+
* the affinity setting requires it.
1408+
*/
1409+
if (of_machine_is_compatible("renesas,emev2")) {
1410+
static_branch_enable(&needs_rmw_access);
1411+
return true;
1412+
}
1413+
1414+
return false;
1415+
}
1416+
1417+
static const struct gic_quirk gic_quirks[] = {
1418+
{
1419+
.desc = "broken byte access",
1420+
.compatible = "arm,pl390",
1421+
.init = gic_enable_rmw_access,
1422+
},
1423+
{ },
1424+
};
1425+
13781426
static int gic_of_setup(struct gic_chip_data *gic, struct device_node *node)
13791427
{
13801428
if (!gic || !node)
@@ -1391,6 +1439,8 @@ static int gic_of_setup(struct gic_chip_data *gic, struct device_node *node)
13911439
if (of_property_read_u32(node, "cpu-offset", &gic->percpu_offset))
13921440
gic->percpu_offset = 0;
13931441

1442+
gic_enable_of_quirks(node, gic_quirks, gic);
1443+
13941444
return 0;
13951445

13961446
error:

0 commit comments

Comments
 (0)