Skip to content

Commit

Permalink
rng: Add Qualcomm MSM PRNG driver
Browse files Browse the repository at this point in the history
Add support for the hardware pseudo random number generator found in Qualcomm SoC-s.

Signed-off-by: Robert Marko <[email protected]>
Cc: Luka Perkov <[email protected]>
  • Loading branch information
robimarko authored and trini committed Oct 22, 2020
1 parent 5297341 commit 033ec63
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 0 deletions.
1 change: 1 addition & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ F: drivers/reset/reset-ipq4019.c
F: drivers/phy/phy-qcom-ipq4019-usb.c
F: drivers/spi/spi-qup.c
F: drivers/net/mdio-ipq4019.c
F: drivers/rng/msm_rng.c

ARM MARVELL KIRKWOOD ARMADA-XP ARMADA-38X ARMADA-37XX ARMADA-7K/8K
M: Stefan Roese <[email protected]>
Expand Down
7 changes: 7 additions & 0 deletions drivers/rng/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ config RNG_SANDBOX
Enable random number generator for sandbox. This is an
emulation of a rng device.

config RNG_MSM
bool "Qualcomm SoCs Random Number Generator support"
depends on DM_RNG
help
This driver provides support for the Random Number
Generator hardware found on Qualcomm SoCs.

config RNG_STM32MP1
bool "Enable random number generator for STM32MP1"
depends on ARCH_STM32MP
Expand Down
1 change: 1 addition & 0 deletions drivers/rng/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
obj-$(CONFIG_DM_RNG) += rng-uclass.o
obj-$(CONFIG_RNG_MESON) += meson-rng.o
obj-$(CONFIG_RNG_SANDBOX) += sandbox_rng.o
obj-$(CONFIG_RNG_MSM) += msm_rng.o
obj-$(CONFIG_RNG_STM32MP1) += stm32mp1_rng.o
obj-$(CONFIG_RNG_ROCKCHIP) += rockchip_rng.o
143 changes: 143 additions & 0 deletions drivers/rng/msm_rng.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* PRNG driver for Qualcomm IPQ40xx
*
* Copyright (c) 2020 Sartura Ltd.
*
* Author: Robert Marko <[email protected]>
*
* Based on Linux driver
*/

#include <asm/io.h>
#include <clk.h>
#include <common.h>
#include <dm.h>
#include <linux/bitops.h>
#include <rng.h>

/* Device specific register offsets */
#define PRNG_DATA_OUT 0x0000
#define PRNG_STATUS 0x0004
#define PRNG_LFSR_CFG 0x0100
#define PRNG_CONFIG 0x0104

/* Device specific register masks and config values */
#define PRNG_LFSR_CFG_MASK 0x0000ffff
#define PRNG_LFSR_CFG_CLOCKS 0x0000dddd
#define PRNG_CONFIG_HW_ENABLE BIT(1)
#define PRNG_STATUS_DATA_AVAIL BIT(0)

#define MAX_HW_FIFO_DEPTH 16
#define MAX_HW_FIFO_SIZE (MAX_HW_FIFO_DEPTH * 4)
#define WORD_SZ 4

struct msm_rng_priv {
phys_addr_t base;
struct clk clk;
};

static int msm_rng_read(struct udevice *dev, void *data, size_t len)
{
struct msm_rng_priv *priv = dev_get_priv(dev);
size_t currsize = 0;
u32 *retdata = data;
size_t maxsize;
u32 val;

/* calculate max size bytes to transfer back to caller */
maxsize = min_t(size_t, MAX_HW_FIFO_SIZE, len);

/* read random data from hardware */
do {
val = readl_relaxed(priv->base + PRNG_STATUS);
if (!(val & PRNG_STATUS_DATA_AVAIL))
break;

val = readl_relaxed(priv->base + PRNG_DATA_OUT);
if (!val)
break;

*retdata++ = val;
currsize += WORD_SZ;

/* make sure we stay on 32bit boundary */
if ((maxsize - currsize) < WORD_SZ)
break;
} while (currsize < maxsize);

return 0;
}

static int msm_rng_enable(struct msm_rng_priv *priv, int enable)
{
u32 val;

if (enable) {
/* Enable PRNG only if it is not already enabled */
val = readl_relaxed(priv->base + PRNG_CONFIG);
if (val & PRNG_CONFIG_HW_ENABLE) {
val = readl_relaxed(priv->base + PRNG_LFSR_CFG);
val &= ~PRNG_LFSR_CFG_MASK;
val |= PRNG_LFSR_CFG_CLOCKS;
writel(val, priv->base + PRNG_LFSR_CFG);

val = readl_relaxed(priv->base + PRNG_CONFIG);
val |= PRNG_CONFIG_HW_ENABLE;
writel(val, priv->base + PRNG_CONFIG);
}
} else {
val = readl_relaxed(priv->base + PRNG_CONFIG);
val &= ~PRNG_CONFIG_HW_ENABLE;
writel(val, priv->base + PRNG_CONFIG);
}

return 0;
}

static int msm_rng_probe(struct udevice *dev)
{
struct msm_rng_priv *priv = dev_get_priv(dev);

int ret;

priv->base = dev_read_addr(dev);
if (priv->base == FDT_ADDR_T_NONE)
return -EINVAL;

ret = clk_get_by_index(dev, 0, &priv->clk);
if (ret)
return ret;

ret = clk_enable(&priv->clk);
if (ret < 0)
return ret;

return msm_rng_enable(priv, 1);
}

static int msm_rng_remove(struct udevice *dev)
{
struct msm_rng_priv *priv = dev_get_priv(dev);

return msm_rng_enable(priv, 0);
}

static const struct dm_rng_ops msm_rng_ops = {
.read = msm_rng_read,
};

static const struct udevice_id msm_rng_match[] = {
{ .compatible = "qcom,prng", },
{},
};

U_BOOT_DRIVER(msm_rng) = {
.name = "msm-rng",
.id = UCLASS_RNG,
.of_match = msm_rng_match,
.ops = &msm_rng_ops,
.probe = msm_rng_probe,
.remove = msm_rng_remove,
.priv_auto_alloc_size = sizeof(struct msm_rng_priv),
};

0 comments on commit 033ec63

Please sign in to comment.