drivers/char/hw_random/atmel-rng.c

Source file repositories/reference/linux-study-clean/drivers/char/hw_random/atmel-rng.c

File Facts

System
Linux kernel
Corpus path
drivers/char/hw_random/atmel-rng.c
Extension
.c
Size
5248 bytes
Lines
232
Domain
Driver Families
Bucket
drivers/char
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 atmel_trng_data {
	bool has_half_rate;
};

struct atmel_trng {
	struct clk *clk;
	void __iomem *base;
	struct hwrng rng;
	struct device *dev;
	bool has_half_rate;
};

static bool atmel_trng_wait_ready(struct atmel_trng *trng, bool wait)
{
	int ready;

	ready = readl(trng->base + TRNG_ISR) & TRNG_ISR_DATRDY;
	if (!ready && wait)
		readl_poll_timeout(trng->base + TRNG_ISR, ready,
				   ready & TRNG_ISR_DATRDY, 1000, 20000);

	return !!ready;
}

static int atmel_trng_read(struct hwrng *rng, void *buf, size_t max,
			   bool wait)
{
	struct atmel_trng *trng = container_of(rng, struct atmel_trng, rng);
	u32 *data = buf;
	int ret;

	ret = pm_runtime_get_sync(trng->dev);
	if (ret < 0) {
		pm_runtime_put_sync(trng->dev);
		return ret;
	}

	ret = atmel_trng_wait_ready(trng, wait);
	if (!ret)
		goto out;

	*data = readl(trng->base + TRNG_ODATA);
	/*
	 * ensure data ready is only set again AFTER the next data word is ready
	 * in case it got set between checking ISR and reading ODATA, so we
	 * don't risk re-reading the same word
	 */
	readl(trng->base + TRNG_ISR);
	ret = 4;

out:
	pm_runtime_put_sync_autosuspend(trng->dev);
	return ret;
}

static int atmel_trng_init(struct atmel_trng *trng)
{
	unsigned long rate;
	int ret;

	ret = clk_prepare_enable(trng->clk);
	if (ret)
		return ret;

	if (trng->has_half_rate) {
		rate = clk_get_rate(trng->clk);

		/* if peripheral clk is above 100MHz, set HALFR */
		if (rate > 100000000)
			writel(TRNG_HALFR, trng->base + TRNG_MR);
	}

	writel(TRNG_KEY | 1, trng->base + TRNG_CR);

	return 0;
}

static void atmel_trng_cleanup(struct atmel_trng *trng)
{
	writel(TRNG_KEY, trng->base + TRNG_CR);
	clk_disable_unprepare(trng->clk);
}

static int atmel_trng_probe(struct platform_device *pdev)
{
	struct atmel_trng *trng;
	const struct atmel_trng_data *data;
	int ret;

	trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);

Annotation

Implementation Notes