Commit | Line | Data |
---|---|---|
6cd225cc ŁS |
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> | |
ac316725 | 22 | #include <linux/mod_devicetable.h> |
6cd225cc ŁS |
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 | ||
48 | struct exynos_trng_dev { | |
49 | struct device *dev; | |
50 | void __iomem *mem; | |
51 | struct clk *clk; | |
52 | struct hwrng rng; | |
53 | }; | |
54 | ||
55 | static int exynos_trng_do_read(struct hwrng *rng, void *data, size_t max, | |
56 | bool wait) | |
57 | { | |
58 | struct exynos_trng_dev *trng; | |
a8bc71d4 | 59 | int val; |
6cd225cc ŁS |
60 | |
61 | max = min_t(size_t, max, (EXYNOS_TRNG_FIFO_LEN * 4)); | |
62 | ||
63 | trng = (struct exynos_trng_dev *)rng->priv; | |
64 | ||
65 | writel_relaxed(max * 8, trng->mem + EXYNOS_TRNG_FIFO_CTRL); | |
66 | val = readl_poll_timeout(trng->mem + EXYNOS_TRNG_FIFO_CTRL, val, | |
67 | val == 0, 200, 1000000); | |
68 | if (val < 0) | |
69 | return val; | |
70 | ||
71 | memcpy_fromio(data, trng->mem + EXYNOS_TRNG_FIFO_0, max); | |
72 | ||
73 | return max; | |
74 | } | |
75 | ||
76 | static int exynos_trng_init(struct hwrng *rng) | |
77 | { | |
78 | struct exynos_trng_dev *trng = (struct exynos_trng_dev *)rng->priv; | |
79 | unsigned long sss_rate; | |
80 | u32 val; | |
81 | ||
82 | sss_rate = clk_get_rate(trng->clk); | |
83 | ||
84 | /* | |
85 | * For most TRNG circuits the clock frequency of under 500 kHz | |
86 | * is safe. | |
87 | */ | |
88 | val = sss_rate / (EXYNOS_TRNG_CLOCK_RATE * 2); | |
89 | if (val > 0x7fff) { | |
90 | dev_err(trng->dev, "clock divider too large: %d", val); | |
91 | return -ERANGE; | |
92 | } | |
93 | val = val << 1; | |
94 | writel_relaxed(val, trng->mem + EXYNOS_TRNG_CLKDIV); | |
95 | ||
96 | /* Enable the generator. */ | |
97 | val = EXYNOS_TRNG_CTRL_RNGEN; | |
98 | writel_relaxed(val, trng->mem + EXYNOS_TRNG_CTRL); | |
99 | ||
100 | /* | |
101 | * Disable post-processing. /dev/hwrng is supposed to deliver | |
102 | * unprocessed data. | |
103 | */ | |
104 | writel_relaxed(0, trng->mem + EXYNOS_TRNG_POST_CTRL); | |
105 | ||
106 | return 0; | |
107 | } | |
108 | ||
109 | static int exynos_trng_probe(struct platform_device *pdev) | |
110 | { | |
111 | struct exynos_trng_dev *trng; | |
112 | struct resource *res; | |
113 | int ret = -ENOMEM; | |
114 | ||
115 | trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL); | |
116 | if (!trng) | |
117 | return ret; | |
118 | ||
119 | trng->rng.name = devm_kstrdup(&pdev->dev, dev_name(&pdev->dev), | |
120 | GFP_KERNEL); | |
121 | if (!trng->rng.name) | |
122 | return ret; | |
123 | ||
124 | trng->rng.init = exynos_trng_init; | |
125 | trng->rng.read = exynos_trng_do_read; | |
126 | trng->rng.priv = (unsigned long) trng; | |
127 | ||
128 | platform_set_drvdata(pdev, trng); | |
129 | trng->dev = &pdev->dev; | |
130 | ||
131 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
132 | trng->mem = devm_ioremap_resource(&pdev->dev, res); | |
2273f42d | 133 | if (IS_ERR(trng->mem)) |
6cd225cc | 134 | return PTR_ERR(trng->mem); |
6cd225cc ŁS |
135 | |
136 | pm_runtime_enable(&pdev->dev); | |
137 | ret = pm_runtime_get_sync(&pdev->dev); | |
138 | if (ret < 0) { | |
139 | dev_err(&pdev->dev, "Could not get runtime PM.\n"); | |
140 | goto err_pm_get; | |
141 | } | |
142 | ||
143 | trng->clk = devm_clk_get(&pdev->dev, "secss"); | |
144 | if (IS_ERR(trng->clk)) { | |
145 | ret = PTR_ERR(trng->clk); | |
146 | dev_err(&pdev->dev, "Could not get clock.\n"); | |
147 | goto err_clock; | |
148 | } | |
149 | ||
150 | ret = clk_prepare_enable(trng->clk); | |
151 | if (ret) { | |
152 | dev_err(&pdev->dev, "Could not enable the clk.\n"); | |
153 | goto err_clock; | |
154 | } | |
155 | ||
156 | ret = hwrng_register(&trng->rng); | |
157 | if (ret) { | |
158 | dev_err(&pdev->dev, "Could not register hwrng device.\n"); | |
159 | goto err_register; | |
160 | } | |
161 | ||
162 | dev_info(&pdev->dev, "Exynos True Random Number Generator.\n"); | |
163 | ||
164 | return 0; | |
165 | ||
166 | err_register: | |
167 | clk_disable_unprepare(trng->clk); | |
168 | ||
169 | err_clock: | |
170 | pm_runtime_put_sync(&pdev->dev); | |
171 | ||
172 | err_pm_get: | |
173 | pm_runtime_disable(&pdev->dev); | |
174 | ||
175 | return ret; | |
176 | } | |
177 | ||
178 | static int exynos_trng_remove(struct platform_device *pdev) | |
179 | { | |
180 | struct exynos_trng_dev *trng = platform_get_drvdata(pdev); | |
181 | ||
182 | hwrng_unregister(&trng->rng); | |
183 | clk_disable_unprepare(trng->clk); | |
184 | ||
185 | pm_runtime_put_sync(&pdev->dev); | |
186 | pm_runtime_disable(&pdev->dev); | |
187 | ||
188 | return 0; | |
189 | } | |
190 | ||
191 | static int __maybe_unused exynos_trng_suspend(struct device *dev) | |
192 | { | |
193 | pm_runtime_put_sync(dev); | |
194 | ||
195 | return 0; | |
196 | } | |
197 | ||
198 | static int __maybe_unused exynos_trng_resume(struct device *dev) | |
199 | { | |
200 | int ret; | |
201 | ||
202 | ret = pm_runtime_get_sync(dev); | |
203 | if (ret < 0) { | |
204 | dev_err(dev, "Could not get runtime PM.\n"); | |
205 | pm_runtime_put_noidle(dev); | |
206 | return ret; | |
207 | } | |
208 | ||
209 | return 0; | |
210 | } | |
211 | ||
212 | static SIMPLE_DEV_PM_OPS(exynos_trng_pm_ops, exynos_trng_suspend, | |
213 | exynos_trng_resume); | |
214 | ||
215 | static const struct of_device_id exynos_trng_dt_match[] = { | |
216 | { | |
217 | .compatible = "samsung,exynos5250-trng", | |
218 | }, | |
219 | { }, | |
220 | }; | |
221 | MODULE_DEVICE_TABLE(of, exynos_trng_dt_match); | |
222 | ||
223 | static struct platform_driver exynos_trng_driver = { | |
224 | .driver = { | |
225 | .name = "exynos-trng", | |
226 | .pm = &exynos_trng_pm_ops, | |
227 | .of_match_table = exynos_trng_dt_match, | |
228 | }, | |
229 | .probe = exynos_trng_probe, | |
230 | .remove = exynos_trng_remove, | |
231 | }; | |
232 | ||
233 | module_platform_driver(exynos_trng_driver); | |
234 | MODULE_AUTHOR("Łukasz Stelmach"); | |
235 | MODULE_DESCRIPTION("H/W TRNG driver for Exynos chips"); | |
236 | MODULE_LICENSE("GPL v2"); |