1 | /* |
2 | * omap-rng.c - RNG driver for TI OMAP CPU family |
3 | * |
4 | * Author: Deepak Saxena <dsaxena@plexity.net> |
5 | * |
6 | * Copyright 2005 (c) MontaVista Software, Inc. |
7 | * |
8 | * Mostly based on original driver: |
9 | * |
10 | * Copyright (C) 2005 Nokia Corporation |
11 | * Author: Juha Yrjölä <juha.yrjola@nokia.com> |
12 | * |
13 | * This file is licensed under the terms of the GNU General Public |
14 | * License version 2. This program is licensed "as is" without any |
15 | * warranty of any kind, whether express or implied. |
16 | */ |
17 | |
18 | #include <linux/module.h> |
19 | #include <linux/init.h> |
20 | #include <linux/random.h> |
21 | #include <linux/err.h> |
22 | #include <linux/platform_device.h> |
23 | #include <linux/hw_random.h> |
24 | #include <linux/delay.h> |
25 | #include <linux/kernel.h> |
26 | #include <linux/slab.h> |
27 | #include <linux/pm_runtime.h> |
28 | #include <linux/of.h> |
29 | #include <linux/interrupt.h> |
30 | #include <linux/clk.h> |
31 | #include <linux/io.h> |
32 | |
33 | #define RNG_REG_STATUS_RDY (1 << 0) |
34 | |
35 | #define RNG_REG_INTACK_RDY_MASK (1 << 0) |
36 | #define RNG_REG_INTACK_SHUTDOWN_OFLO_MASK (1 << 1) |
37 | #define RNG_SHUTDOWN_OFLO_MASK (1 << 1) |
38 | |
39 | #define RNG_CONTROL_STARTUP_CYCLES_SHIFT 16 |
40 | #define RNG_CONTROL_STARTUP_CYCLES_MASK (0xffff << 16) |
41 | #define RNG_CONTROL_ENABLE_TRNG_SHIFT 10 |
42 | #define RNG_CONTROL_ENABLE_TRNG_MASK (1 << 10) |
43 | |
44 | #define RNG_CONFIG_MAX_REFIL_CYCLES_SHIFT 16 |
45 | #define RNG_CONFIG_MAX_REFIL_CYCLES_MASK (0xffff << 16) |
46 | #define RNG_CONFIG_MIN_REFIL_CYCLES_SHIFT 0 |
47 | #define RNG_CONFIG_MIN_REFIL_CYCLES_MASK (0xff << 0) |
48 | |
49 | #define RNG_CONTROL_STARTUP_CYCLES 0xff |
50 | #define RNG_CONFIG_MIN_REFIL_CYCLES 0x21 |
51 | #define RNG_CONFIG_MAX_REFIL_CYCLES 0x22 |
52 | |
53 | #define RNG_ALARMCNT_ALARM_TH_SHIFT 0x0 |
54 | #define RNG_ALARMCNT_ALARM_TH_MASK (0xff << 0) |
55 | #define RNG_ALARMCNT_SHUTDOWN_TH_SHIFT 16 |
56 | #define RNG_ALARMCNT_SHUTDOWN_TH_MASK (0x1f << 16) |
57 | #define RNG_ALARM_THRESHOLD 0xff |
58 | #define RNG_SHUTDOWN_THRESHOLD 0x4 |
59 | |
60 | #define RNG_REG_FROENABLE_MASK 0xffffff |
61 | #define RNG_REG_FRODETUNE_MASK 0xffffff |
62 | |
63 | #define OMAP2_RNG_OUTPUT_SIZE 0x4 |
64 | #define OMAP4_RNG_OUTPUT_SIZE 0x8 |
65 | #define EIP76_RNG_OUTPUT_SIZE 0x10 |
66 | |
67 | /* |
68 | * EIP76 RNG takes approx. 700us to produce 16 bytes of output data |
69 | * as per testing results. And to account for the lack of udelay()'s |
70 | * reliability, we keep the timeout as 1000us. |
71 | */ |
72 | #define RNG_DATA_FILL_TIMEOUT 100 |
73 | |
74 | enum { |
75 | RNG_OUTPUT_0_REG = 0, |
76 | RNG_OUTPUT_1_REG, |
77 | RNG_OUTPUT_2_REG, |
78 | RNG_OUTPUT_3_REG, |
79 | RNG_STATUS_REG, |
80 | RNG_INTMASK_REG, |
81 | RNG_INTACK_REG, |
82 | RNG_CONTROL_REG, |
83 | RNG_CONFIG_REG, |
84 | RNG_ALARMCNT_REG, |
85 | RNG_FROENABLE_REG, |
86 | RNG_FRODETUNE_REG, |
87 | RNG_ALARMMASK_REG, |
88 | RNG_ALARMSTOP_REG, |
89 | RNG_REV_REG, |
90 | RNG_SYSCONFIG_REG, |
91 | }; |
92 | |
93 | static const u16 reg_map_omap2[] = { |
94 | [RNG_OUTPUT_0_REG] = 0x0, |
95 | [RNG_STATUS_REG] = 0x4, |
96 | [RNG_CONFIG_REG] = 0x28, |
97 | [RNG_REV_REG] = 0x3c, |
98 | [RNG_SYSCONFIG_REG] = 0x40, |
99 | }; |
100 | |
101 | static const u16 reg_map_omap4[] = { |
102 | [RNG_OUTPUT_0_REG] = 0x0, |
103 | [RNG_OUTPUT_1_REG] = 0x4, |
104 | [RNG_STATUS_REG] = 0x8, |
105 | [RNG_INTMASK_REG] = 0xc, |
106 | [RNG_INTACK_REG] = 0x10, |
107 | [RNG_CONTROL_REG] = 0x14, |
108 | [RNG_CONFIG_REG] = 0x18, |
109 | [RNG_ALARMCNT_REG] = 0x1c, |
110 | [RNG_FROENABLE_REG] = 0x20, |
111 | [RNG_FRODETUNE_REG] = 0x24, |
112 | [RNG_ALARMMASK_REG] = 0x28, |
113 | [RNG_ALARMSTOP_REG] = 0x2c, |
114 | [RNG_REV_REG] = 0x1FE0, |
115 | [RNG_SYSCONFIG_REG] = 0x1FE4, |
116 | }; |
117 | |
118 | static const u16 reg_map_eip76[] = { |
119 | [RNG_OUTPUT_0_REG] = 0x0, |
120 | [RNG_OUTPUT_1_REG] = 0x4, |
121 | [RNG_OUTPUT_2_REG] = 0x8, |
122 | [RNG_OUTPUT_3_REG] = 0xc, |
123 | [RNG_STATUS_REG] = 0x10, |
124 | [RNG_INTACK_REG] = 0x10, |
125 | [RNG_CONTROL_REG] = 0x14, |
126 | [RNG_CONFIG_REG] = 0x18, |
127 | [RNG_ALARMCNT_REG] = 0x1c, |
128 | [RNG_FROENABLE_REG] = 0x20, |
129 | [RNG_FRODETUNE_REG] = 0x24, |
130 | [RNG_ALARMMASK_REG] = 0x28, |
131 | [RNG_ALARMSTOP_REG] = 0x2c, |
132 | [RNG_REV_REG] = 0x7c, |
133 | }; |
134 | |
135 | struct omap_rng_dev; |
136 | /** |
137 | * struct omap_rng_pdata - RNG IP block-specific data |
138 | * @regs: Pointer to the register offsets structure. |
139 | * @data_size: No. of bytes in RNG output. |
140 | * @data_present: Callback to determine if data is available. |
141 | * @init: Callback for IP specific initialization sequence. |
142 | * @cleanup: Callback for IP specific cleanup sequence. |
143 | */ |
144 | struct omap_rng_pdata { |
145 | u16 *regs; |
146 | u32 data_size; |
147 | u32 (*data_present)(struct omap_rng_dev *priv); |
148 | int (*init)(struct omap_rng_dev *priv); |
149 | void (*cleanup)(struct omap_rng_dev *priv); |
150 | }; |
151 | |
152 | struct omap_rng_dev { |
153 | void __iomem *base; |
154 | struct device *dev; |
155 | const struct omap_rng_pdata *pdata; |
156 | struct hwrng rng; |
157 | struct clk *clk; |
158 | struct clk *clk_reg; |
159 | }; |
160 | |
161 | static inline u32 omap_rng_read(struct omap_rng_dev *priv, u16 reg) |
162 | { |
163 | return __raw_readl(addr: priv->base + priv->pdata->regs[reg]); |
164 | } |
165 | |
166 | static inline void omap_rng_write(struct omap_rng_dev *priv, u16 reg, |
167 | u32 val) |
168 | { |
169 | __raw_writel(val, addr: priv->base + priv->pdata->regs[reg]); |
170 | } |
171 | |
172 | |
173 | static int omap_rng_do_read(struct hwrng *rng, void *data, size_t max, |
174 | bool wait) |
175 | { |
176 | struct omap_rng_dev *priv; |
177 | int i, present; |
178 | |
179 | priv = (struct omap_rng_dev *)rng->priv; |
180 | |
181 | if (max < priv->pdata->data_size) |
182 | return 0; |
183 | |
184 | for (i = 0; i < RNG_DATA_FILL_TIMEOUT; i++) { |
185 | present = priv->pdata->data_present(priv); |
186 | if (present || !wait) |
187 | break; |
188 | |
189 | udelay(10); |
190 | } |
191 | if (!present) |
192 | return 0; |
193 | |
194 | memcpy_fromio(data, priv->base + priv->pdata->regs[RNG_OUTPUT_0_REG], |
195 | priv->pdata->data_size); |
196 | |
197 | if (priv->pdata->regs[RNG_INTACK_REG]) |
198 | omap_rng_write(priv, reg: RNG_INTACK_REG, RNG_REG_INTACK_RDY_MASK); |
199 | |
200 | return priv->pdata->data_size; |
201 | } |
202 | |
203 | static int omap_rng_init(struct hwrng *rng) |
204 | { |
205 | struct omap_rng_dev *priv; |
206 | |
207 | priv = (struct omap_rng_dev *)rng->priv; |
208 | return priv->pdata->init(priv); |
209 | } |
210 | |
211 | static void omap_rng_cleanup(struct hwrng *rng) |
212 | { |
213 | struct omap_rng_dev *priv; |
214 | |
215 | priv = (struct omap_rng_dev *)rng->priv; |
216 | priv->pdata->cleanup(priv); |
217 | } |
218 | |
219 | |
220 | static inline u32 omap2_rng_data_present(struct omap_rng_dev *priv) |
221 | { |
222 | return omap_rng_read(priv, reg: RNG_STATUS_REG) ? 0 : 1; |
223 | } |
224 | |
225 | static int omap2_rng_init(struct omap_rng_dev *priv) |
226 | { |
227 | omap_rng_write(priv, reg: RNG_SYSCONFIG_REG, val: 0x1); |
228 | return 0; |
229 | } |
230 | |
231 | static void omap2_rng_cleanup(struct omap_rng_dev *priv) |
232 | { |
233 | omap_rng_write(priv, reg: RNG_SYSCONFIG_REG, val: 0x0); |
234 | } |
235 | |
236 | static struct omap_rng_pdata omap2_rng_pdata = { |
237 | .regs = (u16 *)reg_map_omap2, |
238 | .data_size = OMAP2_RNG_OUTPUT_SIZE, |
239 | .data_present = omap2_rng_data_present, |
240 | .init = omap2_rng_init, |
241 | .cleanup = omap2_rng_cleanup, |
242 | }; |
243 | |
244 | static inline u32 omap4_rng_data_present(struct omap_rng_dev *priv) |
245 | { |
246 | return omap_rng_read(priv, reg: RNG_STATUS_REG) & RNG_REG_STATUS_RDY; |
247 | } |
248 | |
249 | static int eip76_rng_init(struct omap_rng_dev *priv) |
250 | { |
251 | u32 val; |
252 | |
253 | /* Return if RNG is already running. */ |
254 | if (omap_rng_read(priv, reg: RNG_CONTROL_REG) & RNG_CONTROL_ENABLE_TRNG_MASK) |
255 | return 0; |
256 | |
257 | /* Number of 512 bit blocks of raw Noise Source output data that must |
258 | * be processed by either the Conditioning Function or the |
259 | * SP 800-90 DRBG ‘BC_DF’ functionality to yield a ‘full entropy’ |
260 | * output value. |
261 | */ |
262 | val = 0x5 << RNG_CONFIG_MIN_REFIL_CYCLES_SHIFT; |
263 | |
264 | /* Number of FRO samples that are XOR-ed together into one bit to be |
265 | * shifted into the main shift register |
266 | */ |
267 | val |= RNG_CONFIG_MAX_REFIL_CYCLES << RNG_CONFIG_MAX_REFIL_CYCLES_SHIFT; |
268 | omap_rng_write(priv, reg: RNG_CONFIG_REG, val); |
269 | |
270 | /* Enable all available FROs */ |
271 | omap_rng_write(priv, reg: RNG_FRODETUNE_REG, val: 0x0); |
272 | omap_rng_write(priv, reg: RNG_FROENABLE_REG, RNG_REG_FROENABLE_MASK); |
273 | |
274 | /* Enable TRNG */ |
275 | val = RNG_CONTROL_ENABLE_TRNG_MASK; |
276 | omap_rng_write(priv, reg: RNG_CONTROL_REG, val); |
277 | |
278 | return 0; |
279 | } |
280 | |
281 | static int omap4_rng_init(struct omap_rng_dev *priv) |
282 | { |
283 | u32 val; |
284 | |
285 | /* Return if RNG is already running. */ |
286 | if (omap_rng_read(priv, reg: RNG_CONTROL_REG) & RNG_CONTROL_ENABLE_TRNG_MASK) |
287 | return 0; |
288 | |
289 | val = RNG_CONFIG_MIN_REFIL_CYCLES << RNG_CONFIG_MIN_REFIL_CYCLES_SHIFT; |
290 | val |= RNG_CONFIG_MAX_REFIL_CYCLES << RNG_CONFIG_MAX_REFIL_CYCLES_SHIFT; |
291 | omap_rng_write(priv, reg: RNG_CONFIG_REG, val); |
292 | |
293 | omap_rng_write(priv, reg: RNG_FRODETUNE_REG, val: 0x0); |
294 | omap_rng_write(priv, reg: RNG_FROENABLE_REG, RNG_REG_FROENABLE_MASK); |
295 | val = RNG_ALARM_THRESHOLD << RNG_ALARMCNT_ALARM_TH_SHIFT; |
296 | val |= RNG_SHUTDOWN_THRESHOLD << RNG_ALARMCNT_SHUTDOWN_TH_SHIFT; |
297 | omap_rng_write(priv, reg: RNG_ALARMCNT_REG, val); |
298 | |
299 | val = RNG_CONTROL_STARTUP_CYCLES << RNG_CONTROL_STARTUP_CYCLES_SHIFT; |
300 | val |= RNG_CONTROL_ENABLE_TRNG_MASK; |
301 | omap_rng_write(priv, reg: RNG_CONTROL_REG, val); |
302 | |
303 | return 0; |
304 | } |
305 | |
306 | static void omap4_rng_cleanup(struct omap_rng_dev *priv) |
307 | { |
308 | int val; |
309 | |
310 | val = omap_rng_read(priv, reg: RNG_CONTROL_REG); |
311 | val &= ~RNG_CONTROL_ENABLE_TRNG_MASK; |
312 | omap_rng_write(priv, reg: RNG_CONTROL_REG, val); |
313 | } |
314 | |
315 | static irqreturn_t omap4_rng_irq(int irq, void *dev_id) |
316 | { |
317 | struct omap_rng_dev *priv = dev_id; |
318 | u32 fro_detune, fro_enable; |
319 | |
320 | /* |
321 | * Interrupt raised by a fro shutdown threshold, do the following: |
322 | * 1. Clear the alarm events. |
323 | * 2. De tune the FROs which are shutdown. |
324 | * 3. Re enable the shutdown FROs. |
325 | */ |
326 | omap_rng_write(priv, reg: RNG_ALARMMASK_REG, val: 0x0); |
327 | omap_rng_write(priv, reg: RNG_ALARMSTOP_REG, val: 0x0); |
328 | |
329 | fro_enable = omap_rng_read(priv, reg: RNG_FROENABLE_REG); |
330 | fro_detune = ~fro_enable & RNG_REG_FRODETUNE_MASK; |
331 | fro_detune = fro_detune | omap_rng_read(priv, reg: RNG_FRODETUNE_REG); |
332 | fro_enable = RNG_REG_FROENABLE_MASK; |
333 | |
334 | omap_rng_write(priv, reg: RNG_FRODETUNE_REG, val: fro_detune); |
335 | omap_rng_write(priv, reg: RNG_FROENABLE_REG, val: fro_enable); |
336 | |
337 | omap_rng_write(priv, reg: RNG_INTACK_REG, RNG_REG_INTACK_SHUTDOWN_OFLO_MASK); |
338 | |
339 | return IRQ_HANDLED; |
340 | } |
341 | |
342 | static struct omap_rng_pdata omap4_rng_pdata = { |
343 | .regs = (u16 *)reg_map_omap4, |
344 | .data_size = OMAP4_RNG_OUTPUT_SIZE, |
345 | .data_present = omap4_rng_data_present, |
346 | .init = omap4_rng_init, |
347 | .cleanup = omap4_rng_cleanup, |
348 | }; |
349 | |
350 | static struct omap_rng_pdata eip76_rng_pdata = { |
351 | .regs = (u16 *)reg_map_eip76, |
352 | .data_size = EIP76_RNG_OUTPUT_SIZE, |
353 | .data_present = omap4_rng_data_present, |
354 | .init = eip76_rng_init, |
355 | .cleanup = omap4_rng_cleanup, |
356 | }; |
357 | |
358 | static const struct of_device_id omap_rng_of_match[] __maybe_unused = { |
359 | { |
360 | .compatible = "ti,omap2-rng" , |
361 | .data = &omap2_rng_pdata, |
362 | }, |
363 | { |
364 | .compatible = "ti,omap4-rng" , |
365 | .data = &omap4_rng_pdata, |
366 | }, |
367 | { |
368 | .compatible = "inside-secure,safexcel-eip76" , |
369 | .data = &eip76_rng_pdata, |
370 | }, |
371 | {}, |
372 | }; |
373 | MODULE_DEVICE_TABLE(of, omap_rng_of_match); |
374 | |
375 | static int of_get_omap_rng_device_details(struct omap_rng_dev *priv, |
376 | struct platform_device *pdev) |
377 | { |
378 | struct device *dev = &pdev->dev; |
379 | int irq, err; |
380 | |
381 | priv->pdata = of_device_get_match_data(dev); |
382 | if (!priv->pdata) |
383 | return -ENODEV; |
384 | |
385 | |
386 | if (of_device_is_compatible(device: dev->of_node, "ti,omap4-rng" ) || |
387 | of_device_is_compatible(device: dev->of_node, "inside-secure,safexcel-eip76" )) { |
388 | irq = platform_get_irq(pdev, 0); |
389 | if (irq < 0) |
390 | return irq; |
391 | |
392 | err = devm_request_irq(dev, irq, handler: omap4_rng_irq, |
393 | IRQF_TRIGGER_NONE, devname: dev_name(dev), dev_id: priv); |
394 | if (err) { |
395 | dev_err(dev, "unable to request irq %d, err = %d\n" , |
396 | irq, err); |
397 | return err; |
398 | } |
399 | |
400 | /* |
401 | * On OMAP4, enabling the shutdown_oflo interrupt is |
402 | * done in the interrupt mask register. There is no |
403 | * such register on EIP76, and it's enabled by the |
404 | * same bit in the control register |
405 | */ |
406 | if (priv->pdata->regs[RNG_INTMASK_REG]) |
407 | omap_rng_write(priv, reg: RNG_INTMASK_REG, |
408 | RNG_SHUTDOWN_OFLO_MASK); |
409 | else |
410 | omap_rng_write(priv, reg: RNG_CONTROL_REG, |
411 | RNG_SHUTDOWN_OFLO_MASK); |
412 | } |
413 | return 0; |
414 | } |
415 | |
416 | static int get_omap_rng_device_details(struct omap_rng_dev *omap_rng) |
417 | { |
418 | /* Only OMAP2/3 can be non-DT */ |
419 | omap_rng->pdata = &omap2_rng_pdata; |
420 | return 0; |
421 | } |
422 | |
423 | static int omap_rng_probe(struct platform_device *pdev) |
424 | { |
425 | struct omap_rng_dev *priv; |
426 | struct device *dev = &pdev->dev; |
427 | int ret; |
428 | |
429 | priv = devm_kzalloc(dev, size: sizeof(struct omap_rng_dev), GFP_KERNEL); |
430 | if (!priv) |
431 | return -ENOMEM; |
432 | |
433 | priv->rng.read = omap_rng_do_read; |
434 | priv->rng.init = omap_rng_init; |
435 | priv->rng.cleanup = omap_rng_cleanup; |
436 | priv->rng.quality = 900; |
437 | |
438 | priv->rng.priv = (unsigned long)priv; |
439 | platform_set_drvdata(pdev, data: priv); |
440 | priv->dev = dev; |
441 | |
442 | priv->base = devm_platform_ioremap_resource(pdev, index: 0); |
443 | if (IS_ERR(ptr: priv->base)) { |
444 | ret = PTR_ERR(ptr: priv->base); |
445 | goto err_ioremap; |
446 | } |
447 | |
448 | priv->rng.name = devm_kstrdup(dev, s: dev_name(dev), GFP_KERNEL); |
449 | if (!priv->rng.name) { |
450 | ret = -ENOMEM; |
451 | goto err_ioremap; |
452 | } |
453 | |
454 | pm_runtime_enable(dev: &pdev->dev); |
455 | ret = pm_runtime_resume_and_get(dev: &pdev->dev); |
456 | if (ret < 0) { |
457 | dev_err(&pdev->dev, "Failed to runtime_get device: %d\n" , ret); |
458 | goto err_ioremap; |
459 | } |
460 | |
461 | priv->clk = devm_clk_get(dev: &pdev->dev, NULL); |
462 | if (PTR_ERR(ptr: priv->clk) == -EPROBE_DEFER) |
463 | return -EPROBE_DEFER; |
464 | if (!IS_ERR(ptr: priv->clk)) { |
465 | ret = clk_prepare_enable(clk: priv->clk); |
466 | if (ret) { |
467 | dev_err(&pdev->dev, |
468 | "Unable to enable the clk: %d\n" , ret); |
469 | goto err_register; |
470 | } |
471 | } |
472 | |
473 | priv->clk_reg = devm_clk_get(dev: &pdev->dev, id: "reg" ); |
474 | if (PTR_ERR(ptr: priv->clk_reg) == -EPROBE_DEFER) |
475 | return -EPROBE_DEFER; |
476 | if (!IS_ERR(ptr: priv->clk_reg)) { |
477 | ret = clk_prepare_enable(clk: priv->clk_reg); |
478 | if (ret) { |
479 | dev_err(&pdev->dev, |
480 | "Unable to enable the register clk: %d\n" , |
481 | ret); |
482 | goto err_register; |
483 | } |
484 | } |
485 | |
486 | ret = (dev->of_node) ? of_get_omap_rng_device_details(priv, pdev) : |
487 | get_omap_rng_device_details(omap_rng: priv); |
488 | if (ret) |
489 | goto err_register; |
490 | |
491 | ret = devm_hwrng_register(dev: &pdev->dev, rng: &priv->rng); |
492 | if (ret) |
493 | goto err_register; |
494 | |
495 | dev_info(&pdev->dev, "Random Number Generator ver. %02x\n" , |
496 | omap_rng_read(priv, RNG_REV_REG)); |
497 | |
498 | return 0; |
499 | |
500 | err_register: |
501 | priv->base = NULL; |
502 | pm_runtime_put_sync(dev: &pdev->dev); |
503 | pm_runtime_disable(dev: &pdev->dev); |
504 | |
505 | clk_disable_unprepare(clk: priv->clk_reg); |
506 | clk_disable_unprepare(clk: priv->clk); |
507 | err_ioremap: |
508 | dev_err(dev, "initialization failed.\n" ); |
509 | return ret; |
510 | } |
511 | |
512 | static int omap_rng_remove(struct platform_device *pdev) |
513 | { |
514 | struct omap_rng_dev *priv = platform_get_drvdata(pdev); |
515 | |
516 | |
517 | priv->pdata->cleanup(priv); |
518 | |
519 | pm_runtime_put_sync(dev: &pdev->dev); |
520 | pm_runtime_disable(dev: &pdev->dev); |
521 | |
522 | clk_disable_unprepare(clk: priv->clk); |
523 | clk_disable_unprepare(clk: priv->clk_reg); |
524 | |
525 | return 0; |
526 | } |
527 | |
528 | static int __maybe_unused omap_rng_suspend(struct device *dev) |
529 | { |
530 | struct omap_rng_dev *priv = dev_get_drvdata(dev); |
531 | |
532 | priv->pdata->cleanup(priv); |
533 | pm_runtime_put_sync(dev); |
534 | |
535 | return 0; |
536 | } |
537 | |
538 | static int __maybe_unused omap_rng_resume(struct device *dev) |
539 | { |
540 | struct omap_rng_dev *priv = dev_get_drvdata(dev); |
541 | int ret; |
542 | |
543 | ret = pm_runtime_resume_and_get(dev); |
544 | if (ret < 0) { |
545 | dev_err(dev, "Failed to runtime_get device: %d\n" , ret); |
546 | return ret; |
547 | } |
548 | |
549 | priv->pdata->init(priv); |
550 | |
551 | return 0; |
552 | } |
553 | |
554 | static SIMPLE_DEV_PM_OPS(omap_rng_pm, omap_rng_suspend, omap_rng_resume); |
555 | |
556 | static struct platform_driver omap_rng_driver = { |
557 | .driver = { |
558 | .name = "omap_rng" , |
559 | .pm = &omap_rng_pm, |
560 | .of_match_table = of_match_ptr(omap_rng_of_match), |
561 | }, |
562 | .probe = omap_rng_probe, |
563 | .remove = omap_rng_remove, |
564 | }; |
565 | |
566 | module_platform_driver(omap_rng_driver); |
567 | MODULE_ALIAS("platform:omap_rng" ); |
568 | MODULE_AUTHOR("Deepak Saxena (and others)" ); |
569 | MODULE_LICENSE("GPL" ); |
570 | |