uboot/drivers/rng/msm_rng.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * PRNG driver for Qualcomm IPQ40xx
   4 *
   5 * Copyright (c) 2020 Sartura Ltd.
   6 *
   7 * Author: Robert Marko <robert.marko@sartura.hr>
   8 *
   9 * Based on Linux driver
  10 */
  11
  12#include <asm/io.h>
  13#include <clk.h>
  14#include <common.h>
  15#include <dm.h>
  16#include <linux/bitops.h>
  17#include <rng.h>
  18
  19/* Device specific register offsets */
  20#define PRNG_DATA_OUT           0x0000
  21#define PRNG_STATUS                     0x0004
  22#define PRNG_LFSR_CFG           0x0100
  23#define PRNG_CONFIG                     0x0104
  24
  25/* Device specific register masks and config values */
  26#define PRNG_LFSR_CFG_MASK              0x0000ffff
  27#define PRNG_LFSR_CFG_CLOCKS    0x0000dddd
  28#define PRNG_CONFIG_HW_ENABLE   BIT(1)
  29#define PRNG_STATUS_DATA_AVAIL  BIT(0)
  30
  31#define MAX_HW_FIFO_DEPTH               16
  32#define MAX_HW_FIFO_SIZE                (MAX_HW_FIFO_DEPTH * 4)
  33#define WORD_SZ                                 4
  34
  35struct msm_rng_priv {
  36        phys_addr_t base;
  37        struct clk clk;
  38};
  39
  40static int msm_rng_read(struct udevice *dev, void *data, size_t len)
  41{
  42        struct msm_rng_priv *priv = dev_get_priv(dev);
  43        size_t currsize = 0;
  44        u32 *retdata = data;
  45        size_t maxsize;
  46        u32 val;
  47
  48        /* calculate max size bytes to transfer back to caller */
  49        maxsize = min_t(size_t, MAX_HW_FIFO_SIZE, len);
  50
  51        /* read random data from hardware */
  52        do {
  53                val = readl_relaxed(priv->base + PRNG_STATUS);
  54                if (!(val & PRNG_STATUS_DATA_AVAIL))
  55                        break;
  56
  57                val = readl_relaxed(priv->base + PRNG_DATA_OUT);
  58                if (!val)
  59                        break;
  60
  61                *retdata++ = val;
  62                currsize += WORD_SZ;
  63
  64                /* make sure we stay on 32bit boundary */
  65                if ((maxsize - currsize) < WORD_SZ)
  66                        break;
  67        } while (currsize < maxsize);
  68
  69        return 0;
  70}
  71
  72static int msm_rng_enable(struct msm_rng_priv *priv, int enable)
  73{
  74        u32 val;
  75
  76        if (enable) {
  77                /* Enable PRNG only if it is not already enabled */
  78                val = readl_relaxed(priv->base + PRNG_CONFIG);
  79                if (val & PRNG_CONFIG_HW_ENABLE) {
  80                        val = readl_relaxed(priv->base + PRNG_LFSR_CFG);
  81                        val &= ~PRNG_LFSR_CFG_MASK;
  82                        val |= PRNG_LFSR_CFG_CLOCKS;
  83                        writel(val, priv->base + PRNG_LFSR_CFG);
  84
  85                        val = readl_relaxed(priv->base + PRNG_CONFIG);
  86                        val |= PRNG_CONFIG_HW_ENABLE;
  87                        writel(val, priv->base + PRNG_CONFIG);
  88                }
  89        } else {
  90                val = readl_relaxed(priv->base + PRNG_CONFIG);
  91                val &= ~PRNG_CONFIG_HW_ENABLE;
  92                writel(val, priv->base + PRNG_CONFIG);
  93        }
  94
  95        return 0;
  96}
  97
  98static int msm_rng_probe(struct udevice *dev)
  99{
 100        struct msm_rng_priv *priv = dev_get_priv(dev);
 101
 102        int ret;
 103
 104        priv->base = dev_read_addr(dev);
 105        if (priv->base == FDT_ADDR_T_NONE)
 106                return -EINVAL;
 107
 108        ret = clk_get_by_index(dev, 0, &priv->clk);
 109        if (ret)
 110                return ret;
 111
 112        ret = clk_enable(&priv->clk);
 113        if (ret < 0)
 114                return ret;
 115
 116        return msm_rng_enable(priv, 1);
 117}
 118
 119static int msm_rng_remove(struct udevice *dev)
 120{
 121        struct msm_rng_priv *priv = dev_get_priv(dev);
 122
 123        return msm_rng_enable(priv, 0);
 124}
 125
 126static const struct dm_rng_ops msm_rng_ops = {
 127        .read = msm_rng_read,
 128};
 129
 130static const struct udevice_id msm_rng_match[] = {
 131        { .compatible = "qcom,prng", },
 132        {},
 133};
 134
 135U_BOOT_DRIVER(msm_rng) = {
 136        .name = "msm-rng",
 137        .id = UCLASS_RNG,
 138        .of_match = msm_rng_match,
 139        .ops = &msm_rng_ops,
 140        .probe = msm_rng_probe,
 141        .remove = msm_rng_remove,
 142        .priv_auto      = sizeof(struct msm_rng_priv),
 143};
 144