18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci* Copyright (C) 2015 Broadcom Corporation 38c2ecf20Sopenharmony_ci* 48c2ecf20Sopenharmony_ci* This program is free software; you can redistribute it and/or 58c2ecf20Sopenharmony_ci* modify it under the terms of the GNU General Public License as 68c2ecf20Sopenharmony_ci* published by the Free Software Foundation version 2. 78c2ecf20Sopenharmony_ci* 88c2ecf20Sopenharmony_ci* This program is distributed "as is" WITHOUT ANY WARRANTY of any 98c2ecf20Sopenharmony_ci* kind, whether express or implied; without even the implied warranty 108c2ecf20Sopenharmony_ci* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 118c2ecf20Sopenharmony_ci* GNU General Public License for more details. 128c2ecf20Sopenharmony_ci*/ 138c2ecf20Sopenharmony_ci/* 148c2ecf20Sopenharmony_ci * DESCRIPTION: The Broadcom iProc RNG200 Driver 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/hw_random.h> 188c2ecf20Sopenharmony_ci#include <linux/init.h> 198c2ecf20Sopenharmony_ci#include <linux/io.h> 208c2ecf20Sopenharmony_ci#include <linux/kernel.h> 218c2ecf20Sopenharmony_ci#include <linux/module.h> 228c2ecf20Sopenharmony_ci#include <linux/of_address.h> 238c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 248c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 258c2ecf20Sopenharmony_ci#include <linux/delay.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* Registers */ 288c2ecf20Sopenharmony_ci#define RNG_CTRL_OFFSET 0x00 298c2ecf20Sopenharmony_ci#define RNG_CTRL_RNG_RBGEN_MASK 0x00001FFF 308c2ecf20Sopenharmony_ci#define RNG_CTRL_RNG_RBGEN_ENABLE 0x00000001 318c2ecf20Sopenharmony_ci#define RNG_CTRL_RNG_RBGEN_DISABLE 0x00000000 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define RNG_SOFT_RESET_OFFSET 0x04 348c2ecf20Sopenharmony_ci#define RNG_SOFT_RESET 0x00000001 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define RBG_SOFT_RESET_OFFSET 0x08 378c2ecf20Sopenharmony_ci#define RBG_SOFT_RESET 0x00000001 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define RNG_INT_STATUS_OFFSET 0x18 408c2ecf20Sopenharmony_ci#define RNG_INT_STATUS_MASTER_FAIL_LOCKOUT_IRQ_MASK 0x80000000 418c2ecf20Sopenharmony_ci#define RNG_INT_STATUS_STARTUP_TRANSITIONS_MET_IRQ_MASK 0x00020000 428c2ecf20Sopenharmony_ci#define RNG_INT_STATUS_NIST_FAIL_IRQ_MASK 0x00000020 438c2ecf20Sopenharmony_ci#define RNG_INT_STATUS_TOTAL_BITS_COUNT_IRQ_MASK 0x00000001 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define RNG_FIFO_DATA_OFFSET 0x20 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define RNG_FIFO_COUNT_OFFSET 0x24 488c2ecf20Sopenharmony_ci#define RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK 0x000000FF 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistruct iproc_rng200_dev { 518c2ecf20Sopenharmony_ci struct hwrng rng; 528c2ecf20Sopenharmony_ci void __iomem *base; 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define to_rng_priv(rng) container_of(rng, struct iproc_rng200_dev, rng) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic void iproc_rng200_restart(void __iomem *rng_base) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci uint32_t val; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci /* Disable RBG */ 628c2ecf20Sopenharmony_ci val = ioread32(rng_base + RNG_CTRL_OFFSET); 638c2ecf20Sopenharmony_ci val &= ~RNG_CTRL_RNG_RBGEN_MASK; 648c2ecf20Sopenharmony_ci val |= RNG_CTRL_RNG_RBGEN_DISABLE; 658c2ecf20Sopenharmony_ci iowrite32(val, rng_base + RNG_CTRL_OFFSET); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci /* Clear all interrupt status */ 688c2ecf20Sopenharmony_ci iowrite32(0xFFFFFFFFUL, rng_base + RNG_INT_STATUS_OFFSET); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* Reset RNG and RBG */ 718c2ecf20Sopenharmony_ci val = ioread32(rng_base + RBG_SOFT_RESET_OFFSET); 728c2ecf20Sopenharmony_ci val |= RBG_SOFT_RESET; 738c2ecf20Sopenharmony_ci iowrite32(val, rng_base + RBG_SOFT_RESET_OFFSET); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci val = ioread32(rng_base + RNG_SOFT_RESET_OFFSET); 768c2ecf20Sopenharmony_ci val |= RNG_SOFT_RESET; 778c2ecf20Sopenharmony_ci iowrite32(val, rng_base + RNG_SOFT_RESET_OFFSET); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci val = ioread32(rng_base + RNG_SOFT_RESET_OFFSET); 808c2ecf20Sopenharmony_ci val &= ~RNG_SOFT_RESET; 818c2ecf20Sopenharmony_ci iowrite32(val, rng_base + RNG_SOFT_RESET_OFFSET); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci val = ioread32(rng_base + RBG_SOFT_RESET_OFFSET); 848c2ecf20Sopenharmony_ci val &= ~RBG_SOFT_RESET; 858c2ecf20Sopenharmony_ci iowrite32(val, rng_base + RBG_SOFT_RESET_OFFSET); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci /* Enable RBG */ 888c2ecf20Sopenharmony_ci val = ioread32(rng_base + RNG_CTRL_OFFSET); 898c2ecf20Sopenharmony_ci val &= ~RNG_CTRL_RNG_RBGEN_MASK; 908c2ecf20Sopenharmony_ci val |= RNG_CTRL_RNG_RBGEN_ENABLE; 918c2ecf20Sopenharmony_ci iowrite32(val, rng_base + RNG_CTRL_OFFSET); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic int iproc_rng200_read(struct hwrng *rng, void *buf, size_t max, 958c2ecf20Sopenharmony_ci bool wait) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci struct iproc_rng200_dev *priv = to_rng_priv(rng); 988c2ecf20Sopenharmony_ci uint32_t num_remaining = max; 998c2ecf20Sopenharmony_ci uint32_t status; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci #define MAX_RESETS_PER_READ 1 1028c2ecf20Sopenharmony_ci uint32_t num_resets = 0; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci #define MAX_IDLE_TIME (1 * HZ) 1058c2ecf20Sopenharmony_ci unsigned long idle_endtime = jiffies + MAX_IDLE_TIME; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci while ((num_remaining > 0) && time_before(jiffies, idle_endtime)) { 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* Is RNG sane? If not, reset it. */ 1108c2ecf20Sopenharmony_ci status = ioread32(priv->base + RNG_INT_STATUS_OFFSET); 1118c2ecf20Sopenharmony_ci if ((status & (RNG_INT_STATUS_MASTER_FAIL_LOCKOUT_IRQ_MASK | 1128c2ecf20Sopenharmony_ci RNG_INT_STATUS_NIST_FAIL_IRQ_MASK)) != 0) { 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (num_resets >= MAX_RESETS_PER_READ) 1158c2ecf20Sopenharmony_ci return max - num_remaining; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci iproc_rng200_restart(priv->base); 1188c2ecf20Sopenharmony_ci num_resets++; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* Are there any random numbers available? */ 1228c2ecf20Sopenharmony_ci if ((ioread32(priv->base + RNG_FIFO_COUNT_OFFSET) & 1238c2ecf20Sopenharmony_ci RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK) > 0) { 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (num_remaining >= sizeof(uint32_t)) { 1268c2ecf20Sopenharmony_ci /* Buffer has room to store entire word */ 1278c2ecf20Sopenharmony_ci *(uint32_t *)buf = ioread32(priv->base + 1288c2ecf20Sopenharmony_ci RNG_FIFO_DATA_OFFSET); 1298c2ecf20Sopenharmony_ci buf += sizeof(uint32_t); 1308c2ecf20Sopenharmony_ci num_remaining -= sizeof(uint32_t); 1318c2ecf20Sopenharmony_ci } else { 1328c2ecf20Sopenharmony_ci /* Buffer can only store partial word */ 1338c2ecf20Sopenharmony_ci uint32_t rnd_number = ioread32(priv->base + 1348c2ecf20Sopenharmony_ci RNG_FIFO_DATA_OFFSET); 1358c2ecf20Sopenharmony_ci memcpy(buf, &rnd_number, num_remaining); 1368c2ecf20Sopenharmony_ci buf += num_remaining; 1378c2ecf20Sopenharmony_ci num_remaining = 0; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* Reset the IDLE timeout */ 1418c2ecf20Sopenharmony_ci idle_endtime = jiffies + MAX_IDLE_TIME; 1428c2ecf20Sopenharmony_ci } else { 1438c2ecf20Sopenharmony_ci if (!wait) 1448c2ecf20Sopenharmony_ci /* Cannot wait, return immediately */ 1458c2ecf20Sopenharmony_ci return max - num_remaining; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* Can wait, give others chance to run */ 1488c2ecf20Sopenharmony_ci usleep_range(min(num_remaining * 10, 500U), 500); 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return max - num_remaining; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int iproc_rng200_init(struct hwrng *rng) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct iproc_rng200_dev *priv = to_rng_priv(rng); 1588c2ecf20Sopenharmony_ci uint32_t val; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* Setup RNG. */ 1618c2ecf20Sopenharmony_ci val = ioread32(priv->base + RNG_CTRL_OFFSET); 1628c2ecf20Sopenharmony_ci val &= ~RNG_CTRL_RNG_RBGEN_MASK; 1638c2ecf20Sopenharmony_ci val |= RNG_CTRL_RNG_RBGEN_ENABLE; 1648c2ecf20Sopenharmony_ci iowrite32(val, priv->base + RNG_CTRL_OFFSET); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return 0; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic void iproc_rng200_cleanup(struct hwrng *rng) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct iproc_rng200_dev *priv = to_rng_priv(rng); 1728c2ecf20Sopenharmony_ci uint32_t val; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* Disable RNG hardware */ 1758c2ecf20Sopenharmony_ci val = ioread32(priv->base + RNG_CTRL_OFFSET); 1768c2ecf20Sopenharmony_ci val &= ~RNG_CTRL_RNG_RBGEN_MASK; 1778c2ecf20Sopenharmony_ci val |= RNG_CTRL_RNG_RBGEN_DISABLE; 1788c2ecf20Sopenharmony_ci iowrite32(val, priv->base + RNG_CTRL_OFFSET); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic int iproc_rng200_probe(struct platform_device *pdev) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct iproc_rng200_dev *priv; 1848c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1858c2ecf20Sopenharmony_ci int ret; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 1888c2ecf20Sopenharmony_ci if (!priv) 1898c2ecf20Sopenharmony_ci return -ENOMEM; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* Map peripheral */ 1928c2ecf20Sopenharmony_ci priv->base = devm_platform_ioremap_resource(pdev, 0); 1938c2ecf20Sopenharmony_ci if (IS_ERR(priv->base)) { 1948c2ecf20Sopenharmony_ci dev_err(dev, "failed to remap rng regs\n"); 1958c2ecf20Sopenharmony_ci return PTR_ERR(priv->base); 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci dev_set_drvdata(dev, priv); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci priv->rng.name = "iproc-rng200"; 2018c2ecf20Sopenharmony_ci priv->rng.read = iproc_rng200_read; 2028c2ecf20Sopenharmony_ci priv->rng.init = iproc_rng200_init; 2038c2ecf20Sopenharmony_ci priv->rng.cleanup = iproc_rng200_cleanup; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* Register driver */ 2068c2ecf20Sopenharmony_ci ret = devm_hwrng_register(dev, &priv->rng); 2078c2ecf20Sopenharmony_ci if (ret) { 2088c2ecf20Sopenharmony_ci dev_err(dev, "hwrng registration failed\n"); 2098c2ecf20Sopenharmony_ci return ret; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci dev_info(dev, "hwrng registered\n"); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci return 0; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int __maybe_unused iproc_rng200_suspend(struct device *dev) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci struct iproc_rng200_dev *priv = dev_get_drvdata(dev); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci iproc_rng200_cleanup(&priv->rng); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return 0; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic int __maybe_unused iproc_rng200_resume(struct device *dev) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci struct iproc_rng200_dev *priv = dev_get_drvdata(dev); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci iproc_rng200_init(&priv->rng); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci return 0; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic const struct dev_pm_ops iproc_rng200_pm_ops = { 2368c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(iproc_rng200_suspend, iproc_rng200_resume) 2378c2ecf20Sopenharmony_ci}; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic const struct of_device_id iproc_rng200_of_match[] = { 2408c2ecf20Sopenharmony_ci { .compatible = "brcm,bcm2711-rng200", }, 2418c2ecf20Sopenharmony_ci { .compatible = "brcm,bcm7211-rng200", }, 2428c2ecf20Sopenharmony_ci { .compatible = "brcm,bcm7278-rng200", }, 2438c2ecf20Sopenharmony_ci { .compatible = "brcm,iproc-rng200", }, 2448c2ecf20Sopenharmony_ci {}, 2458c2ecf20Sopenharmony_ci}; 2468c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, iproc_rng200_of_match); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic struct platform_driver iproc_rng200_driver = { 2498c2ecf20Sopenharmony_ci .driver = { 2508c2ecf20Sopenharmony_ci .name = "iproc-rng200", 2518c2ecf20Sopenharmony_ci .of_match_table = iproc_rng200_of_match, 2528c2ecf20Sopenharmony_ci .pm = &iproc_rng200_pm_ops, 2538c2ecf20Sopenharmony_ci }, 2548c2ecf20Sopenharmony_ci .probe = iproc_rng200_probe, 2558c2ecf20Sopenharmony_ci}; 2568c2ecf20Sopenharmony_cimodule_platform_driver(iproc_rng200_driver); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ciMODULE_AUTHOR("Broadcom"); 2598c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("iProc RNG200 Random Number Generator driver"); 2608c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 261