drivers/char/hw_random/bcm2835-rng.c

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

File Facts

System
Linux kernel
Corpus path
drivers/char/hw_random/bcm2835-rng.c
Extension
.c
Size
5103 bytes
Lines
211
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 bcm2835_rng_priv {
	struct hwrng rng;
	void __iomem *base;
	bool mask_interrupts;
	struct clk *clk;
	struct reset_control *reset;
};

static inline struct bcm2835_rng_priv *to_rng_priv(struct hwrng *rng)
{
	return container_of(rng, struct bcm2835_rng_priv, rng);
}

static inline u32 rng_readl(struct bcm2835_rng_priv *priv, u32 offset)
{
	/* MIPS chips strapped for BE will automagically configure the
	 * peripheral registers for CPU-native byte order.
	 */
	if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
		return __raw_readl(priv->base + offset);
	else
		return readl(priv->base + offset);
}

static inline void rng_writel(struct bcm2835_rng_priv *priv, u32 val,
			      u32 offset)
{
	if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
		__raw_writel(val, priv->base + offset);
	else
		writel(val, priv->base + offset);
}

static int bcm2835_rng_read(struct hwrng *rng, void *buf, size_t max,
			       bool wait)
{
	struct bcm2835_rng_priv *priv = to_rng_priv(rng);
	u32 max_words = max / sizeof(u32);
	u32 num_words, count;

	while ((rng_readl(priv, RNG_STATUS) >> 24) == 0) {
		if (!wait)
			return 0;
		hwrng_yield(rng);
	}

	num_words = rng_readl(priv, RNG_STATUS) >> 24;
	if (num_words > max_words)
		num_words = max_words;

	for (count = 0; count < num_words; count++)
		((u32 *)buf)[count] = rng_readl(priv, RNG_DATA);

	return num_words * sizeof(u32);
}

static int bcm2835_rng_init(struct hwrng *rng)
{
	struct bcm2835_rng_priv *priv = to_rng_priv(rng);
	int ret = 0;
	u32 val;

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

	ret = reset_control_reset(priv->reset);
	if (ret) {
		clk_disable_unprepare(priv->clk);
		return ret;
	}

	if (priv->mask_interrupts) {
		/* mask the interrupt */
		val = rng_readl(priv, RNG_INT_MASK);
		val |= RNG_INT_OFF;
		rng_writel(priv, val, RNG_INT_MASK);
	}

	/* set warm-up count & enable */
	rng_writel(priv, RNG_WARMUP_COUNT, RNG_STATUS);
	rng_writel(priv, RNG_RBGEN, RNG_CTRL);

	return ret;
}

static void bcm2835_rng_cleanup(struct hwrng *rng)
{
	struct bcm2835_rng_priv *priv = to_rng_priv(rng);

Annotation

Implementation Notes