Loading...
1/*
2 * Copyright (c) 2011 Peter Korsgaard <jacmet@sunsite.dk>
3 *
4 * This file is licensed under the terms of the GNU General Public
5 * License version 2. This program is licensed "as is" without any
6 * warranty of any kind, whether express or implied.
7 */
8
9#include <linux/kernel.h>
10#include <linux/module.h>
11#include <linux/mod_devicetable.h>
12#include <linux/slab.h>
13#include <linux/err.h>
14#include <linux/clk.h>
15#include <linux/io.h>
16#include <linux/iopoll.h>
17#include <linux/hw_random.h>
18#include <linux/of.h>
19#include <linux/platform_device.h>
20#include <linux/pm_runtime.h>
21
22#define TRNG_CR 0x00
23#define TRNG_MR 0x04
24#define TRNG_ISR 0x1c
25#define TRNG_ISR_DATRDY BIT(0)
26#define TRNG_ODATA 0x50
27
28#define TRNG_KEY 0x524e4700 /* RNG */
29
30#define TRNG_HALFR BIT(0) /* generate RN every 168 cycles */
31
32struct atmel_trng_data {
33 bool has_half_rate;
34};
35
36struct atmel_trng {
37 struct clk *clk;
38 void __iomem *base;
39 struct hwrng rng;
40 bool has_half_rate;
41};
42
43static bool atmel_trng_wait_ready(struct atmel_trng *trng, bool wait)
44{
45 int ready;
46
47 ready = readl(trng->base + TRNG_ISR) & TRNG_ISR_DATRDY;
48 if (!ready && wait)
49 readl_poll_timeout(trng->base + TRNG_ISR, ready,
50 ready & TRNG_ISR_DATRDY, 1000, 20000);
51
52 return !!ready;
53}
54
55static int atmel_trng_read(struct hwrng *rng, void *buf, size_t max,
56 bool wait)
57{
58 struct atmel_trng *trng = container_of(rng, struct atmel_trng, rng);
59 u32 *data = buf;
60 int ret;
61
62 ret = pm_runtime_get_sync((struct device *)trng->rng.priv);
63 if (ret < 0) {
64 pm_runtime_put_sync((struct device *)trng->rng.priv);
65 return ret;
66 }
67
68 ret = atmel_trng_wait_ready(trng, wait);
69 if (!ret)
70 goto out;
71
72 *data = readl(trng->base + TRNG_ODATA);
73 /*
74 * ensure data ready is only set again AFTER the next data word is ready
75 * in case it got set between checking ISR and reading ODATA, so we
76 * don't risk re-reading the same word
77 */
78 readl(trng->base + TRNG_ISR);
79 ret = 4;
80
81out:
82 pm_runtime_mark_last_busy((struct device *)trng->rng.priv);
83 pm_runtime_put_sync_autosuspend((struct device *)trng->rng.priv);
84 return ret;
85}
86
87static int atmel_trng_init(struct atmel_trng *trng)
88{
89 unsigned long rate;
90 int ret;
91
92 ret = clk_prepare_enable(trng->clk);
93 if (ret)
94 return ret;
95
96 if (trng->has_half_rate) {
97 rate = clk_get_rate(trng->clk);
98
99 /* if peripheral clk is above 100MHz, set HALFR */
100 if (rate > 100000000)
101 writel(TRNG_HALFR, trng->base + TRNG_MR);
102 }
103
104 writel(TRNG_KEY | 1, trng->base + TRNG_CR);
105
106 return 0;
107}
108
109static void atmel_trng_cleanup(struct atmel_trng *trng)
110{
111 writel(TRNG_KEY, trng->base + TRNG_CR);
112 clk_disable_unprepare(trng->clk);
113}
114
115static int atmel_trng_probe(struct platform_device *pdev)
116{
117 struct atmel_trng *trng;
118 const struct atmel_trng_data *data;
119 int ret;
120
121 trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
122 if (!trng)
123 return -ENOMEM;
124
125 trng->base = devm_platform_ioremap_resource(pdev, 0);
126 if (IS_ERR(trng->base))
127 return PTR_ERR(trng->base);
128
129 trng->clk = devm_clk_get(&pdev->dev, NULL);
130 if (IS_ERR(trng->clk))
131 return PTR_ERR(trng->clk);
132 data = of_device_get_match_data(&pdev->dev);
133 if (!data)
134 return -ENODEV;
135
136 trng->has_half_rate = data->has_half_rate;
137 trng->rng.name = pdev->name;
138 trng->rng.read = atmel_trng_read;
139 trng->rng.priv = (unsigned long)&pdev->dev;
140 platform_set_drvdata(pdev, trng);
141
142#ifndef CONFIG_PM
143 ret = atmel_trng_init(trng);
144 if (ret)
145 return ret;
146#endif
147
148 pm_runtime_set_autosuspend_delay(&pdev->dev, 100);
149 pm_runtime_use_autosuspend(&pdev->dev);
150 pm_runtime_enable(&pdev->dev);
151
152 ret = devm_hwrng_register(&pdev->dev, &trng->rng);
153 if (ret) {
154 pm_runtime_disable(&pdev->dev);
155 pm_runtime_set_suspended(&pdev->dev);
156#ifndef CONFIG_PM
157 atmel_trng_cleanup(trng);
158#endif
159 }
160
161 return ret;
162}
163
164static void atmel_trng_remove(struct platform_device *pdev)
165{
166 struct atmel_trng *trng = platform_get_drvdata(pdev);
167
168 atmel_trng_cleanup(trng);
169 pm_runtime_disable(&pdev->dev);
170 pm_runtime_set_suspended(&pdev->dev);
171}
172
173static int __maybe_unused atmel_trng_runtime_suspend(struct device *dev)
174{
175 struct atmel_trng *trng = dev_get_drvdata(dev);
176
177 atmel_trng_cleanup(trng);
178
179 return 0;
180}
181
182static int __maybe_unused atmel_trng_runtime_resume(struct device *dev)
183{
184 struct atmel_trng *trng = dev_get_drvdata(dev);
185
186 return atmel_trng_init(trng);
187}
188
189static const struct dev_pm_ops __maybe_unused atmel_trng_pm_ops = {
190 SET_RUNTIME_PM_OPS(atmel_trng_runtime_suspend,
191 atmel_trng_runtime_resume, NULL)
192 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
193 pm_runtime_force_resume)
194};
195
196static const struct atmel_trng_data at91sam9g45_config = {
197 .has_half_rate = false,
198};
199
200static const struct atmel_trng_data sam9x60_config = {
201 .has_half_rate = true,
202};
203
204static const struct of_device_id atmel_trng_dt_ids[] = {
205 {
206 .compatible = "atmel,at91sam9g45-trng",
207 .data = &at91sam9g45_config,
208 }, {
209 .compatible = "microchip,sam9x60-trng",
210 .data = &sam9x60_config,
211 }, {
212 /* sentinel */
213 }
214};
215MODULE_DEVICE_TABLE(of, atmel_trng_dt_ids);
216
217static struct platform_driver atmel_trng_driver = {
218 .probe = atmel_trng_probe,
219 .remove_new = atmel_trng_remove,
220 .driver = {
221 .name = "atmel-trng",
222 .pm = pm_ptr(&atmel_trng_pm_ops),
223 .of_match_table = atmel_trng_dt_ids,
224 },
225};
226
227module_platform_driver(atmel_trng_driver);
228
229MODULE_LICENSE("GPL");
230MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>");
231MODULE_DESCRIPTION("Atmel true random number generator driver");
1/*
2 * Copyright (c) 2011 Peter Korsgaard <jacmet@sunsite.dk>
3 *
4 * This file is licensed under the terms of the GNU General Public
5 * License version 2. This program is licensed "as is" without any
6 * warranty of any kind, whether express or implied.
7 */
8
9#include <linux/kernel.h>
10#include <linux/module.h>
11#include <linux/slab.h>
12#include <linux/err.h>
13#include <linux/clk.h>
14#include <linux/io.h>
15#include <linux/hw_random.h>
16#include <linux/platform_device.h>
17
18#define TRNG_CR 0x00
19#define TRNG_ISR 0x1c
20#define TRNG_ODATA 0x50
21
22#define TRNG_KEY 0x524e4700 /* RNG */
23
24struct atmel_trng {
25 struct clk *clk;
26 void __iomem *base;
27 struct hwrng rng;
28};
29
30static int atmel_trng_read(struct hwrng *rng, void *buf, size_t max,
31 bool wait)
32{
33 struct atmel_trng *trng = container_of(rng, struct atmel_trng, rng);
34 u32 *data = buf;
35
36 /* data ready? */
37 if (readl(trng->base + TRNG_ISR) & 1) {
38 *data = readl(trng->base + TRNG_ODATA);
39 /*
40 ensure data ready is only set again AFTER the next data
41 word is ready in case it got set between checking ISR
42 and reading ODATA, so we don't risk re-reading the
43 same word
44 */
45 readl(trng->base + TRNG_ISR);
46 return 4;
47 } else
48 return 0;
49}
50
51static int atmel_trng_probe(struct platform_device *pdev)
52{
53 struct atmel_trng *trng;
54 struct resource *res;
55 int ret;
56
57 trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
58 if (!trng)
59 return -ENOMEM;
60
61 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
62 trng->base = devm_ioremap_resource(&pdev->dev, res);
63 if (IS_ERR(trng->base))
64 return PTR_ERR(trng->base);
65
66 trng->clk = devm_clk_get(&pdev->dev, NULL);
67 if (IS_ERR(trng->clk))
68 return PTR_ERR(trng->clk);
69
70 ret = clk_prepare_enable(trng->clk);
71 if (ret)
72 return ret;
73
74 writel(TRNG_KEY | 1, trng->base + TRNG_CR);
75 trng->rng.name = pdev->name;
76 trng->rng.read = atmel_trng_read;
77
78 ret = hwrng_register(&trng->rng);
79 if (ret)
80 goto err_register;
81
82 platform_set_drvdata(pdev, trng);
83
84 return 0;
85
86err_register:
87 clk_disable(trng->clk);
88 return ret;
89}
90
91static int atmel_trng_remove(struct platform_device *pdev)
92{
93 struct atmel_trng *trng = platform_get_drvdata(pdev);
94
95 hwrng_unregister(&trng->rng);
96
97 writel(TRNG_KEY, trng->base + TRNG_CR);
98 clk_disable_unprepare(trng->clk);
99
100 return 0;
101}
102
103#ifdef CONFIG_PM
104static int atmel_trng_suspend(struct device *dev)
105{
106 struct atmel_trng *trng = dev_get_drvdata(dev);
107
108 clk_disable_unprepare(trng->clk);
109
110 return 0;
111}
112
113static int atmel_trng_resume(struct device *dev)
114{
115 struct atmel_trng *trng = dev_get_drvdata(dev);
116
117 return clk_prepare_enable(trng->clk);
118}
119
120static const struct dev_pm_ops atmel_trng_pm_ops = {
121 .suspend = atmel_trng_suspend,
122 .resume = atmel_trng_resume,
123};
124#endif /* CONFIG_PM */
125
126static const struct of_device_id atmel_trng_dt_ids[] = {
127 { .compatible = "atmel,at91sam9g45-trng" },
128 { /* sentinel */ }
129};
130MODULE_DEVICE_TABLE(of, atmel_trng_dt_ids);
131
132static struct platform_driver atmel_trng_driver = {
133 .probe = atmel_trng_probe,
134 .remove = atmel_trng_remove,
135 .driver = {
136 .name = "atmel-trng",
137#ifdef CONFIG_PM
138 .pm = &atmel_trng_pm_ops,
139#endif /* CONFIG_PM */
140 .of_match_table = atmel_trng_dt_ids,
141 },
142};
143
144module_platform_driver(atmel_trng_driver);
145
146MODULE_LICENSE("GPL");
147MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>");
148MODULE_DESCRIPTION("Atmel true random number generator driver");