18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Random Number Generator driver for the Keystone SOC 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Texas Instruments Incorporated - https://www.ti.com 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Authors: Sandeep Nair 88c2ecf20Sopenharmony_ci * Vitaly Andrianov 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/hw_random.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/clk.h> 178c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 188c2ecf20Sopenharmony_ci#include <linux/err.h> 198c2ecf20Sopenharmony_ci#include <linux/regmap.h> 208c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 218c2ecf20Sopenharmony_ci#include <linux/of.h> 228c2ecf20Sopenharmony_ci#include <linux/of_address.h> 238c2ecf20Sopenharmony_ci#include <linux/delay.h> 248c2ecf20Sopenharmony_ci#include <linux/timekeeping.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define SA_CMD_STATUS_OFS 0x8 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* TRNG enable control in SA System module*/ 298c2ecf20Sopenharmony_ci#define SA_CMD_STATUS_REG_TRNG_ENABLE BIT(3) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* TRNG start control in TRNG module */ 328c2ecf20Sopenharmony_ci#define TRNG_CNTL_REG_TRNG_ENABLE BIT(10) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* Data ready indicator in STATUS register */ 358c2ecf20Sopenharmony_ci#define TRNG_STATUS_REG_READY BIT(0) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* Data ready clear control in INTACK register */ 388c2ecf20Sopenharmony_ci#define TRNG_INTACK_REG_READY BIT(0) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* 418c2ecf20Sopenharmony_ci * Number of samples taken to gather entropy during startup. 428c2ecf20Sopenharmony_ci * If value is 0, the number of samples is 2^24 else 438c2ecf20Sopenharmony_ci * equals value times 2^8. 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_ci#define TRNG_DEF_STARTUP_CYCLES 0 468c2ecf20Sopenharmony_ci#define TRNG_CNTL_REG_STARTUP_CYCLES_SHIFT 16 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* 498c2ecf20Sopenharmony_ci * Minimum number of samples taken to regenerate entropy 508c2ecf20Sopenharmony_ci * If value is 0, the number of samples is 2^24 else 518c2ecf20Sopenharmony_ci * equals value times 2^6. 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_ci#define TRNG_DEF_MIN_REFILL_CYCLES 1 548c2ecf20Sopenharmony_ci#define TRNG_CFG_REG_MIN_REFILL_CYCLES_SHIFT 0 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* 578c2ecf20Sopenharmony_ci * Maximum number of samples taken to regenerate entropy 588c2ecf20Sopenharmony_ci * If value is 0, the number of samples is 2^24 else 598c2ecf20Sopenharmony_ci * equals value times 2^8. 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_ci#define TRNG_DEF_MAX_REFILL_CYCLES 0 628c2ecf20Sopenharmony_ci#define TRNG_CFG_REG_MAX_REFILL_CYCLES_SHIFT 16 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* Number of CLK input cycles between samples */ 658c2ecf20Sopenharmony_ci#define TRNG_DEF_CLK_DIV_CYCLES 0 668c2ecf20Sopenharmony_ci#define TRNG_CFG_REG_SAMPLE_DIV_SHIFT 8 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* Maximum retries to get rng data */ 698c2ecf20Sopenharmony_ci#define SA_MAX_RNG_DATA_RETRIES 5 708c2ecf20Sopenharmony_ci/* Delay between retries (in usecs) */ 718c2ecf20Sopenharmony_ci#define SA_RNG_DATA_RETRY_DELAY 5 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistruct trng_regs { 748c2ecf20Sopenharmony_ci u32 output_l; 758c2ecf20Sopenharmony_ci u32 output_h; 768c2ecf20Sopenharmony_ci u32 status; 778c2ecf20Sopenharmony_ci u32 intmask; 788c2ecf20Sopenharmony_ci u32 intack; 798c2ecf20Sopenharmony_ci u32 control; 808c2ecf20Sopenharmony_ci u32 config; 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistruct ks_sa_rng { 848c2ecf20Sopenharmony_ci struct device *dev; 858c2ecf20Sopenharmony_ci struct hwrng rng; 868c2ecf20Sopenharmony_ci struct clk *clk; 878c2ecf20Sopenharmony_ci struct regmap *regmap_cfg; 888c2ecf20Sopenharmony_ci struct trng_regs __iomem *reg_rng; 898c2ecf20Sopenharmony_ci u64 ready_ts; 908c2ecf20Sopenharmony_ci unsigned int refill_delay_ns; 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic unsigned int cycles_to_ns(unsigned long clk_rate, unsigned int cycles) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci return DIV_ROUND_UP_ULL((TRNG_DEF_CLK_DIV_CYCLES + 1) * 1000000000ull * 968c2ecf20Sopenharmony_ci cycles, clk_rate); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic unsigned int startup_delay_ns(unsigned long clk_rate) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci if (!TRNG_DEF_STARTUP_CYCLES) 1028c2ecf20Sopenharmony_ci return cycles_to_ns(clk_rate, BIT(24)); 1038c2ecf20Sopenharmony_ci return cycles_to_ns(clk_rate, 256 * TRNG_DEF_STARTUP_CYCLES); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic unsigned int refill_delay_ns(unsigned long clk_rate) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci if (!TRNG_DEF_MAX_REFILL_CYCLES) 1098c2ecf20Sopenharmony_ci return cycles_to_ns(clk_rate, BIT(24)); 1108c2ecf20Sopenharmony_ci return cycles_to_ns(clk_rate, 256 * TRNG_DEF_MAX_REFILL_CYCLES); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic int ks_sa_rng_init(struct hwrng *rng) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci u32 value; 1168c2ecf20Sopenharmony_ci struct device *dev = (struct device *)rng->priv; 1178c2ecf20Sopenharmony_ci struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev); 1188c2ecf20Sopenharmony_ci unsigned long clk_rate = clk_get_rate(ks_sa_rng->clk); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* Enable RNG module */ 1218c2ecf20Sopenharmony_ci regmap_write_bits(ks_sa_rng->regmap_cfg, SA_CMD_STATUS_OFS, 1228c2ecf20Sopenharmony_ci SA_CMD_STATUS_REG_TRNG_ENABLE, 1238c2ecf20Sopenharmony_ci SA_CMD_STATUS_REG_TRNG_ENABLE); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* Configure RNG module */ 1268c2ecf20Sopenharmony_ci writel(0, &ks_sa_rng->reg_rng->control); 1278c2ecf20Sopenharmony_ci value = TRNG_DEF_STARTUP_CYCLES << TRNG_CNTL_REG_STARTUP_CYCLES_SHIFT; 1288c2ecf20Sopenharmony_ci writel(value, &ks_sa_rng->reg_rng->control); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci value = (TRNG_DEF_MIN_REFILL_CYCLES << 1318c2ecf20Sopenharmony_ci TRNG_CFG_REG_MIN_REFILL_CYCLES_SHIFT) | 1328c2ecf20Sopenharmony_ci (TRNG_DEF_MAX_REFILL_CYCLES << 1338c2ecf20Sopenharmony_ci TRNG_CFG_REG_MAX_REFILL_CYCLES_SHIFT) | 1348c2ecf20Sopenharmony_ci (TRNG_DEF_CLK_DIV_CYCLES << 1358c2ecf20Sopenharmony_ci TRNG_CFG_REG_SAMPLE_DIV_SHIFT); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci writel(value, &ks_sa_rng->reg_rng->config); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* Disable all interrupts from TRNG */ 1408c2ecf20Sopenharmony_ci writel(0, &ks_sa_rng->reg_rng->intmask); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* Enable RNG */ 1438c2ecf20Sopenharmony_ci value = readl(&ks_sa_rng->reg_rng->control); 1448c2ecf20Sopenharmony_ci value |= TRNG_CNTL_REG_TRNG_ENABLE; 1458c2ecf20Sopenharmony_ci writel(value, &ks_sa_rng->reg_rng->control); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci ks_sa_rng->refill_delay_ns = refill_delay_ns(clk_rate); 1488c2ecf20Sopenharmony_ci ks_sa_rng->ready_ts = ktime_get_ns() + 1498c2ecf20Sopenharmony_ci startup_delay_ns(clk_rate); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return 0; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic void ks_sa_rng_cleanup(struct hwrng *rng) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct device *dev = (struct device *)rng->priv; 1578c2ecf20Sopenharmony_ci struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* Disable RNG */ 1608c2ecf20Sopenharmony_ci writel(0, &ks_sa_rng->reg_rng->control); 1618c2ecf20Sopenharmony_ci regmap_write_bits(ks_sa_rng->regmap_cfg, SA_CMD_STATUS_OFS, 1628c2ecf20Sopenharmony_ci SA_CMD_STATUS_REG_TRNG_ENABLE, 0); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic int ks_sa_rng_data_read(struct hwrng *rng, u32 *data) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct device *dev = (struct device *)rng->priv; 1688c2ecf20Sopenharmony_ci struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* Read random data */ 1718c2ecf20Sopenharmony_ci data[0] = readl(&ks_sa_rng->reg_rng->output_l); 1728c2ecf20Sopenharmony_ci data[1] = readl(&ks_sa_rng->reg_rng->output_h); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci writel(TRNG_INTACK_REG_READY, &ks_sa_rng->reg_rng->intack); 1758c2ecf20Sopenharmony_ci ks_sa_rng->ready_ts = ktime_get_ns() + ks_sa_rng->refill_delay_ns; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci return sizeof(u32) * 2; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic int ks_sa_rng_data_present(struct hwrng *rng, int wait) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci struct device *dev = (struct device *)rng->priv; 1838c2ecf20Sopenharmony_ci struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev); 1848c2ecf20Sopenharmony_ci u64 now = ktime_get_ns(); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci u32 ready; 1878c2ecf20Sopenharmony_ci int j; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (wait && now < ks_sa_rng->ready_ts) { 1908c2ecf20Sopenharmony_ci /* Max delay expected here is 81920000 ns */ 1918c2ecf20Sopenharmony_ci unsigned long min_delay = 1928c2ecf20Sopenharmony_ci DIV_ROUND_UP((u32)(ks_sa_rng->ready_ts - now), 1000); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci usleep_range(min_delay, min_delay + SA_RNG_DATA_RETRY_DELAY); 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci for (j = 0; j < SA_MAX_RNG_DATA_RETRIES; j++) { 1988c2ecf20Sopenharmony_ci ready = readl(&ks_sa_rng->reg_rng->status); 1998c2ecf20Sopenharmony_ci ready &= TRNG_STATUS_REG_READY; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (ready || !wait) 2028c2ecf20Sopenharmony_ci break; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci udelay(SA_RNG_DATA_RETRY_DELAY); 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci return ready; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic int ks_sa_rng_probe(struct platform_device *pdev) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci struct ks_sa_rng *ks_sa_rng; 2138c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 2148c2ecf20Sopenharmony_ci int ret; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci ks_sa_rng = devm_kzalloc(dev, sizeof(*ks_sa_rng), GFP_KERNEL); 2178c2ecf20Sopenharmony_ci if (!ks_sa_rng) 2188c2ecf20Sopenharmony_ci return -ENOMEM; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci ks_sa_rng->dev = dev; 2218c2ecf20Sopenharmony_ci ks_sa_rng->rng = (struct hwrng) { 2228c2ecf20Sopenharmony_ci .name = "ks_sa_hwrng", 2238c2ecf20Sopenharmony_ci .init = ks_sa_rng_init, 2248c2ecf20Sopenharmony_ci .data_read = ks_sa_rng_data_read, 2258c2ecf20Sopenharmony_ci .data_present = ks_sa_rng_data_present, 2268c2ecf20Sopenharmony_ci .cleanup = ks_sa_rng_cleanup, 2278c2ecf20Sopenharmony_ci }; 2288c2ecf20Sopenharmony_ci ks_sa_rng->rng.priv = (unsigned long)dev; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci ks_sa_rng->reg_rng = devm_platform_ioremap_resource(pdev, 0); 2318c2ecf20Sopenharmony_ci if (IS_ERR(ks_sa_rng->reg_rng)) 2328c2ecf20Sopenharmony_ci return PTR_ERR(ks_sa_rng->reg_rng); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci ks_sa_rng->regmap_cfg = 2358c2ecf20Sopenharmony_ci syscon_regmap_lookup_by_phandle(dev->of_node, 2368c2ecf20Sopenharmony_ci "ti,syscon-sa-cfg"); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (IS_ERR(ks_sa_rng->regmap_cfg)) { 2398c2ecf20Sopenharmony_ci dev_err(dev, "syscon_node_to_regmap failed\n"); 2408c2ecf20Sopenharmony_ci return -EINVAL; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 2448c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(dev); 2458c2ecf20Sopenharmony_ci if (ret < 0) { 2468c2ecf20Sopenharmony_ci dev_err(dev, "Failed to enable SA power-domain\n"); 2478c2ecf20Sopenharmony_ci pm_runtime_put_noidle(dev); 2488c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 2498c2ecf20Sopenharmony_ci return ret; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, ks_sa_rng); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return devm_hwrng_register(&pdev->dev, &ks_sa_rng->rng); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic int ks_sa_rng_remove(struct platform_device *pdev) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci pm_runtime_put_sync(&pdev->dev); 2608c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci return 0; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic const struct of_device_id ks_sa_rng_dt_match[] = { 2668c2ecf20Sopenharmony_ci { 2678c2ecf20Sopenharmony_ci .compatible = "ti,keystone-rng", 2688c2ecf20Sopenharmony_ci }, 2698c2ecf20Sopenharmony_ci { }, 2708c2ecf20Sopenharmony_ci}; 2718c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ks_sa_rng_dt_match); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic struct platform_driver ks_sa_rng_driver = { 2748c2ecf20Sopenharmony_ci .driver = { 2758c2ecf20Sopenharmony_ci .name = "ks-sa-rng", 2768c2ecf20Sopenharmony_ci .of_match_table = ks_sa_rng_dt_match, 2778c2ecf20Sopenharmony_ci }, 2788c2ecf20Sopenharmony_ci .probe = ks_sa_rng_probe, 2798c2ecf20Sopenharmony_ci .remove = ks_sa_rng_remove, 2808c2ecf20Sopenharmony_ci}; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cimodule_platform_driver(ks_sa_rng_driver); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Keystone NETCP SA H/W Random Number Generator driver"); 2858c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vitaly Andrianov <vitalya@ti.com>"); 2868c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 287