static int exynos_read(struct hwrng *rng, void *buf, size_t max, bool wait) { struct exynos_rng *exynos_rng = container_of(rng, struct exynos_rng, rng); u32 *data = buf; int retry = 100; int ret = 4; pm_runtime_get_sync(exynos_rng->dev); exynos_rng_writel(exynos_rng, PRNG_START, 0); while (!(exynos_rng_readl(exynos_rng, EXYNOS_PRNG_STATUS_OFFSET) & PRNG_DONE) && --retry) cpu_relax(); if (!retry) { ret = -ETIMEDOUT; goto out; } exynos_rng_writel(exynos_rng, PRNG_DONE, EXYNOS_PRNG_STATUS_OFFSET); *data = exynos_rng_readl(exynos_rng, EXYNOS_PRNG_OUT1_OFFSET); out: pm_runtime_mark_last_busy(exynos_rng->dev); pm_runtime_put_sync_autosuspend(exynos_rng->dev); return ret; }
static int exynos_rng_set_seed(struct exynos_rng_dev *rng, const u8 *seed, unsigned int slen) { u32 val; int i; /* Round seed length because loop iterates over full register size */ slen = ALIGN_DOWN(slen, 4); if (slen < EXYNOS_RNG_SEED_SIZE) return -EINVAL; for (i = 0; i < slen ; i += 4) { unsigned int seed_reg = (i / 4) % EXYNOS_RNG_SEED_REGS; val = seed[i] << 24; val |= seed[i + 1] << 16; val |= seed[i + 2] << 8; val |= seed[i + 3] << 0; exynos_rng_writel(rng, val, EXYNOS_RNG_SEED(seed_reg)); } val = exynos_rng_readl(rng, EXYNOS_RNG_STATUS); if (!(val & EXYNOS_RNG_STATUS_SEED_SETTING_DONE)) { dev_warn(rng->dev, "Seed setting not finished\n"); return -EIO; } rng->last_seeding = jiffies; rng->bytes_seeding = 0; return 0; }
/* * Start the engine and poll for finish. Then read from output registers * filling the 'dst' buffer up to 'dlen' bytes or up to size of generated * random data (EXYNOS_RNG_SEED_SIZE). * * On success: return 0 and store number of read bytes under 'read' address. * On error: return -ERRNO. */ static int exynos_rng_get_random(struct exynos_rng_dev *rng, u8 *dst, unsigned int dlen, unsigned int *read) { int retry = EXYNOS_RNG_WAIT_RETRIES; if (rng->type == EXYNOS_PRNG_EXYNOS4) { exynos_rng_writel(rng, EXYNOS_RNG_CONTROL_START, EXYNOS_RNG_CONTROL); } else if (rng->type == EXYNOS_PRNG_EXYNOS5) { exynos_rng_writel(rng, EXYNOS_RNG_GEN_PRNG, EXYNOS_RNG_SEED_CONF); } while (!(exynos_rng_readl(rng, EXYNOS_RNG_STATUS) & EXYNOS_RNG_STATUS_RNG_DONE) && --retry) cpu_relax(); if (!retry) return -ETIMEDOUT; /* Clear status bit */ exynos_rng_writel(rng, EXYNOS_RNG_STATUS_RNG_DONE, EXYNOS_RNG_STATUS); *read = min_t(size_t, dlen, EXYNOS_RNG_SEED_SIZE); memcpy_fromio(dst, rng->mem + EXYNOS_RNG_OUT_BASE, *read); rng->bytes_seeding += *read; return 0; }
static int exynos_rng_configure(struct exynos_rng *exynos_rng) { int i; int ret = 0; for (i = 0 ; i < 5 ; i++) exynos_rng_writel(exynos_rng, jiffies, EXYNOS_PRNG_SEED_OFFSET + 4*i); if (!(exynos_rng_readl(exynos_rng, EXYNOS_PRNG_STATUS_OFFSET) & SEED_SETTING_DONE)) ret = -EIO; return ret; }