uboot/drivers/rng/iproc_rng200.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright 2020, Matthias Brugger <mbrugger@suse.com>
   4 *
   5 * Driver for Raspberry Pi hardware random number generator
   6 */
   7
   8#include <common.h>
   9#include <dm.h>
  10#include <linux/delay.h>
  11#include <rng.h>
  12#include <asm/io.h>
  13
  14#define usleep_range(a, b) udelay((b))
  15
  16#define RNG_CTRL_OFFSET                                 0x00
  17#define RNG_CTRL_RNG_RBGEN_MASK                         0x00001FFF
  18#define RNG_CTRL_RNG_RBGEN_ENABLE                       0x00000001
  19#define RNG_CTRL_RNG_RBGEN_DISABLE                      0x00000000
  20
  21#define RNG_SOFT_RESET_OFFSET                           0x04
  22#define RNG_SOFT_RESET                                  0x00000001
  23
  24#define RBG_SOFT_RESET_OFFSET                           0x08
  25#define RBG_SOFT_RESET                                  0x00000001
  26
  27#define RNG_INT_STATUS_OFFSET                           0x18
  28#define RNG_INT_STATUS_MASTER_FAIL_LOCKOUT_IRQ_MASK     0x80000000
  29#define RNG_INT_STATUS_NIST_FAIL_IRQ_MASK               0x00000020
  30
  31#define RNG_FIFO_DATA_OFFSET                            0x20
  32
  33#define RNG_FIFO_COUNT_OFFSET                           0x24
  34#define RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK              0x000000FF
  35
  36struct iproc_rng200_plat {
  37        void __iomem *base;
  38};
  39
  40static void iproc_rng200_enable(struct iproc_rng200_plat *pdata, bool enable)
  41{
  42        void __iomem *rng_base = pdata->base;
  43        u32 val;
  44
  45        val = readl(rng_base + RNG_CTRL_OFFSET);
  46        val &= ~RNG_CTRL_RNG_RBGEN_MASK;
  47        if (enable)
  48                val |= RNG_CTRL_RNG_RBGEN_ENABLE;
  49        else
  50                val &= ~RNG_CTRL_RNG_RBGEN_ENABLE;
  51
  52        writel(val, rng_base + RNG_CTRL_OFFSET);
  53}
  54
  55static void iproc_rng200_restart(struct iproc_rng200_plat *pdata)
  56{
  57        void __iomem *rng_base = pdata->base;
  58        u32 val;
  59
  60        iproc_rng200_enable(pdata, false);
  61
  62        /* Clear all interrupt status */
  63        writel(0xFFFFFFFFUL, rng_base + RNG_INT_STATUS_OFFSET);
  64
  65        /* Reset RNG and RBG */
  66        val = readl(rng_base + RBG_SOFT_RESET_OFFSET);
  67        val |= RBG_SOFT_RESET;
  68        writel(val, rng_base + RBG_SOFT_RESET_OFFSET);
  69
  70        val = readl(rng_base + RNG_SOFT_RESET_OFFSET);
  71        val |= RNG_SOFT_RESET;
  72        writel(val, rng_base + RNG_SOFT_RESET_OFFSET);
  73
  74        val = readl(rng_base + RNG_SOFT_RESET_OFFSET);
  75        val &= ~RNG_SOFT_RESET;
  76        writel(val, rng_base + RNG_SOFT_RESET_OFFSET);
  77
  78        val = readl(rng_base + RBG_SOFT_RESET_OFFSET);
  79        val &= ~RBG_SOFT_RESET;
  80        writel(val, rng_base + RBG_SOFT_RESET_OFFSET);
  81
  82        iproc_rng200_enable(pdata, true);
  83}
  84
  85static int iproc_rng200_read(struct udevice *dev, void *data, size_t len)
  86{
  87        struct iproc_rng200_plat *priv = dev_get_plat(dev);
  88        char *buf = (char *)data;
  89        u32 num_remaining = len;
  90        u32 status;
  91
  92        #define MAX_RESETS_PER_READ     1
  93        u32 num_resets = 0;
  94
  95        while (num_remaining > 0) {
  96
  97                /* Is RNG sane? If not, reset it. */
  98                status = readl(priv->base + RNG_INT_STATUS_OFFSET);
  99                if ((status & (RNG_INT_STATUS_MASTER_FAIL_LOCKOUT_IRQ_MASK |
 100                        RNG_INT_STATUS_NIST_FAIL_IRQ_MASK)) != 0) {
 101
 102                        if (num_resets >= MAX_RESETS_PER_READ)
 103                                return len - num_remaining;
 104
 105                        iproc_rng200_restart(priv);
 106                        num_resets++;
 107                }
 108
 109                /* Are there any random numbers available? */
 110                if ((readl(priv->base + RNG_FIFO_COUNT_OFFSET) &
 111                                RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK) > 0) {
 112
 113                        if (num_remaining >= sizeof(u32)) {
 114                                /* Buffer has room to store entire word */
 115                                *(u32 *)buf = readl(priv->base +
 116                                                        RNG_FIFO_DATA_OFFSET);
 117                                buf += sizeof(u32);
 118                                num_remaining -= sizeof(u32);
 119                        } else {
 120                                /* Buffer can only store partial word */
 121                                u32 rnd_number = readl(priv->base +
 122                                                        RNG_FIFO_DATA_OFFSET);
 123                                memcpy(buf, &rnd_number, num_remaining);
 124                                buf += num_remaining;
 125                                num_remaining = 0;
 126                        }
 127
 128                } else {
 129                        /* Can wait, give others chance to run */
 130                        usleep_range(min(num_remaining * 10, 500U), 500);
 131                }
 132        }
 133
 134        return 0;
 135}
 136
 137static int iproc_rng200_probe(struct udevice *dev)
 138{
 139        struct iproc_rng200_plat *priv = dev_get_plat(dev);
 140
 141        iproc_rng200_enable(priv, true);
 142
 143        return 0;
 144}
 145
 146static int iproc_rng200_remove(struct udevice *dev)
 147{
 148        struct iproc_rng200_plat *priv = dev_get_plat(dev);
 149
 150        iproc_rng200_enable(priv, false);
 151
 152        return 0;
 153}
 154
 155static int iproc_rng200_of_to_plat(struct udevice *dev)
 156{
 157        struct iproc_rng200_plat *pdata = dev_get_plat(dev);
 158
 159        pdata->base = devfdt_map_physmem(dev, sizeof(void *));
 160        if (!pdata->base)
 161                return -ENODEV;
 162
 163        return 0;
 164}
 165
 166static const struct dm_rng_ops iproc_rng200_ops = {
 167        .read = iproc_rng200_read,
 168};
 169
 170static const struct udevice_id iproc_rng200_rng_match[] = {
 171        { .compatible = "brcm,bcm2711-rng200", },
 172        { .compatible = "brcm,iproc-rng200", },
 173        {},
 174};
 175
 176U_BOOT_DRIVER(iproc_rng200_rng) = {
 177        .name = "iproc_rng200-rng",
 178        .id = UCLASS_RNG,
 179        .of_match = iproc_rng200_rng_match,
 180        .ops = &iproc_rng200_ops,
 181        .probe = iproc_rng200_probe,
 182        .remove = iproc_rng200_remove,
 183        .priv_auto = sizeof(struct iproc_rng200_plat),
 184        .of_to_plat = iproc_rng200_of_to_plat,
 185};
 186