xref: /linux/drivers/char/hw_random/exynos-trng.c (revision 76536caabedbeafb01d364690914c88ebadab5b3)
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/clk.h>
14 #include <linux/crypto.h>
15 #include <linux/delay.h>
16 #include <linux/err.h>
17 #include <linux/hw_random.h>
18 #include <linux/io.h>
19 #include <linux/iopoll.h>
20 #include <linux/kernel.h>
21 #include <linux/module.h>
22 #include <linux/mod_devicetable.h>
23 #include <linux/platform_device.h>
24 #include <linux/pm_runtime.h>
25 
26 #define EXYNOS_TRNG_CLKDIV		0x0
27 
28 #define EXYNOS_TRNG_CTRL		0x20
29 #define EXYNOS_TRNG_CTRL_RNGEN		BIT(31)
30 
31 #define EXYNOS_TRNG_POST_CTRL		0x30
32 #define EXYNOS_TRNG_ONLINE_CTRL		0x40
33 #define EXYNOS_TRNG_ONLINE_STAT		0x44
34 #define EXYNOS_TRNG_ONLINE_MAXCHI2	0x48
35 #define EXYNOS_TRNG_FIFO_CTRL		0x50
36 #define EXYNOS_TRNG_FIFO_0		0x80
37 #define EXYNOS_TRNG_FIFO_1		0x84
38 #define EXYNOS_TRNG_FIFO_2		0x88
39 #define EXYNOS_TRNG_FIFO_3		0x8c
40 #define EXYNOS_TRNG_FIFO_4		0x90
41 #define EXYNOS_TRNG_FIFO_5		0x94
42 #define EXYNOS_TRNG_FIFO_6		0x98
43 #define EXYNOS_TRNG_FIFO_7		0x9c
44 #define EXYNOS_TRNG_FIFO_LEN		8
45 #define EXYNOS_TRNG_CLOCK_RATE		500000
46 
47 struct exynos_trng_dev {
48 	struct device	*dev;
49 	void __iomem	*mem;
50 	struct clk	*clk;
51 	struct hwrng	rng;
52 };
53 
54 static int exynos_trng_do_read(struct hwrng *rng, void *data, size_t max,
55 			       bool wait)
56 {
57 	struct exynos_trng_dev *trng = (struct exynos_trng_dev *)rng->priv;
58 	int val;
59 
60 	max = min_t(size_t, max, (EXYNOS_TRNG_FIFO_LEN * 4));
61 	writel_relaxed(max * 8, trng->mem + EXYNOS_TRNG_FIFO_CTRL);
62 	val = readl_poll_timeout(trng->mem + EXYNOS_TRNG_FIFO_CTRL, val,
63 				 val == 0, 200, 1000000);
64 	if (val < 0)
65 		return val;
66 
67 	memcpy_fromio(data, trng->mem + EXYNOS_TRNG_FIFO_0, max);
68 
69 	return max;
70 }
71 
72 static int exynos_trng_init(struct hwrng *rng)
73 {
74 	struct exynos_trng_dev *trng = (struct exynos_trng_dev *)rng->priv;
75 	unsigned long sss_rate;
76 	u32 val;
77 
78 	sss_rate = clk_get_rate(trng->clk);
79 
80 	/*
81 	 * For most TRNG circuits the clock frequency of under 500 kHz
82 	 * is safe.
83 	 */
84 	val = sss_rate / (EXYNOS_TRNG_CLOCK_RATE * 2);
85 	if (val > 0x7fff) {
86 		dev_err(trng->dev, "clock divider too large: %d\n", val);
87 		return -ERANGE;
88 	}
89 	val = val << 1;
90 	writel_relaxed(val, trng->mem + EXYNOS_TRNG_CLKDIV);
91 
92 	/* Enable the generator. */
93 	val = EXYNOS_TRNG_CTRL_RNGEN;
94 	writel_relaxed(val, trng->mem + EXYNOS_TRNG_CTRL);
95 
96 	/*
97 	 * Disable post-processing. /dev/hwrng is supposed to deliver
98 	 * unprocessed data.
99 	 */
100 	writel_relaxed(0, trng->mem + EXYNOS_TRNG_POST_CTRL);
101 
102 	return 0;
103 }
104 
105 static int exynos_trng_probe(struct platform_device *pdev)
106 {
107 	struct exynos_trng_dev *trng;
108 	int ret = -ENOMEM;
109 
110 	trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
111 	if (!trng)
112 		return ret;
113 
114 	trng->rng.name = devm_kstrdup(&pdev->dev, dev_name(&pdev->dev),
115 				      GFP_KERNEL);
116 	if (!trng->rng.name)
117 		return ret;
118 
119 	trng->rng.init = exynos_trng_init;
120 	trng->rng.read = exynos_trng_do_read;
121 	trng->rng.priv = (unsigned long)trng;
122 
123 	platform_set_drvdata(pdev, trng);
124 	trng->dev = &pdev->dev;
125 
126 	trng->mem = devm_platform_ioremap_resource(pdev, 0);
127 	if (IS_ERR(trng->mem))
128 		return PTR_ERR(trng->mem);
129 
130 	pm_runtime_enable(&pdev->dev);
131 	ret = pm_runtime_resume_and_get(&pdev->dev);
132 	if (ret < 0) {
133 		dev_err(&pdev->dev, "Could not get runtime PM.\n");
134 		goto err_pm_get;
135 	}
136 
137 	trng->clk = devm_clk_get(&pdev->dev, "secss");
138 	if (IS_ERR(trng->clk)) {
139 		ret = PTR_ERR(trng->clk);
140 		dev_err(&pdev->dev, "Could not get clock.\n");
141 		goto err_clock;
142 	}
143 
144 	ret = clk_prepare_enable(trng->clk);
145 	if (ret) {
146 		dev_err(&pdev->dev, "Could not enable the clk.\n");
147 		goto err_clock;
148 	}
149 
150 	ret = devm_hwrng_register(&pdev->dev, &trng->rng);
151 	if (ret) {
152 		dev_err(&pdev->dev, "Could not register hwrng device.\n");
153 		goto err_register;
154 	}
155 
156 	dev_info(&pdev->dev, "Exynos True Random Number Generator.\n");
157 
158 	return 0;
159 
160 err_register:
161 	clk_disable_unprepare(trng->clk);
162 
163 err_clock:
164 	pm_runtime_put_noidle(&pdev->dev);
165 
166 err_pm_get:
167 	pm_runtime_disable(&pdev->dev);
168 
169 	return ret;
170 }
171 
172 static void exynos_trng_remove(struct platform_device *pdev)
173 {
174 	struct exynos_trng_dev *trng = platform_get_drvdata(pdev);
175 
176 	clk_disable_unprepare(trng->clk);
177 
178 	pm_runtime_put_sync(&pdev->dev);
179 	pm_runtime_disable(&pdev->dev);
180 }
181 
182 static int exynos_trng_suspend(struct device *dev)
183 {
184 	pm_runtime_put_sync(dev);
185 
186 	return 0;
187 }
188 
189 static int exynos_trng_resume(struct device *dev)
190 {
191 	int ret;
192 
193 	ret = pm_runtime_resume_and_get(dev);
194 	if (ret < 0) {
195 		dev_err(dev, "Could not get runtime PM.\n");
196 		return ret;
197 	}
198 
199 	return 0;
200 }
201 
202 static DEFINE_SIMPLE_DEV_PM_OPS(exynos_trng_pm_ops, exynos_trng_suspend,
203 				exynos_trng_resume);
204 
205 static const struct of_device_id exynos_trng_dt_match[] = {
206 	{
207 		.compatible = "samsung,exynos5250-trng",
208 	},
209 	{ },
210 };
211 MODULE_DEVICE_TABLE(of, exynos_trng_dt_match);
212 
213 static struct platform_driver exynos_trng_driver = {
214 	.driver = {
215 		.name = "exynos-trng",
216 		.pm = pm_sleep_ptr(&exynos_trng_pm_ops),
217 		.of_match_table = exynos_trng_dt_match,
218 	},
219 	.probe = exynos_trng_probe,
220 	.remove_new = exynos_trng_remove,
221 };
222 
223 module_platform_driver(exynos_trng_driver);
224 
225 MODULE_AUTHOR("Łukasz Stelmach");
226 MODULE_DESCRIPTION("H/W TRNG driver for Exynos chips");
227 MODULE_LICENSE("GPL v2");
228