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