1 // SPDX-License-Identifier: GPL-2.0
3 * rockchip-rng.c True Random Number Generator driver for Rockchip RK3568 SoC
5 * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd.
6 * Copyright (c) 2022, Aurelien Jarno
8 * Lin Jinhan <troy.lin@rock-chips.com>
9 * Aurelien Jarno <aurelien@aurel32.net>
11 #include <linux/clk.h>
12 #include <linux/hw_random.h>
14 #include <linux/iopoll.h>
15 #include <linux/kernel.h>
16 #include <linux/module.h>
18 #include <linux/platform_device.h>
19 #include <linux/pm_runtime.h>
20 #include <linux/reset.h>
21 #include <linux/slab.h>
23 #define RK_RNG_AUTOSUSPEND_DELAY 100
24 #define RK_RNG_MAX_BYTE 32
25 #define RK_RNG_POLL_PERIOD_US 100
26 #define RK_RNG_POLL_TIMEOUT_US 10000
29 * TRNG collects osc ring output bit every RK_RNG_SAMPLE_CNT time. The value is
30 * a tradeoff between speed and quality and has been adjusted to get a quality
31 * of ~900 (~87.5% of FIPS 140-2 successes).
33 #define RK_RNG_SAMPLE_CNT 1000
35 /* TRNG registers from RK3568 TRM-Part2, section 5.4.1 */
36 #define TRNG_RST_CTL 0x0004
37 #define TRNG_RNG_CTL 0x0400
38 #define TRNG_RNG_CTL_LEN_64_BIT (0x00 << 4)
39 #define TRNG_RNG_CTL_LEN_128_BIT (0x01 << 4)
40 #define TRNG_RNG_CTL_LEN_192_BIT (0x02 << 4)
41 #define TRNG_RNG_CTL_LEN_256_BIT (0x03 << 4)
42 #define TRNG_RNG_CTL_OSC_RING_SPEED_0 (0x00 << 2)
43 #define TRNG_RNG_CTL_OSC_RING_SPEED_1 (0x01 << 2)
44 #define TRNG_RNG_CTL_OSC_RING_SPEED_2 (0x02 << 2)
45 #define TRNG_RNG_CTL_OSC_RING_SPEED_3 (0x03 << 2)
46 #define TRNG_RNG_CTL_MASK GENMASK(15, 0)
47 #define TRNG_RNG_CTL_ENABLE BIT(1)
48 #define TRNG_RNG_CTL_START BIT(0)
49 #define TRNG_RNG_SAMPLE_CNT 0x0404
50 #define TRNG_RNG_DOUT 0x0410
56 struct clk_bulk_data
*clk_bulks
;
59 /* The mask in the upper 16 bits determines the bits that are updated */
60 static void rk_rng_write_ctl(struct rk_rng
*rng
, u32 val
, u32 mask
)
62 writel((mask
<< 16) | val
, rng
->base
+ TRNG_RNG_CTL
);
65 static int rk_rng_init(struct hwrng
*rng
)
67 struct rk_rng
*rk_rng
= container_of(rng
, struct rk_rng
, rng
);
71 ret
= clk_bulk_prepare_enable(rk_rng
->clk_num
, rk_rng
->clk_bulks
);
73 dev_err((struct device
*) rk_rng
->rng
.priv
,
74 "Failed to enable clks %d\n", ret
);
78 /* set the sample period */
79 writel(RK_RNG_SAMPLE_CNT
, rk_rng
->base
+ TRNG_RNG_SAMPLE_CNT
);
81 /* set osc ring speed and enable it */
82 rk_rng_write_ctl(rk_rng
, TRNG_RNG_CTL_LEN_256_BIT
|
83 TRNG_RNG_CTL_OSC_RING_SPEED_0
|
90 static void rk_rng_cleanup(struct hwrng
*rng
)
92 struct rk_rng
*rk_rng
= container_of(rng
, struct rk_rng
, rng
);
95 rk_rng_write_ctl(rk_rng
, 0, TRNG_RNG_CTL_MASK
);
98 clk_bulk_disable_unprepare(rk_rng
->clk_num
, rk_rng
->clk_bulks
);
101 static int rk_rng_read(struct hwrng
*rng
, void *buf
, size_t max
, bool wait
)
103 struct rk_rng
*rk_rng
= container_of(rng
, struct rk_rng
, rng
);
104 size_t to_read
= min_t(size_t, max
, RK_RNG_MAX_BYTE
);
108 ret
= pm_runtime_resume_and_get((struct device
*) rk_rng
->rng
.priv
);
112 /* Start collecting random data */
113 rk_rng_write_ctl(rk_rng
, TRNG_RNG_CTL_START
, TRNG_RNG_CTL_START
);
115 ret
= readl_poll_timeout(rk_rng
->base
+ TRNG_RNG_CTL
, reg
,
116 !(reg
& TRNG_RNG_CTL_START
),
117 RK_RNG_POLL_PERIOD_US
,
118 RK_RNG_POLL_TIMEOUT_US
);
122 /* Read random data stored in the registers */
123 memcpy_fromio(buf
, rk_rng
->base
+ TRNG_RNG_DOUT
, to_read
);
125 pm_runtime_mark_last_busy((struct device
*) rk_rng
->rng
.priv
);
126 pm_runtime_put_sync_autosuspend((struct device
*) rk_rng
->rng
.priv
);
128 return (ret
< 0) ? ret
: to_read
;
131 static int rk_rng_probe(struct platform_device
*pdev
)
133 struct device
*dev
= &pdev
->dev
;
134 struct reset_control
*rst
;
135 struct rk_rng
*rk_rng
;
138 rk_rng
= devm_kzalloc(dev
, sizeof(*rk_rng
), GFP_KERNEL
);
142 rk_rng
->base
= devm_platform_ioremap_resource(pdev
, 0);
143 if (IS_ERR(rk_rng
->base
))
144 return PTR_ERR(rk_rng
->base
);
146 rk_rng
->clk_num
= devm_clk_bulk_get_all(dev
, &rk_rng
->clk_bulks
);
147 if (rk_rng
->clk_num
< 0)
148 return dev_err_probe(dev
, rk_rng
->clk_num
,
149 "Failed to get clks property\n");
151 rst
= devm_reset_control_array_get_exclusive(&pdev
->dev
);
153 return dev_err_probe(dev
, PTR_ERR(rst
), "Failed to get reset property\n");
155 reset_control_assert(rst
);
157 reset_control_deassert(rst
);
159 platform_set_drvdata(pdev
, rk_rng
);
161 rk_rng
->rng
.name
= dev_driver_string(dev
);
162 if (!IS_ENABLED(CONFIG_PM
)) {
163 rk_rng
->rng
.init
= rk_rng_init
;
164 rk_rng
->rng
.cleanup
= rk_rng_cleanup
;
166 rk_rng
->rng
.read
= rk_rng_read
;
167 rk_rng
->rng
.priv
= (unsigned long) dev
;
168 rk_rng
->rng
.quality
= 900;
170 pm_runtime_set_autosuspend_delay(dev
, RK_RNG_AUTOSUSPEND_DELAY
);
171 pm_runtime_use_autosuspend(dev
);
172 ret
= devm_pm_runtime_enable(dev
);
174 return dev_err_probe(&pdev
->dev
, ret
, "Runtime pm activation failed.\n");
176 ret
= devm_hwrng_register(dev
, &rk_rng
->rng
);
178 return dev_err_probe(&pdev
->dev
, ret
, "Failed to register Rockchip hwrng\n");
183 static int __maybe_unused
rk_rng_runtime_suspend(struct device
*dev
)
185 struct rk_rng
*rk_rng
= dev_get_drvdata(dev
);
187 rk_rng_cleanup(&rk_rng
->rng
);
192 static int __maybe_unused
rk_rng_runtime_resume(struct device
*dev
)
194 struct rk_rng
*rk_rng
= dev_get_drvdata(dev
);
196 return rk_rng_init(&rk_rng
->rng
);
199 static const struct dev_pm_ops rk_rng_pm_ops
= {
200 SET_RUNTIME_PM_OPS(rk_rng_runtime_suspend
,
201 rk_rng_runtime_resume
, NULL
)
202 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend
,
203 pm_runtime_force_resume
)
206 static const struct of_device_id rk_rng_dt_match
[] = {
207 { .compatible
= "rockchip,rk3568-rng", },
211 MODULE_DEVICE_TABLE(of
, rk_rng_dt_match
);
213 static struct platform_driver rk_rng_driver
= {
215 .name
= "rockchip-rng",
216 .pm
= &rk_rng_pm_ops
,
217 .of_match_table
= rk_rng_dt_match
,
219 .probe
= rk_rng_probe
,
222 module_platform_driver(rk_rng_driver
);
224 MODULE_DESCRIPTION("Rockchip RK3568 True Random Number Generator driver");
225 MODULE_AUTHOR("Lin Jinhan <troy.lin@rock-chips.com>");
226 MODULE_AUTHOR("Aurelien Jarno <aurelien@aurel32.net>");
227 MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
228 MODULE_LICENSE("GPL");