diff options
author | Duc Dang <dhdang@apm.com> | 2015-10-06 18:32:38 -0400 |
---|---|---|
committer | Marc Zyngier <marc.zyngier@arm.com> | 2015-10-09 17:16:58 -0400 |
commit | ee5f7d6462c56ba083d5d80aa0d69914068a59ae (patch) | |
tree | d9c03c9b09537ee96b35e43bcf51e53e33d157e2 /drivers/irqchip/irq-gic-v2m.c | |
parent | 6d32ab2d8a982ffd46c4dcad9739292f57bc26de (diff) |
irqchip/gic-v2m: Add workaround for APM X-Gene GICv2m erratum
APM X-Gene GICv2m implementation has an erratum where the
MSI data needs to be the offset from the spi_start in order to
trigger the correct MSI interrupt. This is different from the
standard GICv2m implementation where the MSI data is the absolute
value within the range from spi_start to (spi_start + num_spis)
of each v2m frame.
This patch reads MSI_IIDR register (present in all GICv2m
implementations) to identify X-Gene GICv2m implementation and
apply workaround to change the data portion of MSI vector.
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Duc Dang <dhdang@apm.com>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Diffstat (limited to 'drivers/irqchip/irq-gic-v2m.c')
-rw-r--r-- | drivers/irqchip/irq-gic-v2m.c | 22 |
1 files changed, 22 insertions, 0 deletions
diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index 12985daa66ab..9a36ab0b544c 100644 --- a/drivers/irqchip/irq-gic-v2m.c +++ b/drivers/irqchip/irq-gic-v2m.c | |||
@@ -37,12 +37,19 @@ | |||
37 | #define V2M_MSI_SETSPI_NS 0x040 | 37 | #define V2M_MSI_SETSPI_NS 0x040 |
38 | #define V2M_MIN_SPI 32 | 38 | #define V2M_MIN_SPI 32 |
39 | #define V2M_MAX_SPI 1019 | 39 | #define V2M_MAX_SPI 1019 |
40 | #define V2M_MSI_IIDR 0xFCC | ||
40 | 41 | ||
41 | #define V2M_MSI_TYPER_BASE_SPI(x) \ | 42 | #define V2M_MSI_TYPER_BASE_SPI(x) \ |
42 | (((x) >> V2M_MSI_TYPER_BASE_SHIFT) & V2M_MSI_TYPER_BASE_MASK) | 43 | (((x) >> V2M_MSI_TYPER_BASE_SHIFT) & V2M_MSI_TYPER_BASE_MASK) |
43 | 44 | ||
44 | #define V2M_MSI_TYPER_NUM_SPI(x) ((x) & V2M_MSI_TYPER_NUM_MASK) | 45 | #define V2M_MSI_TYPER_NUM_SPI(x) ((x) & V2M_MSI_TYPER_NUM_MASK) |
45 | 46 | ||
47 | /* APM X-Gene with GICv2m MSI_IIDR register value */ | ||
48 | #define XGENE_GICV2M_MSI_IIDR 0x06000170 | ||
49 | |||
50 | /* List of flags for specific v2m implementation */ | ||
51 | #define GICV2M_NEEDS_SPI_OFFSET 0x00000001 | ||
52 | |||
46 | struct v2m_data { | 53 | struct v2m_data { |
47 | spinlock_t msi_cnt_lock; | 54 | spinlock_t msi_cnt_lock; |
48 | struct resource res; /* GICv2m resource */ | 55 | struct resource res; /* GICv2m resource */ |
@@ -50,6 +57,7 @@ struct v2m_data { | |||
50 | u32 spi_start; /* The SPI number that MSIs start */ | 57 | u32 spi_start; /* The SPI number that MSIs start */ |
51 | u32 nr_spis; /* The number of SPIs for MSIs */ | 58 | u32 nr_spis; /* The number of SPIs for MSIs */ |
52 | unsigned long *bm; /* MSI vector bitmap */ | 59 | unsigned long *bm; /* MSI vector bitmap */ |
60 | u32 flags; /* v2m flags for specific implementation */ | ||
53 | }; | 61 | }; |
54 | 62 | ||
55 | static void gicv2m_mask_msi_irq(struct irq_data *d) | 63 | static void gicv2m_mask_msi_irq(struct irq_data *d) |
@@ -98,6 +106,9 @@ static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) | |||
98 | msg->address_hi = upper_32_bits(addr); | 106 | msg->address_hi = upper_32_bits(addr); |
99 | msg->address_lo = lower_32_bits(addr); | 107 | msg->address_lo = lower_32_bits(addr); |
100 | msg->data = data->hwirq; | 108 | msg->data = data->hwirq; |
109 | |||
110 | if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET) | ||
111 | msg->data -= v2m->spi_start; | ||
101 | } | 112 | } |
102 | 113 | ||
103 | static struct irq_chip gicv2m_irq_chip = { | 114 | static struct irq_chip gicv2m_irq_chip = { |
@@ -266,6 +277,17 @@ static int __init gicv2m_init_one(struct device_node *node, | |||
266 | goto err_iounmap; | 277 | goto err_iounmap; |
267 | } | 278 | } |
268 | 279 | ||
280 | /* | ||
281 | * APM X-Gene GICv2m implementation has an erratum where | ||
282 | * the MSI data needs to be the offset from the spi_start | ||
283 | * in order to trigger the correct MSI interrupt. This is | ||
284 | * different from the standard GICv2m implementation where | ||
285 | * the MSI data is the absolute value within the range from | ||
286 | * spi_start to (spi_start + num_spis). | ||
287 | */ | ||
288 | if (readl_relaxed(v2m->base + V2M_MSI_IIDR) == XGENE_GICV2M_MSI_IIDR) | ||
289 | v2m->flags |= GICV2M_NEEDS_SPI_OFFSET; | ||
290 | |||
269 | v2m->bm = kzalloc(sizeof(long) * BITS_TO_LONGS(v2m->nr_spis), | 291 | v2m->bm = kzalloc(sizeof(long) * BITS_TO_LONGS(v2m->nr_spis), |
270 | GFP_KERNEL); | 292 | GFP_KERNEL); |
271 | if (!v2m->bm) { | 293 | if (!v2m->bm) { |