diff options
author | Scott Branden <sbranden@broadcom.com> | 2015-03-04 15:42:14 -0500 |
---|---|---|
committer | Herbert Xu <herbert@gondor.apana.org.au> | 2015-03-06 06:51:34 -0500 |
commit | c83d45d5685f63e02f4b038e20450a28232d4da2 (patch) | |
tree | 0ca082baef57764833b540cf173e22a1662046be | |
parent | 259d317f8d584503c5e3c43bbb96d80149390939 (diff) |
hwrng: iproc-rng200 - Add Broadcom IPROC RNG driver
This adds a driver for random number generator present on Broadcom
IPROC devices.
Reviewed-by: Ray Jui <rjui@broadcom.com>
Signed-off-by: Scott Branden <sbranden@broadcom.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
-rw-r--r-- | drivers/char/hw_random/Kconfig | 13 | ||||
-rw-r--r-- | drivers/char/hw_random/Makefile | 1 | ||||
-rw-r--r-- | drivers/char/hw_random/iproc-rng200.c | 254 |
3 files changed, 268 insertions, 0 deletions
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index de57b38809c7..f48cf11c655e 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig | |||
@@ -101,6 +101,19 @@ config HW_RANDOM_BCM2835 | |||
101 | 101 | ||
102 | If unsure, say Y. | 102 | If unsure, say Y. |
103 | 103 | ||
104 | config HW_RANDOM_IPROC_RNG200 | ||
105 | tristate "Broadcom iProc RNG200 support" | ||
106 | depends on ARCH_BCM_IPROC | ||
107 | default HW_RANDOM | ||
108 | ---help--- | ||
109 | This driver provides kernel-side support for the RNG200 | ||
110 | hardware found on the Broadcom iProc SoCs. | ||
111 | |||
112 | To compile this driver as a module, choose M here: the | ||
113 | module will be called iproc-rng200 | ||
114 | |||
115 | If unsure, say Y. | ||
116 | |||
104 | config HW_RANDOM_GEODE | 117 | config HW_RANDOM_GEODE |
105 | tristate "AMD Geode HW Random Number Generator support" | 118 | tristate "AMD Geode HW Random Number Generator support" |
106 | depends on X86_32 && PCI | 119 | depends on X86_32 && PCI |
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index 0b4cd57f4e24..055bb01510ad 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile | |||
@@ -28,5 +28,6 @@ obj-$(CONFIG_HW_RANDOM_POWERNV) += powernv-rng.o | |||
28 | obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-rng.o | 28 | obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-rng.o |
29 | obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o | 29 | obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o |
30 | obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o | 30 | obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o |
31 | obj-$(CONFIG_HW_RANDOM_IPROC_RNG200) += iproc-rng200.o | ||
31 | obj-$(CONFIG_HW_RANDOM_MSM) += msm-rng.o | 32 | obj-$(CONFIG_HW_RANDOM_MSM) += msm-rng.o |
32 | obj-$(CONFIG_HW_RANDOM_XGENE) += xgene-rng.o | 33 | obj-$(CONFIG_HW_RANDOM_XGENE) += xgene-rng.o |
diff --git a/drivers/char/hw_random/iproc-rng200.c b/drivers/char/hw_random/iproc-rng200.c new file mode 100644 index 000000000000..276cb8a93bac --- /dev/null +++ b/drivers/char/hw_random/iproc-rng200.c | |||
@@ -0,0 +1,254 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2015 Broadcom Corporation | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License as | ||
6 | * published by the Free Software Foundation version 2. | ||
7 | * | ||
8 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | ||
9 | * kind, whether express or implied; without even the implied warranty | ||
10 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | /* | ||
14 | * DESCRIPTION: The Broadcom iProc RNG200 Driver | ||
15 | */ | ||
16 | |||
17 | #include <linux/hw_random.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/io.h> | ||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/of_address.h> | ||
23 | #include <linux/of_platform.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | #include <linux/delay.h> | ||
26 | |||
27 | /* Registers */ | ||
28 | #define RNG_CTRL_OFFSET 0x00 | ||
29 | #define RNG_CTRL_RNG_RBGEN_MASK 0x00001FFF | ||
30 | #define RNG_CTRL_RNG_RBGEN_ENABLE 0x00000001 | ||
31 | #define RNG_CTRL_RNG_RBGEN_DISABLE 0x00000000 | ||
32 | |||
33 | #define RNG_SOFT_RESET_OFFSET 0x04 | ||
34 | #define RNG_SOFT_RESET 0x00000001 | ||
35 | |||
36 | #define RBG_SOFT_RESET_OFFSET 0x08 | ||
37 | #define RBG_SOFT_RESET 0x00000001 | ||
38 | |||
39 | #define RNG_INT_STATUS_OFFSET 0x18 | ||
40 | #define RNG_INT_STATUS_MASTER_FAIL_LOCKOUT_IRQ_MASK 0x80000000 | ||
41 | #define RNG_INT_STATUS_STARTUP_TRANSITIONS_MET_IRQ_MASK 0x00020000 | ||
42 | #define RNG_INT_STATUS_NIST_FAIL_IRQ_MASK 0x00000020 | ||
43 | #define RNG_INT_STATUS_TOTAL_BITS_COUNT_IRQ_MASK 0x00000001 | ||
44 | |||
45 | #define RNG_FIFO_DATA_OFFSET 0x20 | ||
46 | |||
47 | #define RNG_FIFO_COUNT_OFFSET 0x24 | ||
48 | #define RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK 0x000000FF | ||
49 | |||
50 | struct iproc_rng200_dev { | ||
51 | void __iomem *base; | ||
52 | }; | ||
53 | |||
54 | static void iproc_rng200_restart(void __iomem *rng_base) | ||
55 | { | ||
56 | uint32_t val; | ||
57 | |||
58 | /* Disable RBG */ | ||
59 | val = ioread32(rng_base + RNG_CTRL_OFFSET); | ||
60 | val &= ~RNG_CTRL_RNG_RBGEN_MASK; | ||
61 | val |= RNG_CTRL_RNG_RBGEN_DISABLE; | ||
62 | iowrite32(val, rng_base + RNG_CTRL_OFFSET); | ||
63 | |||
64 | /* Clear all interrupt status */ | ||
65 | iowrite32(0xFFFFFFFFUL, rng_base + RNG_INT_STATUS_OFFSET); | ||
66 | |||
67 | /* Reset RNG and RBG */ | ||
68 | val = ioread32(rng_base + RBG_SOFT_RESET_OFFSET); | ||
69 | val |= RBG_SOFT_RESET; | ||
70 | iowrite32(val, rng_base + RBG_SOFT_RESET_OFFSET); | ||
71 | |||
72 | val = ioread32(rng_base + RNG_SOFT_RESET_OFFSET); | ||
73 | val |= RNG_SOFT_RESET; | ||
74 | iowrite32(val, rng_base + RNG_SOFT_RESET_OFFSET); | ||
75 | |||
76 | val = ioread32(rng_base + RNG_SOFT_RESET_OFFSET); | ||
77 | val &= ~RNG_SOFT_RESET; | ||
78 | iowrite32(val, rng_base + RNG_SOFT_RESET_OFFSET); | ||
79 | |||
80 | val = ioread32(rng_base + RBG_SOFT_RESET_OFFSET); | ||
81 | val &= ~RBG_SOFT_RESET; | ||
82 | iowrite32(val, rng_base + RBG_SOFT_RESET_OFFSET); | ||
83 | |||
84 | /* Enable RBG */ | ||
85 | val = ioread32(rng_base + RNG_CTRL_OFFSET); | ||
86 | val &= ~RNG_CTRL_RNG_RBGEN_MASK; | ||
87 | val |= RNG_CTRL_RNG_RBGEN_ENABLE; | ||
88 | iowrite32(val, rng_base + RNG_CTRL_OFFSET); | ||
89 | } | ||
90 | |||
91 | static int iproc_rng200_read(struct hwrng *rng, void *buf, size_t max, | ||
92 | bool wait) | ||
93 | { | ||
94 | uint32_t status; | ||
95 | uint32_t num_remaining = max; | ||
96 | struct iproc_rng200_dev *priv = (struct iproc_rng200_dev *)rng->priv; | ||
97 | |||
98 | #define MAX_RESETS_PER_READ 1 | ||
99 | uint32_t num_resets = 0; | ||
100 | |||
101 | #define MAX_IDLE_TIME (1 * HZ) | ||
102 | unsigned long idle_endtime = jiffies + MAX_IDLE_TIME; | ||
103 | |||
104 | while ((num_remaining > 0) && time_before(jiffies, idle_endtime)) { | ||
105 | |||
106 | /* Is RNG sane? If not, reset it. */ | ||
107 | status = ioread32(priv->base + RNG_INT_STATUS_OFFSET); | ||
108 | if ((status & (RNG_INT_STATUS_MASTER_FAIL_LOCKOUT_IRQ_MASK | | ||
109 | RNG_INT_STATUS_NIST_FAIL_IRQ_MASK)) != 0) { | ||
110 | |||
111 | if (num_resets >= MAX_RESETS_PER_READ) | ||
112 | return max - num_remaining; | ||
113 | |||
114 | iproc_rng200_restart(priv->base); | ||
115 | num_resets++; | ||
116 | } | ||
117 | |||
118 | /* Are there any random numbers available? */ | ||
119 | if ((ioread32(priv->base + RNG_FIFO_COUNT_OFFSET) & | ||
120 | RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK) > 0) { | ||
121 | |||
122 | if (num_remaining >= sizeof(uint32_t)) { | ||
123 | /* Buffer has room to store entire word */ | ||
124 | *(uint32_t *)buf = ioread32(priv->base + | ||
125 | RNG_FIFO_DATA_OFFSET); | ||
126 | buf += sizeof(uint32_t); | ||
127 | num_remaining -= sizeof(uint32_t); | ||
128 | } else { | ||
129 | /* Buffer can only store partial word */ | ||
130 | uint32_t rnd_number = ioread32(priv->base + | ||
131 | RNG_FIFO_DATA_OFFSET); | ||
132 | memcpy(buf, &rnd_number, num_remaining); | ||
133 | buf += num_remaining; | ||
134 | num_remaining = 0; | ||
135 | } | ||
136 | |||
137 | /* Reset the IDLE timeout */ | ||
138 | idle_endtime = jiffies + MAX_IDLE_TIME; | ||
139 | } else { | ||
140 | if (!wait) | ||
141 | /* Cannot wait, return immediately */ | ||
142 | return max - num_remaining; | ||
143 | |||
144 | /* Can wait, give others chance to run */ | ||
145 | usleep_range(min(num_remaining * 10, 500U), 500); | ||
146 | } | ||
147 | } | ||
148 | |||
149 | return max - num_remaining; | ||
150 | } | ||
151 | |||
152 | static int iproc_rng200_init(struct hwrng *rng) | ||
153 | { | ||
154 | uint32_t val; | ||
155 | struct iproc_rng200_dev *priv; | ||
156 | |||
157 | priv = (struct iproc_rng200_dev *)rng->priv; | ||
158 | |||
159 | /* Setup RNG. */ | ||
160 | val = ioread32(priv->base + RNG_CTRL_OFFSET); | ||
161 | val &= ~RNG_CTRL_RNG_RBGEN_MASK; | ||
162 | val |= RNG_CTRL_RNG_RBGEN_ENABLE; | ||
163 | iowrite32(val, priv->base + RNG_CTRL_OFFSET); | ||
164 | |||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | static void iproc_rng200_cleanup(struct hwrng *rng) | ||
169 | { | ||
170 | uint32_t val; | ||
171 | struct iproc_rng200_dev *priv; | ||
172 | |||
173 | priv = (struct iproc_rng200_dev *)rng->priv; | ||
174 | |||
175 | /* Disable RNG hardware */ | ||
176 | val = ioread32(priv->base + RNG_CTRL_OFFSET); | ||
177 | val &= ~RNG_CTRL_RNG_RBGEN_MASK; | ||
178 | val |= RNG_CTRL_RNG_RBGEN_DISABLE; | ||
179 | iowrite32(val, priv->base + RNG_CTRL_OFFSET); | ||
180 | } | ||
181 | |||
182 | static struct hwrng iproc_rng200_ops = { | ||
183 | .name = "iproc-rng200", | ||
184 | .read = iproc_rng200_read, | ||
185 | .init = iproc_rng200_init, | ||
186 | .cleanup = iproc_rng200_cleanup, | ||
187 | }; | ||
188 | |||
189 | static int iproc_rng200_probe(struct platform_device *pdev) | ||
190 | { | ||
191 | struct iproc_rng200_dev *priv; | ||
192 | struct resource *res; | ||
193 | struct device *dev = &pdev->dev; | ||
194 | int ret; | ||
195 | |||
196 | priv = devm_kzalloc(dev, sizeof(struct iproc_rng200_dev), GFP_KERNEL); | ||
197 | if (!priv) | ||
198 | return -ENOMEM; | ||
199 | |||
200 | iproc_rng200_ops.priv = (unsigned long)priv; | ||
201 | platform_set_drvdata(pdev, priv); | ||
202 | |||
203 | /* Map peripheral */ | ||
204 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
205 | if (!res) { | ||
206 | dev_err(dev, "failed to get rng resources\n"); | ||
207 | return -EINVAL; | ||
208 | } | ||
209 | |||
210 | priv->base = devm_ioremap_resource(dev, res); | ||
211 | if (IS_ERR(priv->base)) { | ||
212 | dev_err(dev, "failed to remap rng regs\n"); | ||
213 | return PTR_ERR(priv->base); | ||
214 | } | ||
215 | |||
216 | /* Register driver */ | ||
217 | ret = hwrng_register(&iproc_rng200_ops); | ||
218 | if (ret) { | ||
219 | dev_err(dev, "hwrng registration failed\n"); | ||
220 | return ret; | ||
221 | } | ||
222 | |||
223 | dev_info(dev, "hwrng registered\n"); | ||
224 | |||
225 | return 0; | ||
226 | } | ||
227 | |||
228 | static int iproc_rng200_remove(struct platform_device *pdev) | ||
229 | { | ||
230 | /* Unregister driver */ | ||
231 | hwrng_unregister(&iproc_rng200_ops); | ||
232 | |||
233 | return 0; | ||
234 | } | ||
235 | |||
236 | static const struct of_device_id iproc_rng200_of_match[] = { | ||
237 | { .compatible = "brcm,iproc-rng200", }, | ||
238 | {}, | ||
239 | }; | ||
240 | MODULE_DEVICE_TABLE(of, iproc_rng200_of_match); | ||
241 | |||
242 | static struct platform_driver iproc_rng200_driver = { | ||
243 | .driver = { | ||
244 | .name = "iproc-rng200", | ||
245 | .of_match_table = iproc_rng200_of_match, | ||
246 | }, | ||
247 | .probe = iproc_rng200_probe, | ||
248 | .remove = iproc_rng200_remove, | ||
249 | }; | ||
250 | module_platform_driver(iproc_rng200_driver); | ||
251 | |||
252 | MODULE_AUTHOR("Broadcom"); | ||
253 | MODULE_DESCRIPTION("iProc RNG200 Random Number Generator driver"); | ||
254 | MODULE_LICENSE("GPL v2"); | ||