aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/hw_random
diff options
context:
space:
mode:
authorDan Douglass <dan.douglass@freescale.com>2014-02-20 12:25:56 -0500
committerNitin Garg <nitin.garg@freescale.com>2014-04-16 09:57:35 -0400
commitfcc6fc800470df5651b5339c2521340fbc039860 (patch)
treed1ac8be1dfbb6ee6241329d751641a108efb2baf /drivers/char/hw_random
parent43bf6edb7035d3c9a2c02fb3f0359ca640ec8071 (diff)
ENGR00292341 imx6sl hwrng
Add hwrng support for i.MX6SL. 1. Add RNG driver. This driver originated as fsl-rngc.c. It has been modified to support device tree. The name has been changed since it supports both b and c variants of RNG. 2. Added clock and compatible info to the device tree data. 3. Added the entry in the options in the Kconfig for hwrng. Signed-off-by: Dan Douglass <dan.douglass@freescale.com>
Diffstat (limited to 'drivers/char/hw_random')
-rw-r--r--drivers/char/hw_random/Kconfig12
-rw-r--r--drivers/char/hw_random/Makefile1
-rw-r--r--drivers/char/hw_random/imx-rng.c440
3 files changed, 453 insertions, 0 deletions
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index 2f9dbf7568fb..1bd1603a276d 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -226,6 +226,18 @@ config HW_RANDOM_MXC_RNGA
226 226
227 If unsure, say Y. 227 If unsure, say Y.
228 228
229config HW_RANDOM_IMX_RNG
230 tristate "Freescale RNG B/C Random Number Generator"
231 depends on HW_RANDOM && ARCH_MXC && HAVE_IMX_RNG
232 ---help---
233 This driver provides kernel-side support for the Random Number
234 Generator (RNGBB and RNGC) hardware found on Freescale i.MX processors.
235
236 To compile this driver as a module, choose M here: the
237 module will be called fsl-rngc.
238
239 If unsure, say Y.
240
229config HW_RANDOM_NOMADIK 241config HW_RANDOM_NOMADIK
230 tristate "ST-Ericsson Nomadik Random Number Generator support" 242 tristate "ST-Ericsson Nomadik Random Number Generator support"
231 depends on HW_RANDOM && ARCH_NOMADIK 243 depends on HW_RANDOM && ARCH_NOMADIK
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
index bed467c9300e..face64c5fcf0 100644
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_HW_RANDOM_PASEMI) += pasemi-rng.o
19obj-$(CONFIG_HW_RANDOM_VIRTIO) += virtio-rng.o 19obj-$(CONFIG_HW_RANDOM_VIRTIO) += virtio-rng.o
20obj-$(CONFIG_HW_RANDOM_TX4939) += tx4939-rng.o 20obj-$(CONFIG_HW_RANDOM_TX4939) += tx4939-rng.o
21obj-$(CONFIG_HW_RANDOM_MXC_RNGA) += mxc-rnga.o 21obj-$(CONFIG_HW_RANDOM_MXC_RNGA) += mxc-rnga.o
22obj-$(CONFIG_HW_RANDOM_IMX_RNG) += imx-rng.o
22obj-$(CONFIG_HW_RANDOM_OCTEON) += octeon-rng.o 23obj-$(CONFIG_HW_RANDOM_OCTEON) += octeon-rng.o
23obj-$(CONFIG_HW_RANDOM_NOMADIK) += nomadik-rng.o 24obj-$(CONFIG_HW_RANDOM_NOMADIK) += nomadik-rng.o
24obj-$(CONFIG_HW_RANDOM_PICOXCELL) += picoxcell-rng.o 25obj-$(CONFIG_HW_RANDOM_PICOXCELL) += picoxcell-rng.o
diff --git a/drivers/char/hw_random/imx-rng.c b/drivers/char/hw_random/imx-rng.c
new file mode 100644
index 000000000000..f1d7bd5d91cf
--- /dev/null
+++ b/drivers/char/hw_random/imx-rng.c
@@ -0,0 +1,440 @@
1/*
2 * RNG driver for Freescale RNG B/C
3 *
4 * Copyright (C) 2008-2014 Freescale Semiconductor, Inc.
5 */
6
7/*
8 * The code contained herein is licensed under the GNU General Public
9 * License. You may obtain a copy of the GNU General Public License
10 * Version 2 or later at the following locations:
11 *
12 * http://www.opensource.org/licenses/gpl-license.html
13 * http://www.gnu.org/copyleft/gpl.html
14 */
15
16/*
17 * Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
18 * (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
19 *
20 * derived from
21 *
22 * Hardware driver for the AMD 768 Random Number Generator (RNG)
23 * (c) Copyright 2001 Red Hat Inc <alan@redhat.com>
24 *
25 * derived from
26 *
27 * Hardware driver for Intel i810 Random Number Generator (RNG)
28 * Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
29 * Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
30 *
31 * This file is licensed under the terms of the GNU General Public
32 * License version 2. This program is licensed "as is" without any
33 * warranty of any kind, whether express or implied.
34 */
35
36#include <linux/module.h>
37#include <linux/init.h>
38#include <linux/kernel.h>
39#include <linux/clk.h>
40#include <linux/err.h>
41#include <linux/platform_device.h>
42#include <linux/of_address.h>
43#include <linux/interrupt.h>
44#include <linux/hw_random.h>
45#include <linux/io.h>
46#include <linux/slab.h>
47
48#define MODULE_NAME "imx-rng"
49
50#define RNGC_VERSION_MAJOR3 3
51
52#define RNGC_VERSION_ID 0x0000
53#define RNGC_COMMAND 0x0004
54#define RNGC_CONTROL 0x0008
55#define RNGC_STATUS 0x000C
56#define RNGC_ERROR 0x0010
57#define RNGC_FIFO 0x0014
58#define RNGC_VERIF_CTRL 0x0020
59#define RNGC_OSC_CTRL_COUNT 0x0028
60#define RNGC_OSC_COUNT 0x002C
61#define RNGC_OSC_COUNT_STATUS 0x0030
62
63#define RNGC_VERID_ZEROS_MASK 0x0f000000
64#define RNGC_VERID_RNG_TYPE_MASK 0xf0000000
65#define RNGC_VERID_RNG_TYPE_SHIFT 28
66#define RNGC_VERID_CHIP_VERSION_MASK 0x00ff0000
67#define RNGC_VERID_CHIP_VERSION_SHIFT 16
68#define RNGC_VERID_VERSION_MAJOR_MASK 0x0000ff00
69#define RNGC_VERID_VERSION_MAJOR_SHIFT 8
70#define RNGC_VERID_VERSION_MINOR_MASK 0x000000ff
71#define RNGC_VERID_VERSION_MINOR_SHIFT 0
72
73#define RNGC_CMD_ZEROS_MASK 0xffffff8c
74#define RNGC_CMD_SW_RST 0x00000040
75#define RNGC_CMD_CLR_ERR 0x00000020
76#define RNGC_CMD_CLR_INT 0x00000010
77#define RNGC_CMD_SEED 0x00000002
78#define RNGC_CMD_SELF_TEST 0x00000001
79
80#define RNGC_CTRL_ZEROS_MASK 0xfffffc8c
81#define RNGC_CTRL_CTL_ACC 0x00000200
82#define RNGC_CTRL_VERIF_MODE 0x00000100
83#define RNGC_CTRL_MASK_ERROR 0x00000040
84
85#define RNGC_CTRL_MASK_DONE 0x00000020
86#define RNGC_CTRL_AUTO_SEED 0x00000010
87#define RNGC_CTRL_FIFO_UFLOW_MASK 0x00000003
88#define RNGC_CTRL_FIFO_UFLOW_SHIFT 0
89
90#define RNGC_CTRL_FIFO_UFLOW_ZEROS_ERROR 0
91#define RNGC_CTRL_FIFO_UFLOW_ZEROS_ERROR2 1
92#define RNGC_CTRL_FIFO_UFLOW_BUS_XFR 2
93#define RNGC_CTRL_FIFO_UFLOW_ZEROS_INTR 3
94
95#define RNGC_STATUS_ST_PF_MASK 0x00c00000
96#define RNGC_STATUS_ST_PF_SHIFT 22
97#define RNGC_STATUS_ST_PF_TRNG 0x00800000
98#define RNGC_STATUS_ST_PF_PRNG 0x00400000
99#define RNGC_STATUS_ERROR 0x00010000
100#define RNGC_STATUS_FIFO_SIZE_MASK 0x0000f000
101#define RNGC_STATUS_FIFO_SIZE_SHIFT 12
102#define RNGC_STATUS_FIFO_LEVEL_MASK 0x00000f00
103#define RNGC_STATUS_FIFO_LEVEL_SHIFT 8
104#define RNGC_STATUS_NEXT_SEED_DONE 0x00000040
105#define RNGC_STATUS_SEED_DONE 0x00000020
106#define RNGC_STATUS_ST_DONE 0x00000010
107#define RNGC_STATUS_RESEED 0x00000008
108#define RNGC_STATUS_SLEEP 0x00000004
109#define RNGC_STATUS_BUSY 0x00000002
110#define RNGC_STATUS_SEC_STATE 0x00000001
111
112#define RNGC_ERROR_STATUS_ZEROS_MASK 0xffffffc0
113#define RNGC_ERROR_STATUS_BAD_KEY 0x00000040
114#define RNGC_ERROR_STATUS_RAND_ERR 0x00000020
115#define RNGC_ERROR_STATUS_FIFO_ERR 0x00000010
116#define RNGC_ERROR_STATUS_STAT_ERR 0x00000008
117#define RNGC_ERROR_STATUS_ST_ERR 0x00000004
118#define RNGC_ERROR_STATUS_OSC_ERR 0x00000002
119#define RNGC_ERROR_STATUS_LFSR_ERR 0x00000001
120
121#define RNG_ADDR_RANGE 0x34
122
123static DECLARE_COMPLETION(rng_self_testing);
124static DECLARE_COMPLETION(rng_seed_done);
125
126static struct platform_device *imx_rng_dev;
127
128struct imx_rng_priv_data {
129 void __iomem *reg_base;
130};
131
132int irq_rng;
133
134static int imx_rng_data_present(struct hwrng *rng, int wait)
135{
136 int level;
137 struct imx_rng_priv_data *prv = (struct imx_rng_priv_data *)rng->priv;
138
139 /* how many random numbers are in FIFO? [0-16] */
140 level = (readl(prv->reg_base + RNGC_STATUS) &
141 RNGC_STATUS_FIFO_LEVEL_MASK) >> RNGC_STATUS_FIFO_LEVEL_SHIFT;
142
143 return level > 0 ? 1 : 0;
144}
145
146static int imx_rng_data_read(struct hwrng *rng, u32 * data)
147{
148 int err;
149 struct imx_rng_priv_data *prv = (struct imx_rng_priv_data *)rng->priv;
150
151 /* retrieve a random number from FIFO */
152 *data = readl(prv->reg_base + RNGC_FIFO);
153
154 /* is there some error while reading this random number? */
155 err = readl(prv->reg_base + RNGC_STATUS) & RNGC_STATUS_ERROR;
156
157 /* if error happened doesn't return random number */
158 return err ? 0 : 4;
159}
160
161static irqreturn_t imx_rng_irq(int irq, void *dev)
162{
163 int handled = 0;
164 struct imx_rng_priv_data *prv = (struct imx_rng_priv_data *)dev;
165
166 /* is the seed creation done? */
167 if (readl(prv->reg_base + RNGC_STATUS) & RNGC_STATUS_SEED_DONE) {
168 complete(&rng_seed_done);
169 writel(RNGC_CMD_CLR_INT | RNGC_CMD_CLR_ERR,
170 prv->reg_base + RNGC_COMMAND);
171 handled = 1;
172 }
173
174 /* is the self test done? */
175 if (readl(prv->reg_base + RNGC_STATUS) & RNGC_STATUS_ST_DONE) {
176 complete(&rng_self_testing);
177 writel(RNGC_CMD_CLR_INT | RNGC_CMD_CLR_ERR,
178 prv->reg_base + RNGC_COMMAND);
179 handled = 1;
180 }
181
182 /* is there any error? */
183 if (readl(prv->reg_base + RNGC_STATUS) & RNGC_STATUS_ERROR) {
184 /* clear interrupt */
185 writel(RNGC_CMD_CLR_INT | RNGC_CMD_CLR_ERR,
186 prv->reg_base + RNGC_COMMAND);
187 handled = 1;
188 }
189
190 return handled;
191}
192
193static int imx_rng_init(struct hwrng *rng)
194{
195 int err;
196 struct imx_rng_priv_data *prv = (struct imx_rng_priv_data *)rng->priv;
197 u32 cmd, ctrl, osc;
198
199 INIT_COMPLETION(rng_self_testing);
200 INIT_COMPLETION(rng_seed_done);
201
202 err = readl(prv->reg_base + RNGC_STATUS) & RNGC_STATUS_ERROR;
203 if (err) {
204 /* is this a bad keys error ? */
205 if (readl(prv->reg_base + RNGC_ERROR) &
206 RNGC_ERROR_STATUS_BAD_KEY) {
207 dev_err(&imx_rng_dev->dev, "Can't start, Bad Keys.\n");
208 return -EIO;
209 }
210 }
211
212 /* mask all interrupts, will be unmasked soon */
213 ctrl = readl(prv->reg_base + RNGC_CONTROL);
214 writel(ctrl | RNGC_CTRL_MASK_DONE | RNGC_CTRL_MASK_ERROR,
215 prv->reg_base + RNGC_CONTROL);
216
217 /* verify if oscillator is working */
218 osc = readl(prv->reg_base + RNGC_ERROR);
219 if (osc & RNGC_ERROR_STATUS_OSC_ERR) {
220 dev_err(&imx_rng_dev->dev, "RNGC Oscillator is dead!\n");
221 return -EIO;
222 }
223
224 err = request_irq(irq_rng, imx_rng_irq,
225 0, "imx-rng", (void *)rng->priv);
226 if (err) {
227 dev_err(&imx_rng_dev->dev, "Can't get interrupt working.\n");
228 return -EIO;
229 }
230
231 /* do self test, repeat until get success */
232 do {
233 /* clear error */
234 cmd = readl(prv->reg_base + RNGC_COMMAND);
235 writel(cmd | RNGC_CMD_CLR_ERR, prv->reg_base + RNGC_COMMAND);
236
237 /* unmask all interrupt */
238 ctrl = readl(prv->reg_base + RNGC_CONTROL);
239 writel(ctrl & ~(RNGC_CTRL_MASK_DONE | RNGC_CTRL_MASK_ERROR),
240 prv->reg_base + RNGC_CONTROL);
241
242 /* run self test */
243 cmd = readl(prv->reg_base + RNGC_COMMAND);
244 writel(cmd | RNGC_CMD_SELF_TEST,
245 prv->reg_base + RNGC_COMMAND);
246
247 wait_for_completion(&rng_self_testing);
248
249 } while (readl(prv->reg_base + RNGC_ERROR) &
250 RNGC_ERROR_STATUS_ST_ERR);
251
252 /* clear interrupt. Is it really necessary here? */
253 writel(RNGC_CMD_CLR_INT | RNGC_CMD_CLR_ERR,
254 prv->reg_base + RNGC_COMMAND);
255
256 /* create seed, repeat while there is some statistical error */
257 do {
258 /* clear error */
259 cmd = readl(prv->reg_base + RNGC_COMMAND);
260 writel(cmd | RNGC_CMD_CLR_ERR, prv->reg_base + RNGC_COMMAND);
261
262 /* seed creation */
263 cmd = readl(prv->reg_base + RNGC_COMMAND);
264 writel(cmd | RNGC_CMD_SEED, prv->reg_base + RNGC_COMMAND);
265
266 wait_for_completion(&rng_seed_done);
267
268 } while (readl(prv->reg_base + RNGC_ERROR) &
269 RNGC_ERROR_STATUS_STAT_ERR);
270
271 err = readl(prv->reg_base + RNGC_ERROR) &
272 (RNGC_ERROR_STATUS_STAT_ERR |
273 RNGC_ERROR_STATUS_RAND_ERR |
274 RNGC_ERROR_STATUS_FIFO_ERR |
275 RNGC_ERROR_STATUS_ST_ERR |
276 RNGC_ERROR_STATUS_OSC_ERR |
277 RNGC_ERROR_STATUS_LFSR_ERR);
278
279 if (err) {
280 dev_err(&imx_rng_dev->dev, "iMX RNG appears inoperable.\n");
281 return -EIO;
282 }
283
284 return 0;
285}
286
287static struct hwrng imx_rng = {
288 .name = "imx-rng",
289 .init = imx_rng_init,
290 .data_present = imx_rng_data_present,
291 .data_read = imx_rng_data_read
292};
293
294static int __init imx_rng_probe(struct platform_device *pdev)
295{
296 struct device *dev = &pdev->dev;
297 struct device_node *np = dev->of_node;
298 struct clk *clk;
299 struct imx_rng_priv_data *priv;
300 int err = -ENODEV;
301
302 if (imx_rng_dev)
303 return -EBUSY;
304
305 /* Enable the clock */
306 clk = of_clk_get(np, 0);
307 if (IS_ERR(clk)) {
308 dev_err(dev, "Can not get clock.\n");
309 return PTR_ERR(clk);
310 }
311 clk_enable(clk);
312
313 /* Allocate private data memory */
314 priv = kzalloc(sizeof(struct imx_rng_priv_data), GFP_KERNEL);
315 if (!priv)
316 return -ENOMEM;
317
318 imx_rng.priv = (unsigned long)priv;
319 dev_set_drvdata(dev, priv);
320
321 /* ioremap that register space */
322 priv->reg_base = of_iomap(np, 0);
323 if (!priv->reg_base) {
324 kfree(priv);
325 dev_err(dev, "Failed to remap register space.\n");
326 return -ENODEV;
327 }
328
329 irq_rng = platform_get_irq(pdev, 0);
330
331 err = hwrng_register(&imx_rng);
332 if (err) {
333 iounmap(priv->reg_base);
334 kfree(priv);
335 dev_err(dev, "failed to register hwrng (%d)\n", err);
336 return err;
337 }
338
339 imx_rng_dev = pdev;
340 dev_info(dev, "iMX RNG Registered.\n");
341
342 return 0;
343}
344
345static int __exit imx_rng_remove(struct platform_device *pdev)
346{
347 struct device *dev = &pdev->dev;
348 struct device_node *np = dev->of_node;
349 struct clk *clk;
350 struct imx_rng_priv_data *priv = dev_get_drvdata(dev);
351
352 /* Disable the clock */
353 clk = of_clk_get(np, 0);
354
355 if (IS_ERR(clk))
356 dev_err(dev, "Can not get clock.\n");
357 else
358 clk_disable(clk);
359
360 hwrng_unregister(&imx_rng);
361
362 iounmap(priv->reg_base);
363
364 kfree(priv);
365
366 return 0;
367}
368
369static int imx_rng_suspend(struct platform_device *pdev, pm_message_t state)
370{
371#ifdef CONFIG_PM
372 struct device *dev = &pdev->dev;
373 struct device_node *np = dev->of_node;
374 struct clk *clk = of_clk_get(np, 0);
375
376 if (IS_ERR(clk)) {
377 dev_err(&pdev->dev, "Can not get rng_clk\n");
378 return PTR_ERR(clk);
379 }
380
381 clk_disable(clk);
382#endif
383
384 return 0;
385}
386
387static int imx_rng_resume(struct platform_device *pdev)
388{
389#ifdef CONFIG_PM
390 struct device *dev = &pdev->dev;
391 struct device_node *np = dev->of_node;
392 struct clk *clk = of_clk_get(np, 0);
393
394 if (IS_ERR(clk)) {
395 dev_err(&pdev->dev, "Can not get rng_clk\n");
396 return PTR_ERR(clk);
397 }
398
399 clk_enable(clk);
400#endif
401
402 return 0;
403}
404
405static struct of_device_id imx_rng_dt_ids[] = {
406 { .compatible = "imx-rng",},
407 { .compatible = "fsl,imx-rng",},
408 { .compatible = "fsl,imx6sl-rng",},
409 { },
410};
411
412MODULE_DEVICE_TABLE(of, imx_rng_dt_ids);
413
414static struct platform_driver imx_rng_driver = {
415 .driver = {
416 .name = MODULE_NAME,
417 .owner = THIS_MODULE,
418 .of_match_table = imx_rng_dt_ids,
419 },
420 .remove = __exit_p(imx_rng_remove),
421 .suspend = imx_rng_suspend,
422 .resume = imx_rng_resume,
423};
424
425static int __init mod_init(void)
426{
427 return platform_driver_probe(&imx_rng_driver, imx_rng_probe);
428}
429
430static void __exit mod_exit(void)
431{
432 platform_driver_unregister(&imx_rng_driver);
433}
434
435module_init(mod_init);
436module_exit(mod_exit);
437
438MODULE_AUTHOR("Freescale Semiconductor, Inc.");
439MODULE_DESCRIPTION("H/W RNG(B/C) driver for i.MX");
440MODULE_LICENSE("GPL");