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 | |
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; |
59 | int val; |
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(clk: 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 | int ret = -ENOMEM; |
113 | |
114 | trng = devm_kzalloc(dev: &pdev->dev, size: sizeof(*trng), GFP_KERNEL); |
115 | if (!trng) |
116 | return ret; |
117 | |
118 | trng->rng.name = devm_kstrdup(dev: &pdev->dev, s: dev_name(dev: &pdev->dev), |
119 | GFP_KERNEL); |
120 | if (!trng->rng.name) |
121 | return ret; |
122 | |
123 | trng->rng.init = exynos_trng_init; |
124 | trng->rng.read = exynos_trng_do_read; |
125 | trng->rng.priv = (unsigned long) trng; |
126 | |
127 | platform_set_drvdata(pdev, data: trng); |
128 | trng->dev = &pdev->dev; |
129 | |
130 | trng->mem = devm_platform_ioremap_resource(pdev, index: 0); |
131 | if (IS_ERR(ptr: trng->mem)) |
132 | return PTR_ERR(ptr: trng->mem); |
133 | |
134 | pm_runtime_enable(dev: &pdev->dev); |
135 | ret = pm_runtime_resume_and_get(dev: &pdev->dev); |
136 | if (ret < 0) { |
137 | dev_err(&pdev->dev, "Could not get runtime PM.\n" ); |
138 | goto err_pm_get; |
139 | } |
140 | |
141 | trng->clk = devm_clk_get(dev: &pdev->dev, id: "secss" ); |
142 | if (IS_ERR(ptr: trng->clk)) { |
143 | ret = PTR_ERR(ptr: trng->clk); |
144 | dev_err(&pdev->dev, "Could not get clock.\n" ); |
145 | goto err_clock; |
146 | } |
147 | |
148 | ret = clk_prepare_enable(clk: trng->clk); |
149 | if (ret) { |
150 | dev_err(&pdev->dev, "Could not enable the clk.\n" ); |
151 | goto err_clock; |
152 | } |
153 | |
154 | ret = devm_hwrng_register(dev: &pdev->dev, rng: &trng->rng); |
155 | if (ret) { |
156 | dev_err(&pdev->dev, "Could not register hwrng device.\n" ); |
157 | goto err_register; |
158 | } |
159 | |
160 | dev_info(&pdev->dev, "Exynos True Random Number Generator.\n" ); |
161 | |
162 | return 0; |
163 | |
164 | err_register: |
165 | clk_disable_unprepare(clk: trng->clk); |
166 | |
167 | err_clock: |
168 | pm_runtime_put_noidle(dev: &pdev->dev); |
169 | |
170 | err_pm_get: |
171 | pm_runtime_disable(dev: &pdev->dev); |
172 | |
173 | return ret; |
174 | } |
175 | |
176 | static int exynos_trng_remove(struct platform_device *pdev) |
177 | { |
178 | struct exynos_trng_dev *trng = platform_get_drvdata(pdev); |
179 | |
180 | clk_disable_unprepare(clk: trng->clk); |
181 | |
182 | pm_runtime_put_sync(dev: &pdev->dev); |
183 | pm_runtime_disable(dev: &pdev->dev); |
184 | |
185 | return 0; |
186 | } |
187 | |
188 | static int exynos_trng_suspend(struct device *dev) |
189 | { |
190 | pm_runtime_put_sync(dev); |
191 | |
192 | return 0; |
193 | } |
194 | |
195 | static int exynos_trng_resume(struct device *dev) |
196 | { |
197 | int ret; |
198 | |
199 | ret = pm_runtime_resume_and_get(dev); |
200 | if (ret < 0) { |
201 | dev_err(dev, "Could not get runtime PM.\n" ); |
202 | return ret; |
203 | } |
204 | |
205 | return 0; |
206 | } |
207 | |
208 | static DEFINE_SIMPLE_DEV_PM_OPS(exynos_trng_pm_ops, exynos_trng_suspend, |
209 | exynos_trng_resume); |
210 | |
211 | static const struct of_device_id exynos_trng_dt_match[] = { |
212 | { |
213 | .compatible = "samsung,exynos5250-trng" , |
214 | }, |
215 | { }, |
216 | }; |
217 | MODULE_DEVICE_TABLE(of, exynos_trng_dt_match); |
218 | |
219 | static struct platform_driver exynos_trng_driver = { |
220 | .driver = { |
221 | .name = "exynos-trng" , |
222 | .pm = pm_sleep_ptr(&exynos_trng_pm_ops), |
223 | .of_match_table = exynos_trng_dt_match, |
224 | }, |
225 | .probe = exynos_trng_probe, |
226 | .remove = exynos_trng_remove, |
227 | }; |
228 | |
229 | module_platform_driver(exynos_trng_driver); |
230 | MODULE_AUTHOR("Łukasz Stelmach" ); |
231 | MODULE_DESCRIPTION("H/W TRNG driver for Exynos chips" ); |
232 | MODULE_LICENSE("GPL v2" ); |
233 | |