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