Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/char/hw_random/mtk-rng.c
29269 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* Driver for Mediatek Hardware Random Number Generator
4
*
5
* Copyright (C) 2017 Sean Wang <[email protected]>
6
*/
7
#define MTK_RNG_DEV KBUILD_MODNAME
8
9
#include <linux/clk.h>
10
#include <linux/delay.h>
11
#include <linux/err.h>
12
#include <linux/hw_random.h>
13
#include <linux/io.h>
14
#include <linux/iopoll.h>
15
#include <linux/kernel.h>
16
#include <linux/module.h>
17
#include <linux/of.h>
18
#include <linux/platform_device.h>
19
#include <linux/pm_runtime.h>
20
21
/* Runtime PM autosuspend timeout: */
22
#define RNG_AUTOSUSPEND_TIMEOUT 100
23
24
#define USEC_POLL 2
25
#define TIMEOUT_POLL 60
26
27
#define RNG_CTRL 0x00
28
#define RNG_EN BIT(0)
29
#define RNG_READY BIT(31)
30
31
#define RNG_DATA 0x08
32
33
#define to_mtk_rng(p) container_of(p, struct mtk_rng, rng)
34
35
struct mtk_rng {
36
void __iomem *base;
37
struct clk *clk;
38
struct hwrng rng;
39
struct device *dev;
40
};
41
42
static int mtk_rng_init(struct hwrng *rng)
43
{
44
struct mtk_rng *priv = to_mtk_rng(rng);
45
u32 val;
46
int err;
47
48
err = clk_prepare_enable(priv->clk);
49
if (err)
50
return err;
51
52
val = readl(priv->base + RNG_CTRL);
53
val |= RNG_EN;
54
writel(val, priv->base + RNG_CTRL);
55
56
return 0;
57
}
58
59
static void mtk_rng_cleanup(struct hwrng *rng)
60
{
61
struct mtk_rng *priv = to_mtk_rng(rng);
62
u32 val;
63
64
val = readl(priv->base + RNG_CTRL);
65
val &= ~RNG_EN;
66
writel(val, priv->base + RNG_CTRL);
67
68
clk_disable_unprepare(priv->clk);
69
}
70
71
static bool mtk_rng_wait_ready(struct hwrng *rng, bool wait)
72
{
73
struct mtk_rng *priv = to_mtk_rng(rng);
74
int ready;
75
76
ready = readl(priv->base + RNG_CTRL) & RNG_READY;
77
if (!ready && wait)
78
readl_poll_timeout_atomic(priv->base + RNG_CTRL, ready,
79
ready & RNG_READY, USEC_POLL,
80
TIMEOUT_POLL);
81
return !!(ready & RNG_READY);
82
}
83
84
static int mtk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
85
{
86
struct mtk_rng *priv = to_mtk_rng(rng);
87
int retval = 0;
88
89
pm_runtime_get_sync(priv->dev);
90
91
while (max >= sizeof(u32)) {
92
if (!mtk_rng_wait_ready(rng, wait))
93
break;
94
95
*(u32 *)buf = readl(priv->base + RNG_DATA);
96
retval += sizeof(u32);
97
buf += sizeof(u32);
98
max -= sizeof(u32);
99
}
100
101
pm_runtime_put_sync_autosuspend(priv->dev);
102
103
return retval || !wait ? retval : -EIO;
104
}
105
106
static int mtk_rng_probe(struct platform_device *pdev)
107
{
108
int ret;
109
struct mtk_rng *priv;
110
111
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
112
if (!priv)
113
return -ENOMEM;
114
115
priv->dev = &pdev->dev;
116
priv->rng.name = pdev->name;
117
#ifndef CONFIG_PM
118
priv->rng.init = mtk_rng_init;
119
priv->rng.cleanup = mtk_rng_cleanup;
120
#endif
121
priv->rng.read = mtk_rng_read;
122
priv->rng.quality = 900;
123
124
priv->clk = devm_clk_get(&pdev->dev, "rng");
125
if (IS_ERR(priv->clk)) {
126
ret = PTR_ERR(priv->clk);
127
dev_err(&pdev->dev, "no clock for device: %d\n", ret);
128
return ret;
129
}
130
131
priv->base = devm_platform_ioremap_resource(pdev, 0);
132
if (IS_ERR(priv->base))
133
return PTR_ERR(priv->base);
134
135
ret = devm_hwrng_register(&pdev->dev, &priv->rng);
136
if (ret) {
137
dev_err(&pdev->dev, "failed to register rng device: %d\n",
138
ret);
139
return ret;
140
}
141
142
dev_set_drvdata(&pdev->dev, priv);
143
pm_runtime_set_autosuspend_delay(&pdev->dev, RNG_AUTOSUSPEND_TIMEOUT);
144
pm_runtime_use_autosuspend(&pdev->dev);
145
ret = devm_pm_runtime_enable(&pdev->dev);
146
if (ret)
147
return ret;
148
149
dev_info(&pdev->dev, "registered RNG driver\n");
150
151
return 0;
152
}
153
154
#ifdef CONFIG_PM
155
static int mtk_rng_runtime_suspend(struct device *dev)
156
{
157
struct mtk_rng *priv = dev_get_drvdata(dev);
158
159
mtk_rng_cleanup(&priv->rng);
160
161
return 0;
162
}
163
164
static int mtk_rng_runtime_resume(struct device *dev)
165
{
166
struct mtk_rng *priv = dev_get_drvdata(dev);
167
168
return mtk_rng_init(&priv->rng);
169
}
170
171
static const struct dev_pm_ops mtk_rng_pm_ops = {
172
SET_RUNTIME_PM_OPS(mtk_rng_runtime_suspend,
173
mtk_rng_runtime_resume, NULL)
174
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
175
pm_runtime_force_resume)
176
};
177
178
#define MTK_RNG_PM_OPS (&mtk_rng_pm_ops)
179
#else /* CONFIG_PM */
180
#define MTK_RNG_PM_OPS NULL
181
#endif /* CONFIG_PM */
182
183
static const struct of_device_id mtk_rng_match[] = {
184
{ .compatible = "mediatek,mt7986-rng" },
185
{ .compatible = "mediatek,mt7623-rng" },
186
{},
187
};
188
MODULE_DEVICE_TABLE(of, mtk_rng_match);
189
190
static struct platform_driver mtk_rng_driver = {
191
.probe = mtk_rng_probe,
192
.driver = {
193
.name = MTK_RNG_DEV,
194
.pm = MTK_RNG_PM_OPS,
195
.of_match_table = mtk_rng_match,
196
},
197
};
198
199
module_platform_driver(mtk_rng_driver);
200
201
MODULE_DESCRIPTION("Mediatek Random Number Generator Driver");
202
MODULE_AUTHOR("Sean Wang <[email protected]>");
203
MODULE_LICENSE("GPL");
204
205