16cd225ccSŁukasz Stelmach // SPDX-License-Identifier: GPL-2.0 26cd225ccSŁukasz Stelmach /* 36cd225ccSŁukasz Stelmach * RNG driver for Exynos TRNGs 46cd225ccSŁukasz Stelmach * 56cd225ccSŁukasz Stelmach * Author: Łukasz Stelmach <l.stelmach@samsung.com> 66cd225ccSŁukasz Stelmach * 76cd225ccSŁukasz Stelmach * Copyright 2017 (c) Samsung Electronics Software, Inc. 86cd225ccSŁukasz Stelmach * 96cd225ccSŁukasz Stelmach * Based on the Exynos PRNG driver drivers/crypto/exynos-rng by 106cd225ccSŁukasz Stelmach * Krzysztof Kozłowski <krzk@kernel.org> 116cd225ccSŁukasz Stelmach */ 126cd225ccSŁukasz Stelmach 136cd225ccSŁukasz Stelmach #include <linux/clk.h> 146cd225ccSŁukasz Stelmach #include <linux/crypto.h> 156cd225ccSŁukasz Stelmach #include <linux/delay.h> 166cd225ccSŁukasz Stelmach #include <linux/err.h> 176cd225ccSŁukasz Stelmach #include <linux/hw_random.h> 186cd225ccSŁukasz Stelmach #include <linux/io.h> 196cd225ccSŁukasz Stelmach #include <linux/iopoll.h> 206cd225ccSŁukasz Stelmach #include <linux/kernel.h> 216cd225ccSŁukasz Stelmach #include <linux/module.h> 22ac316725SRandy Dunlap #include <linux/mod_devicetable.h> 236cd225ccSŁukasz Stelmach #include <linux/platform_device.h> 246cd225ccSŁukasz Stelmach #include <linux/pm_runtime.h> 256cd225ccSŁukasz Stelmach 266cd225ccSŁukasz Stelmach #define EXYNOS_TRNG_CLKDIV (0x0) 276cd225ccSŁukasz Stelmach 286cd225ccSŁukasz Stelmach #define EXYNOS_TRNG_CTRL (0x20) 296cd225ccSŁukasz Stelmach #define EXYNOS_TRNG_CTRL_RNGEN BIT(31) 306cd225ccSŁukasz Stelmach 316cd225ccSŁukasz Stelmach #define EXYNOS_TRNG_POST_CTRL (0x30) 326cd225ccSŁukasz Stelmach #define EXYNOS_TRNG_ONLINE_CTRL (0x40) 336cd225ccSŁukasz Stelmach #define EXYNOS_TRNG_ONLINE_STAT (0x44) 346cd225ccSŁukasz Stelmach #define EXYNOS_TRNG_ONLINE_MAXCHI2 (0x48) 356cd225ccSŁukasz Stelmach #define EXYNOS_TRNG_FIFO_CTRL (0x50) 366cd225ccSŁukasz Stelmach #define EXYNOS_TRNG_FIFO_0 (0x80) 376cd225ccSŁukasz Stelmach #define EXYNOS_TRNG_FIFO_1 (0x84) 386cd225ccSŁukasz Stelmach #define EXYNOS_TRNG_FIFO_2 (0x88) 396cd225ccSŁukasz Stelmach #define EXYNOS_TRNG_FIFO_3 (0x8c) 406cd225ccSŁukasz Stelmach #define EXYNOS_TRNG_FIFO_4 (0x90) 416cd225ccSŁukasz Stelmach #define EXYNOS_TRNG_FIFO_5 (0x94) 426cd225ccSŁukasz Stelmach #define EXYNOS_TRNG_FIFO_6 (0x98) 436cd225ccSŁukasz Stelmach #define EXYNOS_TRNG_FIFO_7 (0x9c) 446cd225ccSŁukasz Stelmach #define EXYNOS_TRNG_FIFO_LEN (8) 456cd225ccSŁukasz Stelmach #define EXYNOS_TRNG_CLOCK_RATE (500000) 466cd225ccSŁukasz Stelmach 476cd225ccSŁukasz Stelmach 486cd225ccSŁukasz Stelmach struct exynos_trng_dev { 496cd225ccSŁukasz Stelmach struct device *dev; 506cd225ccSŁukasz Stelmach void __iomem *mem; 516cd225ccSŁukasz Stelmach struct clk *clk; 526cd225ccSŁukasz Stelmach struct hwrng rng; 536cd225ccSŁukasz Stelmach }; 546cd225ccSŁukasz Stelmach 556cd225ccSŁukasz Stelmach static int exynos_trng_do_read(struct hwrng *rng, void *data, size_t max, 566cd225ccSŁukasz Stelmach bool wait) 576cd225ccSŁukasz Stelmach { 586cd225ccSŁukasz Stelmach struct exynos_trng_dev *trng; 59a8bc71d4SDan Carpenter int val; 606cd225ccSŁukasz Stelmach 616cd225ccSŁukasz Stelmach max = min_t(size_t, max, (EXYNOS_TRNG_FIFO_LEN * 4)); 626cd225ccSŁukasz Stelmach 636cd225ccSŁukasz Stelmach trng = (struct exynos_trng_dev *)rng->priv; 646cd225ccSŁukasz Stelmach 656cd225ccSŁukasz Stelmach writel_relaxed(max * 8, trng->mem + EXYNOS_TRNG_FIFO_CTRL); 666cd225ccSŁukasz Stelmach val = readl_poll_timeout(trng->mem + EXYNOS_TRNG_FIFO_CTRL, val, 676cd225ccSŁukasz Stelmach val == 0, 200, 1000000); 686cd225ccSŁukasz Stelmach if (val < 0) 696cd225ccSŁukasz Stelmach return val; 706cd225ccSŁukasz Stelmach 716cd225ccSŁukasz Stelmach memcpy_fromio(data, trng->mem + EXYNOS_TRNG_FIFO_0, max); 726cd225ccSŁukasz Stelmach 736cd225ccSŁukasz Stelmach return max; 746cd225ccSŁukasz Stelmach } 756cd225ccSŁukasz Stelmach 766cd225ccSŁukasz Stelmach static int exynos_trng_init(struct hwrng *rng) 776cd225ccSŁukasz Stelmach { 786cd225ccSŁukasz Stelmach struct exynos_trng_dev *trng = (struct exynos_trng_dev *)rng->priv; 796cd225ccSŁukasz Stelmach unsigned long sss_rate; 806cd225ccSŁukasz Stelmach u32 val; 816cd225ccSŁukasz Stelmach 826cd225ccSŁukasz Stelmach sss_rate = clk_get_rate(trng->clk); 836cd225ccSŁukasz Stelmach 846cd225ccSŁukasz Stelmach /* 856cd225ccSŁukasz Stelmach * For most TRNG circuits the clock frequency of under 500 kHz 866cd225ccSŁukasz Stelmach * is safe. 876cd225ccSŁukasz Stelmach */ 886cd225ccSŁukasz Stelmach val = sss_rate / (EXYNOS_TRNG_CLOCK_RATE * 2); 896cd225ccSŁukasz Stelmach if (val > 0x7fff) { 906cd225ccSŁukasz Stelmach dev_err(trng->dev, "clock divider too large: %d", val); 916cd225ccSŁukasz Stelmach return -ERANGE; 926cd225ccSŁukasz Stelmach } 936cd225ccSŁukasz Stelmach val = val << 1; 946cd225ccSŁukasz Stelmach writel_relaxed(val, trng->mem + EXYNOS_TRNG_CLKDIV); 956cd225ccSŁukasz Stelmach 966cd225ccSŁukasz Stelmach /* Enable the generator. */ 976cd225ccSŁukasz Stelmach val = EXYNOS_TRNG_CTRL_RNGEN; 986cd225ccSŁukasz Stelmach writel_relaxed(val, trng->mem + EXYNOS_TRNG_CTRL); 996cd225ccSŁukasz Stelmach 1006cd225ccSŁukasz Stelmach /* 1016cd225ccSŁukasz Stelmach * Disable post-processing. /dev/hwrng is supposed to deliver 1026cd225ccSŁukasz Stelmach * unprocessed data. 1036cd225ccSŁukasz Stelmach */ 1046cd225ccSŁukasz Stelmach writel_relaxed(0, trng->mem + EXYNOS_TRNG_POST_CTRL); 1056cd225ccSŁukasz Stelmach 1066cd225ccSŁukasz Stelmach return 0; 1076cd225ccSŁukasz Stelmach } 1086cd225ccSŁukasz Stelmach 1096cd225ccSŁukasz Stelmach static int exynos_trng_probe(struct platform_device *pdev) 1106cd225ccSŁukasz Stelmach { 1116cd225ccSŁukasz Stelmach struct exynos_trng_dev *trng; 1126cd225ccSŁukasz Stelmach int ret = -ENOMEM; 1136cd225ccSŁukasz Stelmach 1146cd225ccSŁukasz Stelmach trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL); 1156cd225ccSŁukasz Stelmach if (!trng) 1166cd225ccSŁukasz Stelmach return ret; 1176cd225ccSŁukasz Stelmach 1186cd225ccSŁukasz Stelmach trng->rng.name = devm_kstrdup(&pdev->dev, dev_name(&pdev->dev), 1196cd225ccSŁukasz Stelmach GFP_KERNEL); 1206cd225ccSŁukasz Stelmach if (!trng->rng.name) 1216cd225ccSŁukasz Stelmach return ret; 1226cd225ccSŁukasz Stelmach 1236cd225ccSŁukasz Stelmach trng->rng.init = exynos_trng_init; 1246cd225ccSŁukasz Stelmach trng->rng.read = exynos_trng_do_read; 1256cd225ccSŁukasz Stelmach trng->rng.priv = (unsigned long) trng; 1266cd225ccSŁukasz Stelmach 1276cd225ccSŁukasz Stelmach platform_set_drvdata(pdev, trng); 1286cd225ccSŁukasz Stelmach trng->dev = &pdev->dev; 1296cd225ccSŁukasz Stelmach 1303e3c97c6SYueHaibing trng->mem = devm_platform_ioremap_resource(pdev, 0); 1312273f42dSweiyongjun \(A\) if (IS_ERR(trng->mem)) 1326cd225ccSŁukasz Stelmach return PTR_ERR(trng->mem); 1336cd225ccSŁukasz Stelmach 1346cd225ccSŁukasz Stelmach pm_runtime_enable(&pdev->dev); 1350cdbabf8SŁukasz Stelmach ret = pm_runtime_resume_and_get(&pdev->dev); 1366cd225ccSŁukasz Stelmach if (ret < 0) { 1376cd225ccSŁukasz Stelmach dev_err(&pdev->dev, "Could not get runtime PM.\n"); 1386cd225ccSŁukasz Stelmach goto err_pm_get; 1396cd225ccSŁukasz Stelmach } 1406cd225ccSŁukasz Stelmach 1416cd225ccSŁukasz Stelmach trng->clk = devm_clk_get(&pdev->dev, "secss"); 1426cd225ccSŁukasz Stelmach if (IS_ERR(trng->clk)) { 1436cd225ccSŁukasz Stelmach ret = PTR_ERR(trng->clk); 1446cd225ccSŁukasz Stelmach dev_err(&pdev->dev, "Could not get clock.\n"); 1456cd225ccSŁukasz Stelmach goto err_clock; 1466cd225ccSŁukasz Stelmach } 1476cd225ccSŁukasz Stelmach 1486cd225ccSŁukasz Stelmach ret = clk_prepare_enable(trng->clk); 1496cd225ccSŁukasz Stelmach if (ret) { 1506cd225ccSŁukasz Stelmach dev_err(&pdev->dev, "Could not enable the clk.\n"); 1516cd225ccSŁukasz Stelmach goto err_clock; 1526cd225ccSŁukasz Stelmach } 1536cd225ccSŁukasz Stelmach 1543e75241bSChuhong Yuan ret = devm_hwrng_register(&pdev->dev, &trng->rng); 1556cd225ccSŁukasz Stelmach if (ret) { 1566cd225ccSŁukasz Stelmach dev_err(&pdev->dev, "Could not register hwrng device.\n"); 1576cd225ccSŁukasz Stelmach goto err_register; 1586cd225ccSŁukasz Stelmach } 1596cd225ccSŁukasz Stelmach 1606cd225ccSŁukasz Stelmach dev_info(&pdev->dev, "Exynos True Random Number Generator.\n"); 1616cd225ccSŁukasz Stelmach 1626cd225ccSŁukasz Stelmach return 0; 1636cd225ccSŁukasz Stelmach 1646cd225ccSŁukasz Stelmach err_register: 1656cd225ccSŁukasz Stelmach clk_disable_unprepare(trng->clk); 1666cd225ccSŁukasz Stelmach 1676cd225ccSŁukasz Stelmach err_clock: 1680cdbabf8SŁukasz Stelmach pm_runtime_put_noidle(&pdev->dev); 1696cd225ccSŁukasz Stelmach 1706cd225ccSŁukasz Stelmach err_pm_get: 1716cd225ccSŁukasz Stelmach pm_runtime_disable(&pdev->dev); 1726cd225ccSŁukasz Stelmach 1736cd225ccSŁukasz Stelmach return ret; 1746cd225ccSŁukasz Stelmach } 1756cd225ccSŁukasz Stelmach 1766cd225ccSŁukasz Stelmach static int exynos_trng_remove(struct platform_device *pdev) 1776cd225ccSŁukasz Stelmach { 1786cd225ccSŁukasz Stelmach struct exynos_trng_dev *trng = platform_get_drvdata(pdev); 1796cd225ccSŁukasz Stelmach 1806cd225ccSŁukasz Stelmach clk_disable_unprepare(trng->clk); 1816cd225ccSŁukasz Stelmach 1826cd225ccSŁukasz Stelmach pm_runtime_put_sync(&pdev->dev); 1836cd225ccSŁukasz Stelmach pm_runtime_disable(&pdev->dev); 1846cd225ccSŁukasz Stelmach 1856cd225ccSŁukasz Stelmach return 0; 1866cd225ccSŁukasz Stelmach } 1876cd225ccSŁukasz Stelmach 1886cd225ccSŁukasz Stelmach static int __maybe_unused exynos_trng_suspend(struct device *dev) 1896cd225ccSŁukasz Stelmach { 1906cd225ccSŁukasz Stelmach pm_runtime_put_sync(dev); 1916cd225ccSŁukasz Stelmach 1926cd225ccSŁukasz Stelmach return 0; 1936cd225ccSŁukasz Stelmach } 1946cd225ccSŁukasz Stelmach 1956cd225ccSŁukasz Stelmach static int __maybe_unused exynos_trng_resume(struct device *dev) 1966cd225ccSŁukasz Stelmach { 1976cd225ccSŁukasz Stelmach int ret; 1986cd225ccSŁukasz Stelmach 199*5d0421d6STian Tao ret = pm_runtime_resume_and_get(dev); 2006cd225ccSŁukasz Stelmach if (ret < 0) { 2016cd225ccSŁukasz Stelmach dev_err(dev, "Could not get runtime PM.\n"); 2026cd225ccSŁukasz Stelmach return ret; 2036cd225ccSŁukasz Stelmach } 2046cd225ccSŁukasz Stelmach 2056cd225ccSŁukasz Stelmach return 0; 2066cd225ccSŁukasz Stelmach } 2076cd225ccSŁukasz Stelmach 2086cd225ccSŁukasz Stelmach static SIMPLE_DEV_PM_OPS(exynos_trng_pm_ops, exynos_trng_suspend, 2096cd225ccSŁukasz Stelmach exynos_trng_resume); 2106cd225ccSŁukasz Stelmach 2116cd225ccSŁukasz Stelmach static const struct of_device_id exynos_trng_dt_match[] = { 2126cd225ccSŁukasz Stelmach { 2136cd225ccSŁukasz Stelmach .compatible = "samsung,exynos5250-trng", 2146cd225ccSŁukasz Stelmach }, 2156cd225ccSŁukasz Stelmach { }, 2166cd225ccSŁukasz Stelmach }; 2176cd225ccSŁukasz Stelmach MODULE_DEVICE_TABLE(of, exynos_trng_dt_match); 2186cd225ccSŁukasz Stelmach 2196cd225ccSŁukasz Stelmach static struct platform_driver exynos_trng_driver = { 2206cd225ccSŁukasz Stelmach .driver = { 2216cd225ccSŁukasz Stelmach .name = "exynos-trng", 2226cd225ccSŁukasz Stelmach .pm = &exynos_trng_pm_ops, 2236cd225ccSŁukasz Stelmach .of_match_table = exynos_trng_dt_match, 2246cd225ccSŁukasz Stelmach }, 2256cd225ccSŁukasz Stelmach .probe = exynos_trng_probe, 2266cd225ccSŁukasz Stelmach .remove = exynos_trng_remove, 2276cd225ccSŁukasz Stelmach }; 2286cd225ccSŁukasz Stelmach 2296cd225ccSŁukasz Stelmach module_platform_driver(exynos_trng_driver); 2306cd225ccSŁukasz Stelmach MODULE_AUTHOR("Łukasz Stelmach"); 2316cd225ccSŁukasz Stelmach MODULE_DESCRIPTION("H/W TRNG driver for Exynos chips"); 2326cd225ccSŁukasz Stelmach MODULE_LICENSE("GPL v2"); 233