drivers/char/hw_random/ba431-rng.c

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

File Facts

System
Linux kernel
Corpus path
drivers/char/hw_random/ba431-rng.c
Extension
.c
Size
5307 bytes
Lines
220
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 ba431_trng {
	struct device *dev;
	void __iomem *base;
	struct hwrng rng;
	atomic_t reset_pending;
	struct work_struct reset_work;
};

static inline u32 ba431_trng_read_reg(struct ba431_trng *ba431, u32 reg)
{
	return ioread32(ba431->base + reg);
}

static inline void ba431_trng_write_reg(struct ba431_trng *ba431, u32 reg,
					u32 val)
{
	iowrite32(val, ba431->base + reg);
}

static inline enum ba431_state ba431_trng_get_state(struct ba431_trng *ba431)
{
	u32 status = ba431_trng_read_reg(ba431, BA431_REG_STATUS);

	return (status & BA431_STATUS_STATE_MASK) >> BA431_STATUS_STATE_OFFSET;
}

static int ba431_trng_is_in_error(struct ba431_trng *ba431)
{
	enum ba431_state state = ba431_trng_get_state(ba431);

	if ((state < BA431_STATE_STARTUP) ||
	    (state >= BA431_STATE_ERROR))
		return 1;

	return 0;
}

static int ba431_trng_reset(struct ba431_trng *ba431)
{
	int ret;

	/* Disable interrupts, random generation and enable the softreset */
	ba431_trng_write_reg(ba431, BA431_REG_CTRL, BA431_CTRL_SOFTRESET);
	udelay(BA431_RESET_DELAY);
	ba431_trng_write_reg(ba431, BA431_REG_CTRL, BA431_CTRL_ENABLE);

	/* Wait until the state changed */
	if (readx_poll_timeout(ba431_trng_is_in_error, ba431, ret, !ret,
			       BA431_RESET_READ_STATUS_INTERVAL,
			       BA431_RESET_READ_STATUS_TIMEOUT)) {
		dev_err(ba431->dev, "reset failed (state: %d)\n",
			ba431_trng_get_state(ba431));
		return -ETIMEDOUT;
	}

	dev_info(ba431->dev, "reset done\n");

	return 0;
}

static void ba431_trng_reset_work(struct work_struct *work)
{
	struct ba431_trng *ba431 = container_of(work, struct ba431_trng,
						reset_work);
	ba431_trng_reset(ba431);
	atomic_set(&ba431->reset_pending, 0);
}

static void ba431_trng_schedule_reset(struct ba431_trng *ba431)
{
	if (atomic_cmpxchg(&ba431->reset_pending, 0, 1))
		return;

	schedule_work(&ba431->reset_work);
}

static int ba431_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
{
	struct ba431_trng *ba431 = container_of(rng, struct ba431_trng, rng);
	u32 *data = buf;
	unsigned int level, i;
	int n = 0;

	while (max > 0) {
		level = ba431_trng_read_reg(ba431, BA431_REG_FIFO_LEVEL);
		if (!level) {
			if (ba431_trng_is_in_error(ba431)) {
				ba431_trng_schedule_reset(ba431);
				break;
			}

Annotation

Implementation Notes