aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/hw_random/omap-rng.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char/hw_random/omap-rng.c')
-rw-r--r--drivers/char/hw_random/omap-rng.c386
1 files changed, 315 insertions, 71 deletions
diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c
index 6843ec87b98b..9b89ff4881de 100644
--- a/drivers/char/hw_random/omap-rng.c
+++ b/drivers/char/hw_random/omap-rng.c
@@ -24,57 +24,131 @@
24#include <linux/delay.h> 24#include <linux/delay.h>
25#include <linux/slab.h> 25#include <linux/slab.h>
26#include <linux/pm_runtime.h> 26#include <linux/pm_runtime.h>
27#include <linux/of.h>
28#include <linux/of_device.h>
29#include <linux/of_address.h>
30#include <linux/interrupt.h>
27 31
28#include <asm/io.h> 32#include <asm/io.h>
29 33
30#define RNG_OUT_REG 0x00 /* Output register */ 34#define RNG_REG_STATUS_RDY (1 << 0)
31#define RNG_STAT_REG 0x04 /* Status register 35
32 [0] = STAT_BUSY */ 36#define RNG_REG_INTACK_RDY_MASK (1 << 0)
33#define RNG_ALARM_REG 0x24 /* Alarm register 37#define RNG_REG_INTACK_SHUTDOWN_OFLO_MASK (1 << 1)
34 [7:0] = ALARM_COUNTER */ 38#define RNG_SHUTDOWN_OFLO_MASK (1 << 1)
35#define RNG_CONFIG_REG 0x28 /* Configuration register 39
36 [11:6] = RESET_COUNT 40#define RNG_CONTROL_STARTUP_CYCLES_SHIFT 16
37 [5:3] = RING2_DELAY 41#define RNG_CONTROL_STARTUP_CYCLES_MASK (0xffff << 16)
38 [2:0] = RING1_DELAY */ 42#define RNG_CONTROL_ENABLE_TRNG_SHIFT 10
39#define RNG_REV_REG 0x3c /* Revision register 43#define RNG_CONTROL_ENABLE_TRNG_MASK (1 << 10)
40 [7:0] = REV_NB */ 44
41#define RNG_MASK_REG 0x40 /* Mask and reset register 45#define RNG_CONFIG_MAX_REFIL_CYCLES_SHIFT 16
42 [2] = IT_EN 46#define RNG_CONFIG_MAX_REFIL_CYCLES_MASK (0xffff << 16)
43 [1] = SOFTRESET 47#define RNG_CONFIG_MIN_REFIL_CYCLES_SHIFT 0
44 [0] = AUTOIDLE */ 48#define RNG_CONFIG_MIN_REFIL_CYCLES_MASK (0xff << 0)
45#define RNG_SYSSTATUS 0x44 /* System status 49
46 [0] = RESETDONE */ 50#define RNG_CONTROL_STARTUP_CYCLES 0xff
51#define RNG_CONFIG_MIN_REFIL_CYCLES 0x21
52#define RNG_CONFIG_MAX_REFIL_CYCLES 0x22
53
54#define RNG_ALARMCNT_ALARM_TH_SHIFT 0x0
55#define RNG_ALARMCNT_ALARM_TH_MASK (0xff << 0)
56#define RNG_ALARMCNT_SHUTDOWN_TH_SHIFT 16
57#define RNG_ALARMCNT_SHUTDOWN_TH_MASK (0x1f << 16)
58#define RNG_ALARM_THRESHOLD 0xff
59#define RNG_SHUTDOWN_THRESHOLD 0x4
60
61#define RNG_REG_FROENABLE_MASK 0xffffff
62#define RNG_REG_FRODETUNE_MASK 0xffffff
63
64#define OMAP2_RNG_OUTPUT_SIZE 0x4
65#define OMAP4_RNG_OUTPUT_SIZE 0x8
66
67enum {
68 RNG_OUTPUT_L_REG = 0,
69 RNG_OUTPUT_H_REG,
70 RNG_STATUS_REG,
71 RNG_INTMASK_REG,
72 RNG_INTACK_REG,
73 RNG_CONTROL_REG,
74 RNG_CONFIG_REG,
75 RNG_ALARMCNT_REG,
76 RNG_FROENABLE_REG,
77 RNG_FRODETUNE_REG,
78 RNG_ALARMMASK_REG,
79 RNG_ALARMSTOP_REG,
80 RNG_REV_REG,
81 RNG_SYSCONFIG_REG,
82};
83
84static const u16 reg_map_omap2[] = {
85 [RNG_OUTPUT_L_REG] = 0x0,
86 [RNG_STATUS_REG] = 0x4,
87 [RNG_CONFIG_REG] = 0x28,
88 [RNG_REV_REG] = 0x3c,
89 [RNG_SYSCONFIG_REG] = 0x40,
90};
47 91
92static const u16 reg_map_omap4[] = {
93 [RNG_OUTPUT_L_REG] = 0x0,
94 [RNG_OUTPUT_H_REG] = 0x4,
95 [RNG_STATUS_REG] = 0x8,
96 [RNG_INTMASK_REG] = 0xc,
97 [RNG_INTACK_REG] = 0x10,
98 [RNG_CONTROL_REG] = 0x14,
99 [RNG_CONFIG_REG] = 0x18,
100 [RNG_ALARMCNT_REG] = 0x1c,
101 [RNG_FROENABLE_REG] = 0x20,
102 [RNG_FRODETUNE_REG] = 0x24,
103 [RNG_ALARMMASK_REG] = 0x28,
104 [RNG_ALARMSTOP_REG] = 0x2c,
105 [RNG_REV_REG] = 0x1FE0,
106 [RNG_SYSCONFIG_REG] = 0x1FE4,
107};
108
109struct omap_rng_dev;
48/** 110/**
49 * struct omap_rng_private_data - RNG IP block-specific data 111 * struct omap_rng_pdata - RNG IP block-specific data
50 * @base: virtual address of the beginning of the RNG IP block registers 112 * @regs: Pointer to the register offsets structure.
51 * @mem_res: struct resource * for the IP block registers physical memory 113 * @data_size: No. of bytes in RNG output.
114 * @data_present: Callback to determine if data is available.
115 * @init: Callback for IP specific initialization sequence.
116 * @cleanup: Callback for IP specific cleanup sequence.
52 */ 117 */
53struct omap_rng_private_data { 118struct omap_rng_pdata {
54 void __iomem *base; 119 u16 *regs;
55 struct resource *mem_res; 120 u32 data_size;
121 u32 (*data_present)(struct omap_rng_dev *priv);
122 int (*init)(struct omap_rng_dev *priv);
123 void (*cleanup)(struct omap_rng_dev *priv);
56}; 124};
57 125
58static inline u32 omap_rng_read_reg(struct omap_rng_private_data *priv, int reg) 126struct omap_rng_dev {
127 void __iomem *base;
128 struct device *dev;
129 const struct omap_rng_pdata *pdata;
130};
131
132static inline u32 omap_rng_read(struct omap_rng_dev *priv, u16 reg)
59{ 133{
60 return __raw_readl(priv->base + reg); 134 return __raw_readl(priv->base + priv->pdata->regs[reg]);
61} 135}
62 136
63static inline void omap_rng_write_reg(struct omap_rng_private_data *priv, 137static inline void omap_rng_write(struct omap_rng_dev *priv, u16 reg,
64 int reg, u32 val) 138 u32 val)
65{ 139{
66 __raw_writel(val, priv->base + reg); 140 __raw_writel(val, priv->base + priv->pdata->regs[reg]);
67} 141}
68 142
69static int omap_rng_data_present(struct hwrng *rng, int wait) 143static int omap_rng_data_present(struct hwrng *rng, int wait)
70{ 144{
71 struct omap_rng_private_data *priv; 145 struct omap_rng_dev *priv;
72 int data, i; 146 int data, i;
73 147
74 priv = (struct omap_rng_private_data *)rng->priv; 148 priv = (struct omap_rng_dev *)rng->priv;
75 149
76 for (i = 0; i < 20; i++) { 150 for (i = 0; i < 20; i++) {
77 data = omap_rng_read_reg(priv, RNG_STAT_REG) ? 0 : 1; 151 data = priv->pdata->data_present(priv);
78 if (data || !wait) 152 if (data || !wait)
79 break; 153 break;
80 /* RNG produces data fast enough (2+ MBit/sec, even 154 /* RNG produces data fast enough (2+ MBit/sec, even
@@ -89,27 +163,212 @@ static int omap_rng_data_present(struct hwrng *rng, int wait)
89 163
90static int omap_rng_data_read(struct hwrng *rng, u32 *data) 164static int omap_rng_data_read(struct hwrng *rng, u32 *data)
91{ 165{
92 struct omap_rng_private_data *priv; 166 struct omap_rng_dev *priv;
167 u32 data_size, i;
168
169 priv = (struct omap_rng_dev *)rng->priv;
170 data_size = priv->pdata->data_size;
171
172 for (i = 0; i < data_size / sizeof(u32); i++)
173 data[i] = omap_rng_read(priv, RNG_OUTPUT_L_REG + i);
174
175 if (priv->pdata->regs[RNG_INTACK_REG])
176 omap_rng_write(priv, RNG_INTACK_REG, RNG_REG_INTACK_RDY_MASK);
177 return data_size;
178}
179
180static int omap_rng_init(struct hwrng *rng)
181{
182 struct omap_rng_dev *priv;
93 183
94 priv = (struct omap_rng_private_data *)rng->priv; 184 priv = (struct omap_rng_dev *)rng->priv;
185 return priv->pdata->init(priv);
186}
95 187
96 *data = omap_rng_read_reg(priv, RNG_OUT_REG); 188static void omap_rng_cleanup(struct hwrng *rng)
189{
190 struct omap_rng_dev *priv;
97 191
98 return sizeof(u32); 192 priv = (struct omap_rng_dev *)rng->priv;
193 priv->pdata->cleanup(priv);
99} 194}
100 195
101static struct hwrng omap_rng_ops = { 196static struct hwrng omap_rng_ops = {
102 .name = "omap", 197 .name = "omap",
103 .data_present = omap_rng_data_present, 198 .data_present = omap_rng_data_present,
104 .data_read = omap_rng_data_read, 199 .data_read = omap_rng_data_read,
200 .init = omap_rng_init,
201 .cleanup = omap_rng_cleanup,
202};
203
204static inline u32 omap2_rng_data_present(struct omap_rng_dev *priv)
205{
206 return omap_rng_read(priv, RNG_STATUS_REG) ? 0 : 1;
207}
208
209static int omap2_rng_init(struct omap_rng_dev *priv)
210{
211 omap_rng_write(priv, RNG_SYSCONFIG_REG, 0x1);
212 return 0;
213}
214
215static void omap2_rng_cleanup(struct omap_rng_dev *priv)
216{
217 omap_rng_write(priv, RNG_SYSCONFIG_REG, 0x0);
218}
219
220static struct omap_rng_pdata omap2_rng_pdata = {
221 .regs = (u16 *)reg_map_omap2,
222 .data_size = OMAP2_RNG_OUTPUT_SIZE,
223 .data_present = omap2_rng_data_present,
224 .init = omap2_rng_init,
225 .cleanup = omap2_rng_cleanup,
105}; 226};
106 227
228#if defined(CONFIG_OF)
229static inline u32 omap4_rng_data_present(struct omap_rng_dev *priv)
230{
231 return omap_rng_read(priv, RNG_STATUS_REG) & RNG_REG_STATUS_RDY;
232}
233
234static int omap4_rng_init(struct omap_rng_dev *priv)
235{
236 u32 val;
237
238 /* Return if RNG is already running. */
239 if (omap_rng_read(priv, RNG_CONFIG_REG) & RNG_CONTROL_ENABLE_TRNG_MASK)
240 return 0;
241
242 val = RNG_CONFIG_MIN_REFIL_CYCLES << RNG_CONFIG_MIN_REFIL_CYCLES_SHIFT;
243 val |= RNG_CONFIG_MAX_REFIL_CYCLES << RNG_CONFIG_MAX_REFIL_CYCLES_SHIFT;
244 omap_rng_write(priv, RNG_CONFIG_REG, val);
245
246 omap_rng_write(priv, RNG_FRODETUNE_REG, 0x0);
247 omap_rng_write(priv, RNG_FROENABLE_REG, RNG_REG_FROENABLE_MASK);
248 val = RNG_ALARM_THRESHOLD << RNG_ALARMCNT_ALARM_TH_SHIFT;
249 val |= RNG_SHUTDOWN_THRESHOLD << RNG_ALARMCNT_SHUTDOWN_TH_SHIFT;
250 omap_rng_write(priv, RNG_ALARMCNT_REG, val);
251
252 val = RNG_CONTROL_STARTUP_CYCLES << RNG_CONTROL_STARTUP_CYCLES_SHIFT;
253 val |= RNG_CONTROL_ENABLE_TRNG_MASK;
254 omap_rng_write(priv, RNG_CONTROL_REG, val);
255
256 return 0;
257}
258
259static void omap4_rng_cleanup(struct omap_rng_dev *priv)
260{
261 int val;
262
263 val = omap_rng_read(priv, RNG_CONTROL_REG);
264 val &= ~RNG_CONTROL_ENABLE_TRNG_MASK;
265 omap_rng_write(priv, RNG_CONFIG_REG, val);
266}
267
268static irqreturn_t omap4_rng_irq(int irq, void *dev_id)
269{
270 struct omap_rng_dev *priv = dev_id;
271 u32 fro_detune, fro_enable;
272
273 /*
274 * Interrupt raised by a fro shutdown threshold, do the following:
275 * 1. Clear the alarm events.
276 * 2. De tune the FROs which are shutdown.
277 * 3. Re enable the shutdown FROs.
278 */
279 omap_rng_write(priv, RNG_ALARMMASK_REG, 0x0);
280 omap_rng_write(priv, RNG_ALARMSTOP_REG, 0x0);
281
282 fro_enable = omap_rng_read(priv, RNG_FROENABLE_REG);
283 fro_detune = ~fro_enable & RNG_REG_FRODETUNE_MASK;
284 fro_detune = fro_detune | omap_rng_read(priv, RNG_FRODETUNE_REG);
285 fro_enable = RNG_REG_FROENABLE_MASK;
286
287 omap_rng_write(priv, RNG_FRODETUNE_REG, fro_detune);
288 omap_rng_write(priv, RNG_FROENABLE_REG, fro_enable);
289
290 omap_rng_write(priv, RNG_INTACK_REG, RNG_REG_INTACK_SHUTDOWN_OFLO_MASK);
291
292 return IRQ_HANDLED;
293}
294
295static struct omap_rng_pdata omap4_rng_pdata = {
296 .regs = (u16 *)reg_map_omap4,
297 .data_size = OMAP4_RNG_OUTPUT_SIZE,
298 .data_present = omap4_rng_data_present,
299 .init = omap4_rng_init,
300 .cleanup = omap4_rng_cleanup,
301};
302
303static const struct of_device_id omap_rng_of_match[] = {
304 {
305 .compatible = "ti,omap2-rng",
306 .data = &omap2_rng_pdata,
307 },
308 {
309 .compatible = "ti,omap4-rng",
310 .data = &omap4_rng_pdata,
311 },
312 {},
313};
314MODULE_DEVICE_TABLE(of, omap_rng_of_match);
315
316static int of_get_omap_rng_device_details(struct omap_rng_dev *priv,
317 struct platform_device *pdev)
318{
319 const struct of_device_id *match;
320 struct device *dev = &pdev->dev;
321 int irq, err;
322
323 match = of_match_device(of_match_ptr(omap_rng_of_match), dev);
324 if (!match) {
325 dev_err(dev, "no compatible OF match\n");
326 return -EINVAL;
327 }
328 priv->pdata = match->data;
329
330 if (of_device_is_compatible(dev->of_node, "ti,omap4-rng")) {
331 irq = platform_get_irq(pdev, 0);
332 if (irq < 0) {
333 dev_err(dev, "%s: error getting IRQ resource - %d\n",
334 __func__, irq);
335 return irq;
336 }
337
338 err = devm_request_irq(dev, irq, omap4_rng_irq,
339 IRQF_TRIGGER_NONE, dev_name(dev), priv);
340 if (err) {
341 dev_err(dev, "unable to request irq %d, err = %d\n",
342 irq, err);
343 return err;
344 }
345 omap_rng_write(priv, RNG_INTMASK_REG, RNG_SHUTDOWN_OFLO_MASK);
346 }
347 return 0;
348}
349#else
350static int of_get_omap_rng_device_details(struct omap_rng_dev *omap_rng,
351 struct platform_device *pdev)
352{
353 return -EINVAL;
354}
355#endif
356
357static int get_omap_rng_device_details(struct omap_rng_dev *omap_rng)
358{
359 /* Only OMAP2/3 can be non-DT */
360 omap_rng->pdata = &omap2_rng_pdata;
361 return 0;
362}
363
107static int omap_rng_probe(struct platform_device *pdev) 364static int omap_rng_probe(struct platform_device *pdev)
108{ 365{
109 struct omap_rng_private_data *priv; 366 struct omap_rng_dev *priv;
367 struct resource *res;
368 struct device *dev = &pdev->dev;
110 int ret; 369 int ret;
111 370
112 priv = kzalloc(sizeof(struct omap_rng_private_data), GFP_KERNEL); 371 priv = devm_kzalloc(dev, sizeof(struct omap_rng_dev), GFP_KERNEL);
113 if (!priv) { 372 if (!priv) {
114 dev_err(&pdev->dev, "could not allocate memory\n"); 373 dev_err(&pdev->dev, "could not allocate memory\n");
115 return -ENOMEM; 374 return -ENOMEM;
@@ -117,26 +376,29 @@ static int omap_rng_probe(struct platform_device *pdev)
117 376
118 omap_rng_ops.priv = (unsigned long)priv; 377 omap_rng_ops.priv = (unsigned long)priv;
119 platform_set_drvdata(pdev, priv); 378 platform_set_drvdata(pdev, priv);
379 priv->dev = dev;
120 380
121 priv->mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 381 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
122 priv->base = devm_ioremap_resource(&pdev->dev, priv->mem_res); 382 priv->base = devm_ioremap_resource(dev, res);
123 if (IS_ERR(priv->base)) { 383 if (IS_ERR(priv->base)) {
124 ret = PTR_ERR(priv->base); 384 ret = PTR_ERR(priv->base);
125 goto err_ioremap; 385 goto err_ioremap;
126 } 386 }
127 platform_set_drvdata(pdev, priv);
128 387
129 pm_runtime_enable(&pdev->dev); 388 pm_runtime_enable(&pdev->dev);
130 pm_runtime_get_sync(&pdev->dev); 389 pm_runtime_get_sync(&pdev->dev);
131 390
391 ret = (dev->of_node) ? of_get_omap_rng_device_details(priv, pdev) :
392 get_omap_rng_device_details(priv);
393 if (ret)
394 goto err_ioremap;
395
132 ret = hwrng_register(&omap_rng_ops); 396 ret = hwrng_register(&omap_rng_ops);
133 if (ret) 397 if (ret)
134 goto err_register; 398 goto err_register;
135 399
136 dev_info(&pdev->dev, "OMAP Random Number Generator ver. %02x\n", 400 dev_info(&pdev->dev, "OMAP Random Number Generator ver. %02x\n",
137 omap_rng_read_reg(priv, RNG_REV_REG)); 401 omap_rng_read(priv, RNG_REV_REG));
138
139 omap_rng_write_reg(priv, RNG_MASK_REG, 0x1);
140 402
141 return 0; 403 return 0;
142 404
@@ -144,26 +406,21 @@ err_register:
144 priv->base = NULL; 406 priv->base = NULL;
145 pm_runtime_disable(&pdev->dev); 407 pm_runtime_disable(&pdev->dev);
146err_ioremap: 408err_ioremap:
147 kfree(priv); 409 dev_err(dev, "initialization failed.\n");
148
149 return ret; 410 return ret;
150} 411}
151 412
152static int __exit omap_rng_remove(struct platform_device *pdev) 413static int __exit omap_rng_remove(struct platform_device *pdev)
153{ 414{
154 struct omap_rng_private_data *priv = platform_get_drvdata(pdev); 415 struct omap_rng_dev *priv = platform_get_drvdata(pdev);
155 416
156 hwrng_unregister(&omap_rng_ops); 417 hwrng_unregister(&omap_rng_ops);
157 418
158 omap_rng_write_reg(priv, RNG_MASK_REG, 0x0); 419 priv->pdata->cleanup(priv);
159 420
160 pm_runtime_put_sync(&pdev->dev); 421 pm_runtime_put_sync(&pdev->dev);
161 pm_runtime_disable(&pdev->dev); 422 pm_runtime_disable(&pdev->dev);
162 423
163 release_mem_region(priv->mem_res->start, resource_size(priv->mem_res));
164
165 kfree(priv);
166
167 return 0; 424 return 0;
168} 425}
169 426
@@ -171,9 +428,9 @@ static int __exit omap_rng_remove(struct platform_device *pdev)
171 428
172static int omap_rng_suspend(struct device *dev) 429static int omap_rng_suspend(struct device *dev)
173{ 430{
174 struct omap_rng_private_data *priv = dev_get_drvdata(dev); 431 struct omap_rng_dev *priv = dev_get_drvdata(dev);
175 432
176 omap_rng_write_reg(priv, RNG_MASK_REG, 0x0); 433 priv->pdata->cleanup(priv);
177 pm_runtime_put_sync(dev); 434 pm_runtime_put_sync(dev);
178 435
179 return 0; 436 return 0;
@@ -181,10 +438,10 @@ static int omap_rng_suspend(struct device *dev)
181 438
182static int omap_rng_resume(struct device *dev) 439static int omap_rng_resume(struct device *dev)
183{ 440{
184 struct omap_rng_private_data *priv = dev_get_drvdata(dev); 441 struct omap_rng_dev *priv = dev_get_drvdata(dev);
185 442
186 pm_runtime_get_sync(dev); 443 pm_runtime_get_sync(dev);
187 omap_rng_write_reg(priv, RNG_MASK_REG, 0x1); 444 priv->pdata->init(priv);
188 445
189 return 0; 446 return 0;
190} 447}
@@ -198,31 +455,18 @@ static SIMPLE_DEV_PM_OPS(omap_rng_pm, omap_rng_suspend, omap_rng_resume);
198 455
199#endif 456#endif
200 457
201/* work with hotplug and coldplug */
202MODULE_ALIAS("platform:omap_rng");
203
204static struct platform_driver omap_rng_driver = { 458static struct platform_driver omap_rng_driver = {
205 .driver = { 459 .driver = {
206 .name = "omap_rng", 460 .name = "omap_rng",
207 .owner = THIS_MODULE, 461 .owner = THIS_MODULE,
208 .pm = OMAP_RNG_PM, 462 .pm = OMAP_RNG_PM,
463 .of_match_table = of_match_ptr(omap_rng_of_match),
209 }, 464 },
210 .probe = omap_rng_probe, 465 .probe = omap_rng_probe,
211 .remove = __exit_p(omap_rng_remove), 466 .remove = __exit_p(omap_rng_remove),
212}; 467};
213 468
214static int __init omap_rng_init(void) 469module_platform_driver(omap_rng_driver);
215{ 470MODULE_ALIAS("platform:omap_rng");
216 return platform_driver_register(&omap_rng_driver);
217}
218
219static void __exit omap_rng_exit(void)
220{
221 platform_driver_unregister(&omap_rng_driver);
222}
223
224module_init(omap_rng_init);
225module_exit(omap_rng_exit);
226
227MODULE_AUTHOR("Deepak Saxena (and others)"); 471MODULE_AUTHOR("Deepak Saxena (and others)");
228MODULE_LICENSE("GPL"); 472MODULE_LICENSE("GPL");