drivers/gpu/drm/panfrost/panfrost_mmu.c

Source file repositories/reference/linux-study-clean/drivers/gpu/drm/panfrost/panfrost_mmu.c

File Facts

System
Linux kernel
Corpus path
drivers/gpu/drm/panfrost/panfrost_mmu.c
Extension
.c
Size
25410 bytes
Lines
988
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

if (!(outer & 3) || !(outer & 4) || !(inner & 4)) {
			out_attr = AS_MEMATTR_AARCH64_INNER_OUTER_NC |
				   AS_MEMATTR_AARCH64_SH_MIDGARD_INNER |
				   AS_MEMATTR_AARCH64_INNER_ALLOC_EXPL(false, false);
		} else {
			out_attr = AS_MEMATTR_AARCH64_INNER_OUTER_WB |
				   AS_MEMATTR_AARCH64_INNER_ALLOC_EXPL(inner & 1, inner & 2);
			/* Use SH_MIDGARD_INNER mode when device isn't coherent,
			 * so SH_IS, which is used when IOMMU_CACHE is set, maps
			 * to Mali's internal-shareable mode. As per the Mali
			 * Spec, inner and outer-shareable modes aren't allowed
			 * for WB memory when coherency is disabled.
			 * Use SH_CPU_INNER mode when coherency is enabled, so
			 * that SH_IS actually maps to the standard definition of
			 * inner-shareable.
			 */
			if (!coherent)
				out_attr |= AS_MEMATTR_AARCH64_SH_MIDGARD_INNER;
			else
				out_attr |= AS_MEMATTR_AARCH64_SH_CPU_INNER;
		}

		memattr |= (u64)out_attr << (8 * i);
	}

	return memattr;
}

static int wait_ready(struct panfrost_device *pfdev, u32 as_nr)
{
	int ret;
	u32 val;

	/* Wait for the MMU status to indicate there is no active command, in
	 * case one is pending. */
	ret = readl_relaxed_poll_timeout_atomic(pfdev->iomem + AS_STATUS(as_nr),
		val, !(val & AS_STATUS_AS_ACTIVE), 10, 100000);

	if (ret) {
		/* The GPU hung, let's trigger a reset */
		panfrost_device_schedule_reset(pfdev);
		dev_err(pfdev->base.dev, "AS_ACTIVE bit stuck\n");
	}

	return ret;
}

static int write_cmd(struct panfrost_device *pfdev, u32 as_nr, u32 cmd)
{
	int status;

	/* write AS_COMMAND when MMU is ready to accept another command */
	status = wait_ready(pfdev, as_nr);
	if (!status)
		mmu_write(pfdev, AS_COMMAND(as_nr), cmd);

	return status;
}

static void lock_region(struct panfrost_device *pfdev, u32 as_nr,
			u64 region_start, u64 size)
{
	u8 region_width;
	u64 region;
	u64 region_end = region_start + size;

	if (!size)
		return;

	/*
	 * The locked region is a naturally aligned power of 2 block encoded as
	 * log2 minus(1).
	 * Calculate the desired start/end and look for the highest bit which
	 * differs. The smallest naturally aligned block must include this bit
	 * change, the desired region starts with this bit (and subsequent bits)
	 * zeroed and ends with the bit (and subsequent bits) set to one.
	 */
	region_width = max(fls64(region_start ^ (region_end - 1)),
			   const_ilog2(AS_LOCK_REGION_MIN_SIZE)) - 1;

	/*
	 * Mask off the low bits of region_start (which would be ignored by
	 * the hardware anyway)
	 */
	region_start &= GENMASK_ULL(63, region_width);

	region = region_width | region_start;

	/* Lock the region that needs to be updated */
	mmu_write(pfdev, AS_LOCKADDR_LO(as_nr), lower_32_bits(region));

Annotation

Implementation Notes