drivers/net/ipa/gsi_trans.c

Source file repositories/reference/linux-study-clean/drivers/net/ipa/gsi_trans.c

File Facts

System
Linux kernel
Corpus path
drivers/net/ipa/gsi_trans.c
Extension
.c
Size
24247 bytes
Lines
789
Domain
Driver Families
Bucket
drivers/net
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct gsi_tre {
	__le64 addr;		/* DMA address */
	__le16 len_opcode;	/* length in bytes or enum IPA_CMD_* */
	__le16 reserved;
	__le32 flags;		/* TRE_FLAGS_* */
};

/* gsi_tre->flags mask values (in CPU byte order) */
#define TRE_FLAGS_CHAIN_FMASK	GENMASK(0, 0)
#define TRE_FLAGS_IEOT_FMASK	GENMASK(9, 9)
#define TRE_FLAGS_BEI_FMASK	GENMASK(10, 10)
#define TRE_FLAGS_TYPE_FMASK	GENMASK(23, 16)

int gsi_trans_pool_init(struct gsi_trans_pool *pool, size_t size, u32 count,
			u32 max_alloc)
{
	size_t alloc_size;
	void *virt;

	if (!size)
		return -EINVAL;
	if (count < max_alloc)
		return -EINVAL;
	if (!max_alloc)
		return -EINVAL;

	/* By allocating a few extra entries in our pool (one less
	 * than the maximum number that will be requested in a
	 * single allocation), we can always satisfy requests without
	 * ever worrying about straddling the end of the pool array.
	 * If there aren't enough entries starting at the free index,
	 * we just allocate free entries from the beginning of the pool.
	 */
	alloc_size = size_mul(count + max_alloc - 1, size);
	alloc_size = kmalloc_size_roundup(alloc_size);
	virt = kzalloc(alloc_size, GFP_KERNEL);
	if (!virt)
		return -ENOMEM;

	pool->base = virt;
	/* If the allocator gave us any extra memory, use it */
	pool->count = alloc_size / size;
	pool->free = 0;
	pool->max_alloc = max_alloc;
	pool->size = size;
	pool->addr = 0;		/* Only used for DMA pools */

	return 0;
}

void gsi_trans_pool_exit(struct gsi_trans_pool *pool)
{
	kfree(pool->base);
	memset(pool, 0, sizeof(*pool));
}

/* Home-grown DMA pool.  This way we can preallocate the pool, and guarantee
 * allocations will succeed.  The immediate commands in a transaction can
 * require up to max_alloc elements from the pool.  But we only allow
 * allocation of a single element from a DMA pool at a time.
 */
int gsi_trans_pool_init_dma(struct device *dev, struct gsi_trans_pool *pool,
			    size_t size, u32 count, u32 max_alloc)
{
	size_t total_size;
	dma_addr_t addr;
	void *virt;

	if (!size)
		return -EINVAL;
	if (count < max_alloc)
		return -EINVAL;
	if (!max_alloc)
		return -EINVAL;

	/* Don't let allocations cross a power-of-two boundary */
	size = __roundup_pow_of_two(size);
	total_size = (count + max_alloc - 1) * size;

	/* The allocator will give us a power-of-2 number of pages
	 * sufficient to satisfy our request.  Round up our requested
	 * size to avoid any unused space in the allocation.  This way
	 * gsi_trans_pool_exit_dma() can assume the total allocated
	 * size is exactly (count * size).
	 */
	total_size = PAGE_SIZE << get_order(total_size);

	virt = dma_alloc_coherent(dev, total_size, &addr, GFP_KERNEL);
	if (!virt)
		return -ENOMEM;

Annotation

Implementation Notes