aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/irqchip/irq-gic-v2m.c
diff options
context:
space:
mode:
authorDuc Dang <dhdang@apm.com>2015-10-06 18:32:38 -0400
committerMarc Zyngier <marc.zyngier@arm.com>2015-10-09 17:16:58 -0400
commitee5f7d6462c56ba083d5d80aa0d69914068a59ae (patch)
treed9c03c9b09537ee96b35e43bcf51e53e33d157e2 /drivers/irqchip/irq-gic-v2m.c
parent6d32ab2d8a982ffd46c4dcad9739292f57bc26de (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.c22
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
46struct v2m_data { 53struct 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
55static void gicv2m_mask_msi_irq(struct irq_data *d) 63static 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
103static struct irq_chip gicv2m_irq_chip = { 114static 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) {