Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/char/hw_random/ingenic-trng.c
29269 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Ingenic True Random Number Generator driver
4
* Copyright (c) 2019 漆鹏振 (Qi Pengzhen) <[email protected]>
5
* Copyright (c) 2020 周琰杰 (Zhou Yanjie) <[email protected]>
6
*/
7
8
#include <linux/clk.h>
9
#include <linux/err.h>
10
#include <linux/kernel.h>
11
#include <linux/hw_random.h>
12
#include <linux/io.h>
13
#include <linux/iopoll.h>
14
#include <linux/mod_devicetable.h>
15
#include <linux/module.h>
16
#include <linux/platform_device.h>
17
#include <linux/slab.h>
18
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
23
24
/* bits within the CFG register */
25
#define CFG_GEN_EN BIT(0)
26
27
/* bits within the STATUS register */
28
#define STATUS_RANDOM_RDY BIT(0)
29
30
struct ingenic_trng {
31
void __iomem *base;
32
struct hwrng rng;
33
};
34
35
static int ingenic_trng_init(struct hwrng *rng)
36
{
37
struct ingenic_trng *trng = container_of(rng, struct ingenic_trng, rng);
38
unsigned int ctrl;
39
40
ctrl = readl(trng->base + TRNG_REG_CFG_OFFSET);
41
ctrl |= CFG_GEN_EN;
42
writel(ctrl, trng->base + TRNG_REG_CFG_OFFSET);
43
44
return 0;
45
}
46
47
static void ingenic_trng_cleanup(struct hwrng *rng)
48
{
49
struct ingenic_trng *trng = container_of(rng, struct ingenic_trng, rng);
50
unsigned int ctrl;
51
52
ctrl = readl(trng->base + TRNG_REG_CFG_OFFSET);
53
ctrl &= ~CFG_GEN_EN;
54
writel(ctrl, trng->base + TRNG_REG_CFG_OFFSET);
55
}
56
57
static int ingenic_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
58
{
59
struct ingenic_trng *trng = container_of(rng, struct ingenic_trng, rng);
60
u32 *data = buf;
61
u32 status;
62
int ret;
63
64
ret = readl_poll_timeout(trng->base + TRNG_REG_STATUS_OFFSET, status,
65
status & STATUS_RANDOM_RDY, 10, 1000);
66
if (ret == -ETIMEDOUT) {
67
pr_err("%s: Wait for DTRNG data ready timeout\n", __func__);
68
return ret;
69
}
70
71
*data = readl(trng->base + TRNG_REG_RANDOMNUM_OFFSET);
72
73
return 4;
74
}
75
76
static int ingenic_trng_probe(struct platform_device *pdev)
77
{
78
struct ingenic_trng *trng;
79
struct clk *clk;
80
int ret;
81
82
trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
83
if (!trng)
84
return -ENOMEM;
85
86
trng->base = devm_platform_ioremap_resource(pdev, 0);
87
if (IS_ERR(trng->base))
88
return dev_err_probe(&pdev->dev, PTR_ERR(trng->base),
89
"%s: Failed to map DTRNG registers\n", __func__);
90
91
clk = devm_clk_get_enabled(&pdev->dev, NULL);
92
if (IS_ERR(clk))
93
return dev_err_probe(&pdev->dev, PTR_ERR(clk),
94
"%s: Cannot get and enable DTRNG clock\n", __func__);
95
96
trng->rng.name = pdev->name;
97
trng->rng.init = ingenic_trng_init;
98
trng->rng.cleanup = ingenic_trng_cleanup;
99
trng->rng.read = ingenic_trng_read;
100
101
ret = devm_hwrng_register(&pdev->dev, &trng->rng);
102
if (ret)
103
return dev_err_probe(&pdev->dev, ret, "Failed to register hwrng\n");
104
105
platform_set_drvdata(pdev, trng);
106
107
dev_info(&pdev->dev, "Ingenic DTRNG driver registered\n");
108
return 0;
109
}
110
111
static const struct of_device_id ingenic_trng_of_match[] = {
112
{ .compatible = "ingenic,x1830-dtrng" },
113
{ /* sentinel */ }
114
};
115
MODULE_DEVICE_TABLE(of, ingenic_trng_of_match);
116
117
static struct platform_driver ingenic_trng_driver = {
118
.probe = ingenic_trng_probe,
119
.driver = {
120
.name = "ingenic-trng",
121
.of_match_table = ingenic_trng_of_match,
122
},
123
};
124
125
module_platform_driver(ingenic_trng_driver);
126
127
MODULE_LICENSE("GPL");
128
MODULE_AUTHOR("漆鹏振 (Qi Pengzhen) <[email protected]>");
129
MODULE_AUTHOR("周琰杰 (Zhou Yanjie) <[email protected]>");
130
MODULE_DESCRIPTION("Ingenic True Random Number Generator driver");
131
132