aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorScott Branden <sbranden@broadcom.com>2015-03-04 15:42:14 -0500
committerHerbert Xu <herbert@gondor.apana.org.au>2015-03-06 06:51:34 -0500
commitc83d45d5685f63e02f4b038e20450a28232d4da2 (patch)
tree0ca082baef57764833b540cf173e22a1662046be
parent259d317f8d584503c5e3c43bbb96d80149390939 (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/Kconfig13
-rw-r--r--drivers/char/hw_random/Makefile1
-rw-r--r--drivers/char/hw_random/iproc-rng200.c254
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
104config 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
104config HW_RANDOM_GEODE 117config 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
28obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-rng.o 28obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-rng.o
29obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o 29obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o
30obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o 30obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o
31obj-$(CONFIG_HW_RANDOM_IPROC_RNG200) += iproc-rng200.o
31obj-$(CONFIG_HW_RANDOM_MSM) += msm-rng.o 32obj-$(CONFIG_HW_RANDOM_MSM) += msm-rng.o
32obj-$(CONFIG_HW_RANDOM_XGENE) += xgene-rng.o 33obj-$(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
50struct iproc_rng200_dev {
51 void __iomem *base;
52};
53
54static 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
91static 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
152static 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
168static 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
182static 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
189static 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
228static int iproc_rng200_remove(struct platform_device *pdev)
229{
230 /* Unregister driver */
231 hwrng_unregister(&iproc_rng200_ops);
232
233 return 0;
234}
235
236static const struct of_device_id iproc_rng200_of_match[] = {
237 { .compatible = "brcm,iproc-rng200", },
238 {},
239};
240MODULE_DEVICE_TABLE(of, iproc_rng200_of_match);
241
242static 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};
250module_platform_driver(iproc_rng200_driver);
251
252MODULE_AUTHOR("Broadcom");
253MODULE_DESCRIPTION("iProc RNG200 Random Number Generator driver");
254MODULE_LICENSE("GPL v2");