File Index
Files and directories
Sign in
FILE

exynos-trng.c

File preview with syntax highlighting.

linux-6.19.14/drivers/char/hw_random/exynos-trng.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * RNG driver for Exynos TRNGs
  4 *
  5 * Author: Łukasz Stelmach <l.stelmach@samsung.com>
  6 *
  7 * Copyright 2017 (c) Samsung Electronics Software, Inc.
  8 *
  9 * Based on the Exynos PRNG driver drivers/crypto/exynos-rng by
 10 * Krzysztof Kozłowski <krzk@kernel.org>
 11 */
 12
 13#include <linux/arm-smccc.h>
 14#include <linux/clk.h>
 15#include <linux/crypto.h>
 16#include <linux/delay.h>
 17#include <linux/err.h>
 18#include <linux/hw_random.h>
 19#include <linux/io.h>
 20#include <linux/iopoll.h>
 21#include <linux/kernel.h>
 22#include <linux/module.h>
 23#include <linux/mod_devicetable.h>
 24#include <linux/platform_device.h>
 25#include <linux/pm_runtime.h>
 26#include <linux/property.h>
 27
 28#define EXYNOS_TRNG_CLKDIV		0x0
 29
 30#define EXYNOS_TRNG_CTRL		0x20
 31#define EXYNOS_TRNG_CTRL_RNGEN		BIT(31)
 32
 33#define EXYNOS_TRNG_POST_CTRL		0x30
 34#define EXYNOS_TRNG_ONLINE_CTRL		0x40
 35#define EXYNOS_TRNG_ONLINE_STAT		0x44
 36#define EXYNOS_TRNG_ONLINE_MAXCHI2	0x48
 37#define EXYNOS_TRNG_FIFO_CTRL		0x50
 38#define EXYNOS_TRNG_FIFO_0		0x80
 39#define EXYNOS_TRNG_FIFO_1		0x84
 40#define EXYNOS_TRNG_FIFO_2		0x88
 41#define EXYNOS_TRNG_FIFO_3		0x8c
 42#define EXYNOS_TRNG_FIFO_4		0x90
 43#define EXYNOS_TRNG_FIFO_5		0x94
 44#define EXYNOS_TRNG_FIFO_6		0x98
 45#define EXYNOS_TRNG_FIFO_7		0x9c
 46#define EXYNOS_TRNG_FIFO_LEN		8
 47#define EXYNOS_TRNG_CLOCK_RATE		500000
 48
 49/* Driver feature flags */
 50#define EXYNOS_SMC			BIT(0)
 51
 52#define EXYNOS_SMC_CALL_VAL(func_num)			\
 53	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,		\
 54			   ARM_SMCCC_SMC_32,		\
 55			   ARM_SMCCC_OWNER_SIP,		\
 56			   func_num)
 57
 58/* SMC command for DTRNG access */
 59#define SMC_CMD_RANDOM			EXYNOS_SMC_CALL_VAL(0x1012)
 60
 61/* SMC_CMD_RANDOM: arguments */
 62#define HWRNG_INIT			0x0
 63#define HWRNG_EXIT			0x1
 64#define HWRNG_GET_DATA			0x2
 65#define HWRNG_RESUME			0x3
 66
 67/* SMC_CMD_RANDOM: return values */
 68#define HWRNG_RET_OK			0x0
 69#define HWRNG_RET_RETRY_ERROR		0x2
 70
 71#define HWRNG_MAX_TRIES			100
 72
 73struct exynos_trng_dev {
 74	struct device	*dev;
 75	void __iomem	*mem;
 76	struct clk	*clk;	/* operating clock */
 77	struct clk	*pclk;	/* bus clock */
 78	struct hwrng	rng;
 79	unsigned long	flags;
 80};
 81
 82static int exynos_trng_do_read_reg(struct hwrng *rng, void *data, size_t max,
 83				   bool wait)
 84{
 85	struct exynos_trng_dev *trng = (struct exynos_trng_dev *)rng->priv;
 86	int val;
 87
 88	max = min_t(size_t, max, (EXYNOS_TRNG_FIFO_LEN * 4));
 89	writel_relaxed(max * 8, trng->mem + EXYNOS_TRNG_FIFO_CTRL);
 90	val = readl_poll_timeout(trng->mem + EXYNOS_TRNG_FIFO_CTRL, val,
 91				 val == 0, 200, 1000000);
 92	if (val < 0)
 93		return val;
 94
 95	memcpy_fromio(data, trng->mem + EXYNOS_TRNG_FIFO_0, max);
 96
 97	return max;
 98}
 99
100static int exynos_trng_do_read_smc(struct hwrng *rng, void *data, size_t max,
101				   bool wait)
102{
103	struct arm_smccc_res res;
104	unsigned int copied = 0;
105	u32 *buf = data;
106	int tries = 0;
107
108	while (copied < max) {
109		arm_smccc_smc(SMC_CMD_RANDOM, HWRNG_GET_DATA, 0, 0, 0, 0, 0, 0,
110			      &res);
111		switch (res.a0) {
112		case HWRNG_RET_OK:
113			*buf++ = res.a2;
114			*buf++ = res.a3;
115			copied += 8;
116			tries = 0;
117			break;
118		case HWRNG_RET_RETRY_ERROR:
119			if (!wait)
120				return copied;
121			if (++tries >= HWRNG_MAX_TRIES)
122				return copied;
123			cond_resched();
124			break;
125		default:
126			return -EIO;
127		}
128	}
129
130	return copied;
131}
132
133static int exynos_trng_init_reg(struct hwrng *rng)
134{
135	struct exynos_trng_dev *trng = (struct exynos_trng_dev *)rng->priv;
136	unsigned long sss_rate;
137	u32 val;
138
139	sss_rate = clk_get_rate(trng->clk);
140
141	/*
142	 * For most TRNG circuits the clock frequency of under 500 kHz
143	 * is safe.
144	 */
145	val = sss_rate / (EXYNOS_TRNG_CLOCK_RATE * 2);
146	if (val > 0x7fff) {
147		dev_err(trng->dev, "clock divider too large: %d\n", val);
148		return -ERANGE;
149	}
150	val = val << 1;
151	writel_relaxed(val, trng->mem + EXYNOS_TRNG_CLKDIV);
152
153	/* Enable the generator. */
154	val = EXYNOS_TRNG_CTRL_RNGEN;
155	writel_relaxed(val, trng->mem + EXYNOS_TRNG_CTRL);
156
157	/*
158	 * Disable post-processing. /dev/hwrng is supposed to deliver
159	 * unprocessed data.
160	 */
161	writel_relaxed(0, trng->mem + EXYNOS_TRNG_POST_CTRL);
162
163	return 0;
164}
165
166static int exynos_trng_init_smc(struct hwrng *rng)
167{
168	struct exynos_trng_dev *trng = (struct exynos_trng_dev *)rng->priv;
169	struct arm_smccc_res res;
170	int ret = 0;
171
172	arm_smccc_smc(SMC_CMD_RANDOM, HWRNG_INIT, 0, 0, 0, 0, 0, 0, &res);
173	if (res.a0 != HWRNG_RET_OK) {
174		dev_err(trng->dev, "SMC command for TRNG init failed (%d)\n",
175			(int)res.a0);
176		ret = -EIO;
177	}
178	if ((int)res.a0 == -1)
179		dev_info(trng->dev, "Make sure LDFW is loaded by your BL\n");
180
181	return ret;
182}
183
184static int exynos_trng_probe(struct platform_device *pdev)
185{
186	struct exynos_trng_dev *trng;
187	int ret = -ENOMEM;
188
189	trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
190	if (!trng)
191		return ret;
192
193	platform_set_drvdata(pdev, trng);
194	trng->dev = &pdev->dev;
195
196	trng->flags = (unsigned long)device_get_match_data(&pdev->dev);
197
198	trng->rng.name = devm_kstrdup(&pdev->dev, dev_name(&pdev->dev),
199				      GFP_KERNEL);
200	if (!trng->rng.name)
201		return ret;
202
203	trng->rng.priv = (unsigned long)trng;
204
205	if (trng->flags & EXYNOS_SMC) {
206		trng->rng.init = exynos_trng_init_smc;
207		trng->rng.read = exynos_trng_do_read_smc;
208	} else {
209		trng->rng.init = exynos_trng_init_reg;
210		trng->rng.read = exynos_trng_do_read_reg;
211
212		trng->mem = devm_platform_ioremap_resource(pdev, 0);
213		if (IS_ERR(trng->mem))
214			return PTR_ERR(trng->mem);
215	}
216
217	pm_runtime_enable(&pdev->dev);
218	ret = pm_runtime_resume_and_get(&pdev->dev);
219	if (ret < 0) {
220		dev_err(&pdev->dev, "Could not get runtime PM.\n");
221		goto err_pm_get;
222	}
223
224	trng->clk = devm_clk_get_enabled(&pdev->dev, "secss");
225	if (IS_ERR(trng->clk)) {
226		ret = dev_err_probe(&pdev->dev, PTR_ERR(trng->clk),
227				    "Could not get clock\n");
228		goto err_clock;
229	}
230
231	trng->pclk = devm_clk_get_optional_enabled(&pdev->dev, "pclk");
232	if (IS_ERR(trng->pclk)) {
233		ret = dev_err_probe(&pdev->dev, PTR_ERR(trng->pclk),
234				    "Could not get pclk\n");
235		goto err_clock;
236	}
237
238	ret = devm_hwrng_register(&pdev->dev, &trng->rng);
239	if (ret) {
240		dev_err(&pdev->dev, "Could not register hwrng device.\n");
241		goto err_clock;
242	}
243
244	dev_info(&pdev->dev, "Exynos True Random Number Generator.\n");
245
246	return 0;
247
248err_clock:
249	pm_runtime_put_noidle(&pdev->dev);
250
251err_pm_get:
252	pm_runtime_disable(&pdev->dev);
253
254	return ret;
255}
256
257static void exynos_trng_remove(struct platform_device *pdev)
258{
259	struct exynos_trng_dev *trng = platform_get_drvdata(pdev);
260
261	if (trng->flags & EXYNOS_SMC) {
262		struct arm_smccc_res res;
263
264		arm_smccc_smc(SMC_CMD_RANDOM, HWRNG_EXIT, 0, 0, 0, 0, 0, 0,
265			      &res);
266	}
267
268	pm_runtime_put_sync(&pdev->dev);
269	pm_runtime_disable(&pdev->dev);
270}
271
272static int exynos_trng_suspend(struct device *dev)
273{
274	struct exynos_trng_dev *trng = dev_get_drvdata(dev);
275	struct arm_smccc_res res;
276
277	if (trng->flags & EXYNOS_SMC) {
278		arm_smccc_smc(SMC_CMD_RANDOM, HWRNG_EXIT, 0, 0, 0, 0, 0, 0,
279			      &res);
280		if (res.a0 != HWRNG_RET_OK)
281			return -EIO;
282	}
283
284	pm_runtime_put_sync(dev);
285
286	return 0;
287}
288
289static int exynos_trng_resume(struct device *dev)
290{
291	struct exynos_trng_dev *trng = dev_get_drvdata(dev);
292	int ret;
293
294	ret = pm_runtime_resume_and_get(dev);
295	if (ret < 0) {
296		dev_err(dev, "Could not get runtime PM.\n");
297		return ret;
298	}
299
300	if (trng->flags & EXYNOS_SMC) {
301		struct arm_smccc_res res;
302
303		arm_smccc_smc(SMC_CMD_RANDOM, HWRNG_RESUME, 0, 0, 0, 0, 0, 0,
304			      &res);
305		if (res.a0 != HWRNG_RET_OK)
306			return -EIO;
307
308		arm_smccc_smc(SMC_CMD_RANDOM, HWRNG_INIT, 0, 0, 0, 0, 0, 0,
309			      &res);
310		if (res.a0 != HWRNG_RET_OK)
311			return -EIO;
312	}
313
314	return 0;
315}
316
317static DEFINE_SIMPLE_DEV_PM_OPS(exynos_trng_pm_ops, exynos_trng_suspend,
318				exynos_trng_resume);
319
320static const struct of_device_id exynos_trng_dt_match[] = {
321	{
322		.compatible = "samsung,exynos5250-trng",
323	}, {
324		.compatible = "samsung,exynos850-trng",
325		.data = (void *)EXYNOS_SMC,
326	},
327	{ },
328};
329MODULE_DEVICE_TABLE(of, exynos_trng_dt_match);
330
331static struct platform_driver exynos_trng_driver = {
332	.driver = {
333		.name = "exynos-trng",
334		.pm = pm_sleep_ptr(&exynos_trng_pm_ops),
335		.of_match_table = exynos_trng_dt_match,
336	},
337	.probe = exynos_trng_probe,
338	.remove = exynos_trng_remove,
339};
340
341module_platform_driver(exynos_trng_driver);
342
343MODULE_AUTHOR("Łukasz Stelmach");
344MODULE_DESCRIPTION("H/W TRNG driver for Exynos chips");
345MODULE_LICENSE("GPL v2");
~/ Read-only file preview.