|
@@ -101,6 +101,7 @@ struct its_node {
|
|
|
u32 ite_size;
|
|
|
u32 device_ids;
|
|
|
int numa_node;
|
|
|
+ bool is_v4;
|
|
|
};
|
|
|
|
|
|
#define ITS_ITT_ALIGN SZ_256
|
|
@@ -133,6 +134,14 @@ static DEFINE_SPINLOCK(its_lock);
|
|
|
static struct rdists *gic_rdists;
|
|
|
static struct irq_domain *its_parent;
|
|
|
|
|
|
+/*
|
|
|
+ * We have a maximum number of 16 ITSs in the whole system if we're
|
|
|
+ * using the ITSList mechanism
|
|
|
+ */
|
|
|
+#define ITS_LIST_MAX 16
|
|
|
+
|
|
|
+static unsigned long its_list_map;
|
|
|
+
|
|
|
#define gic_data_rdist() (raw_cpu_ptr(gic_rdists->rdist))
|
|
|
#define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base)
|
|
|
|
|
@@ -1679,13 +1688,51 @@ static int its_init_domain(struct fwnode_handle *handle, struct its_node *its)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int __init its_compute_its_list_map(struct resource *res,
|
|
|
+ void __iomem *its_base)
|
|
|
+{
|
|
|
+ int its_number;
|
|
|
+ u32 ctlr;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * This is assumed to be done early enough that we're
|
|
|
+ * guaranteed to be single-threaded, hence no
|
|
|
+ * locking. Should this change, we should address
|
|
|
+ * this.
|
|
|
+ */
|
|
|
+ its_number = find_first_zero_bit(&its_list_map, ITS_LIST_MAX);
|
|
|
+ if (its_number >= ITS_LIST_MAX) {
|
|
|
+ pr_err("ITS@%pa: No ITSList entry available!\n",
|
|
|
+ &res->start);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ ctlr = readl_relaxed(its_base + GITS_CTLR);
|
|
|
+ ctlr &= ~GITS_CTLR_ITS_NUMBER;
|
|
|
+ ctlr |= its_number << GITS_CTLR_ITS_NUMBER_SHIFT;
|
|
|
+ writel_relaxed(ctlr, its_base + GITS_CTLR);
|
|
|
+ ctlr = readl_relaxed(its_base + GITS_CTLR);
|
|
|
+ if ((ctlr & GITS_CTLR_ITS_NUMBER) != (its_number << GITS_CTLR_ITS_NUMBER_SHIFT)) {
|
|
|
+ its_number = ctlr & GITS_CTLR_ITS_NUMBER;
|
|
|
+ its_number >>= GITS_CTLR_ITS_NUMBER_SHIFT;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (test_and_set_bit(its_number, &its_list_map)) {
|
|
|
+ pr_err("ITS@%pa: Duplicate ITSList entry %d\n",
|
|
|
+ &res->start, its_number);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return its_number;
|
|
|
+}
|
|
|
+
|
|
|
static int __init its_probe_one(struct resource *res,
|
|
|
struct fwnode_handle *handle, int numa_node)
|
|
|
{
|
|
|
struct its_node *its;
|
|
|
void __iomem *its_base;
|
|
|
- u32 val;
|
|
|
- u64 baser, tmp;
|
|
|
+ u32 val, ctlr;
|
|
|
+ u64 baser, tmp, typer;
|
|
|
int err;
|
|
|
|
|
|
its_base = ioremap(res->start, resource_size(res));
|
|
@@ -1718,9 +1765,24 @@ static int __init its_probe_one(struct resource *res,
|
|
|
raw_spin_lock_init(&its->lock);
|
|
|
INIT_LIST_HEAD(&its->entry);
|
|
|
INIT_LIST_HEAD(&its->its_device_list);
|
|
|
+ typer = gic_read_typer(its_base + GITS_TYPER);
|
|
|
its->base = its_base;
|
|
|
its->phys_base = res->start;
|
|
|
- its->ite_size = ((gic_read_typer(its_base + GITS_TYPER) >> 4) & 0xf) + 1;
|
|
|
+ its->ite_size = GITS_TYPER_ITT_ENTRY_SIZE(typer);
|
|
|
+ its->is_v4 = !!(typer & GITS_TYPER_VLPIS);
|
|
|
+ if (its->is_v4) {
|
|
|
+ if (!(typer & GITS_TYPER_VMOVP)) {
|
|
|
+ err = its_compute_its_list_map(res, its_base);
|
|
|
+ if (err < 0)
|
|
|
+ goto out_free_its;
|
|
|
+
|
|
|
+ pr_info("ITS@%pa: Using ITS number %d\n",
|
|
|
+ &res->start, err);
|
|
|
+ } else {
|
|
|
+ pr_info("ITS@%pa: Single VMOVP capable\n", &res->start);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
its->numa_node = numa_node;
|
|
|
|
|
|
its->cmd_base = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
|
|
@@ -1767,7 +1829,8 @@ static int __init its_probe_one(struct resource *res,
|
|
|
}
|
|
|
|
|
|
gits_write_cwriter(0, its->base + GITS_CWRITER);
|
|
|
- writel_relaxed(GITS_CTLR_ENABLE, its->base + GITS_CTLR);
|
|
|
+ ctlr = readl_relaxed(its->base + GITS_CTLR);
|
|
|
+ writel_relaxed(ctlr | GITS_CTLR_ENABLE, its->base + GITS_CTLR);
|
|
|
|
|
|
err = its_init_domain(handle, its);
|
|
|
if (err)
|