drivers/cache/hisi_soc_hha.c

Source file repositories/reference/linux-study-clean/drivers/cache/hisi_soc_hha.c

File Facts

System
Linux kernel
Corpus path
drivers/cache/hisi_soc_hha.c
Extension
.c
Size
5417 bytes
Lines
195
Domain
Driver Families
Bucket
drivers/cache
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 hisi_soc_hha {
	/* Must be first element */
	struct cache_coherency_ops_inst cci;
	/* Locks HHA instance to forbid overlapping access. */
	struct mutex lock;
	void __iomem *base;
};

static bool hisi_hha_cache_maintain_wait_finished(struct hisi_soc_hha *soc_hha)
{
	u32 val;

	return !readl_poll_timeout_atomic(soc_hha->base + HISI_HHA_CTRL, val,
					  !(val & HISI_HHA_CTRL_EN),
					  HISI_HHA_POLL_GAP_US,
					  HISI_HHA_POLL_TIMEOUT_US);
}

static int hisi_soc_hha_wbinv(struct cache_coherency_ops_inst *cci,
			struct cc_inval_params *invp)
{
	struct hisi_soc_hha *soc_hha =
		container_of(cci, struct hisi_soc_hha, cci);
	phys_addr_t top, addr = invp->addr;
	size_t size = invp->size;
	u32 reg;

	if (!size)
		return -EINVAL;

	addr = ALIGN_DOWN(addr, HISI_HHA_MAINT_ALIGN);
	top = ALIGN(addr + size, HISI_HHA_MAINT_ALIGN);
	size = top - addr;

	guard(mutex)(&soc_hha->lock);

	if (!hisi_hha_cache_maintain_wait_finished(soc_hha))
		return -EBUSY;

	/*
	 * Hardware will search for addresses ranging [addr, addr + size - 1],
	 * last byte included, and perform maintenance in 128 byte granules
	 * on those cachelines which contain the addresses. If a given instance
	 * is either not responsible for a cacheline or that cacheline is not
	 * currently present then the search will fail, no operation will be
	 * necessary and the device will report success.
	 */
	size -= 1;

	writel(lower_32_bits(addr), soc_hha->base + HISI_HHA_START_L);
	writel(upper_32_bits(addr), soc_hha->base + HISI_HHA_START_H);
	writel(lower_32_bits(size), soc_hha->base + HISI_HHA_LEN_L);
	writel(upper_32_bits(size), soc_hha->base + HISI_HHA_LEN_H);

	reg = FIELD_PREP(HISI_HHA_CTRL_TYPE, 1); /* Clean Invalid */
	reg |= HISI_HHA_CTRL_RANGE | HISI_HHA_CTRL_EN;
	writel(reg, soc_hha->base + HISI_HHA_CTRL);

	return 0;
}

static int hisi_soc_hha_done(struct cache_coherency_ops_inst *cci)
{
	struct hisi_soc_hha *soc_hha =
		container_of(cci, struct hisi_soc_hha, cci);

	guard(mutex)(&soc_hha->lock);
	if (!hisi_hha_cache_maintain_wait_finished(soc_hha))
		return -ETIMEDOUT;

	return 0;
}

static const struct cache_coherency_ops hha_ops = {
	.wbinv = hisi_soc_hha_wbinv,
	.done = hisi_soc_hha_done,
};

static int hisi_soc_hha_probe(struct platform_device *pdev)
{
	struct hisi_soc_hha *soc_hha;
	struct resource *mem;
	int ret;

	soc_hha = cache_coherency_ops_instance_alloc(&hha_ops,
						     struct hisi_soc_hha, cci);
	if (!soc_hha)
		return -ENOMEM;

	platform_set_drvdata(pdev, soc_hha);

Annotation

Implementation Notes