blob: 7ee74718759b2cd17c6286440753021ad9fe7143 [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>
Simon Glassf7ae49f2020-05-10 11:40:05 -06009#include <log.h>
Sughosh Ganu231ec902019-12-28 23:58:29 +053010#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
29struct stm32_rng_platdata {
30 fdt_addr_t base;
31 struct clk clk;
32 struct reset_ctl rst;
33};
34
35static int stm32_rng_read(struct udevice *dev, void *data, size_t len)
36{
Heinrich Schuchardt250b3032020-02-16 10:11:18 +010037 int retval, i;
Sughosh Ganu231ec902019-12-28 23:58:29 +053038 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
79static 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
96static 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
103static 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
114static 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
121static 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
141static const struct dm_rng_ops stm32_rng_ops = {
142 .read = stm32_rng_read,
143};
144
145static const struct udevice_id stm32_rng_match[] = {
146 {
147 .compatible = "st,stm32-rng",
148 },
149 {},
150};
151
152U_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};