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> |
414a3c1b | 25 | #include <linux/kernel.h> |
02666360 | 26 | #include <linux/slab.h> |
665d92fa | 27 | #include <linux/pm_runtime.h> |
c903970c | 28 | #include <linux/of.h> |
e83872c9 | 29 | #include <linux/interrupt.h> |
38321242 | 30 | #include <linux/clk.h> |
f0ba303b | 31 | #include <linux/io.h> |
ebc915ad | 32 | |
e83872c9 LV |
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 | |
38321242 | 65 | #define EIP76_RNG_OUTPUT_SIZE 0x10 |
e83872c9 | 66 | |
be867f98 SG |
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 | ||
e83872c9 | 74 | enum { |
e54feeb0 RP |
75 | RNG_OUTPUT_0_REG = 0, |
76 | RNG_OUTPUT_1_REG, | |
77 | RNG_OUTPUT_2_REG, | |
78 | RNG_OUTPUT_3_REG, | |
e83872c9 LV |
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[] = { | |
e54feeb0 | 94 | [RNG_OUTPUT_0_REG] = 0x0, |
e83872c9 LV |
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[] = { | |
e54feeb0 RP |
102 | [RNG_OUTPUT_0_REG] = 0x0, |
103 | [RNG_OUTPUT_1_REG] = 0x4, | |
e83872c9 LV |
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 | }; | |
ebc915ad | 117 | |
38321242 RP |
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 | ||
e83872c9 | 135 | struct omap_rng_dev; |
02666360 | 136 | /** |
e83872c9 LV |
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. | |
02666360 | 143 | */ |
e83872c9 LV |
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); | |
02666360 | 150 | }; |
ebc915ad | 151 | |
e83872c9 LV |
152 | struct omap_rng_dev { |
153 | void __iomem *base; | |
154 | struct device *dev; | |
155 | const struct omap_rng_pdata *pdata; | |
b23d2d92 | 156 | struct hwrng rng; |
38321242 | 157 | struct clk *clk; |
b166be00 | 158 | struct clk *clk_reg; |
e83872c9 LV |
159 | }; |
160 | ||
161 | static inline u32 omap_rng_read(struct omap_rng_dev *priv, u16 reg) | |
162 | { | |
163 | return __raw_readl(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) | |
ebc915ad | 168 | { |
e83872c9 | 169 | __raw_writel(val, priv->base + priv->pdata->regs[reg]); |
ebc915ad MB |
170 | } |
171 | ||
69eb4d01 RP |
172 | |
173 | static int omap_rng_do_read(struct hwrng *rng, void *data, size_t max, | |
174 | bool wait) | |
ebc915ad | 175 | { |
e83872c9 | 176 | struct omap_rng_dev *priv; |
69eb4d01 | 177 | int i, present; |
984e976f | 178 | |
e83872c9 | 179 | priv = (struct omap_rng_dev *)rng->priv; |
02666360 | 180 | |
69eb4d01 RP |
181 | if (max < priv->pdata->data_size) |
182 | return 0; | |
183 | ||
be867f98 | 184 | for (i = 0; i < RNG_DATA_FILL_TIMEOUT; i++) { |
69eb4d01 RP |
185 | present = priv->pdata->data_present(priv); |
186 | if (present || !wait) | |
984e976f | 187 | break; |
69eb4d01 | 188 | |
984e976f PM |
189 | udelay(10); |
190 | } | |
69eb4d01 RP |
191 | if (!present) |
192 | return 0; | |
e83872c9 | 193 | |
e54feeb0 | 194 | memcpy_fromio(data, priv->base + priv->pdata->regs[RNG_OUTPUT_0_REG], |
69eb4d01 | 195 | priv->pdata->data_size); |
e83872c9 LV |
196 | |
197 | if (priv->pdata->regs[RNG_INTACK_REG]) | |
198 | omap_rng_write(priv, RNG_INTACK_REG, RNG_REG_INTACK_RDY_MASK); | |
69eb4d01 RP |
199 | |
200 | return priv->pdata->data_size; | |
e83872c9 LV |
201 | } |
202 | ||
a246968e OJ |
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 | ||
a246968e OJ |
219 | |
220 | static inline u32 omap2_rng_data_present(struct omap_rng_dev *priv) | |
221 | { | |
222 | return omap_rng_read(priv, RNG_STATUS_REG) ? 0 : 1; | |
223 | } | |
224 | ||
225 | static int omap2_rng_init(struct omap_rng_dev *priv) | |
226 | { | |
227 | omap_rng_write(priv, RNG_SYSCONFIG_REG, 0x1); | |
228 | return 0; | |
229 | } | |
230 | ||
231 | static void omap2_rng_cleanup(struct omap_rng_dev *priv) | |
232 | { | |
233 | omap_rng_write(priv, RNG_SYSCONFIG_REG, 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 | ||
a246968e OJ |
244 | static inline u32 omap4_rng_data_present(struct omap_rng_dev *priv) |
245 | { | |
246 | return omap_rng_read(priv, RNG_STATUS_REG) & RNG_REG_STATUS_RDY; | |
247 | } | |
248 | ||
38321242 RP |
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, 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, RNG_CONFIG_REG, val); | |
269 | ||
270 | /* Enable all available FROs */ | |
271 | omap_rng_write(priv, RNG_FRODETUNE_REG, 0x0); | |
272 | omap_rng_write(priv, RNG_FROENABLE_REG, RNG_REG_FROENABLE_MASK); | |
273 | ||
274 | /* Enable TRNG */ | |
275 | val = RNG_CONTROL_ENABLE_TRNG_MASK; | |
276 | omap_rng_write(priv, RNG_CONTROL_REG, val); | |
277 | ||
278 | return 0; | |
279 | } | |
280 | ||
e83872c9 LV |
281 | static int omap4_rng_init(struct omap_rng_dev *priv) |
282 | { | |
283 | u32 val; | |
284 | ||
285 | /* Return if RNG is already running. */ | |
656d7e7e | 286 | if (omap_rng_read(priv, RNG_CONTROL_REG) & RNG_CONTROL_ENABLE_TRNG_MASK) |
e83872c9 LV |
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, RNG_CONFIG_REG, val); | |
292 | ||
293 | omap_rng_write(priv, RNG_FRODETUNE_REG, 0x0); | |
294 | omap_rng_write(priv, 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, 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, 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, RNG_CONTROL_REG); | |
311 | val &= ~RNG_CONTROL_ENABLE_TRNG_MASK; | |
1a5addfe | 312 | omap_rng_write(priv, RNG_CONTROL_REG, val); |
e83872c9 LV |
313 | } |
314 | ||
e83872c9 LV |
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, RNG_ALARMMASK_REG, 0x0); | |
327 | omap_rng_write(priv, RNG_ALARMSTOP_REG, 0x0); | |
328 | ||
329 | fro_enable = omap_rng_read(priv, RNG_FROENABLE_REG); | |
330 | fro_detune = ~fro_enable & RNG_REG_FRODETUNE_MASK; | |
331 | fro_detune = fro_detune | omap_rng_read(priv, RNG_FRODETUNE_REG); | |
332 | fro_enable = RNG_REG_FROENABLE_MASK; | |
02666360 | 333 | |
e83872c9 LV |
334 | omap_rng_write(priv, RNG_FRODETUNE_REG, fro_detune); |
335 | omap_rng_write(priv, RNG_FROENABLE_REG, fro_enable); | |
02666360 | 336 | |
e83872c9 | 337 | omap_rng_write(priv, RNG_INTACK_REG, RNG_REG_INTACK_SHUTDOWN_OFLO_MASK); |
ebc915ad | 338 | |
e83872c9 | 339 | return IRQ_HANDLED; |
ebc915ad MB |
340 | } |
341 | ||
e83872c9 LV |
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 | ||
38321242 RP |
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 | ||
414a3c1b | 358 | static const struct of_device_id omap_rng_of_match[] __maybe_unused = { |
e83872c9 LV |
359 | { |
360 | .compatible = "ti,omap2-rng", | |
361 | .data = &omap2_rng_pdata, | |
362 | }, | |
363 | { | |
364 | .compatible = "ti,omap4-rng", | |
365 | .data = &omap4_rng_pdata, | |
366 | }, | |
38321242 RP |
367 | { |
368 | .compatible = "inside-secure,safexcel-eip76", | |
369 | .data = &eip76_rng_pdata, | |
370 | }, | |
c903970c LV |
371 | {}, |
372 | }; | |
373 | MODULE_DEVICE_TABLE(of, omap_rng_of_match); | |
e83872c9 LV |
374 | |
375 | static int of_get_omap_rng_device_details(struct omap_rng_dev *priv, | |
376 | struct platform_device *pdev) | |
377 | { | |
e83872c9 LV |
378 | struct device *dev = &pdev->dev; |
379 | int irq, err; | |
380 | ||
1015f19b TT |
381 | priv->pdata = of_device_get_match_data(dev); |
382 | if (!priv->pdata) | |
383 | return -ENODEV; | |
384 | ||
e83872c9 | 385 | |
38321242 RP |
386 | if (of_device_is_compatible(dev->of_node, "ti,omap4-rng") || |
387 | of_device_is_compatible(dev->of_node, "inside-secure,safexcel-eip76")) { | |
e83872c9 | 388 | irq = platform_get_irq(pdev, 0); |
b111418a | 389 | if (irq < 0) |
e83872c9 | 390 | return irq; |
e83872c9 LV |
391 | |
392 | err = devm_request_irq(dev, irq, omap4_rng_irq, | |
393 | IRQF_TRIGGER_NONE, dev_name(dev), priv); | |
394 | if (err) { | |
395 | dev_err(dev, "unable to request irq %d, err = %d\n", | |
396 | irq, err); | |
397 | return err; | |
398 | } | |
38321242 | 399 | |
b985735b TP |
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, RNG_INTMASK_REG, | |
408 | RNG_SHUTDOWN_OFLO_MASK); | |
409 | else | |
410 | omap_rng_write(priv, RNG_CONTROL_REG, | |
411 | RNG_SHUTDOWN_OFLO_MASK); | |
e83872c9 LV |
412 | } |
413 | return 0; | |
414 | } | |
c903970c | 415 | |
e83872c9 LV |
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 | ||
bcd2982a | 423 | static int omap_rng_probe(struct platform_device *pdev) |
ebc915ad | 424 | { |
e83872c9 | 425 | struct omap_rng_dev *priv; |
e83872c9 | 426 | struct device *dev = &pdev->dev; |
ebc915ad MB |
427 | int ret; |
428 | ||
e83872c9 | 429 | priv = devm_kzalloc(dev, sizeof(struct omap_rng_dev), GFP_KERNEL); |
9e9026a7 | 430 | if (!priv) |
02666360 | 431 | return -ENOMEM; |
02666360 | 432 | |
b23d2d92 RP |
433 | priv->rng.read = omap_rng_do_read; |
434 | priv->rng.init = omap_rng_init; | |
435 | priv->rng.cleanup = omap_rng_cleanup; | |
62f95ae8 | 436 | priv->rng.quality = 900; |
b23d2d92 RP |
437 | |
438 | priv->rng.priv = (unsigned long)priv; | |
1f539bcb | 439 | platform_set_drvdata(pdev, priv); |
e83872c9 | 440 | priv->dev = dev; |
ebc915ad | 441 | |
c7c16c58 | 442 | priv->base = devm_platform_ioremap_resource(pdev, 0); |
c7c9e1c3 TR |
443 | if (IS_ERR(priv->base)) { |
444 | ret = PTR_ERR(priv->base); | |
55c381e4 RK |
445 | goto err_ioremap; |
446 | } | |
ebc915ad | 447 | |
b23d2d92 RP |
448 | priv->rng.name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); |
449 | if (!priv->rng.name) { | |
450 | ret = -ENOMEM; | |
451 | goto err_ioremap; | |
452 | } | |
453 | ||
665d92fa | 454 | pm_runtime_enable(&pdev->dev); |
b21d14d9 | 455 | ret = pm_runtime_resume_and_get(&pdev->dev); |
ad8529fd | 456 | if (ret < 0) { |
61dc0a44 | 457 | dev_err(&pdev->dev, "Failed to runtime_get device: %d\n", ret); |
61dc0a44 NM |
458 | goto err_ioremap; |
459 | } | |
665d92fa | 460 | |
43ec540e | 461 | priv->clk = devm_clk_get(&pdev->dev, NULL); |
45586c70 | 462 | if (PTR_ERR(priv->clk) == -EPROBE_DEFER) |
43ec540e TP |
463 | return -EPROBE_DEFER; |
464 | if (!IS_ERR(priv->clk)) { | |
465 | ret = clk_prepare_enable(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 | ||
b166be00 | 473 | priv->clk_reg = devm_clk_get(&pdev->dev, "reg"); |
45586c70 | 474 | if (PTR_ERR(priv->clk_reg) == -EPROBE_DEFER) |
b166be00 GC |
475 | return -EPROBE_DEFER; |
476 | if (!IS_ERR(priv->clk_reg)) { | |
477 | ret = clk_prepare_enable(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 | ||
e83872c9 LV |
486 | ret = (dev->of_node) ? of_get_omap_rng_device_details(priv, pdev) : |
487 | get_omap_rng_device_details(priv); | |
488 | if (ret) | |
38321242 | 489 | goto err_register; |
e83872c9 | 490 | |
3e75241b | 491 | ret = devm_hwrng_register(&pdev->dev, &priv->rng); |
55c381e4 RK |
492 | if (ret) |
493 | goto err_register; | |
ebc915ad | 494 | |
f0d5a112 | 495 | dev_info(&pdev->dev, "Random Number Generator ver. %02x\n", |
e83872c9 | 496 | omap_rng_read(priv, RNG_REV_REG)); |
ebc915ad MB |
497 | |
498 | return 0; | |
55c381e4 RK |
499 | |
500 | err_register: | |
02666360 | 501 | priv->base = NULL; |
38321242 | 502 | pm_runtime_put_sync(&pdev->dev); |
665d92fa | 503 | pm_runtime_disable(&pdev->dev); |
38321242 | 504 | |
b166be00 | 505 | clk_disable_unprepare(priv->clk_reg); |
10bc320b | 506 | clk_disable_unprepare(priv->clk); |
55c381e4 | 507 | err_ioremap: |
e83872c9 | 508 | dev_err(dev, "initialization failed.\n"); |
55c381e4 | 509 | return ret; |
ebc915ad MB |
510 | } |
511 | ||
4da4a48b | 512 | static void omap_rng_remove(struct platform_device *pdev) |
ebc915ad | 513 | { |
e83872c9 | 514 | struct omap_rng_dev *priv = platform_get_drvdata(pdev); |
02666360 | 515 | |
ebc915ad | 516 | |
e83872c9 | 517 | priv->pdata->cleanup(priv); |
02666360 | 518 | |
665d92fa PW |
519 | pm_runtime_put_sync(&pdev->dev); |
520 | pm_runtime_disable(&pdev->dev); | |
ebc915ad | 521 | |
10bc320b | 522 | clk_disable_unprepare(priv->clk); |
b166be00 | 523 | clk_disable_unprepare(priv->clk_reg); |
ebc915ad MB |
524 | } |
525 | ||
a308d66f | 526 | static int __maybe_unused omap_rng_suspend(struct device *dev) |
ebc915ad | 527 | { |
e83872c9 | 528 | struct omap_rng_dev *priv = dev_get_drvdata(dev); |
02666360 | 529 | |
e83872c9 | 530 | priv->pdata->cleanup(priv); |
665d92fa | 531 | pm_runtime_put_sync(dev); |
02666360 | 532 | |
ebc915ad MB |
533 | return 0; |
534 | } | |
535 | ||
a308d66f | 536 | static int __maybe_unused omap_rng_resume(struct device *dev) |
ebc915ad | 537 | { |
e83872c9 | 538 | struct omap_rng_dev *priv = dev_get_drvdata(dev); |
61dc0a44 NM |
539 | int ret; |
540 | ||
b21d14d9 | 541 | ret = pm_runtime_resume_and_get(dev); |
ad8529fd | 542 | if (ret < 0) { |
61dc0a44 | 543 | dev_err(dev, "Failed to runtime_get device: %d\n", ret); |
61dc0a44 NM |
544 | return ret; |
545 | } | |
02666360 | 546 | |
e83872c9 | 547 | priv->pdata->init(priv); |
02666360 | 548 | |
af2bc7d2 | 549 | return 0; |
ebc915ad MB |
550 | } |
551 | ||
7650572a | 552 | static SIMPLE_DEV_PM_OPS(omap_rng_pm, omap_rng_suspend, omap_rng_resume); |
ebc915ad | 553 | |
af2bc7d2 DB |
554 | static struct platform_driver omap_rng_driver = { |
555 | .driver = { | |
556 | .name = "omap_rng", | |
a308d66f | 557 | .pm = &omap_rng_pm, |
c903970c | 558 | .of_match_table = of_match_ptr(omap_rng_of_match), |
af2bc7d2 | 559 | }, |
ebc915ad | 560 | .probe = omap_rng_probe, |
4da4a48b | 561 | .remove_new = omap_rng_remove, |
ebc915ad MB |
562 | }; |
563 | ||
4390f77b LV |
564 | module_platform_driver(omap_rng_driver); |
565 | MODULE_ALIAS("platform:omap_rng"); | |
ebc915ad | 566 | MODULE_AUTHOR("Deepak Saxena (and others)"); |
6d4e1993 | 567 | MODULE_DESCRIPTION("RNG driver for TI OMAP CPU family"); |
ebc915ad | 568 | MODULE_LICENSE("GPL"); |