Merge branch 'master' of https://source.denx.de/u-boot/custodians/u-boot-riscv
[platform/kernel/u-boot.git] / drivers / rng / stm32mp1_rng.c
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 #define RNG_CR_CONDRST  BIT(30)
25
26 #define RNG_SR          0x04
27 #define RNG_SR_SEIS     BIT(6)
28 #define RNG_SR_CEIS     BIT(5)
29 #define RNG_SR_SECS     BIT(2)
30 #define RNG_SR_DRDY     BIT(0)
31
32 #define RNG_DR          0x08
33
34 struct stm32_rng_data {
35         bool has_cond_reset;
36 };
37
38 struct stm32_rng_plat {
39         fdt_addr_t base;
40         struct clk clk;
41         struct reset_ctl rst;
42         const struct stm32_rng_data *data;
43 };
44
45 static int stm32_rng_read(struct udevice *dev, void *data, size_t len)
46 {
47         int retval, i;
48         u32 sr, count, reg;
49         size_t increment;
50         struct stm32_rng_plat *pdata = dev_get_plat(dev);
51
52         while (len > 0) {
53                 retval = readl_poll_timeout(pdata->base + RNG_SR, sr,
54                                             sr & RNG_SR_DRDY, 10000);
55                 if (retval)
56                         return retval;
57
58                 if (sr & (RNG_SR_SEIS | RNG_SR_SECS)) {
59                         /* As per SoC TRM */
60                         clrbits_le32(pdata->base + RNG_SR, RNG_SR_SEIS);
61                         for (i = 0; i < 12; i++)
62                                 readl(pdata->base + RNG_DR);
63                         if (readl(pdata->base + RNG_SR) & RNG_SR_SEIS) {
64                                 log_err("RNG Noise");
65                                 return -EIO;
66                         }
67                         /* start again */
68                         continue;
69                 }
70
71                 /*
72                  * Once the DRDY bit is set, the RNG_DR register can
73                  * be read four consecutive times.
74                  */
75                 count = 4;
76                 while (len && count) {
77                         reg = readl(pdata->base + RNG_DR);
78                         memcpy(data, &reg, min(len, sizeof(u32)));
79                         increment = min(len, sizeof(u32));
80                         data += increment;
81                         len -= increment;
82                         count--;
83                 }
84         }
85
86         return 0;
87 }
88
89 static int stm32_rng_init(struct stm32_rng_plat *pdata)
90 {
91         int err;
92         u32 cr, sr;
93
94         err = clk_enable(&pdata->clk);
95         if (err)
96                 return err;
97
98         cr = readl(pdata->base + RNG_CR);
99
100         /* Disable CED */
101         cr |= RNG_CR_CED;
102         if (pdata->data->has_cond_reset) {
103                 cr |= RNG_CR_CONDRST;
104                 writel(cr, pdata->base + RNG_CR);
105                 cr &= ~RNG_CR_CONDRST;
106                 writel(cr, pdata->base + RNG_CR);
107                 err = readl_poll_timeout(pdata->base + RNG_CR, cr,
108                                          (!(cr & RNG_CR_CONDRST)), 10000);
109                 if (err)
110                         return err;
111         }
112
113         /* clear error indicators */
114         writel(0, pdata->base + RNG_SR);
115
116         cr |= RNG_CR_RNGEN;
117         writel(cr, pdata->base + RNG_CR);
118
119         err = readl_poll_timeout(pdata->base + RNG_SR, sr,
120                                  sr & RNG_SR_DRDY, 10000);
121         return err;
122 }
123
124 static int stm32_rng_cleanup(struct stm32_rng_plat *pdata)
125 {
126         writel(0, pdata->base + RNG_CR);
127
128         return clk_disable(&pdata->clk);
129 }
130
131 static int stm32_rng_probe(struct udevice *dev)
132 {
133         struct stm32_rng_plat *pdata = dev_get_plat(dev);
134
135         pdata->data = (struct stm32_rng_data *)dev_get_driver_data(dev);
136
137         reset_assert(&pdata->rst);
138         udelay(20);
139         reset_deassert(&pdata->rst);
140
141         return stm32_rng_init(pdata);
142 }
143
144 static int stm32_rng_remove(struct udevice *dev)
145 {
146         struct stm32_rng_plat *pdata = dev_get_plat(dev);
147
148         return stm32_rng_cleanup(pdata);
149 }
150
151 static int stm32_rng_of_to_plat(struct udevice *dev)
152 {
153         struct stm32_rng_plat *pdata = dev_get_plat(dev);
154         int err;
155
156         pdata->base = dev_read_addr(dev);
157         if (!pdata->base)
158                 return -ENOMEM;
159
160         err = clk_get_by_index(dev, 0, &pdata->clk);
161         if (err)
162                 return err;
163
164         err = reset_get_by_index(dev, 0, &pdata->rst);
165         if (err)
166                 return err;
167
168         return 0;
169 }
170
171 static const struct dm_rng_ops stm32_rng_ops = {
172         .read = stm32_rng_read,
173 };
174
175 static const struct stm32_rng_data stm32mp13_rng_data = {
176         .has_cond_reset = true,
177 };
178
179 static const struct stm32_rng_data stm32_rng_data = {
180         .has_cond_reset = false,
181 };
182
183 static const struct udevice_id stm32_rng_match[] = {
184         {.compatible = "st,stm32mp13-rng", .data = (ulong)&stm32mp13_rng_data},
185         {.compatible = "st,stm32-rng", .data = (ulong)&stm32_rng_data},
186         {},
187 };
188
189 U_BOOT_DRIVER(stm32_rng) = {
190         .name = "stm32-rng",
191         .id = UCLASS_RNG,
192         .of_match = stm32_rng_match,
193         .ops = &stm32_rng_ops,
194         .probe = stm32_rng_probe,
195         .remove = stm32_rng_remove,
196         .plat_auto      = sizeof(struct stm32_rng_plat),
197         .of_to_plat = stm32_rng_of_to_plat,
198 };