diff options
author | Joshua Henderson <joshua.henderson@microchip.com> | 2016-02-08 16:17:53 -0500 |
---|---|---|
committer | Herbert Xu <herbert@gondor.apana.org.au> | 2016-02-16 15:07:48 -0500 |
commit | 730d02e27670fa5b6a55778d11023c5897d87d57 (patch) | |
tree | 9a5443f8d945c460a2ac8d8290cef96c47148338 | |
parent | d489d170eaf6ebd8562ba77d113304c8640113b7 (diff) |
hwrng: pic32 - Add PIC32 RNG hardware driver
Add support for the hardware true random number generator
peripheral found on PIC32.
Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
Signed-off-by: Purna Chandra Mandal <purna.mandal@microchip.com>
Reviewed-by: Daniel Thompson <daniel.thompson@linaro.org>
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/pic32-rng.c | 155 |
3 files changed, 169 insertions, 0 deletions
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index 8d895908d608..967904f5bbcb 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig | |||
@@ -381,6 +381,19 @@ config HW_RANDOM_STM32 | |||
381 | 381 | ||
382 | If unsure, say N. | 382 | If unsure, say N. |
383 | 383 | ||
384 | config HW_RANDOM_PIC32 | ||
385 | tristate "Microchip PIC32 Random Number Generator support" | ||
386 | depends on HW_RANDOM && MACH_PIC32 | ||
387 | default y | ||
388 | ---help--- | ||
389 | This driver provides kernel-side support for the Random Number | ||
390 | Generator hardware found on a PIC32. | ||
391 | |||
392 | To compile this driver as a module, choose M here. the | ||
393 | module will be called pic32-rng. | ||
394 | |||
395 | If unsure, say Y. | ||
396 | |||
384 | endif # HW_RANDOM | 397 | endif # HW_RANDOM |
385 | 398 | ||
386 | config UML_RANDOM | 399 | config UML_RANDOM |
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index 5ad397635128..f5a6fa7690e7 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile | |||
@@ -33,3 +33,4 @@ obj-$(CONFIG_HW_RANDOM_MSM) += msm-rng.o | |||
33 | obj-$(CONFIG_HW_RANDOM_ST) += st-rng.o | 33 | obj-$(CONFIG_HW_RANDOM_ST) += st-rng.o |
34 | obj-$(CONFIG_HW_RANDOM_XGENE) += xgene-rng.o | 34 | obj-$(CONFIG_HW_RANDOM_XGENE) += xgene-rng.o |
35 | obj-$(CONFIG_HW_RANDOM_STM32) += stm32-rng.o | 35 | obj-$(CONFIG_HW_RANDOM_STM32) += stm32-rng.o |
36 | obj-$(CONFIG_HW_RANDOM_PIC32) += pic32-rng.o | ||
diff --git a/drivers/char/hw_random/pic32-rng.c b/drivers/char/hw_random/pic32-rng.c new file mode 100644 index 000000000000..108897bea2d0 --- /dev/null +++ b/drivers/char/hw_random/pic32-rng.c | |||
@@ -0,0 +1,155 @@ | |||
1 | /* | ||
2 | * PIC32 RNG driver | ||
3 | * | ||
4 | * Joshua Henderson <joshua.henderson@microchip.com> | ||
5 | * Copyright (C) 2016 Microchip Technology Inc. All rights reserved. | ||
6 | * | ||
7 | * This program is free software; you can distribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License (Version 2) as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
14 | * for more details. | ||
15 | */ | ||
16 | |||
17 | #include <linux/clk.h> | ||
18 | #include <linux/clkdev.h> | ||
19 | #include <linux/err.h> | ||
20 | #include <linux/hw_random.h> | ||
21 | #include <linux/io.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/of.h> | ||
25 | #include <linux/of_device.h> | ||
26 | #include <linux/platform_device.h> | ||
27 | #include <linux/slab.h> | ||
28 | |||
29 | #define RNGCON 0x04 | ||
30 | #define TRNGEN BIT(8) | ||
31 | #define PRNGEN BIT(9) | ||
32 | #define PRNGCONT BIT(10) | ||
33 | #define TRNGMOD BIT(11) | ||
34 | #define SEEDLOAD BIT(12) | ||
35 | #define RNGPOLY1 0x08 | ||
36 | #define RNGPOLY2 0x0C | ||
37 | #define RNGNUMGEN1 0x10 | ||
38 | #define RNGNUMGEN2 0x14 | ||
39 | #define RNGSEED1 0x18 | ||
40 | #define RNGSEED2 0x1C | ||
41 | #define RNGRCNT 0x20 | ||
42 | #define RCNT_MASK 0x7F | ||
43 | |||
44 | struct pic32_rng { | ||
45 | void __iomem *base; | ||
46 | struct hwrng rng; | ||
47 | struct clk *clk; | ||
48 | }; | ||
49 | |||
50 | /* | ||
51 | * The TRNG can generate up to 24Mbps. This is a timeout that should be safe | ||
52 | * enough given the instructions in the loop and that the TRNG may not always | ||
53 | * be at maximum rate. | ||
54 | */ | ||
55 | #define RNG_TIMEOUT 500 | ||
56 | |||
57 | static int pic32_rng_read(struct hwrng *rng, void *buf, size_t max, | ||
58 | bool wait) | ||
59 | { | ||
60 | struct pic32_rng *priv = container_of(rng, struct pic32_rng, rng); | ||
61 | u64 *data = buf; | ||
62 | u32 t; | ||
63 | unsigned int timeout = RNG_TIMEOUT; | ||
64 | |||
65 | if (max < 8) | ||
66 | return 0; | ||
67 | |||
68 | do { | ||
69 | t = readl(priv->base + RNGRCNT) & RCNT_MASK; | ||
70 | if (t == 64) { | ||
71 | /* TRNG value comes through the seed registers */ | ||
72 | *data = ((u64)readl(priv->base + RNGSEED2) << 32) + | ||
73 | readl(priv->base + RNGSEED1); | ||
74 | return 8; | ||
75 | } | ||
76 | } while (wait && --timeout); | ||
77 | |||
78 | return -EIO; | ||
79 | } | ||
80 | |||
81 | static int pic32_rng_probe(struct platform_device *pdev) | ||
82 | { | ||
83 | struct pic32_rng *priv; | ||
84 | struct resource *res; | ||
85 | u32 v; | ||
86 | int ret; | ||
87 | |||
88 | priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); | ||
89 | if (!priv) | ||
90 | return -ENOMEM; | ||
91 | |||
92 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
93 | priv->base = devm_ioremap_resource(&pdev->dev, res); | ||
94 | if (IS_ERR(priv->base)) | ||
95 | return PTR_ERR(priv->base); | ||
96 | |||
97 | priv->clk = devm_clk_get(&pdev->dev, NULL); | ||
98 | if (IS_ERR(priv->clk)) | ||
99 | return PTR_ERR(priv->clk); | ||
100 | |||
101 | ret = clk_prepare_enable(priv->clk); | ||
102 | if (ret) | ||
103 | return ret; | ||
104 | |||
105 | /* enable TRNG in enhanced mode */ | ||
106 | v = TRNGEN | TRNGMOD; | ||
107 | writel(v, priv->base + RNGCON); | ||
108 | |||
109 | priv->rng.name = pdev->name; | ||
110 | priv->rng.read = pic32_rng_read; | ||
111 | |||
112 | ret = hwrng_register(&priv->rng); | ||
113 | if (ret) | ||
114 | goto err_register; | ||
115 | |||
116 | platform_set_drvdata(pdev, priv); | ||
117 | |||
118 | return 0; | ||
119 | |||
120 | err_register: | ||
121 | clk_disable_unprepare(priv->clk); | ||
122 | return ret; | ||
123 | } | ||
124 | |||
125 | static int pic32_rng_remove(struct platform_device *pdev) | ||
126 | { | ||
127 | struct pic32_rng *rng = platform_get_drvdata(pdev); | ||
128 | |||
129 | hwrng_unregister(&rng->rng); | ||
130 | writel(0, rng->base + RNGCON); | ||
131 | clk_disable_unprepare(rng->clk); | ||
132 | return 0; | ||
133 | } | ||
134 | |||
135 | static const struct of_device_id pic32_rng_of_match[] = { | ||
136 | { .compatible = "microchip,pic32mzda-rng", }, | ||
137 | { /* sentinel */ } | ||
138 | }; | ||
139 | MODULE_DEVICE_TABLE(of, pic32_rng_of_match); | ||
140 | |||
141 | static struct platform_driver pic32_rng_driver = { | ||
142 | .probe = pic32_rng_probe, | ||
143 | .remove = pic32_rng_remove, | ||
144 | .driver = { | ||
145 | .name = "pic32-rng", | ||
146 | .owner = THIS_MODULE, | ||
147 | .of_match_table = of_match_ptr(pic32_rng_of_match), | ||
148 | }, | ||
149 | }; | ||
150 | |||
151 | module_platform_driver(pic32_rng_driver); | ||
152 | |||
153 | MODULE_LICENSE("GPL"); | ||
154 | MODULE_AUTHOR("Joshua Henderson <joshua.henderson@microchip.com>"); | ||
155 | MODULE_DESCRIPTION("Microchip PIC32 RNG Driver"); | ||