1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 27701d1ffSSean Wang /* 37701d1ffSSean Wang * Driver for Mediatek Hardware Random Number Generator 47701d1ffSSean Wang * 57701d1ffSSean Wang * Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com> 67701d1ffSSean Wang */ 77701d1ffSSean Wang #define MTK_RNG_DEV KBUILD_MODNAME 87701d1ffSSean Wang 97701d1ffSSean Wang #include <linux/clk.h> 107701d1ffSSean Wang #include <linux/delay.h> 117701d1ffSSean Wang #include <linux/err.h> 127701d1ffSSean Wang #include <linux/hw_random.h> 137701d1ffSSean Wang #include <linux/io.h> 147701d1ffSSean Wang #include <linux/iopoll.h> 157701d1ffSSean Wang #include <linux/kernel.h> 167701d1ffSSean Wang #include <linux/module.h> 177701d1ffSSean Wang #include <linux/of.h> 187701d1ffSSean Wang #include <linux/platform_device.h> 1981d2b345SSean Wang #include <linux/pm_runtime.h> 2081d2b345SSean Wang 2181d2b345SSean Wang /* Runtime PM autosuspend timeout: */ 2281d2b345SSean Wang #define RNG_AUTOSUSPEND_TIMEOUT 100 237701d1ffSSean Wang 247701d1ffSSean Wang #define USEC_POLL 2 257701d1ffSSean Wang #define TIMEOUT_POLL 20 267701d1ffSSean Wang 277701d1ffSSean Wang #define RNG_CTRL 0x00 287701d1ffSSean Wang #define RNG_EN BIT(0) 297701d1ffSSean Wang #define RNG_READY BIT(31) 307701d1ffSSean Wang 317701d1ffSSean Wang #define RNG_DATA 0x08 327701d1ffSSean Wang 337701d1ffSSean Wang #define to_mtk_rng(p) container_of(p, struct mtk_rng, rng) 347701d1ffSSean Wang 357701d1ffSSean Wang struct mtk_rng { 367701d1ffSSean Wang void __iomem *base; 377701d1ffSSean Wang struct clk *clk; 387701d1ffSSean Wang struct hwrng rng; 397701d1ffSSean Wang }; 407701d1ffSSean Wang 417701d1ffSSean Wang static int mtk_rng_init(struct hwrng *rng) 427701d1ffSSean Wang { 437701d1ffSSean Wang struct mtk_rng *priv = to_mtk_rng(rng); 447701d1ffSSean Wang u32 val; 457701d1ffSSean Wang int err; 467701d1ffSSean Wang 477701d1ffSSean Wang err = clk_prepare_enable(priv->clk); 487701d1ffSSean Wang if (err) 497701d1ffSSean Wang return err; 507701d1ffSSean Wang 517701d1ffSSean Wang val = readl(priv->base + RNG_CTRL); 527701d1ffSSean Wang val |= RNG_EN; 537701d1ffSSean Wang writel(val, priv->base + RNG_CTRL); 547701d1ffSSean Wang 557701d1ffSSean Wang return 0; 567701d1ffSSean Wang } 577701d1ffSSean Wang 587701d1ffSSean Wang static void mtk_rng_cleanup(struct hwrng *rng) 597701d1ffSSean Wang { 607701d1ffSSean Wang struct mtk_rng *priv = to_mtk_rng(rng); 617701d1ffSSean Wang u32 val; 627701d1ffSSean Wang 637701d1ffSSean Wang val = readl(priv->base + RNG_CTRL); 647701d1ffSSean Wang val &= ~RNG_EN; 657701d1ffSSean Wang writel(val, priv->base + RNG_CTRL); 667701d1ffSSean Wang 677701d1ffSSean Wang clk_disable_unprepare(priv->clk); 687701d1ffSSean Wang } 697701d1ffSSean Wang 707701d1ffSSean Wang static bool mtk_rng_wait_ready(struct hwrng *rng, bool wait) 717701d1ffSSean Wang { 727701d1ffSSean Wang struct mtk_rng *priv = to_mtk_rng(rng); 737701d1ffSSean Wang int ready; 747701d1ffSSean Wang 757701d1ffSSean Wang ready = readl(priv->base + RNG_CTRL) & RNG_READY; 767701d1ffSSean Wang if (!ready && wait) 777701d1ffSSean Wang readl_poll_timeout_atomic(priv->base + RNG_CTRL, ready, 787701d1ffSSean Wang ready & RNG_READY, USEC_POLL, 797701d1ffSSean Wang TIMEOUT_POLL); 807701d1ffSSean Wang return !!ready; 817701d1ffSSean Wang } 827701d1ffSSean Wang 837701d1ffSSean Wang static int mtk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) 847701d1ffSSean Wang { 857701d1ffSSean Wang struct mtk_rng *priv = to_mtk_rng(rng); 867701d1ffSSean Wang int retval = 0; 877701d1ffSSean Wang 8881d2b345SSean Wang pm_runtime_get_sync((struct device *)priv->rng.priv); 8981d2b345SSean Wang 907701d1ffSSean Wang while (max >= sizeof(u32)) { 917701d1ffSSean Wang if (!mtk_rng_wait_ready(rng, wait)) 927701d1ffSSean Wang break; 937701d1ffSSean Wang 947701d1ffSSean Wang *(u32 *)buf = readl(priv->base + RNG_DATA); 957701d1ffSSean Wang retval += sizeof(u32); 967701d1ffSSean Wang buf += sizeof(u32); 977701d1ffSSean Wang max -= sizeof(u32); 987701d1ffSSean Wang } 997701d1ffSSean Wang 10081d2b345SSean Wang pm_runtime_mark_last_busy((struct device *)priv->rng.priv); 10181d2b345SSean Wang pm_runtime_put_sync_autosuspend((struct device *)priv->rng.priv); 10281d2b345SSean Wang 1037701d1ffSSean Wang return retval || !wait ? retval : -EIO; 1047701d1ffSSean Wang } 1057701d1ffSSean Wang 1067701d1ffSSean Wang static int mtk_rng_probe(struct platform_device *pdev) 1077701d1ffSSean Wang { 1087701d1ffSSean Wang int ret; 1097701d1ffSSean Wang struct mtk_rng *priv; 1107701d1ffSSean Wang 1117701d1ffSSean Wang priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 1127701d1ffSSean Wang if (!priv) 1137701d1ffSSean Wang return -ENOMEM; 1147701d1ffSSean Wang 1157701d1ffSSean Wang priv->rng.name = pdev->name; 11681d2b345SSean Wang #ifndef CONFIG_PM 1177701d1ffSSean Wang priv->rng.init = mtk_rng_init; 1187701d1ffSSean Wang priv->rng.cleanup = mtk_rng_cleanup; 11981d2b345SSean Wang #endif 1207701d1ffSSean Wang priv->rng.read = mtk_rng_read; 12181d2b345SSean Wang priv->rng.priv = (unsigned long)&pdev->dev; 1224565da7cSSean Wang priv->rng.quality = 900; 1237701d1ffSSean Wang 1247701d1ffSSean Wang priv->clk = devm_clk_get(&pdev->dev, "rng"); 1257701d1ffSSean Wang if (IS_ERR(priv->clk)) { 1267701d1ffSSean Wang ret = PTR_ERR(priv->clk); 1277701d1ffSSean Wang dev_err(&pdev->dev, "no clock for device: %d\n", ret); 1287701d1ffSSean Wang return ret; 1297701d1ffSSean Wang } 1307701d1ffSSean Wang 1310600e9c0SMarkus Elfring priv->base = devm_platform_ioremap_resource(pdev, 0); 1327701d1ffSSean Wang if (IS_ERR(priv->base)) 1337701d1ffSSean Wang return PTR_ERR(priv->base); 1347701d1ffSSean Wang 1357701d1ffSSean Wang ret = devm_hwrng_register(&pdev->dev, &priv->rng); 1367701d1ffSSean Wang if (ret) { 1377701d1ffSSean Wang dev_err(&pdev->dev, "failed to register rng device: %d\n", 1387701d1ffSSean Wang ret); 1397701d1ffSSean Wang return ret; 1407701d1ffSSean Wang } 1417701d1ffSSean Wang 14281d2b345SSean Wang dev_set_drvdata(&pdev->dev, priv); 14381d2b345SSean Wang pm_runtime_set_autosuspend_delay(&pdev->dev, RNG_AUTOSUSPEND_TIMEOUT); 14481d2b345SSean Wang pm_runtime_use_autosuspend(&pdev->dev); 14581d2b345SSean Wang pm_runtime_enable(&pdev->dev); 14681d2b345SSean Wang 1477701d1ffSSean Wang dev_info(&pdev->dev, "registered RNG driver\n"); 1487701d1ffSSean Wang 1497701d1ffSSean Wang return 0; 1507701d1ffSSean Wang } 1517701d1ffSSean Wang 15281d2b345SSean Wang #ifdef CONFIG_PM 15381d2b345SSean Wang static int mtk_rng_runtime_suspend(struct device *dev) 15481d2b345SSean Wang { 15581d2b345SSean Wang struct mtk_rng *priv = dev_get_drvdata(dev); 15681d2b345SSean Wang 15781d2b345SSean Wang mtk_rng_cleanup(&priv->rng); 15881d2b345SSean Wang 15981d2b345SSean Wang return 0; 16081d2b345SSean Wang } 16181d2b345SSean Wang 16281d2b345SSean Wang static int mtk_rng_runtime_resume(struct device *dev) 16381d2b345SSean Wang { 16481d2b345SSean Wang struct mtk_rng *priv = dev_get_drvdata(dev); 16581d2b345SSean Wang 16681d2b345SSean Wang return mtk_rng_init(&priv->rng); 16781d2b345SSean Wang } 16881d2b345SSean Wang 169*b6f5f0c8SMarkus Schneider-Pargmann static const struct dev_pm_ops mtk_rng_pm_ops = { 170*b6f5f0c8SMarkus Schneider-Pargmann SET_RUNTIME_PM_OPS(mtk_rng_runtime_suspend, 171*b6f5f0c8SMarkus Schneider-Pargmann mtk_rng_runtime_resume, NULL) 172*b6f5f0c8SMarkus Schneider-Pargmann SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 173*b6f5f0c8SMarkus Schneider-Pargmann pm_runtime_force_resume) 174*b6f5f0c8SMarkus Schneider-Pargmann }; 175*b6f5f0c8SMarkus Schneider-Pargmann 17681d2b345SSean Wang #define MTK_RNG_PM_OPS (&mtk_rng_pm_ops) 17781d2b345SSean Wang #else /* CONFIG_PM */ 17881d2b345SSean Wang #define MTK_RNG_PM_OPS NULL 17981d2b345SSean Wang #endif /* CONFIG_PM */ 18081d2b345SSean Wang 1817701d1ffSSean Wang static const struct of_device_id mtk_rng_match[] = { 1827701d1ffSSean Wang { .compatible = "mediatek,mt7623-rng" }, 1837701d1ffSSean Wang {}, 1847701d1ffSSean Wang }; 1857701d1ffSSean Wang MODULE_DEVICE_TABLE(of, mtk_rng_match); 1867701d1ffSSean Wang 1877701d1ffSSean Wang static struct platform_driver mtk_rng_driver = { 1887701d1ffSSean Wang .probe = mtk_rng_probe, 1897701d1ffSSean Wang .driver = { 1907701d1ffSSean Wang .name = MTK_RNG_DEV, 19181d2b345SSean Wang .pm = MTK_RNG_PM_OPS, 1927701d1ffSSean Wang .of_match_table = mtk_rng_match, 1937701d1ffSSean Wang }, 1947701d1ffSSean Wang }; 1957701d1ffSSean Wang 1967701d1ffSSean Wang module_platform_driver(mtk_rng_driver); 1977701d1ffSSean Wang 1987701d1ffSSean Wang MODULE_DESCRIPTION("Mediatek Random Number Generator Driver"); 1997701d1ffSSean Wang MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); 2007701d1ffSSean Wang MODULE_LICENSE("GPL"); 201