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