162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * exynos-rng.c - Random Number Generator driver for the Exynos
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2017 Krzysztof Kozlowski <krzk@kernel.org>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Loosely based on old driver from drivers/char/hw_random/exynos-rng.c:
862306a36Sopenharmony_ci * Copyright (C) 2012 Samsung Electronics
962306a36Sopenharmony_ci * Jonghwa Lee <jonghwa3.lee@samsung.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/clk.h>
1362306a36Sopenharmony_ci#include <linux/crypto.h>
1462306a36Sopenharmony_ci#include <linux/err.h>
1562306a36Sopenharmony_ci#include <linux/io.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/mutex.h>
1862306a36Sopenharmony_ci#include <linux/of.h>
1962306a36Sopenharmony_ci#include <linux/platform_device.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <crypto/internal/rng.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define EXYNOS_RNG_CONTROL		0x0
2462306a36Sopenharmony_ci#define EXYNOS_RNG_STATUS		0x10
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define EXYNOS_RNG_SEED_CONF		0x14
2762306a36Sopenharmony_ci#define EXYNOS_RNG_GEN_PRNG	        BIT(1)
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define EXYNOS_RNG_SEED_BASE		0x140
3062306a36Sopenharmony_ci#define EXYNOS_RNG_SEED(n)		(EXYNOS_RNG_SEED_BASE + (n * 0x4))
3162306a36Sopenharmony_ci#define EXYNOS_RNG_OUT_BASE		0x160
3262306a36Sopenharmony_ci#define EXYNOS_RNG_OUT(n)		(EXYNOS_RNG_OUT_BASE + (n * 0x4))
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* EXYNOS_RNG_CONTROL bit fields */
3562306a36Sopenharmony_ci#define EXYNOS_RNG_CONTROL_START	0x18
3662306a36Sopenharmony_ci/* EXYNOS_RNG_STATUS bit fields */
3762306a36Sopenharmony_ci#define EXYNOS_RNG_STATUS_SEED_SETTING_DONE	BIT(1)
3862306a36Sopenharmony_ci#define EXYNOS_RNG_STATUS_RNG_DONE		BIT(5)
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/* Five seed and output registers, each 4 bytes */
4162306a36Sopenharmony_ci#define EXYNOS_RNG_SEED_REGS		5
4262306a36Sopenharmony_ci#define EXYNOS_RNG_SEED_SIZE		(EXYNOS_RNG_SEED_REGS * 4)
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cienum exynos_prng_type {
4562306a36Sopenharmony_ci	EXYNOS_PRNG_UNKNOWN = 0,
4662306a36Sopenharmony_ci	EXYNOS_PRNG_EXYNOS4,
4762306a36Sopenharmony_ci	EXYNOS_PRNG_EXYNOS5,
4862306a36Sopenharmony_ci};
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/*
5162306a36Sopenharmony_ci * Driver re-seeds itself with generated random numbers to hinder
5262306a36Sopenharmony_ci * backtracking of the original seed.
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci * Time for next re-seed in ms.
5562306a36Sopenharmony_ci */
5662306a36Sopenharmony_ci#define EXYNOS_RNG_RESEED_TIME		1000
5762306a36Sopenharmony_ci#define EXYNOS_RNG_RESEED_BYTES		65536
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/*
6062306a36Sopenharmony_ci * In polling mode, do not wait infinitely for the engine to finish the work.
6162306a36Sopenharmony_ci */
6262306a36Sopenharmony_ci#define EXYNOS_RNG_WAIT_RETRIES		100
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/* Context for crypto */
6562306a36Sopenharmony_cistruct exynos_rng_ctx {
6662306a36Sopenharmony_ci	struct exynos_rng_dev		*rng;
6762306a36Sopenharmony_ci};
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/* Device associated memory */
7062306a36Sopenharmony_cistruct exynos_rng_dev {
7162306a36Sopenharmony_ci	struct device			*dev;
7262306a36Sopenharmony_ci	enum exynos_prng_type		type;
7362306a36Sopenharmony_ci	void __iomem			*mem;
7462306a36Sopenharmony_ci	struct clk			*clk;
7562306a36Sopenharmony_ci	struct mutex 			lock;
7662306a36Sopenharmony_ci	/* Generated numbers stored for seeding during resume */
7762306a36Sopenharmony_ci	u8				seed_save[EXYNOS_RNG_SEED_SIZE];
7862306a36Sopenharmony_ci	unsigned int			seed_save_len;
7962306a36Sopenharmony_ci	/* Time of last seeding in jiffies */
8062306a36Sopenharmony_ci	unsigned long			last_seeding;
8162306a36Sopenharmony_ci	/* Bytes generated since last seeding */
8262306a36Sopenharmony_ci	unsigned long			bytes_seeding;
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic struct exynos_rng_dev *exynos_rng_dev;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic u32 exynos_rng_readl(struct exynos_rng_dev *rng, u32 offset)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	return readl_relaxed(rng->mem + offset);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic void exynos_rng_writel(struct exynos_rng_dev *rng, u32 val, u32 offset)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	writel_relaxed(val, rng->mem + offset);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic int exynos_rng_set_seed(struct exynos_rng_dev *rng,
9862306a36Sopenharmony_ci			       const u8 *seed, unsigned int slen)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	u32 val;
10162306a36Sopenharmony_ci	int i;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	/* Round seed length because loop iterates over full register size */
10462306a36Sopenharmony_ci	slen = ALIGN_DOWN(slen, 4);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (slen < EXYNOS_RNG_SEED_SIZE)
10762306a36Sopenharmony_ci		return -EINVAL;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	for (i = 0; i < slen ; i += 4) {
11062306a36Sopenharmony_ci		unsigned int seed_reg = (i / 4) % EXYNOS_RNG_SEED_REGS;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci		val = seed[i] << 24;
11362306a36Sopenharmony_ci		val |= seed[i + 1] << 16;
11462306a36Sopenharmony_ci		val |= seed[i + 2] << 8;
11562306a36Sopenharmony_ci		val |= seed[i + 3] << 0;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci		exynos_rng_writel(rng, val, EXYNOS_RNG_SEED(seed_reg));
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	val = exynos_rng_readl(rng, EXYNOS_RNG_STATUS);
12162306a36Sopenharmony_ci	if (!(val & EXYNOS_RNG_STATUS_SEED_SETTING_DONE)) {
12262306a36Sopenharmony_ci		dev_warn(rng->dev, "Seed setting not finished\n");
12362306a36Sopenharmony_ci		return -EIO;
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	rng->last_seeding = jiffies;
12762306a36Sopenharmony_ci	rng->bytes_seeding = 0;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	return 0;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci/*
13362306a36Sopenharmony_ci * Start the engine and poll for finish.  Then read from output registers
13462306a36Sopenharmony_ci * filling the 'dst' buffer up to 'dlen' bytes or up to size of generated
13562306a36Sopenharmony_ci * random data (EXYNOS_RNG_SEED_SIZE).
13662306a36Sopenharmony_ci *
13762306a36Sopenharmony_ci * On success: return 0 and store number of read bytes under 'read' address.
13862306a36Sopenharmony_ci * On error: return -ERRNO.
13962306a36Sopenharmony_ci */
14062306a36Sopenharmony_cistatic int exynos_rng_get_random(struct exynos_rng_dev *rng,
14162306a36Sopenharmony_ci				 u8 *dst, unsigned int dlen,
14262306a36Sopenharmony_ci				 unsigned int *read)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	int retry = EXYNOS_RNG_WAIT_RETRIES;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	if (rng->type == EXYNOS_PRNG_EXYNOS4) {
14762306a36Sopenharmony_ci		exynos_rng_writel(rng, EXYNOS_RNG_CONTROL_START,
14862306a36Sopenharmony_ci				  EXYNOS_RNG_CONTROL);
14962306a36Sopenharmony_ci	} else if (rng->type == EXYNOS_PRNG_EXYNOS5) {
15062306a36Sopenharmony_ci		exynos_rng_writel(rng, EXYNOS_RNG_GEN_PRNG,
15162306a36Sopenharmony_ci				  EXYNOS_RNG_SEED_CONF);
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	while (!(exynos_rng_readl(rng,
15562306a36Sopenharmony_ci			EXYNOS_RNG_STATUS) & EXYNOS_RNG_STATUS_RNG_DONE) && --retry)
15662306a36Sopenharmony_ci		cpu_relax();
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	if (!retry)
15962306a36Sopenharmony_ci		return -ETIMEDOUT;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	/* Clear status bit */
16262306a36Sopenharmony_ci	exynos_rng_writel(rng, EXYNOS_RNG_STATUS_RNG_DONE,
16362306a36Sopenharmony_ci			  EXYNOS_RNG_STATUS);
16462306a36Sopenharmony_ci	*read = min_t(size_t, dlen, EXYNOS_RNG_SEED_SIZE);
16562306a36Sopenharmony_ci	memcpy_fromio(dst, rng->mem + EXYNOS_RNG_OUT_BASE, *read);
16662306a36Sopenharmony_ci	rng->bytes_seeding += *read;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	return 0;
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci/* Re-seed itself from time to time */
17262306a36Sopenharmony_cistatic void exynos_rng_reseed(struct exynos_rng_dev *rng)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	unsigned long next_seeding = rng->last_seeding + \
17562306a36Sopenharmony_ci				     msecs_to_jiffies(EXYNOS_RNG_RESEED_TIME);
17662306a36Sopenharmony_ci	unsigned long now = jiffies;
17762306a36Sopenharmony_ci	unsigned int read = 0;
17862306a36Sopenharmony_ci	u8 seed[EXYNOS_RNG_SEED_SIZE];
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	if (time_before(now, next_seeding) &&
18162306a36Sopenharmony_ci	    rng->bytes_seeding < EXYNOS_RNG_RESEED_BYTES)
18262306a36Sopenharmony_ci		return;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (exynos_rng_get_random(rng, seed, sizeof(seed), &read))
18562306a36Sopenharmony_ci		return;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	exynos_rng_set_seed(rng, seed, read);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/* Let others do some of their job. */
19062306a36Sopenharmony_ci	mutex_unlock(&rng->lock);
19162306a36Sopenharmony_ci	mutex_lock(&rng->lock);
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic int exynos_rng_generate(struct crypto_rng *tfm,
19562306a36Sopenharmony_ci			       const u8 *src, unsigned int slen,
19662306a36Sopenharmony_ci			       u8 *dst, unsigned int dlen)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	struct exynos_rng_ctx *ctx = crypto_rng_ctx(tfm);
19962306a36Sopenharmony_ci	struct exynos_rng_dev *rng = ctx->rng;
20062306a36Sopenharmony_ci	unsigned int read = 0;
20162306a36Sopenharmony_ci	int ret;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	ret = clk_prepare_enable(rng->clk);
20462306a36Sopenharmony_ci	if (ret)
20562306a36Sopenharmony_ci		return ret;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	mutex_lock(&rng->lock);
20862306a36Sopenharmony_ci	do {
20962306a36Sopenharmony_ci		ret = exynos_rng_get_random(rng, dst, dlen, &read);
21062306a36Sopenharmony_ci		if (ret)
21162306a36Sopenharmony_ci			break;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci		dlen -= read;
21462306a36Sopenharmony_ci		dst += read;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci		exynos_rng_reseed(rng);
21762306a36Sopenharmony_ci	} while (dlen > 0);
21862306a36Sopenharmony_ci	mutex_unlock(&rng->lock);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	clk_disable_unprepare(rng->clk);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	return ret;
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic int exynos_rng_seed(struct crypto_rng *tfm, const u8 *seed,
22662306a36Sopenharmony_ci			   unsigned int slen)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	struct exynos_rng_ctx *ctx = crypto_rng_ctx(tfm);
22962306a36Sopenharmony_ci	struct exynos_rng_dev *rng = ctx->rng;
23062306a36Sopenharmony_ci	int ret;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	ret = clk_prepare_enable(rng->clk);
23362306a36Sopenharmony_ci	if (ret)
23462306a36Sopenharmony_ci		return ret;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	mutex_lock(&rng->lock);
23762306a36Sopenharmony_ci	ret = exynos_rng_set_seed(ctx->rng, seed, slen);
23862306a36Sopenharmony_ci	mutex_unlock(&rng->lock);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	clk_disable_unprepare(rng->clk);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	return ret;
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic int exynos_rng_kcapi_init(struct crypto_tfm *tfm)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	struct exynos_rng_ctx *ctx = crypto_tfm_ctx(tfm);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	ctx->rng = exynos_rng_dev;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	return 0;
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic struct rng_alg exynos_rng_alg = {
25562306a36Sopenharmony_ci	.generate		= exynos_rng_generate,
25662306a36Sopenharmony_ci	.seed			= exynos_rng_seed,
25762306a36Sopenharmony_ci	.seedsize		= EXYNOS_RNG_SEED_SIZE,
25862306a36Sopenharmony_ci	.base			= {
25962306a36Sopenharmony_ci		.cra_name		= "stdrng",
26062306a36Sopenharmony_ci		.cra_driver_name	= "exynos_rng",
26162306a36Sopenharmony_ci		.cra_priority		= 300,
26262306a36Sopenharmony_ci		.cra_ctxsize		= sizeof(struct exynos_rng_ctx),
26362306a36Sopenharmony_ci		.cra_module		= THIS_MODULE,
26462306a36Sopenharmony_ci		.cra_init		= exynos_rng_kcapi_init,
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci};
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic int exynos_rng_probe(struct platform_device *pdev)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	struct exynos_rng_dev *rng;
27162306a36Sopenharmony_ci	int ret;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	if (exynos_rng_dev)
27462306a36Sopenharmony_ci		return -EEXIST;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL);
27762306a36Sopenharmony_ci	if (!rng)
27862306a36Sopenharmony_ci		return -ENOMEM;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	rng->type = (uintptr_t)of_device_get_match_data(&pdev->dev);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	mutex_init(&rng->lock);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	rng->dev = &pdev->dev;
28562306a36Sopenharmony_ci	rng->clk = devm_clk_get(&pdev->dev, "secss");
28662306a36Sopenharmony_ci	if (IS_ERR(rng->clk)) {
28762306a36Sopenharmony_ci		dev_err(&pdev->dev, "Couldn't get clock.\n");
28862306a36Sopenharmony_ci		return PTR_ERR(rng->clk);
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	rng->mem = devm_platform_ioremap_resource(pdev, 0);
29262306a36Sopenharmony_ci	if (IS_ERR(rng->mem))
29362306a36Sopenharmony_ci		return PTR_ERR(rng->mem);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	platform_set_drvdata(pdev, rng);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	exynos_rng_dev = rng;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	ret = crypto_register_rng(&exynos_rng_alg);
30062306a36Sopenharmony_ci	if (ret) {
30162306a36Sopenharmony_ci		dev_err(&pdev->dev,
30262306a36Sopenharmony_ci			"Couldn't register rng crypto alg: %d\n", ret);
30362306a36Sopenharmony_ci		exynos_rng_dev = NULL;
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	return ret;
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic int exynos_rng_remove(struct platform_device *pdev)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	crypto_unregister_rng(&exynos_rng_alg);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	exynos_rng_dev = NULL;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	return 0;
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic int __maybe_unused exynos_rng_suspend(struct device *dev)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	struct exynos_rng_dev *rng = dev_get_drvdata(dev);
32162306a36Sopenharmony_ci	int ret;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	/* If we were never seeded then after resume it will be the same */
32462306a36Sopenharmony_ci	if (!rng->last_seeding)
32562306a36Sopenharmony_ci		return 0;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	rng->seed_save_len = 0;
32862306a36Sopenharmony_ci	ret = clk_prepare_enable(rng->clk);
32962306a36Sopenharmony_ci	if (ret)
33062306a36Sopenharmony_ci		return ret;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	mutex_lock(&rng->lock);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	/* Get new random numbers and store them for seeding on resume. */
33562306a36Sopenharmony_ci	exynos_rng_get_random(rng, rng->seed_save, sizeof(rng->seed_save),
33662306a36Sopenharmony_ci			      &(rng->seed_save_len));
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	mutex_unlock(&rng->lock);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	dev_dbg(rng->dev, "Stored %u bytes for seeding on system resume\n",
34162306a36Sopenharmony_ci		rng->seed_save_len);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	clk_disable_unprepare(rng->clk);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	return 0;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic int __maybe_unused exynos_rng_resume(struct device *dev)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	struct exynos_rng_dev *rng = dev_get_drvdata(dev);
35162306a36Sopenharmony_ci	int ret;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	/* Never seeded so nothing to do */
35462306a36Sopenharmony_ci	if (!rng->last_seeding)
35562306a36Sopenharmony_ci		return 0;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	ret = clk_prepare_enable(rng->clk);
35862306a36Sopenharmony_ci	if (ret)
35962306a36Sopenharmony_ci		return ret;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	mutex_lock(&rng->lock);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	ret = exynos_rng_set_seed(rng, rng->seed_save, rng->seed_save_len);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	mutex_unlock(&rng->lock);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	clk_disable_unprepare(rng->clk);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	return ret;
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(exynos_rng_pm_ops, exynos_rng_suspend,
37362306a36Sopenharmony_ci			 exynos_rng_resume);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_cistatic const struct of_device_id exynos_rng_dt_match[] = {
37662306a36Sopenharmony_ci	{
37762306a36Sopenharmony_ci		.compatible = "samsung,exynos4-rng",
37862306a36Sopenharmony_ci		.data = (const void *)EXYNOS_PRNG_EXYNOS4,
37962306a36Sopenharmony_ci	}, {
38062306a36Sopenharmony_ci		.compatible = "samsung,exynos5250-prng",
38162306a36Sopenharmony_ci		.data = (const void *)EXYNOS_PRNG_EXYNOS5,
38262306a36Sopenharmony_ci	},
38362306a36Sopenharmony_ci	{ },
38462306a36Sopenharmony_ci};
38562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, exynos_rng_dt_match);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_cistatic struct platform_driver exynos_rng_driver = {
38862306a36Sopenharmony_ci	.driver		= {
38962306a36Sopenharmony_ci		.name	= "exynos-rng",
39062306a36Sopenharmony_ci		.pm	= &exynos_rng_pm_ops,
39162306a36Sopenharmony_ci		.of_match_table = exynos_rng_dt_match,
39262306a36Sopenharmony_ci	},
39362306a36Sopenharmony_ci	.probe		= exynos_rng_probe,
39462306a36Sopenharmony_ci	.remove		= exynos_rng_remove,
39562306a36Sopenharmony_ci};
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cimodule_platform_driver(exynos_rng_driver);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ciMODULE_DESCRIPTION("Exynos H/W Random Number Generator driver");
40062306a36Sopenharmony_ciMODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>");
40162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
402