1 // SPDX-License-Identifier: GPL-2.0
3 * Ingenic True Random Number Generator driver
4 * Copyright (c) 2019 漆鹏振 (Qi Pengzhen) <aric.pzqi@ingenic.com>
5 * Copyright (c) 2020 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
10 #include <linux/kernel.h>
11 #include <linux/hw_random.h>
13 #include <linux/iopoll.h>
14 #include <linux/module.h>
15 #include <linux/of_device.h>
16 #include <linux/platform_device.h>
17 #include <linux/slab.h>
19 /* DTRNG register offsets */
20 #define TRNG_REG_CFG_OFFSET 0x00
21 #define TRNG_REG_RANDOMNUM_OFFSET 0x04
22 #define TRNG_REG_STATUS_OFFSET 0x08
24 /* bits within the CFG register */
25 #define CFG_RDY_CLR BIT(12)
26 #define CFG_INT_MASK BIT(11)
27 #define CFG_GEN_EN BIT(0)
29 /* bits within the STATUS register */
30 #define STATUS_RANDOM_RDY BIT(0)
38 static int ingenic_trng_init(struct hwrng
*rng
)
40 struct ingenic_trng
*trng
= container_of(rng
, struct ingenic_trng
, rng
);
43 ctrl
= readl(trng
->base
+ TRNG_REG_CFG_OFFSET
);
45 writel(ctrl
, trng
->base
+ TRNG_REG_CFG_OFFSET
);
50 static void ingenic_trng_cleanup(struct hwrng
*rng
)
52 struct ingenic_trng
*trng
= container_of(rng
, struct ingenic_trng
, rng
);
55 ctrl
= readl(trng
->base
+ TRNG_REG_CFG_OFFSET
);
57 writel(ctrl
, trng
->base
+ TRNG_REG_CFG_OFFSET
);
60 static int ingenic_trng_read(struct hwrng
*rng
, void *buf
, size_t max
, bool wait
)
62 struct ingenic_trng
*trng
= container_of(rng
, struct ingenic_trng
, rng
);
67 ret
= readl_poll_timeout(trng
->base
+ TRNG_REG_STATUS_OFFSET
, status
,
68 status
& STATUS_RANDOM_RDY
, 10, 1000);
69 if (ret
== -ETIMEDOUT
) {
70 pr_err("%s: Wait for DTRNG data ready timeout\n", __func__
);
74 *data
= readl(trng
->base
+ TRNG_REG_RANDOMNUM_OFFSET
);
79 static int ingenic_trng_probe(struct platform_device
*pdev
)
81 struct ingenic_trng
*trng
;
84 trng
= devm_kzalloc(&pdev
->dev
, sizeof(*trng
), GFP_KERNEL
);
88 trng
->base
= devm_platform_ioremap_resource(pdev
, 0);
89 if (IS_ERR(trng
->base
)) {
90 pr_err("%s: Failed to map DTRNG registers\n", __func__
);
91 ret
= PTR_ERR(trng
->base
);
92 return PTR_ERR(trng
->base
);
95 trng
->clk
= devm_clk_get(&pdev
->dev
, NULL
);
96 if (IS_ERR(trng
->clk
)) {
97 ret
= PTR_ERR(trng
->clk
);
98 pr_crit("%s: Cannot get DTRNG clock\n", __func__
);
99 return PTR_ERR(trng
->clk
);
102 ret
= clk_prepare_enable(trng
->clk
);
104 pr_crit("%s: Unable to enable DTRNG clock\n", __func__
);
108 trng
->rng
.name
= pdev
->name
;
109 trng
->rng
.init
= ingenic_trng_init
;
110 trng
->rng
.cleanup
= ingenic_trng_cleanup
;
111 trng
->rng
.read
= ingenic_trng_read
;
113 ret
= hwrng_register(&trng
->rng
);
115 dev_err(&pdev
->dev
, "Failed to register hwrng\n");
119 platform_set_drvdata(pdev
, trng
);
121 dev_info(&pdev
->dev
, "Ingenic DTRNG driver registered\n");
125 static int ingenic_trng_remove(struct platform_device
*pdev
)
127 struct ingenic_trng
*trng
= platform_get_drvdata(pdev
);
130 hwrng_unregister(&trng
->rng
);
132 ctrl
= readl(trng
->base
+ TRNG_REG_CFG_OFFSET
);
134 writel(ctrl
, trng
->base
+ TRNG_REG_CFG_OFFSET
);
136 clk_disable_unprepare(trng
->clk
);
141 static const struct of_device_id ingenic_trng_of_match
[] = {
142 { .compatible
= "ingenic,x1830-dtrng" },
145 MODULE_DEVICE_TABLE(of
, ingenic_trng_of_match
);
147 static struct platform_driver ingenic_trng_driver
= {
148 .probe
= ingenic_trng_probe
,
149 .remove
= ingenic_trng_remove
,
151 .name
= "ingenic-trng",
152 .of_match_table
= ingenic_trng_of_match
,
156 module_platform_driver(ingenic_trng_driver
);
158 MODULE_LICENSE("GPL");
159 MODULE_AUTHOR("漆鹏振 (Qi Pengzhen) <aric.pzqi@ingenic.com>");
160 MODULE_AUTHOR("周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>");
161 MODULE_DESCRIPTION("Ingenic True Random Number Generator driver");