irqchip/gic-v3-its: Add probing for VLPI properties
authorMarc Zyngier <marc.zyngier@arm.com>
Mon, 19 Dec 2016 17:25:54 +0000 (17:25 +0000)
committerMarc Zyngier <marc.zyngier@arm.com>
Wed, 23 Aug 2017 10:09:15 +0000 (11:09 +0100)
Add the probing code for the ITS VLPI support. This includes
configuring the ITS number if not supporting the single VMOVP
command feature.

Reviewed-by: Eric Auger <eric.auger@redhat.com>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
drivers/irqchip/irq-gic-v3-its.c
include/linux/irqchip/arm-gic-v3.h

index 2232250f14425aa2bbef3fdcaeded2f5e55d55e5..89da961e949ee245dca4a97469009047903c2cc8 100644 (file)
@@ -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)
index 20a553423ac7cbe4384d6cf5de745cc472c7cf0a..af8c55105fc22aa3840efaa4a592d8552009a11c 100644 (file)
 #define GITS_TRANSLATER                        0x10040
 
 #define GITS_CTLR_ENABLE               (1U << 0)
+#define        GITS_CTLR_ITS_NUMBER_SHIFT      4
+#define        GITS_CTLR_ITS_NUMBER            (0xFU << GITS_CTLR_ITS_NUMBER_SHIFT)
 #define GITS_CTLR_QUIESCENT            (1U << 31)
 
 #define GITS_TYPER_PLPIS               (1UL << 0)
+#define GITS_TYPER_VLPIS               (1UL << 1)
 #define GITS_TYPER_ITT_ENTRY_SIZE_SHIFT        4
+#define GITS_TYPER_ITT_ENTRY_SIZE(r)   ((((r) >> GITS_TYPER_ITT_ENTRY_SIZE_SHIFT) & 0x1f) + 1)
 #define GITS_TYPER_IDBITS_SHIFT                8
 #define GITS_TYPER_DEVBITS_SHIFT       13
 #define GITS_TYPER_DEVBITS(r)          ((((r) >> GITS_TYPER_DEVBITS_SHIFT) & 0x1f) + 1)
 #define GITS_TYPER_PTA                 (1UL << 19)
 #define GITS_TYPER_HWCOLLCNT_SHIFT     24
+#define GITS_TYPER_VMOVP               (1ULL << 37)
 
 #define GITS_IIDR_REV_SHIFT            12
 #define GITS_IIDR_REV_MASK             (0xf << GITS_IIDR_REV_SHIFT)