uboot/drivers/rng/rockchip_rng.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (c) 2020 Fuzhou Rockchip Electronics Co., Ltd
   4 */
   5#include <asm/arch-rockchip/hardware.h>
   6#include <asm/io.h>
   7#include <common.h>
   8#include <dm.h>
   9#include <linux/bitops.h>
  10#include <linux/iopoll.h>
  11#include <linux/string.h>
  12#include <rng.h>
  13
  14#define RK_HW_RNG_MAX 32
  15
  16#define _SBF(s, v)      ((v) << (s))
  17
  18/* start of CRYPTO V1 register define */
  19#define CRYPTO_V1_CTRL                          0x0008
  20#define CRYPTO_V1_RNG_START                     BIT(8)
  21#define CRYPTO_V1_RNG_FLUSH                     BIT(9)
  22
  23#define CRYPTO_V1_TRNG_CTRL                     0x0200
  24#define CRYPTO_V1_OSC_ENABLE                    BIT(16)
  25#define CRYPTO_V1_TRNG_SAMPLE_PERIOD(x)         (x)
  26
  27#define CRYPTO_V1_TRNG_DOUT_0                   0x0204
  28/* end of CRYPTO V1 register define */
  29
  30/* start of CRYPTO V2 register define */
  31#define CRYPTO_V2_RNG_CTL                       0x0400
  32#define CRYPTO_V2_RNG_64_BIT_LEN                _SBF(4, 0x00)
  33#define CRYPTO_V2_RNG_128_BIT_LEN               _SBF(4, 0x01)
  34#define CRYPTO_V2_RNG_192_BIT_LEN               _SBF(4, 0x02)
  35#define CRYPTO_V2_RNG_256_BIT_LEN               _SBF(4, 0x03)
  36#define CRYPTO_V2_RNG_FATESY_SOC_RING           _SBF(2, 0x00)
  37#define CRYPTO_V2_RNG_SLOWER_SOC_RING_0         _SBF(2, 0x01)
  38#define CRYPTO_V2_RNG_SLOWER_SOC_RING_1         _SBF(2, 0x02)
  39#define CRYPTO_V2_RNG_SLOWEST_SOC_RING          _SBF(2, 0x03)
  40#define CRYPTO_V2_RNG_ENABLE                    BIT(1)
  41#define CRYPTO_V2_RNG_START                     BIT(0)
  42#define CRYPTO_V2_RNG_SAMPLE_CNT                0x0404
  43#define CRYPTO_V2_RNG_DOUT_0                    0x0410
  44/* end of CRYPTO V2 register define */
  45
  46#define RK_RNG_TIME_OUT 50000  /* max 50ms */
  47
  48struct rk_rng_soc_data {
  49        int (*rk_rng_read)(struct udevice *dev, void *data, size_t len);
  50};
  51
  52struct rk_rng_plat {
  53        fdt_addr_t base;
  54        struct rk_rng_soc_data *soc_data;
  55};
  56
  57static int rk_rng_read_regs(fdt_addr_t addr, void *buf, size_t size)
  58{
  59        u32 count = RK_HW_RNG_MAX / sizeof(u32);
  60        u32 reg, tmp_len;
  61
  62        if (size > RK_HW_RNG_MAX)
  63                return -EINVAL;
  64
  65        while (size && count) {
  66                reg = readl(addr);
  67                tmp_len = min(size, sizeof(u32));
  68                memcpy(buf, &reg, tmp_len);
  69                addr += sizeof(u32);
  70                buf += tmp_len;
  71                size -= tmp_len;
  72                count--;
  73        }
  74
  75        return 0;
  76}
  77
  78static int rk_v1_rng_read(struct udevice *dev, void *data, size_t len)
  79{
  80        struct rk_rng_plat *pdata = dev_get_priv(dev);
  81        u32 reg = 0;
  82        int retval;
  83
  84        if (len > RK_HW_RNG_MAX)
  85                return -EINVAL;
  86
  87        /* enable osc_ring to get entropy, sample period is set as 100 */
  88        writel(CRYPTO_V1_OSC_ENABLE | CRYPTO_V1_TRNG_SAMPLE_PERIOD(100),
  89               pdata->base + CRYPTO_V1_TRNG_CTRL);
  90
  91        rk_clrsetreg(pdata->base + CRYPTO_V1_CTRL, CRYPTO_V1_RNG_START,
  92                     CRYPTO_V1_RNG_START);
  93
  94        retval = readl_poll_timeout(pdata->base + CRYPTO_V1_CTRL, reg,
  95                                    !(reg & CRYPTO_V1_RNG_START),
  96                                    RK_RNG_TIME_OUT);
  97        if (retval)
  98                goto exit;
  99
 100        rk_rng_read_regs(pdata->base + CRYPTO_V1_TRNG_DOUT_0, data, len);
 101
 102exit:
 103        /* close TRNG */
 104        rk_clrreg(pdata->base + CRYPTO_V1_CTRL, CRYPTO_V1_RNG_START);
 105
 106        return 0;
 107}
 108
 109static int rk_v2_rng_read(struct udevice *dev, void *data, size_t len)
 110{
 111        struct rk_rng_plat *pdata = dev_get_priv(dev);
 112        u32 reg = 0;
 113        int retval;
 114
 115        if (len > RK_HW_RNG_MAX)
 116                return -EINVAL;
 117
 118        /* enable osc_ring to get entropy, sample period is set as 100 */
 119        writel(100, pdata->base + CRYPTO_V2_RNG_SAMPLE_CNT);
 120
 121        reg |= CRYPTO_V2_RNG_256_BIT_LEN;
 122        reg |= CRYPTO_V2_RNG_SLOWER_SOC_RING_0;
 123        reg |= CRYPTO_V2_RNG_ENABLE;
 124        reg |= CRYPTO_V2_RNG_START;
 125
 126        rk_clrsetreg(pdata->base + CRYPTO_V2_RNG_CTL, 0xffff, reg);
 127
 128        retval = readl_poll_timeout(pdata->base + CRYPTO_V2_RNG_CTL, reg,
 129                                    !(reg & CRYPTO_V2_RNG_START),
 130                                    RK_RNG_TIME_OUT);
 131        if (retval)
 132                goto exit;
 133
 134        rk_rng_read_regs(pdata->base + CRYPTO_V2_RNG_DOUT_0, data, len);
 135
 136exit:
 137        /* close TRNG */
 138        rk_clrreg(pdata->base + CRYPTO_V2_RNG_CTL, 0xffff);
 139
 140        return retval;
 141}
 142
 143static int rockchip_rng_read(struct udevice *dev, void *data, size_t len)
 144{
 145        unsigned char *buf = data;
 146        unsigned int i;
 147        int ret = -EIO;
 148
 149        struct rk_rng_plat *pdata = dev_get_priv(dev);
 150
 151        if (!len)
 152                return 0;
 153
 154        if (!pdata->soc_data || !pdata->soc_data->rk_rng_read)
 155                return -EINVAL;
 156
 157        for (i = 0; i < len / RK_HW_RNG_MAX; i++, buf += RK_HW_RNG_MAX) {
 158                ret = pdata->soc_data->rk_rng_read(dev, buf, RK_HW_RNG_MAX);
 159                if (ret)
 160                        goto exit;
 161        }
 162
 163        if (len % RK_HW_RNG_MAX)
 164                ret = pdata->soc_data->rk_rng_read(dev, buf,
 165                                                   len % RK_HW_RNG_MAX);
 166
 167exit:
 168        return ret;
 169}
 170
 171static int rockchip_rng_of_to_plat(struct udevice *dev)
 172{
 173        struct rk_rng_plat *pdata = dev_get_priv(dev);
 174
 175        memset(pdata, 0x00, sizeof(*pdata));
 176
 177        pdata->base = (fdt_addr_t)dev_read_addr_ptr(dev);
 178        if (!pdata->base)
 179                return -ENOMEM;
 180
 181        return 0;
 182}
 183
 184static int rockchip_rng_probe(struct udevice *dev)
 185{
 186        struct rk_rng_plat *pdata = dev_get_priv(dev);
 187
 188        pdata->soc_data = (struct rk_rng_soc_data *)dev_get_driver_data(dev);
 189
 190        return 0;
 191}
 192
 193static const struct rk_rng_soc_data rk_rng_v1_soc_data = {
 194        .rk_rng_read = rk_v1_rng_read,
 195};
 196
 197static const struct rk_rng_soc_data rk_rng_v2_soc_data = {
 198        .rk_rng_read = rk_v2_rng_read,
 199};
 200
 201static const struct dm_rng_ops rockchip_rng_ops = {
 202        .read = rockchip_rng_read,
 203};
 204
 205static const struct udevice_id rockchip_rng_match[] = {
 206        {
 207                .compatible = "rockchip,cryptov1-rng",
 208                .data = (ulong)&rk_rng_v1_soc_data,
 209        },
 210        {
 211                .compatible = "rockchip,cryptov2-rng",
 212                .data = (ulong)&rk_rng_v2_soc_data,
 213        },
 214        {},
 215};
 216
 217U_BOOT_DRIVER(rockchip_rng) = {
 218        .name = "rockchip-rng",
 219        .id = UCLASS_RNG,
 220        .of_match = rockchip_rng_match,
 221        .ops = &rockchip_rng_ops,
 222        .probe = rockchip_rng_probe,
 223        .of_to_plat = rockchip_rng_of_to_plat,
 224        .priv_auto      = sizeof(struct rk_rng_plat),
 225};
 226