drivers/char/hw_random/exynos-trng.c

Source file repositories/reference/linux-study-clean/drivers/char/hw_random/exynos-trng.c

File Facts

System
Linux kernel
Corpus path
drivers/char/hw_random/exynos-trng.c
Extension
.c
Size
8143 bytes
Lines
346
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 exynos_trng_dev {
	struct device	*dev;
	void __iomem	*mem;
	struct clk	*clk;	/* operating clock */
	struct clk	*pclk;	/* bus clock */
	struct hwrng	rng;
	unsigned long	flags;
};

static int exynos_trng_do_read_reg(struct hwrng *rng, void *data, size_t max,
				   bool wait)
{
	struct exynos_trng_dev *trng = (struct exynos_trng_dev *)rng->priv;
	int val;

	max = min_t(size_t, max, (EXYNOS_TRNG_FIFO_LEN * 4));
	writel_relaxed(max * 8, trng->mem + EXYNOS_TRNG_FIFO_CTRL);
	val = readl_poll_timeout(trng->mem + EXYNOS_TRNG_FIFO_CTRL, val,
				 val == 0, 200, 1000000);
	if (val < 0)
		return val;

	memcpy_fromio(data, trng->mem + EXYNOS_TRNG_FIFO_0, max);

	return max;
}

static int exynos_trng_do_read_smc(struct hwrng *rng, void *data, size_t max,
				   bool wait)
{
	struct arm_smccc_res res;
	unsigned int copied = 0;
	u32 *buf = data;
	int tries = 0;

	while (copied < max) {
		arm_smccc_smc(SMC_CMD_RANDOM, HWRNG_GET_DATA, 0, 0, 0, 0, 0, 0,
			      &res);
		switch (res.a0) {
		case HWRNG_RET_OK:
			*buf++ = res.a2;
			*buf++ = res.a3;
			copied += 8;
			tries = 0;
			break;
		case HWRNG_RET_RETRY_ERROR:
			if (!wait)
				return copied;
			if (++tries >= HWRNG_MAX_TRIES)
				return copied;
			cond_resched();
			break;
		default:
			return -EIO;
		}
	}

	return copied;
}

static int exynos_trng_init_reg(struct hwrng *rng)
{
	struct exynos_trng_dev *trng = (struct exynos_trng_dev *)rng->priv;
	unsigned long sss_rate;
	u32 val;

	sss_rate = clk_get_rate(trng->clk);

	/*
	 * For most TRNG circuits the clock frequency of under 500 kHz
	 * is safe.
	 */
	val = sss_rate / (EXYNOS_TRNG_CLOCK_RATE * 2);
	if (val > 0x7fff) {
		dev_err(trng->dev, "clock divider too large: %d\n", val);
		return -ERANGE;
	}
	val = val << 1;
	writel_relaxed(val, trng->mem + EXYNOS_TRNG_CLKDIV);

	/* Enable the generator. */
	val = EXYNOS_TRNG_CTRL_RNGEN;
	writel_relaxed(val, trng->mem + EXYNOS_TRNG_CTRL);

	/*
	 * Disable post-processing. /dev/hwrng is supposed to deliver
	 * unprocessed data.
	 */
	writel_relaxed(0, trng->mem + EXYNOS_TRNG_POST_CTRL);

Annotation

Implementation Notes