[PATCH 1/1] [V2] irqchip: gicv3-its: Introduce indirect device-ITT table support

From: Shanker Donthineni
Date: Mon Jan 25 2016 - 17:59:01 EST


Current ITS driver implementation limits the device ID to a few
number of bits depending on memory that has been allocated to a
flat DEV-ITT table. Some of the devices are not usable when device
ID is spread out across a large range of 32 bit values.

This patch covers more DEVID bits by implementing the GIC-ITS indirect
device table. This feature is enabled automatically during driver
probe if the flat table is not adequate to hold all DEVID bits.

Signed-off-by: Shanker Donthineni <shankerd@xxxxxxxxxxxxxx>
---
[v1]->[v2]:
-Removed the line "Change-Id: I9c6d005dexxx" from commit message.
-Fixed the its->max_devid field initialization.

drivers/irqchip/irq-gic-v3-its.c | 79 +++++++++++++++++++++++++++++++++++---
include/linux/irqchip/arm-gic-v3.h | 1 +
2 files changed, 75 insertions(+), 5 deletions(-)

diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index e23d1d1..408a358 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -41,6 +41,8 @@

#define ITS_FLAGS_CMDQ_NEEDS_FLUSHING (1ULL << 0)
#define ITS_FLAGS_WORKAROUND_CAVIUM_22375 (1ULL << 1)
+#define ITS_FLAGS_DEVICE_NEEDS_FLUSHING (1ULL << 2)
+#define ITS_FLAGS_INDIRECT_DEVICE_TABLE (1ULL << 3)

#define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING (1 << 0)

@@ -71,6 +73,10 @@ struct its_node {
struct list_head its_device_list;
u64 flags;
u32 ite_size;
+ u32 dev_table_idx;
+ u32 dev_table_shift;
+ u32 max_devid;
+ u32 order;
};

#define ITS_ITT_ALIGN SZ_256
@@ -824,6 +830,8 @@ static int its_alloc_tables(const char *node_name, struct its_node *its)
u64 typer;
u32 ids;

+#define ITS_DEVICE_MAX_ORDER min(MAX_ORDER, get_order(SZ_8M))
+
if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_22375) {
/*
* erratum 22375: only alloc 8MB table size
@@ -867,11 +875,12 @@ static int its_alloc_tables(const char *node_name, struct its_node *its)
*/
order = max(get_order((1UL << ids) * entry_size),
order);
- if (order >= MAX_ORDER) {
- order = MAX_ORDER - 1;
- pr_warn("%s: Device Table too large, reduce its page order to %u\n",
- node_name, order);
- }
+ if (order >= ITS_DEVICE_MAX_ORDER) {
+ /* Update flags for two-level setup */
+ its->flags |= ITS_FLAGS_INDIRECT_DEVICE_TABLE;
+ order = ITS_DEVICE_MAX_ORDER - 1;
+ } else
+ its->max_devid = (1 << ids) - 1;
}

alloc_size = (1 << order) * PAGE_SIZE;
@@ -911,6 +920,26 @@ retry_baser:
break;
}

+ /* Enable two-level (indirect) device table if it is required */
+ if ((type == GITS_BASER_TYPE_DEVICE) &&
+ (its->flags & ITS_FLAGS_INDIRECT_DEVICE_TABLE)) {
+ u32 shift = ilog2(psz / entry_size);
+ u32 max_ids = ilog2(alloc_size >> 3) + shift;
+
+ if (ids > max_ids) {
+ pr_warn(
+ "ITS: @%pa DEVID too large reduce %u->%u\n",
+ &its->phys_base, ids, max_ids);
+ }
+ its->max_devid = (1UL << max_ids) - 1;
+ its->order = get_order(psz);
+ its->dev_table_idx = i;
+ its->dev_table_shift = shift;
+ if (!(val & GITS_BASER_SHAREABILITY_MASK))
+ its->flags |= ITS_FLAGS_DEVICE_NEEDS_FLUSHING;
+ val |= GITS_BASER_INDIRECT;
+ }
+
val |= alloc_pages - 1;

writeq_relaxed(val, its->base + GITS_BASER + i * 8);
@@ -1134,6 +1163,32 @@ static struct its_device *its_find_device(struct its_node *its, u32 dev_id)
return its_dev;
}

+static int its_alloc_device_table(struct its_node *its, u32 dev_id)
+{
+ u64 *devtbl = its->tables[its->dev_table_idx];
+ u32 alloc_size = (1 << its->order) * PAGE_SIZE;
+ u32 idx = dev_id >> its->dev_table_shift;
+ struct page *page;
+
+ /* Do nothing if the level-2 DEV-ITT entry was mapped earlier */
+ if (devtbl[idx])
+ return 0;
+
+ /* Allocate memory for level-2 device table & map to level-1 table */
+ page = alloc_pages(GFP_KERNEL | __GFP_ZERO, its->order);
+ if (!page)
+ return -ENOMEM;
+
+ devtbl[idx] = page_to_phys(page) | GITS_BASER_VALID;
+
+ if (its->flags & ITS_FLAGS_DEVICE_NEEDS_FLUSHING) {
+ __flush_dcache_area(page_address(page), alloc_size);
+ __flush_dcache_area(devtbl + idx, sizeof(*devtbl));
+ }
+
+ return 0;
+}
+
static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
int nvecs)
{
@@ -1147,6 +1202,20 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
int nr_ites;
int sz;

+ if (dev_id > its->max_devid) {
+ pr_err("ITS: dev_id too large %d\n", dev_id);
+ return NULL;
+ }
+
+ /* Ensure memory for level-2 table is allocated for dev_id */
+ if (its->flags & ITS_FLAGS_INDIRECT_DEVICE_TABLE) {
+ int ret;
+
+ ret = its_alloc_device_table(its, dev_id);
+ if (ret)
+ return NULL;
+ }
+
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
/*
* At least one bit of EventID is being used, hence a minimum
diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index d5d798b..04dfe09 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -206,6 +206,7 @@
#define GITS_BASER_NR_REGS 8

#define GITS_BASER_VALID (1UL << 63)
+#define GITS_BASER_INDIRECT (1UL << 62)
#define GITS_BASER_nCnB (0UL << 59)
#define GITS_BASER_nC (1UL << 59)
#define GITS_BASER_RaWt (2UL << 59)
--
Qualcomm Technologies, Inc. on behalf
of the Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc.
is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.