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