Commit bf9529f8 authored by Marc Zyngier's avatar Marc Zyngier Committed by Jason Cooper

irqchip: GICv3: ITS: LPI allocator

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: default avatarMarc Zyngier <marc.zyngier@arm.com>
Link: https://lkml.kernel.org/r/1416839720-18400-7-git-send-email-marc.zyngier@arm.comSigned-off-by: default avatarJason Cooper <jason@lakedaemon.net>
parent c48ed51c
...@@ -586,3 +586,106 @@ static struct irq_chip its_irq_chip = { ...@@ -586,3 +586,106 @@ static struct irq_chip its_irq_chip = {
.irq_eoi = its_eoi_irq, .irq_eoi = its_eoi_irq,
.irq_set_affinity = its_set_affinity, .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);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment