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