blob: e0f0a66c6e7319b40f858cfc135ab0d43ccd2f25 [file] [log] [blame]
Sughosh Ganu231ec902019-12-28 23:58:29 +05301// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2019, Linaro Limited
4 */
5
6#include <common.h>
7#include <clk.h>
8#include <dm.h>
9#include <reset.h>
10#include <rng.h>
11
12#include <asm/io.h>
13#include <linux/iopoll.h>
14#include <linux/kernel.h>
15
16#define RNG_CR 0x00
17#define RNG_CR_RNGEN BIT(2)
18#define RNG_CR_CED BIT(5)
19
20#define RNG_SR 0x04
21#define RNG_SR_SEIS BIT(6)
22#define RNG_SR_CEIS BIT(5)
23#define RNG_SR_SECS BIT(2)
24#define RNG_SR_DRDY BIT(0)
25
26#define RNG_DR 0x08
27
28struct stm32_rng_platdata {
29 fdt_addr_t base;
30 struct clk clk;
31 struct reset_ctl rst;
32};
33
34static int stm32_rng_read(struct udevice *dev, void *data, size_t len)
35{
Heinrich Schuchardt250b3032020-02-16 10:11:18 +010036 int retval, i;
Sughosh Ganu231ec902019-12-28 23:58:29 +053037 u32 sr, count, reg;
38 size_t increment;
39 struct stm32_rng_platdata *pdata = dev_get_platdata(dev);
40
41 while (len > 0) {
42 retval = readl_poll_timeout(pdata->base + RNG_SR, sr,
43 sr & RNG_SR_DRDY, 10000);
44 if (retval)
45 return retval;
46
47 if (sr & (RNG_SR_SEIS | RNG_SR_SECS)) {
48 /* As per SoC TRM */
49 clrbits_le32(pdata->base + RNG_SR, RNG_SR_SEIS);
50 for (i = 0; i < 12; i++)
51 readl(pdata->base + RNG_DR);
52 if (readl(pdata->base + RNG_SR) & RNG_SR_SEIS) {
53 printf("RNG Noise");
54 return -EIO;
55 }
56 /* start again */
57 continue;
58 }
59
60 /*
61 * Once the DRDY bit is set, the RNG_DR register can
62 * be read four consecutive times.
63 */
64 count = 4;
65 while (len && count) {
66 reg = readl(pdata->base + RNG_DR);
67 memcpy(data, &reg, min(len, sizeof(u32)));
68 increment = min(len, sizeof(u32));
69 data += increment;
70 len -= increment;
71 count--;
72 }
73 }
74
75 return 0;
76}
77
78static int stm32_rng_init(struct stm32_rng_platdata *pdata)
79{
80 int err;
81
82 err = clk_enable(&pdata->clk);
83 if (err)
84 return err;
85
86 /* Disable CED */
87 writel(RNG_CR_RNGEN | RNG_CR_CED, pdata->base + RNG_CR);
88
89 /* clear error indicators */
90 writel(0, pdata->base + RNG_SR);
91
92 return 0;
93}
94
95static int stm32_rng_cleanup(struct stm32_rng_platdata *pdata)
96{
97 writel(0, pdata->base + RNG_CR);
98
99 return clk_disable(&pdata->clk);
100}
101
102static int stm32_rng_probe(struct udevice *dev)
103{
104 struct stm32_rng_platdata *pdata = dev_get_platdata(dev);
105
106 reset_assert(&pdata->rst);
107 udelay(20);
108 reset_deassert(&pdata->rst);
109
110 return stm32_rng_init(pdata);
111}
112
113static int stm32_rng_remove(struct udevice *dev)
114{
115 struct stm32_rng_platdata *pdata = dev_get_platdata(dev);
116
117 return stm32_rng_cleanup(pdata);
118}
119
120static int stm32_rng_ofdata_to_platdata(struct udevice *dev)
121{
122 struct stm32_rng_platdata *pdata = dev_get_platdata(dev);
123 int err;
124
125 pdata->base = dev_read_addr(dev);
126 if (!pdata->base)
127 return -ENOMEM;
128
129 err = clk_get_by_index(dev, 0, &pdata->clk);
130 if (err)
131 return err;
132
133 err = reset_get_by_index(dev, 0, &pdata->rst);
134 if (err)
135 return err;
136
137 return 0;
138}
139
140static const struct dm_rng_ops stm32_rng_ops = {
141 .read = stm32_rng_read,
142};
143
144static const struct udevice_id stm32_rng_match[] = {
145 {
146 .compatible = "st,stm32-rng",
147 },
148 {},
149};
150
151U_BOOT_DRIVER(stm32_rng) = {
152 .name = "stm32-rng",
153 .id = UCLASS_RNG,
154 .of_match = stm32_rng_match,
155 .ops = &stm32_rng_ops,
156 .probe = stm32_rng_probe,
157 .remove = stm32_rng_remove,
158 .platdata_auto_alloc_size = sizeof(struct stm32_rng_platdata),
159 .ofdata_to_platdata = stm32_rng_ofdata_to_platdata,
160};