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