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