Skip to content

Commit

Permalink
irqchip: GICv3: ITS: LPI allocator
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Marc Zyngier authored and Jason Cooper committed Nov 26, 2014
1 parent c48ed51 commit bf9529f
Showing 1 changed file with 103 additions and 0 deletions.
103 changes: 103 additions & 0 deletions drivers/irqchip/irq-gic-v3-its.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

0 comments on commit bf9529f

Please sign in to comment.