17701d1ffSSean Wang /* 27701d1ffSSean Wang * Driver for Mediatek Hardware Random Number Generator 37701d1ffSSean Wang * 47701d1ffSSean Wang * Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com> 57701d1ffSSean Wang * 67701d1ffSSean Wang * This program is free software; you can redistribute it and/or 77701d1ffSSean Wang * modify it under the terms of the GNU General Public License as 87701d1ffSSean Wang * published by the Free Software Foundation; either version 2 of 97701d1ffSSean Wang * the License, or (at your option) any later version. 107701d1ffSSean Wang * 117701d1ffSSean Wang * This program is distributed in the hope that it will be useful, 127701d1ffSSean Wang * but WITHOUT ANY WARRANTY; without even the implied warranty of 137701d1ffSSean Wang * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 147701d1ffSSean Wang * GNU General Public License for more details. 157701d1ffSSean Wang */ 167701d1ffSSean Wang #define MTK_RNG_DEV KBUILD_MODNAME 177701d1ffSSean Wang 187701d1ffSSean Wang #include <linux/clk.h> 197701d1ffSSean Wang #include <linux/delay.h> 207701d1ffSSean Wang #include <linux/err.h> 217701d1ffSSean Wang #include <linux/hw_random.h> 227701d1ffSSean Wang #include <linux/io.h> 237701d1ffSSean Wang #include <linux/iopoll.h> 247701d1ffSSean Wang #include <linux/kernel.h> 257701d1ffSSean Wang #include <linux/module.h> 267701d1ffSSean Wang #include <linux/of.h> 277701d1ffSSean Wang #include <linux/platform_device.h> 2881d2b345SSean Wang #include <linux/pm_runtime.h> 2981d2b345SSean Wang 3081d2b345SSean Wang /* Runtime PM autosuspend timeout: */ 3181d2b345SSean Wang #define RNG_AUTOSUSPEND_TIMEOUT 100 327701d1ffSSean Wang 337701d1ffSSean Wang #define USEC_POLL 2 347701d1ffSSean Wang #define TIMEOUT_POLL 20 357701d1ffSSean Wang 367701d1ffSSean Wang #define RNG_CTRL 0x00 377701d1ffSSean Wang #define RNG_EN BIT(0) 387701d1ffSSean Wang #define RNG_READY BIT(31) 397701d1ffSSean Wang 407701d1ffSSean Wang #define RNG_DATA 0x08 417701d1ffSSean Wang 427701d1ffSSean Wang #define to_mtk_rng(p) container_of(p, struct mtk_rng, rng) 437701d1ffSSean Wang 447701d1ffSSean Wang struct mtk_rng { 457701d1ffSSean Wang void __iomem *base; 467701d1ffSSean Wang struct clk *clk; 477701d1ffSSean Wang struct hwrng rng; 487701d1ffSSean Wang }; 497701d1ffSSean Wang 507701d1ffSSean Wang static int mtk_rng_init(struct hwrng *rng) 517701d1ffSSean Wang { 527701d1ffSSean Wang struct mtk_rng *priv = to_mtk_rng(rng); 537701d1ffSSean Wang u32 val; 547701d1ffSSean Wang int err; 557701d1ffSSean Wang 567701d1ffSSean Wang err = clk_prepare_enable(priv->clk); 577701d1ffSSean Wang if (err) 587701d1ffSSean Wang return err; 597701d1ffSSean Wang 607701d1ffSSean Wang val = readl(priv->base + RNG_CTRL); 617701d1ffSSean Wang val |= RNG_EN; 627701d1ffSSean Wang writel(val, priv->base + RNG_CTRL); 637701d1ffSSean Wang 647701d1ffSSean Wang return 0; 657701d1ffSSean Wang } 667701d1ffSSean Wang 677701d1ffSSean Wang static void mtk_rng_cleanup(struct hwrng *rng) 687701d1ffSSean Wang { 697701d1ffSSean Wang struct mtk_rng *priv = to_mtk_rng(rng); 707701d1ffSSean Wang u32 val; 717701d1ffSSean Wang 727701d1ffSSean Wang val = readl(priv->base + RNG_CTRL); 737701d1ffSSean Wang val &= ~RNG_EN; 747701d1ffSSean Wang writel(val, priv->base + RNG_CTRL); 757701d1ffSSean Wang 767701d1ffSSean Wang clk_disable_unprepare(priv->clk); 777701d1ffSSean Wang } 787701d1ffSSean Wang 797701d1ffSSean Wang static bool mtk_rng_wait_ready(struct hwrng *rng, bool wait) 807701d1ffSSean Wang { 817701d1ffSSean Wang struct mtk_rng *priv = to_mtk_rng(rng); 827701d1ffSSean Wang int ready; 837701d1ffSSean Wang 847701d1ffSSean Wang ready = readl(priv->base + RNG_CTRL) & RNG_READY; 857701d1ffSSean Wang if (!ready && wait) 867701d1ffSSean Wang readl_poll_timeout_atomic(priv->base + RNG_CTRL, ready, 877701d1ffSSean Wang ready & RNG_READY, USEC_POLL, 887701d1ffSSean Wang TIMEOUT_POLL); 897701d1ffSSean Wang return !!ready; 907701d1ffSSean Wang } 917701d1ffSSean Wang 927701d1ffSSean Wang static int mtk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) 937701d1ffSSean Wang { 947701d1ffSSean Wang struct mtk_rng *priv = to_mtk_rng(rng); 957701d1ffSSean Wang int retval = 0; 967701d1ffSSean Wang 9781d2b345SSean Wang pm_runtime_get_sync((struct device *)priv->rng.priv); 9881d2b345SSean Wang 997701d1ffSSean Wang while (max >= sizeof(u32)) { 1007701d1ffSSean Wang if (!mtk_rng_wait_ready(rng, wait)) 1017701d1ffSSean Wang break; 1027701d1ffSSean Wang 1037701d1ffSSean Wang *(u32 *)buf = readl(priv->base + RNG_DATA); 1047701d1ffSSean Wang retval += sizeof(u32); 1057701d1ffSSean Wang buf += sizeof(u32); 1067701d1ffSSean Wang max -= sizeof(u32); 1077701d1ffSSean Wang } 1087701d1ffSSean Wang 10981d2b345SSean Wang pm_runtime_mark_last_busy((struct device *)priv->rng.priv); 11081d2b345SSean Wang pm_runtime_put_sync_autosuspend((struct device *)priv->rng.priv); 11181d2b345SSean Wang 1127701d1ffSSean Wang return retval || !wait ? retval : -EIO; 1137701d1ffSSean Wang } 1147701d1ffSSean Wang 1157701d1ffSSean Wang static int mtk_rng_probe(struct platform_device *pdev) 1167701d1ffSSean Wang { 1177701d1ffSSean Wang struct resource *res; 1187701d1ffSSean Wang int ret; 1197701d1ffSSean Wang struct mtk_rng *priv; 1207701d1ffSSean Wang 1217701d1ffSSean Wang res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1227701d1ffSSean Wang if (!res) { 1237701d1ffSSean Wang dev_err(&pdev->dev, "no iomem resource\n"); 1247701d1ffSSean Wang return -ENXIO; 1257701d1ffSSean Wang } 1267701d1ffSSean Wang 1277701d1ffSSean Wang priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 1287701d1ffSSean Wang if (!priv) 1297701d1ffSSean Wang return -ENOMEM; 1307701d1ffSSean Wang 1317701d1ffSSean Wang priv->rng.name = pdev->name; 13281d2b345SSean Wang #ifndef CONFIG_PM 1337701d1ffSSean Wang priv->rng.init = mtk_rng_init; 1347701d1ffSSean Wang priv->rng.cleanup = mtk_rng_cleanup; 13581d2b345SSean Wang #endif 1367701d1ffSSean Wang priv->rng.read = mtk_rng_read; 13781d2b345SSean Wang priv->rng.priv = (unsigned long)&pdev->dev; 138*4565da7cSSean Wang priv->rng.quality = 900; 1397701d1ffSSean Wang 1407701d1ffSSean Wang priv->clk = devm_clk_get(&pdev->dev, "rng"); 1417701d1ffSSean Wang if (IS_ERR(priv->clk)) { 1427701d1ffSSean Wang ret = PTR_ERR(priv->clk); 1437701d1ffSSean Wang dev_err(&pdev->dev, "no clock for device: %d\n", ret); 1447701d1ffSSean Wang return ret; 1457701d1ffSSean Wang } 1467701d1ffSSean Wang 1477701d1ffSSean Wang priv->base = devm_ioremap_resource(&pdev->dev, res); 1487701d1ffSSean Wang if (IS_ERR(priv->base)) 1497701d1ffSSean Wang return PTR_ERR(priv->base); 1507701d1ffSSean Wang 1517701d1ffSSean Wang ret = devm_hwrng_register(&pdev->dev, &priv->rng); 1527701d1ffSSean Wang if (ret) { 1537701d1ffSSean Wang dev_err(&pdev->dev, "failed to register rng device: %d\n", 1547701d1ffSSean Wang ret); 1557701d1ffSSean Wang return ret; 1567701d1ffSSean Wang } 1577701d1ffSSean Wang 15881d2b345SSean Wang dev_set_drvdata(&pdev->dev, priv); 15981d2b345SSean Wang pm_runtime_set_autosuspend_delay(&pdev->dev, RNG_AUTOSUSPEND_TIMEOUT); 16081d2b345SSean Wang pm_runtime_use_autosuspend(&pdev->dev); 16181d2b345SSean Wang pm_runtime_enable(&pdev->dev); 16281d2b345SSean Wang 1637701d1ffSSean Wang dev_info(&pdev->dev, "registered RNG driver\n"); 1647701d1ffSSean Wang 1657701d1ffSSean Wang return 0; 1667701d1ffSSean Wang } 1677701d1ffSSean Wang 16881d2b345SSean Wang #ifdef CONFIG_PM 16981d2b345SSean Wang static int mtk_rng_runtime_suspend(struct device *dev) 17081d2b345SSean Wang { 17181d2b345SSean Wang struct mtk_rng *priv = dev_get_drvdata(dev); 17281d2b345SSean Wang 17381d2b345SSean Wang mtk_rng_cleanup(&priv->rng); 17481d2b345SSean Wang 17581d2b345SSean Wang return 0; 17681d2b345SSean Wang } 17781d2b345SSean Wang 17881d2b345SSean Wang static int mtk_rng_runtime_resume(struct device *dev) 17981d2b345SSean Wang { 18081d2b345SSean Wang struct mtk_rng *priv = dev_get_drvdata(dev); 18181d2b345SSean Wang 18281d2b345SSean Wang return mtk_rng_init(&priv->rng); 18381d2b345SSean Wang } 18481d2b345SSean Wang 18581d2b345SSean Wang static UNIVERSAL_DEV_PM_OPS(mtk_rng_pm_ops, mtk_rng_runtime_suspend, 18681d2b345SSean Wang mtk_rng_runtime_resume, NULL); 18781d2b345SSean Wang #define MTK_RNG_PM_OPS (&mtk_rng_pm_ops) 18881d2b345SSean Wang #else /* CONFIG_PM */ 18981d2b345SSean Wang #define MTK_RNG_PM_OPS NULL 19081d2b345SSean Wang #endif /* CONFIG_PM */ 19181d2b345SSean Wang 1927701d1ffSSean Wang static const struct of_device_id mtk_rng_match[] = { 1937701d1ffSSean Wang { .compatible = "mediatek,mt7623-rng" }, 1947701d1ffSSean Wang {}, 1957701d1ffSSean Wang }; 1967701d1ffSSean Wang MODULE_DEVICE_TABLE(of, mtk_rng_match); 1977701d1ffSSean Wang 1987701d1ffSSean Wang static struct platform_driver mtk_rng_driver = { 1997701d1ffSSean Wang .probe = mtk_rng_probe, 2007701d1ffSSean Wang .driver = { 2017701d1ffSSean Wang .name = MTK_RNG_DEV, 20281d2b345SSean Wang .pm = MTK_RNG_PM_OPS, 2037701d1ffSSean Wang .of_match_table = mtk_rng_match, 2047701d1ffSSean Wang }, 2057701d1ffSSean Wang }; 2067701d1ffSSean Wang 2077701d1ffSSean Wang module_platform_driver(mtk_rng_driver); 2087701d1ffSSean Wang 2097701d1ffSSean Wang MODULE_DESCRIPTION("Mediatek Random Number Generator Driver"); 2107701d1ffSSean Wang MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); 2117701d1ffSSean Wang MODULE_LICENSE("GPL"); 212