df8eb54fd5a306ad972f597b29ce62141df8b4d2
[openwrt/openwrt.git] / target / linux / mediatek / files / drivers / char / hw_random / mtk-rng.c
1 /*
2 * Driver for Mediatek Hardware Random Number Generator
3 *
4 * Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16 #define MTK_RNG_DEV KBUILD_MODNAME
17
18 #include <linux/clk.h>
19 #include <linux/delay.h>
20 #include <linux/err.h>
21 #include <linux/hw_random.h>
22 #include <linux/io.h>
23 #include <linux/iopoll.h>
24 #include <linux/kernel.h>
25 #include <linux/module.h>
26 #include <linux/of.h>
27 #include <linux/platform_device.h>
28
29 #define USEC_POLL 2
30 #define TIMEOUT_POLL 20
31
32 #define RNG_CTRL 0x00
33 #define RNG_EN BIT(0)
34 #define RNG_READY BIT(31)
35
36 #define RNG_DATA 0x08
37
38 #define to_mtk_rng(p) container_of(p, struct mtk_rng, rng)
39
40 struct mtk_rng {
41 void __iomem *base;
42 struct clk *clk;
43 struct hwrng rng;
44 };
45
46 static int mtk_rng_init(struct hwrng *rng)
47 {
48 struct mtk_rng *priv = to_mtk_rng(rng);
49 u32 val;
50 int err;
51
52 err = clk_prepare_enable(priv->clk);
53 if (err)
54 return err;
55
56 val = readl(priv->base + RNG_CTRL);
57 val |= RNG_EN;
58 writel(val, priv->base + RNG_CTRL);
59
60 return 0;
61 }
62
63 static void mtk_rng_cleanup(struct hwrng *rng)
64 {
65 struct mtk_rng *priv = to_mtk_rng(rng);
66 u32 val;
67
68 val = readl(priv->base + RNG_CTRL);
69 val &= ~RNG_EN;
70 writel(val, priv->base + RNG_CTRL);
71
72 clk_disable_unprepare(priv->clk);
73 }
74
75 static bool mtk_rng_wait_ready(struct hwrng *rng, bool wait)
76 {
77 struct mtk_rng *priv = to_mtk_rng(rng);
78 int ready;
79
80 ready = readl(priv->base + RNG_CTRL) & RNG_READY;
81 if (!ready && wait)
82 readl_poll_timeout_atomic(priv->base + RNG_CTRL, ready,
83 ready & RNG_READY, USEC_POLL,
84 TIMEOUT_POLL);
85 return !!ready;
86 }
87
88 static int mtk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
89 {
90 struct mtk_rng *priv = to_mtk_rng(rng);
91 int retval = 0;
92
93 while (max >= sizeof(u32)) {
94 if (!mtk_rng_wait_ready(rng, wait))
95 break;
96
97 *(u32 *)buf = readl(priv->base + RNG_DATA);
98 retval += sizeof(u32);
99 buf += sizeof(u32);
100 max -= sizeof(u32);
101 }
102
103 return retval || !wait ? retval : -EIO;
104 }
105
106 static int mtk_rng_probe(struct platform_device *pdev)
107 {
108 struct resource *res;
109 int ret;
110 struct mtk_rng *priv;
111
112 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
113 if (!res) {
114 dev_err(&pdev->dev, "no iomem resource\n");
115 return -ENXIO;
116 }
117
118 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
119 if (!priv)
120 return -ENOMEM;
121
122 priv->rng.name = pdev->name;
123 priv->rng.init = mtk_rng_init;
124 priv->rng.cleanup = mtk_rng_cleanup;
125 priv->rng.read = mtk_rng_read;
126
127 priv->clk = devm_clk_get(&pdev->dev, "rng");
128 if (IS_ERR(priv->clk)) {
129 ret = PTR_ERR(priv->clk);
130 dev_err(&pdev->dev, "no clock for device: %d\n", ret);
131 return ret;
132 }
133
134 priv->base = devm_ioremap_resource(&pdev->dev, res);
135 if (IS_ERR(priv->base))
136 return PTR_ERR(priv->base);
137
138 ret = devm_hwrng_register(&pdev->dev, &priv->rng);
139 if (ret) {
140 dev_err(&pdev->dev, "failed to register rng device: %d\n",
141 ret);
142 return ret;
143 }
144
145 dev_info(&pdev->dev, "registered RNG driver\n");
146
147 return 0;
148 }
149
150 static const struct of_device_id mtk_rng_match[] = {
151 { .compatible = "mediatek,mt7623-rng" },
152 {},
153 };
154 MODULE_DEVICE_TABLE(of, mtk_rng_match);
155
156 static struct platform_driver mtk_rng_driver = {
157 .probe = mtk_rng_probe,
158 .driver = {
159 .name = MTK_RNG_DEV,
160 .of_match_table = mtk_rng_match,
161 },
162 };
163
164 module_platform_driver(mtk_rng_driver);
165
166 MODULE_DESCRIPTION("Mediatek Random Number Generator Driver");
167 MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
168 MODULE_LICENSE("GPL");