drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c

Source file repositories/reference/linux-study-clean/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c

File Facts

System
Linux kernel
Corpus path
drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c
Extension
.c
Size
8770 bytes
Lines
315
Domain
Driver Families
Bucket
drivers/gpu
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 etnaviv_iommuv2_context {
	struct etnaviv_iommu_context base;
	unsigned short id;
	/* M(aster) TLB aka first level pagetable */
	u32 *mtlb_cpu;
	dma_addr_t mtlb_dma;
	/* S(lave) TLB aka second level pagetable */
	u32 *stlb_cpu[MMUv2_MAX_STLB_ENTRIES];
	dma_addr_t stlb_dma[MMUv2_MAX_STLB_ENTRIES];
};

static struct etnaviv_iommuv2_context *
to_v2_context(struct etnaviv_iommu_context *context)
{
	return container_of(context, struct etnaviv_iommuv2_context, base);
}

static void etnaviv_iommuv2_free(struct etnaviv_iommu_context *context)
{
	struct etnaviv_iommuv2_context *v2_context = to_v2_context(context);
	int i;

	drm_mm_takedown(&context->mm);

	for (i = 0; i < MMUv2_MAX_STLB_ENTRIES; i++) {
		if (v2_context->stlb_cpu[i])
			dma_free_wc(context->global->dev, SZ_4K,
				    v2_context->stlb_cpu[i],
				    v2_context->stlb_dma[i]);
	}

	dma_free_wc(context->global->dev, SZ_4K, v2_context->mtlb_cpu,
		    v2_context->mtlb_dma);

	clear_bit(v2_context->id, context->global->v2.pta_alloc);

	vfree(v2_context);
}
static int
etnaviv_iommuv2_ensure_stlb(struct etnaviv_iommuv2_context *v2_context,
			    int stlb)
{
	if (v2_context->stlb_cpu[stlb])
		return 0;

	v2_context->stlb_cpu[stlb] =
			dma_alloc_wc(v2_context->base.global->dev, SZ_4K,
				     &v2_context->stlb_dma[stlb],
				     GFP_KERNEL);

	if (!v2_context->stlb_cpu[stlb])
		return -ENOMEM;

	memset32(v2_context->stlb_cpu[stlb], MMUv2_PTE_EXCEPTION,
		 SZ_4K / sizeof(u32));

	v2_context->mtlb_cpu[stlb] =
			v2_context->stlb_dma[stlb] | MMUv2_PTE_PRESENT;

	return 0;
}

static int etnaviv_iommuv2_map(struct etnaviv_iommu_context *context,
			       unsigned long iova, phys_addr_t paddr,
			       size_t size, int prot)
{
	struct etnaviv_iommuv2_context *v2_context = to_v2_context(context);
	int mtlb_entry, stlb_entry, ret;
	u32 entry = lower_32_bits(paddr) | MMUv2_PTE_PRESENT;

	if (size != SZ_4K)
		return -EINVAL;

	if (IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT))
		entry |= (upper_32_bits(paddr) & 0xff) << 4;

	if (prot & ETNAVIV_PROT_WRITE)
		entry |= MMUv2_PTE_WRITEABLE;

	mtlb_entry = (iova & MMUv2_MTLB_MASK) >> MMUv2_MTLB_SHIFT;
	stlb_entry = (iova & MMUv2_STLB_MASK) >> MMUv2_STLB_SHIFT;

	ret = etnaviv_iommuv2_ensure_stlb(v2_context, mtlb_entry);
	if (ret)
		return ret;

	v2_context->stlb_cpu[mtlb_entry][stlb_entry] = entry;

	return 0;
}

Annotation

Implementation Notes