11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2eb428ee0SVitaly Andrianov /* 3eb428ee0SVitaly Andrianov * Random Number Generator driver for the Keystone SOC 4eb428ee0SVitaly Andrianov * 5eb428ee0SVitaly Andrianov * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com 6eb428ee0SVitaly Andrianov * 7eb428ee0SVitaly Andrianov * Authors: Sandeep Nair 8eb428ee0SVitaly Andrianov * Vitaly Andrianov 9eb428ee0SVitaly Andrianov */ 10eb428ee0SVitaly Andrianov 11eb428ee0SVitaly Andrianov #include <linux/hw_random.h> 12eb428ee0SVitaly Andrianov #include <linux/kernel.h> 13eb428ee0SVitaly Andrianov #include <linux/module.h> 14eb428ee0SVitaly Andrianov #include <linux/io.h> 15eb428ee0SVitaly Andrianov #include <linux/platform_device.h> 16eb428ee0SVitaly Andrianov #include <linux/clk.h> 17eb428ee0SVitaly Andrianov #include <linux/pm_runtime.h> 18eb428ee0SVitaly Andrianov #include <linux/err.h> 19eb428ee0SVitaly Andrianov #include <linux/regmap.h> 20eb428ee0SVitaly Andrianov #include <linux/mfd/syscon.h> 21eb428ee0SVitaly Andrianov #include <linux/of.h> 22eb428ee0SVitaly Andrianov #include <linux/of_address.h> 23eb428ee0SVitaly Andrianov #include <linux/delay.h> 24eb428ee0SVitaly Andrianov 25eb428ee0SVitaly Andrianov #define SA_CMD_STATUS_OFS 0x8 26eb428ee0SVitaly Andrianov 27eb428ee0SVitaly Andrianov /* TRNG enable control in SA System module*/ 28eb428ee0SVitaly Andrianov #define SA_CMD_STATUS_REG_TRNG_ENABLE BIT(3) 29eb428ee0SVitaly Andrianov 30eb428ee0SVitaly Andrianov /* TRNG start control in TRNG module */ 31eb428ee0SVitaly Andrianov #define TRNG_CNTL_REG_TRNG_ENABLE BIT(10) 32eb428ee0SVitaly Andrianov 33eb428ee0SVitaly Andrianov /* Data ready indicator in STATUS register */ 34eb428ee0SVitaly Andrianov #define TRNG_STATUS_REG_READY BIT(0) 35eb428ee0SVitaly Andrianov 36eb428ee0SVitaly Andrianov /* Data ready clear control in INTACK register */ 37eb428ee0SVitaly Andrianov #define TRNG_INTACK_REG_READY BIT(0) 38eb428ee0SVitaly Andrianov 39eb428ee0SVitaly Andrianov /* 40eb428ee0SVitaly Andrianov * Number of samples taken to gather entropy during startup. 41eb428ee0SVitaly Andrianov * If value is 0, the number of samples is 2^24 else 42eb428ee0SVitaly Andrianov * equals value times 2^8. 43eb428ee0SVitaly Andrianov */ 44eb428ee0SVitaly Andrianov #define TRNG_DEF_STARTUP_CYCLES 0 45eb428ee0SVitaly Andrianov #define TRNG_CNTL_REG_STARTUP_CYCLES_SHIFT 16 46eb428ee0SVitaly Andrianov 47eb428ee0SVitaly Andrianov /* 48eb428ee0SVitaly Andrianov * Minimum number of samples taken to regenerate entropy 49eb428ee0SVitaly Andrianov * If value is 0, the number of samples is 2^24 else 50eb428ee0SVitaly Andrianov * equals value times 2^6. 51eb428ee0SVitaly Andrianov */ 52eb428ee0SVitaly Andrianov #define TRNG_DEF_MIN_REFILL_CYCLES 1 53eb428ee0SVitaly Andrianov #define TRNG_CFG_REG_MIN_REFILL_CYCLES_SHIFT 0 54eb428ee0SVitaly Andrianov 55eb428ee0SVitaly Andrianov /* 56eb428ee0SVitaly Andrianov * Maximum number of samples taken to regenerate entropy 57eb428ee0SVitaly Andrianov * If value is 0, the number of samples is 2^24 else 58eb428ee0SVitaly Andrianov * equals value times 2^8. 59eb428ee0SVitaly Andrianov */ 60eb428ee0SVitaly Andrianov #define TRNG_DEF_MAX_REFILL_CYCLES 0 61eb428ee0SVitaly Andrianov #define TRNG_CFG_REG_MAX_REFILL_CYCLES_SHIFT 16 62eb428ee0SVitaly Andrianov 63eb428ee0SVitaly Andrianov /* Number of CLK input cycles between samples */ 64eb428ee0SVitaly Andrianov #define TRNG_DEF_CLK_DIV_CYCLES 0 65eb428ee0SVitaly Andrianov #define TRNG_CFG_REG_SAMPLE_DIV_SHIFT 8 66eb428ee0SVitaly Andrianov 67eb428ee0SVitaly Andrianov /* Maximum retries to get rng data */ 68eb428ee0SVitaly Andrianov #define SA_MAX_RNG_DATA_RETRIES 5 69eb428ee0SVitaly Andrianov /* Delay between retries (in usecs) */ 70eb428ee0SVitaly Andrianov #define SA_RNG_DATA_RETRY_DELAY 5 71eb428ee0SVitaly Andrianov 72eb428ee0SVitaly Andrianov struct trng_regs { 73eb428ee0SVitaly Andrianov u32 output_l; 74eb428ee0SVitaly Andrianov u32 output_h; 75eb428ee0SVitaly Andrianov u32 status; 76eb428ee0SVitaly Andrianov u32 intmask; 77eb428ee0SVitaly Andrianov u32 intack; 78eb428ee0SVitaly Andrianov u32 control; 79eb428ee0SVitaly Andrianov u32 config; 80eb428ee0SVitaly Andrianov }; 81eb428ee0SVitaly Andrianov 82eb428ee0SVitaly Andrianov struct ks_sa_rng { 83eb428ee0SVitaly Andrianov struct device *dev; 84eb428ee0SVitaly Andrianov struct hwrng rng; 85eb428ee0SVitaly Andrianov struct clk *clk; 86eb428ee0SVitaly Andrianov struct regmap *regmap_cfg; 87*d1569349SBen Dooks struct trng_regs __iomem *reg_rng; 88eb428ee0SVitaly Andrianov }; 89eb428ee0SVitaly Andrianov 90eb428ee0SVitaly Andrianov static int ks_sa_rng_init(struct hwrng *rng) 91eb428ee0SVitaly Andrianov { 92eb428ee0SVitaly Andrianov u32 value; 93eb428ee0SVitaly Andrianov struct device *dev = (struct device *)rng->priv; 94eb428ee0SVitaly Andrianov struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev); 95eb428ee0SVitaly Andrianov 96eb428ee0SVitaly Andrianov /* Enable RNG module */ 97eb428ee0SVitaly Andrianov regmap_write_bits(ks_sa_rng->regmap_cfg, SA_CMD_STATUS_OFS, 98eb428ee0SVitaly Andrianov SA_CMD_STATUS_REG_TRNG_ENABLE, 99eb428ee0SVitaly Andrianov SA_CMD_STATUS_REG_TRNG_ENABLE); 100eb428ee0SVitaly Andrianov 101eb428ee0SVitaly Andrianov /* Configure RNG module */ 102eb428ee0SVitaly Andrianov writel(0, &ks_sa_rng->reg_rng->control); 103eb428ee0SVitaly Andrianov value = TRNG_DEF_STARTUP_CYCLES << TRNG_CNTL_REG_STARTUP_CYCLES_SHIFT; 104eb428ee0SVitaly Andrianov writel(value, &ks_sa_rng->reg_rng->control); 105eb428ee0SVitaly Andrianov 106eb428ee0SVitaly Andrianov value = (TRNG_DEF_MIN_REFILL_CYCLES << 107eb428ee0SVitaly Andrianov TRNG_CFG_REG_MIN_REFILL_CYCLES_SHIFT) | 108eb428ee0SVitaly Andrianov (TRNG_DEF_MAX_REFILL_CYCLES << 109eb428ee0SVitaly Andrianov TRNG_CFG_REG_MAX_REFILL_CYCLES_SHIFT) | 110eb428ee0SVitaly Andrianov (TRNG_DEF_CLK_DIV_CYCLES << 111eb428ee0SVitaly Andrianov TRNG_CFG_REG_SAMPLE_DIV_SHIFT); 112eb428ee0SVitaly Andrianov 113eb428ee0SVitaly Andrianov writel(value, &ks_sa_rng->reg_rng->config); 114eb428ee0SVitaly Andrianov 115eb428ee0SVitaly Andrianov /* Disable all interrupts from TRNG */ 116eb428ee0SVitaly Andrianov writel(0, &ks_sa_rng->reg_rng->intmask); 117eb428ee0SVitaly Andrianov 118eb428ee0SVitaly Andrianov /* Enable RNG */ 119eb428ee0SVitaly Andrianov value = readl(&ks_sa_rng->reg_rng->control); 120eb428ee0SVitaly Andrianov value |= TRNG_CNTL_REG_TRNG_ENABLE; 121eb428ee0SVitaly Andrianov writel(value, &ks_sa_rng->reg_rng->control); 122eb428ee0SVitaly Andrianov 123eb428ee0SVitaly Andrianov return 0; 124eb428ee0SVitaly Andrianov } 125eb428ee0SVitaly Andrianov 126eb428ee0SVitaly Andrianov static void ks_sa_rng_cleanup(struct hwrng *rng) 127eb428ee0SVitaly Andrianov { 128eb428ee0SVitaly Andrianov struct device *dev = (struct device *)rng->priv; 129eb428ee0SVitaly Andrianov struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev); 130eb428ee0SVitaly Andrianov 131eb428ee0SVitaly Andrianov /* Disable RNG */ 132eb428ee0SVitaly Andrianov writel(0, &ks_sa_rng->reg_rng->control); 133eb428ee0SVitaly Andrianov regmap_write_bits(ks_sa_rng->regmap_cfg, SA_CMD_STATUS_OFS, 134eb428ee0SVitaly Andrianov SA_CMD_STATUS_REG_TRNG_ENABLE, 0); 135eb428ee0SVitaly Andrianov } 136eb428ee0SVitaly Andrianov 137eb428ee0SVitaly Andrianov static int ks_sa_rng_data_read(struct hwrng *rng, u32 *data) 138eb428ee0SVitaly Andrianov { 139eb428ee0SVitaly Andrianov struct device *dev = (struct device *)rng->priv; 140eb428ee0SVitaly Andrianov struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev); 141eb428ee0SVitaly Andrianov 142eb428ee0SVitaly Andrianov /* Read random data */ 143eb428ee0SVitaly Andrianov data[0] = readl(&ks_sa_rng->reg_rng->output_l); 144eb428ee0SVitaly Andrianov data[1] = readl(&ks_sa_rng->reg_rng->output_h); 145eb428ee0SVitaly Andrianov 146eb428ee0SVitaly Andrianov writel(TRNG_INTACK_REG_READY, &ks_sa_rng->reg_rng->intack); 147eb428ee0SVitaly Andrianov 148eb428ee0SVitaly Andrianov return sizeof(u32) * 2; 149eb428ee0SVitaly Andrianov } 150eb428ee0SVitaly Andrianov 151eb428ee0SVitaly Andrianov static int ks_sa_rng_data_present(struct hwrng *rng, int wait) 152eb428ee0SVitaly Andrianov { 153eb428ee0SVitaly Andrianov struct device *dev = (struct device *)rng->priv; 154eb428ee0SVitaly Andrianov struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev); 155eb428ee0SVitaly Andrianov 156eb428ee0SVitaly Andrianov u32 ready; 157eb428ee0SVitaly Andrianov int j; 158eb428ee0SVitaly Andrianov 159eb428ee0SVitaly Andrianov for (j = 0; j < SA_MAX_RNG_DATA_RETRIES; j++) { 160eb428ee0SVitaly Andrianov ready = readl(&ks_sa_rng->reg_rng->status); 161eb428ee0SVitaly Andrianov ready &= TRNG_STATUS_REG_READY; 162eb428ee0SVitaly Andrianov 163eb428ee0SVitaly Andrianov if (ready || !wait) 164eb428ee0SVitaly Andrianov break; 165eb428ee0SVitaly Andrianov 166eb428ee0SVitaly Andrianov udelay(SA_RNG_DATA_RETRY_DELAY); 167eb428ee0SVitaly Andrianov } 168eb428ee0SVitaly Andrianov 169eb428ee0SVitaly Andrianov return ready; 170eb428ee0SVitaly Andrianov } 171eb428ee0SVitaly Andrianov 172eb428ee0SVitaly Andrianov static int ks_sa_rng_probe(struct platform_device *pdev) 173eb428ee0SVitaly Andrianov { 174eb428ee0SVitaly Andrianov struct ks_sa_rng *ks_sa_rng; 175eb428ee0SVitaly Andrianov struct device *dev = &pdev->dev; 176eb428ee0SVitaly Andrianov int ret; 177eb428ee0SVitaly Andrianov struct resource *mem; 178eb428ee0SVitaly Andrianov 179eb428ee0SVitaly Andrianov ks_sa_rng = devm_kzalloc(dev, sizeof(*ks_sa_rng), GFP_KERNEL); 180eb428ee0SVitaly Andrianov if (!ks_sa_rng) 181eb428ee0SVitaly Andrianov return -ENOMEM; 182eb428ee0SVitaly Andrianov 183eb428ee0SVitaly Andrianov ks_sa_rng->dev = dev; 184eb428ee0SVitaly Andrianov ks_sa_rng->rng = (struct hwrng) { 185eb428ee0SVitaly Andrianov .name = "ks_sa_hwrng", 186eb428ee0SVitaly Andrianov .init = ks_sa_rng_init, 187eb428ee0SVitaly Andrianov .data_read = ks_sa_rng_data_read, 188eb428ee0SVitaly Andrianov .data_present = ks_sa_rng_data_present, 189eb428ee0SVitaly Andrianov .cleanup = ks_sa_rng_cleanup, 190eb428ee0SVitaly Andrianov }; 191eb428ee0SVitaly Andrianov ks_sa_rng->rng.priv = (unsigned long)dev; 192eb428ee0SVitaly Andrianov 193eb428ee0SVitaly Andrianov mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 194eb428ee0SVitaly Andrianov ks_sa_rng->reg_rng = devm_ioremap_resource(dev, mem); 195eb428ee0SVitaly Andrianov if (IS_ERR(ks_sa_rng->reg_rng)) 196eb428ee0SVitaly Andrianov return PTR_ERR(ks_sa_rng->reg_rng); 197eb428ee0SVitaly Andrianov 198eb428ee0SVitaly Andrianov ks_sa_rng->regmap_cfg = 199eb428ee0SVitaly Andrianov syscon_regmap_lookup_by_phandle(dev->of_node, 200eb428ee0SVitaly Andrianov "ti,syscon-sa-cfg"); 201eb428ee0SVitaly Andrianov 202eb428ee0SVitaly Andrianov if (IS_ERR(ks_sa_rng->regmap_cfg)) { 203eb428ee0SVitaly Andrianov dev_err(dev, "syscon_node_to_regmap failed\n"); 204eb428ee0SVitaly Andrianov return -EINVAL; 205eb428ee0SVitaly Andrianov } 206eb428ee0SVitaly Andrianov 207eb428ee0SVitaly Andrianov pm_runtime_enable(dev); 208eb428ee0SVitaly Andrianov ret = pm_runtime_get_sync(dev); 209eb428ee0SVitaly Andrianov if (ret < 0) { 210eb428ee0SVitaly Andrianov dev_err(dev, "Failed to enable SA power-domain\n"); 211eb428ee0SVitaly Andrianov pm_runtime_disable(dev); 212eb428ee0SVitaly Andrianov return ret; 213eb428ee0SVitaly Andrianov } 214eb428ee0SVitaly Andrianov 215eb428ee0SVitaly Andrianov platform_set_drvdata(pdev, ks_sa_rng); 216eb428ee0SVitaly Andrianov 217eb428ee0SVitaly Andrianov return devm_hwrng_register(&pdev->dev, &ks_sa_rng->rng); 218eb428ee0SVitaly Andrianov } 219eb428ee0SVitaly Andrianov 220eb428ee0SVitaly Andrianov static int ks_sa_rng_remove(struct platform_device *pdev) 221eb428ee0SVitaly Andrianov { 222eb428ee0SVitaly Andrianov pm_runtime_put_sync(&pdev->dev); 223eb428ee0SVitaly Andrianov pm_runtime_disable(&pdev->dev); 224eb428ee0SVitaly Andrianov 225eb428ee0SVitaly Andrianov return 0; 226eb428ee0SVitaly Andrianov } 227eb428ee0SVitaly Andrianov 228eb428ee0SVitaly Andrianov static const struct of_device_id ks_sa_rng_dt_match[] = { 229eb428ee0SVitaly Andrianov { 230eb428ee0SVitaly Andrianov .compatible = "ti,keystone-rng", 231eb428ee0SVitaly Andrianov }, 232eb428ee0SVitaly Andrianov { }, 233eb428ee0SVitaly Andrianov }; 234eb428ee0SVitaly Andrianov MODULE_DEVICE_TABLE(of, ks_sa_rng_dt_match); 235eb428ee0SVitaly Andrianov 236eb428ee0SVitaly Andrianov static struct platform_driver ks_sa_rng_driver = { 237eb428ee0SVitaly Andrianov .driver = { 238eb428ee0SVitaly Andrianov .name = "ks-sa-rng", 239eb428ee0SVitaly Andrianov .of_match_table = ks_sa_rng_dt_match, 240eb428ee0SVitaly Andrianov }, 241eb428ee0SVitaly Andrianov .probe = ks_sa_rng_probe, 242eb428ee0SVitaly Andrianov .remove = ks_sa_rng_remove, 243eb428ee0SVitaly Andrianov }; 244eb428ee0SVitaly Andrianov 245eb428ee0SVitaly Andrianov module_platform_driver(ks_sa_rng_driver); 246eb428ee0SVitaly Andrianov 247eb428ee0SVitaly Andrianov MODULE_DESCRIPTION("Keystone NETCP SA H/W Random Number Generator driver"); 248eb428ee0SVitaly Andrianov MODULE_AUTHOR("Vitaly Andrianov <vitalya@ti.com>"); 249eb428ee0SVitaly Andrianov MODULE_LICENSE("GPL"); 250