Skip to content

Commit 12e1406

Browse files
Marc ZyngierKAGA-KOKO
authored andcommitted
irqchip/GIC: Add workaround for aliased GIC400
The GICv2 architecture mandates that the two 4kB GIC regions are contiguous, and on two separate physical pages (so that access to the second page can be trapped by a hypervisor). This doesn't work very well when PAGE_SIZE is 64kB. A relatively common hack^Wway to work around this is to alias each 4kB region over its own 64kB page. Of course in this case, the base address you want to use is not really the begining of the region, but base + 60kB (so that you get a contiguous 8kB region over two distinct pages). Normally, this would be described in DT with a new property, but some HW is already out there, and the firmware makes sure that it will override whatever you put in the GIC node. Duh. And of course, said firmware source code is not available, despite being based on u-boot. The workaround is to detect the case where the CPU interface size is set to 128kB, and verify the aliasing by checking that the ID register for GIC400 (which is the only GIC wired this way so far) is the same at base and base + 0xF000. In this case, we update the GIC base address and let it roll. And if you feel slightly sick by looking at this, rest assured that I do too... Reported-by: Julien Grall <[email protected]> Signed-off-by: Marc Zyngier <[email protected]> Cc: [email protected] Cc: Stuart Yoder <[email protected]> Cc: Pavel Fedin <[email protected]> Cc: Jason Cooper <[email protected]> Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Thomas Gleixner <[email protected]>
1 parent e4084a1 commit 12e1406

File tree

1 file changed

+39
-5
lines changed

1 file changed

+39
-5
lines changed

drivers/irqchip/irq-gic.c

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,12 +1119,49 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
11191119
#ifdef CONFIG_OF
11201120
static int gic_cnt __initdata;
11211121

1122+
static bool gic_check_eoimode(struct device_node *node, void __iomem **base)
1123+
{
1124+
struct resource cpuif_res;
1125+
1126+
of_address_to_resource(node, 1, &cpuif_res);
1127+
1128+
if (!is_hyp_mode_available())
1129+
return false;
1130+
if (resource_size(&cpuif_res) < SZ_8K)
1131+
return false;
1132+
if (resource_size(&cpuif_res) == SZ_128K) {
1133+
u32 val_low, val_high;
1134+
1135+
/*
1136+
* Verify that we have the first 4kB of a GIC400
1137+
* aliased over the first 64kB by checking the
1138+
* GICC_IIDR register on both ends.
1139+
*/
1140+
val_low = readl_relaxed(*base + GIC_CPU_IDENT);
1141+
val_high = readl_relaxed(*base + GIC_CPU_IDENT + 0xf000);
1142+
if ((val_low & 0xffff0fff) != 0x0202043B ||
1143+
val_low != val_high)
1144+
return false;
1145+
1146+
/*
1147+
* Move the base up by 60kB, so that we have a 8kB
1148+
* contiguous region, which allows us to use GICC_DIR
1149+
* at its normal offset. Please pass me that bucket.
1150+
*/
1151+
*base += 0xf000;
1152+
cpuif_res.start += 0xf000;
1153+
pr_warn("GIC: Adjusting CPU interface base to %pa",
1154+
&cpuif_res.start);
1155+
}
1156+
1157+
return true;
1158+
}
1159+
11221160
static int __init
11231161
gic_of_init(struct device_node *node, struct device_node *parent)
11241162
{
11251163
void __iomem *cpu_base;
11261164
void __iomem *dist_base;
1127-
struct resource cpu_res;
11281165
u32 percpu_offset;
11291166
int irq;
11301167

@@ -1137,14 +1174,11 @@ gic_of_init(struct device_node *node, struct device_node *parent)
11371174
cpu_base = of_iomap(node, 1);
11381175
WARN(!cpu_base, "unable to map gic cpu registers\n");
11391176

1140-
of_address_to_resource(node, 1, &cpu_res);
1141-
11421177
/*
11431178
* Disable split EOI/Deactivate if either HYP is not available
11441179
* or the CPU interface is too small.
11451180
*/
1146-
if (gic_cnt == 0 && (!is_hyp_mode_available() ||
1147-
resource_size(&cpu_res) < SZ_8K))
1181+
if (gic_cnt == 0 && !gic_check_eoimode(node, &cpu_base))
11481182
static_key_slow_dec(&supports_deactivate);
11491183

11501184
if (of_property_read_u32(node, "cpu-offset", &percpu_offset))

0 commit comments

Comments
 (0)