irqchip: GICv3: ITS: LPI allocator
authorMarc Zyngier <marc.zyngier@arm.com>
Mon, 24 Nov 2014 14:35:13 +0000 (14:35 +0000)
committerJason Cooper <jason@lakedaemon.net>
Wed, 26 Nov 2014 15:55:13 +0000 (15:55 +0000)
LPIs are the type of interrupts that are used by the ITS. Given
the size of the namespace (anywhere between 16 and 32bit), interrupt
IDs are allocated in chunks of 32.

Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Link: https://lkml.kernel.org/r/1416839720-18400-7-git-send-email-marc.zyngier@arm.com
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
drivers/irqchip/irq-gic-v3-its.c

index d24bebdfb064d39461c80713d315802590fa8859..4154a1613dba1b9bf109f86462a7ba61b000b09c 100644 (file)
@@ -586,3 +586,106 @@ static struct irq_chip its_irq_chip = {
        .irq_eoi                = its_eoi_irq,
        .irq_set_affinity       = its_set_affinity,
 };
+
+/*
+ * How we allocate LPIs:
+ *
+ * The GIC has id_bits bits for interrupt identifiers. From there, we
+ * must subtract 8192 which are reserved for SGIs/PPIs/SPIs. Then, as
+ * we allocate LPIs by chunks of 32, we can shift the whole thing by 5
+ * bits to the right.
+ *
+ * This gives us (((1UL << id_bits) - 8192) >> 5) possible allocations.
+ */
+#define IRQS_PER_CHUNK_SHIFT   5
+#define IRQS_PER_CHUNK         (1 << IRQS_PER_CHUNK_SHIFT)
+
+static unsigned long *lpi_bitmap;
+static u32 lpi_chunks;
+static DEFINE_SPINLOCK(lpi_lock);
+
+static int its_lpi_to_chunk(int lpi)
+{
+       return (lpi - 8192) >> IRQS_PER_CHUNK_SHIFT;
+}
+
+static int its_chunk_to_lpi(int chunk)
+{
+       return (chunk << IRQS_PER_CHUNK_SHIFT) + 8192;
+}
+
+static int its_lpi_init(u32 id_bits)
+{
+       lpi_chunks = its_lpi_to_chunk(1UL << id_bits);
+
+       lpi_bitmap = kzalloc(BITS_TO_LONGS(lpi_chunks) * sizeof(long),
+                            GFP_KERNEL);
+       if (!lpi_bitmap) {
+               lpi_chunks = 0;
+               return -ENOMEM;
+       }
+
+       pr_info("ITS: Allocated %d chunks for LPIs\n", (int)lpi_chunks);
+       return 0;
+}
+
+static unsigned long *its_lpi_alloc_chunks(int nr_irqs, int *base, int *nr_ids)
+{
+       unsigned long *bitmap = NULL;
+       int chunk_id;
+       int nr_chunks;
+       int i;
+
+       nr_chunks = DIV_ROUND_UP(nr_irqs, IRQS_PER_CHUNK);
+
+       spin_lock(&lpi_lock);
+
+       do {
+               chunk_id = bitmap_find_next_zero_area(lpi_bitmap, lpi_chunks,
+                                                     0, nr_chunks, 0);
+               if (chunk_id < lpi_chunks)
+                       break;
+
+               nr_chunks--;
+       } while (nr_chunks > 0);
+
+       if (!nr_chunks)
+               goto out;
+
+       bitmap = kzalloc(BITS_TO_LONGS(nr_chunks * IRQS_PER_CHUNK) * sizeof (long),
+                        GFP_ATOMIC);
+       if (!bitmap)
+               goto out;
+
+       for (i = 0; i < nr_chunks; i++)
+               set_bit(chunk_id + i, lpi_bitmap);
+
+       *base = its_chunk_to_lpi(chunk_id);
+       *nr_ids = nr_chunks * IRQS_PER_CHUNK;
+
+out:
+       spin_unlock(&lpi_lock);
+
+       return bitmap;
+}
+
+static void its_lpi_free(unsigned long *bitmap, int base, int nr_ids)
+{
+       int lpi;
+
+       spin_lock(&lpi_lock);
+
+       for (lpi = base; lpi < (base + nr_ids); lpi += IRQS_PER_CHUNK) {
+               int chunk = its_lpi_to_chunk(lpi);
+               BUG_ON(chunk > lpi_chunks);
+               if (test_bit(chunk, lpi_bitmap)) {
+                       clear_bit(chunk, lpi_bitmap);
+               } else {
+                       pr_err("Bad LPI chunk %d\n", chunk);
+               }
+       }
+
+       spin_unlock(&lpi_lock);
+
+       kfree(bitmap);
+}