Path: blob/master/drivers/char/hw_random/iproc-rng200.c
29269 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* Copyright (C) 2015 Broadcom Corporation3*4*/5/*6* DESCRIPTION: The Broadcom iProc RNG200 Driver7*/89#include <linux/hw_random.h>10#include <linux/init.h>11#include <linux/io.h>12#include <linux/kernel.h>13#include <linux/module.h>14#include <linux/mod_devicetable.h>15#include <linux/platform_device.h>16#include <linux/delay.h>1718/* Registers */19#define RNG_CTRL_OFFSET 0x0020#define RNG_CTRL_RNG_RBGEN_MASK 0x00001FFF21#define RNG_CTRL_RNG_RBGEN_ENABLE 0x000000012223#define RNG_SOFT_RESET_OFFSET 0x0424#define RNG_SOFT_RESET 0x000000012526#define RBG_SOFT_RESET_OFFSET 0x0827#define RBG_SOFT_RESET 0x000000012829#define RNG_INT_STATUS_OFFSET 0x1830#define RNG_INT_STATUS_MASTER_FAIL_LOCKOUT_IRQ_MASK 0x8000000031#define RNG_INT_STATUS_STARTUP_TRANSITIONS_MET_IRQ_MASK 0x0002000032#define RNG_INT_STATUS_NIST_FAIL_IRQ_MASK 0x0000002033#define RNG_INT_STATUS_TOTAL_BITS_COUNT_IRQ_MASK 0x000000013435#define RNG_FIFO_DATA_OFFSET 0x203637#define RNG_FIFO_COUNT_OFFSET 0x2438#define RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK 0x000000FF3940struct iproc_rng200_dev {41struct hwrng rng;42void __iomem *base;43};4445#define to_rng_priv(rng) container_of(rng, struct iproc_rng200_dev, rng)4647static void iproc_rng200_enable_set(void __iomem *rng_base, bool enable)48{49u32 val;5051val = ioread32(rng_base + RNG_CTRL_OFFSET);52val &= ~RNG_CTRL_RNG_RBGEN_MASK;5354if (enable)55val |= RNG_CTRL_RNG_RBGEN_ENABLE;5657iowrite32(val, rng_base + RNG_CTRL_OFFSET);58}5960static void iproc_rng200_restart(void __iomem *rng_base)61{62uint32_t val;6364iproc_rng200_enable_set(rng_base, false);6566/* Clear all interrupt status */67iowrite32(0xFFFFFFFFUL, rng_base + RNG_INT_STATUS_OFFSET);6869/* Reset RNG and RBG */70val = ioread32(rng_base + RBG_SOFT_RESET_OFFSET);71val |= RBG_SOFT_RESET;72iowrite32(val, rng_base + RBG_SOFT_RESET_OFFSET);7374val = ioread32(rng_base + RNG_SOFT_RESET_OFFSET);75val |= RNG_SOFT_RESET;76iowrite32(val, rng_base + RNG_SOFT_RESET_OFFSET);7778val = ioread32(rng_base + RNG_SOFT_RESET_OFFSET);79val &= ~RNG_SOFT_RESET;80iowrite32(val, rng_base + RNG_SOFT_RESET_OFFSET);8182val = ioread32(rng_base + RBG_SOFT_RESET_OFFSET);83val &= ~RBG_SOFT_RESET;84iowrite32(val, rng_base + RBG_SOFT_RESET_OFFSET);8586iproc_rng200_enable_set(rng_base, true);87}8889static int iproc_rng200_read(struct hwrng *rng, void *buf, size_t max,90bool wait)91{92struct iproc_rng200_dev *priv = to_rng_priv(rng);93uint32_t num_remaining = max;94uint32_t status;9596#define MAX_RESETS_PER_READ 197uint32_t num_resets = 0;9899#define MAX_IDLE_TIME (1 * HZ)100unsigned long idle_endtime = jiffies + MAX_IDLE_TIME;101102while ((num_remaining > 0) && time_before(jiffies, idle_endtime)) {103104/* Is RNG sane? If not, reset it. */105status = ioread32(priv->base + RNG_INT_STATUS_OFFSET);106if ((status & (RNG_INT_STATUS_MASTER_FAIL_LOCKOUT_IRQ_MASK |107RNG_INT_STATUS_NIST_FAIL_IRQ_MASK)) != 0) {108109if (num_resets >= MAX_RESETS_PER_READ)110return max - num_remaining;111112iproc_rng200_restart(priv->base);113num_resets++;114}115116/* Are there any random numbers available? */117if ((ioread32(priv->base + RNG_FIFO_COUNT_OFFSET) &118RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK) > 0) {119120if (num_remaining >= sizeof(uint32_t)) {121/* Buffer has room to store entire word */122*(uint32_t *)buf = ioread32(priv->base +123RNG_FIFO_DATA_OFFSET);124buf += sizeof(uint32_t);125num_remaining -= sizeof(uint32_t);126} else {127/* Buffer can only store partial word */128uint32_t rnd_number = ioread32(priv->base +129RNG_FIFO_DATA_OFFSET);130memcpy(buf, &rnd_number, num_remaining);131buf += num_remaining;132num_remaining = 0;133}134135/* Reset the IDLE timeout */136idle_endtime = jiffies + MAX_IDLE_TIME;137} else {138if (!wait)139/* Cannot wait, return immediately */140return max - num_remaining;141142/* Can wait, give others chance to run */143usleep_range(min(num_remaining * 10, 500U), 500);144}145}146147return max - num_remaining;148}149150static int iproc_rng200_init(struct hwrng *rng)151{152struct iproc_rng200_dev *priv = to_rng_priv(rng);153154iproc_rng200_enable_set(priv->base, true);155156return 0;157}158159static void iproc_rng200_cleanup(struct hwrng *rng)160{161struct iproc_rng200_dev *priv = to_rng_priv(rng);162163iproc_rng200_enable_set(priv->base, false);164}165166static int iproc_rng200_probe(struct platform_device *pdev)167{168struct iproc_rng200_dev *priv;169struct device *dev = &pdev->dev;170int ret;171172priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);173if (!priv)174return -ENOMEM;175176/* Map peripheral */177priv->base = devm_platform_ioremap_resource(pdev, 0);178if (IS_ERR(priv->base)) {179dev_err(dev, "failed to remap rng regs\n");180return PTR_ERR(priv->base);181}182183dev_set_drvdata(dev, priv);184185priv->rng.name = "iproc-rng200";186priv->rng.read = iproc_rng200_read;187priv->rng.init = iproc_rng200_init;188priv->rng.cleanup = iproc_rng200_cleanup;189190/* Register driver */191ret = devm_hwrng_register(dev, &priv->rng);192if (ret) {193dev_err(dev, "hwrng registration failed\n");194return ret;195}196197dev_info(dev, "hwrng registered\n");198199return 0;200}201202static int __maybe_unused iproc_rng200_suspend(struct device *dev)203{204struct iproc_rng200_dev *priv = dev_get_drvdata(dev);205206iproc_rng200_cleanup(&priv->rng);207208return 0;209}210211static int __maybe_unused iproc_rng200_resume(struct device *dev)212{213struct iproc_rng200_dev *priv = dev_get_drvdata(dev);214215iproc_rng200_init(&priv->rng);216217return 0;218}219220static const struct dev_pm_ops iproc_rng200_pm_ops = {221SET_SYSTEM_SLEEP_PM_OPS(iproc_rng200_suspend, iproc_rng200_resume)222};223224static const struct of_device_id iproc_rng200_of_match[] = {225{ .compatible = "brcm,bcm2711-rng200", },226{ .compatible = "brcm,bcm7211-rng200", },227{ .compatible = "brcm,bcm7278-rng200", },228{ .compatible = "brcm,iproc-rng200", },229{},230};231MODULE_DEVICE_TABLE(of, iproc_rng200_of_match);232233static struct platform_driver iproc_rng200_driver = {234.driver = {235.name = "iproc-rng200",236.of_match_table = iproc_rng200_of_match,237.pm = &iproc_rng200_pm_ops,238},239.probe = iproc_rng200_probe,240};241module_platform_driver(iproc_rng200_driver);242243MODULE_AUTHOR("Broadcom");244MODULE_DESCRIPTION("iProc RNG200 Random Number Generator driver");245MODULE_LICENSE("GPL v2");246247248