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/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/hw_random.h>
17#include <linux/of_device.h>
18#include <linux/platform_device.h>
19
20#define TRNG_CR 0x00
21#define TRNG_MR 0x04
22#define TRNG_ISR 0x1c
23#define TRNG_ODATA 0x50
24
25#define TRNG_KEY 0x524e4700 /* RNG */
26
27#define TRNG_HALFR BIT(0) /* generate RN every 168 cycles */
28
29struct atmel_trng_data {
30 bool has_half_rate;
31};
32
33struct atmel_trng {
34 struct clk *clk;
35 void __iomem *base;
36 struct hwrng rng;
37};
38
39static int atmel_trng_read(struct hwrng *rng, void *buf, size_t max,
40 bool wait)
41{
42 struct atmel_trng *trng = container_of(rng, struct atmel_trng, rng);
43 u32 *data = buf;
44
45 /* data ready? */
46 if (readl(trng->base + TRNG_ISR) & 1) {
47 *data = readl(trng->base + TRNG_ODATA);
48 /*
49 ensure data ready is only set again AFTER the next data
50 word is ready in case it got set between checking ISR
51 and reading ODATA, so we don't risk re-reading the
52 same word
53 */
54 readl(trng->base + TRNG_ISR);
55 return 4;
56 } else
57 return 0;
58}
59
60static void atmel_trng_enable(struct atmel_trng *trng)
61{
62 writel(TRNG_KEY | 1, trng->base + TRNG_CR);
63}
64
65static void atmel_trng_disable(struct atmel_trng *trng)
66{
67 writel(TRNG_KEY, trng->base + TRNG_CR);
68}
69
70static int atmel_trng_probe(struct platform_device *pdev)
71{
72 struct atmel_trng *trng;
73 const struct atmel_trng_data *data;
74 int ret;
75
76 trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
77 if (!trng)
78 return -ENOMEM;
79
80 trng->base = devm_platform_ioremap_resource(pdev, 0);
81 if (IS_ERR(trng->base))
82 return PTR_ERR(trng->base);
83
84 trng->clk = devm_clk_get(&pdev->dev, NULL);
85 if (IS_ERR(trng->clk))
86 return PTR_ERR(trng->clk);
87 data = of_device_get_match_data(&pdev->dev);
88 if (!data)
89 return -ENODEV;
90
91 if (data->has_half_rate) {
92 unsigned long rate = clk_get_rate(trng->clk);
93
94 /* if peripheral clk is above 100MHz, set HALFR */
95 if (rate > 100000000)
96 writel(TRNG_HALFR, trng->base + TRNG_MR);
97 }
98
99 ret = clk_prepare_enable(trng->clk);
100 if (ret)
101 return ret;
102
103 atmel_trng_enable(trng);
104 trng->rng.name = pdev->name;
105 trng->rng.read = atmel_trng_read;
106
107 ret = devm_hwrng_register(&pdev->dev, &trng->rng);
108 if (ret)
109 goto err_register;
110
111 platform_set_drvdata(pdev, trng);
112
113 return 0;
114
115err_register:
116 clk_disable_unprepare(trng->clk);
117 return ret;
118}
119
120static int atmel_trng_remove(struct platform_device *pdev)
121{
122 struct atmel_trng *trng = platform_get_drvdata(pdev);
123
124
125 atmel_trng_disable(trng);
126 clk_disable_unprepare(trng->clk);
127
128 return 0;
129}
130
131#ifdef CONFIG_PM
132static int atmel_trng_suspend(struct device *dev)
133{
134 struct atmel_trng *trng = dev_get_drvdata(dev);
135
136 atmel_trng_disable(trng);
137 clk_disable_unprepare(trng->clk);
138
139 return 0;
140}
141
142static int atmel_trng_resume(struct device *dev)
143{
144 struct atmel_trng *trng = dev_get_drvdata(dev);
145 int ret;
146
147 ret = clk_prepare_enable(trng->clk);
148 if (ret)
149 return ret;
150
151 atmel_trng_enable(trng);
152
153 return 0;
154}
155
156static const struct dev_pm_ops atmel_trng_pm_ops = {
157 .suspend = atmel_trng_suspend,
158 .resume = atmel_trng_resume,
159};
160#endif /* CONFIG_PM */
161
162static const struct atmel_trng_data at91sam9g45_config = {
163 .has_half_rate = false,
164};
165
166static const struct atmel_trng_data sam9x60_config = {
167 .has_half_rate = true,
168};
169
170static const struct of_device_id atmel_trng_dt_ids[] = {
171 {
172 .compatible = "atmel,at91sam9g45-trng",
173 .data = &at91sam9g45_config,
174 }, {
175 .compatible = "microchip,sam9x60-trng",
176 .data = &sam9x60_config,
177 }, {
178 /* sentinel */
179 }
180};
181MODULE_DEVICE_TABLE(of, atmel_trng_dt_ids);
182
183static struct platform_driver atmel_trng_driver = {
184 .probe = atmel_trng_probe,
185 .remove = atmel_trng_remove,
186 .driver = {
187 .name = "atmel-trng",
188#ifdef CONFIG_PM
189 .pm = &atmel_trng_pm_ops,
190#endif /* CONFIG_PM */
191 .of_match_table = atmel_trng_dt_ids,
192 },
193};
194
195module_platform_driver(atmel_trng_driver);
196
197MODULE_LICENSE("GPL");
198MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>");
199MODULE_DESCRIPTION("Atmel true random number generator driver");