diff options
Diffstat (limited to 'drivers/rng')
-rw-r--r-- | drivers/rng/meson-rng.c | 72 |
1 files changed, 71 insertions, 1 deletions
diff --git a/drivers/rng/meson-rng.c b/drivers/rng/meson-rng.c index e0a1e8c7e04..fd2988e91b5 100644 --- a/drivers/rng/meson-rng.c +++ b/drivers/rng/meson-rng.c @@ -10,10 +10,23 @@ #include <dm.h> #include <rng.h> #include <asm/io.h> +#include <linux/iopoll.h> + +#define RNG_DATA 0x00 +#define RNG_S4_DATA 0x08 +#define RNG_S4_CFG 0x00 + +#define RUN_BIT BIT(0) +#define SEED_READY_STS_BIT BIT(31) + +struct meson_rng_priv { + u32 (*read)(fdt_addr_t base); +}; struct meson_rng_plat { fdt_addr_t base; struct clk clk; + struct meson_rng_priv *priv; }; /** @@ -27,10 +40,11 @@ struct meson_rng_plat { static int meson_rng_read(struct udevice *dev, void *data, size_t len) { struct meson_rng_plat *pdata = dev_get_plat(dev); + struct meson_rng_priv *priv = pdata->priv; char *buffer = (char *)data; while (len) { - u32 rand = readl(pdata->base); + u32 rand = priv->read(pdata->base); size_t step; if (len >= 4) @@ -44,6 +58,47 @@ static int meson_rng_read(struct udevice *dev, void *data, size_t len) return 0; } +static int meson_rng_wait_status(void __iomem *cfg_addr, int bit) +{ + u32 status = 0; + int ret; + + ret = readl_relaxed_poll_timeout(cfg_addr, + status, !(status & bit), + 10000); + if (ret) + return -EBUSY; + + return 0; +} + +static u32 meson_common_rng_read(fdt_addr_t base) +{ + return readl(base); +} + +static u32 meson_s4_rng_read(fdt_addr_t base) +{ + void __iomem *cfg_addr = (void *)base + RNG_S4_CFG; + int err; + + writel_relaxed(readl_relaxed(cfg_addr) | SEED_READY_STS_BIT, cfg_addr); + + err = meson_rng_wait_status(cfg_addr, SEED_READY_STS_BIT); + if (err) { + pr_err("Seed isn't ready, try again\n"); + return err; + } + + err = meson_rng_wait_status(cfg_addr, RUN_BIT); + if (err) { + pr_err("Can't get random number, try again\n"); + return err; + } + + return readl_relaxed(base + RNG_S4_DATA); +} + /** * meson_rng_probe() - probe rng device * @@ -59,6 +114,8 @@ static int meson_rng_probe(struct udevice *dev) if (err) return err; + pdata->priv = (struct meson_rng_priv *)dev_get_driver_data(dev); + return 0; } @@ -102,9 +159,22 @@ static const struct dm_rng_ops meson_rng_ops = { .read = meson_rng_read, }; +static const struct meson_rng_priv meson_rng_priv = { + .read = meson_common_rng_read, +}; + +static const struct meson_rng_priv meson_rng_priv_s4 = { + .read = meson_s4_rng_read, +}; + static const struct udevice_id meson_rng_match[] = { { .compatible = "amlogic,meson-rng", + .data = (ulong)&meson_rng_priv, + }, + { + .compatible = "amlogic,meson-s4-rng", + .data = (ulong)&meson_rng_priv_s4, }, {}, }; |