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