diff options
Diffstat (limited to 'drivers/char/hw_random')
-rw-r--r-- | drivers/char/hw_random/Kconfig | 14 | ||||
-rw-r--r-- | drivers/char/hw_random/Makefile | 1 | ||||
-rw-r--r-- | drivers/char/hw_random/bcm63xx-rng.c | 175 | ||||
-rw-r--r-- | drivers/char/hw_random/virtio-rng.c | 37 |
4 files changed, 224 insertions, 3 deletions
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index b01d67328243..7c0d391996b5 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -73,6 +73,20 @@ config HW_RANDOM_ATMEL If unsure, say Y. +config HW_RANDOM_BCM63XX + tristate "Broadcom BCM63xx Random Number Generator support" + depends on HW_RANDOM && BCM63XX + default HW_RANDOM + ---help--- + This driver provides kernel-side support for the Random Number + Generator hardware found on the Broadcom BCM63xx SoCs. + + To compile this driver as a module, choose M here: the + module will be called bcm63xx-rng + + If unusure, say Y. + + config HW_RANDOM_GEODE tristate "AMD Geode HW Random Number Generator support" depends on HW_RANDOM && X86_32 && PCI diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index 8d6d173b65e6..39a757ca15b6 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_HW_RANDOM_TIMERIOMEM) += timeriomem-rng.o obj-$(CONFIG_HW_RANDOM_INTEL) += intel-rng.o obj-$(CONFIG_HW_RANDOM_AMD) += amd-rng.o obj-$(CONFIG_HW_RANDOM_ATMEL) += atmel-rng.o +obj-$(CONFIG_HW_RANDOM_BCM63XX) += bcm63xx-rng.o obj-$(CONFIG_HW_RANDOM_GEODE) += geode-rng.o obj-$(CONFIG_HW_RANDOM_N2RNG) += n2-rng.o n2-rng-y := n2-drv.o n2-asm.o diff --git a/drivers/char/hw_random/bcm63xx-rng.c b/drivers/char/hw_random/bcm63xx-rng.c new file mode 100644 index 000000000000..aec6a4277caa --- /dev/null +++ b/drivers/char/hw_random/bcm63xx-rng.c @@ -0,0 +1,175 @@ +/* + * Broadcom BCM63xx Random Number Generator support + * + * Copyright (C) 2011, Florian Fainelli <florian@openwrt.org> + * Copyright (C) 2009, Broadcom Corporation + * + */ +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/hw_random.h> + +#include <bcm63xx_io.h> +#include <bcm63xx_regs.h> + +struct bcm63xx_rng_priv { + struct clk *clk; + void __iomem *regs; +}; + +#define to_rng_priv(rng) ((struct bcm63xx_rng_priv *)rng->priv) + +static int bcm63xx_rng_init(struct hwrng *rng) +{ + struct bcm63xx_rng_priv *priv = to_rng_priv(rng); + u32 val; + + val = bcm_readl(priv->regs + RNG_CTRL); + val |= RNG_EN; + bcm_writel(val, priv->regs + RNG_CTRL); + + return 0; +} + +static void bcm63xx_rng_cleanup(struct hwrng *rng) +{ + struct bcm63xx_rng_priv *priv = to_rng_priv(rng); + u32 val; + + val = bcm_readl(priv->regs + RNG_CTRL); + val &= ~RNG_EN; + bcm_writel(val, priv->regs + RNG_CTRL); +} + +static int bcm63xx_rng_data_present(struct hwrng *rng, int wait) +{ + struct bcm63xx_rng_priv *priv = to_rng_priv(rng); + + return bcm_readl(priv->regs + RNG_STAT) & RNG_AVAIL_MASK; +} + +static int bcm63xx_rng_data_read(struct hwrng *rng, u32 *data) +{ + struct bcm63xx_rng_priv *priv = to_rng_priv(rng); + + *data = bcm_readl(priv->regs + RNG_DATA); + + return 4; +} + +static int __devinit bcm63xx_rng_probe(struct platform_device *pdev) +{ + struct resource *r; + struct clk *clk; + int ret; + struct bcm63xx_rng_priv *priv; + struct hwrng *rng; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "no iomem resource\n"); + ret = -ENXIO; + goto out; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(&pdev->dev, "no memory for private structure\n"); + ret = -ENOMEM; + goto out; + } + + rng = kzalloc(sizeof(*rng), GFP_KERNEL); + if (!rng) { + dev_err(&pdev->dev, "no memory for rng structure\n"); + ret = -ENOMEM; + goto out_free_priv; + } + + platform_set_drvdata(pdev, rng); + rng->priv = (unsigned long)priv; + rng->name = pdev->name; + rng->init = bcm63xx_rng_init; + rng->cleanup = bcm63xx_rng_cleanup; + rng->data_present = bcm63xx_rng_data_present; + rng->data_read = bcm63xx_rng_data_read; + + clk = clk_get(&pdev->dev, "ipsec"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "no clock for device\n"); + ret = PTR_ERR(clk); + goto out_free_rng; + } + + priv->clk = clk; + + if (!devm_request_mem_region(&pdev->dev, r->start, + resource_size(r), pdev->name)) { + dev_err(&pdev->dev, "request mem failed"); + ret = -ENOMEM; + goto out_free_rng; + } + + priv->regs = devm_ioremap_nocache(&pdev->dev, r->start, + resource_size(r)); + if (!priv->regs) { + dev_err(&pdev->dev, "ioremap failed"); + ret = -ENOMEM; + goto out_free_rng; + } + + clk_enable(clk); + + ret = hwrng_register(rng); + if (ret) { + dev_err(&pdev->dev, "failed to register rng device\n"); + goto out_clk_disable; + } + + dev_info(&pdev->dev, "registered RNG driver\n"); + + return 0; + +out_clk_disable: + clk_disable(clk); +out_free_rng: + platform_set_drvdata(pdev, NULL); + kfree(rng); +out_free_priv: + kfree(priv); +out: + return ret; +} + +static int __devexit bcm63xx_rng_remove(struct platform_device *pdev) +{ + struct hwrng *rng = platform_get_drvdata(pdev); + struct bcm63xx_rng_priv *priv = to_rng_priv(rng); + + hwrng_unregister(rng); + clk_disable(priv->clk); + kfree(priv); + kfree(rng); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver bcm63xx_rng_driver = { + .probe = bcm63xx_rng_probe, + .remove = __devexit_p(bcm63xx_rng_remove), + .driver = { + .name = "bcm63xx-rng", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(bcm63xx_rng_driver); + +MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); +MODULE_DESCRIPTION("Broadcom BCM63xx RNG driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index 723725bbb96b..5708299507d0 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -55,6 +55,7 @@ static void register_buffer(u8 *buf, size_t size) static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait) { + int ret; if (!busy) { busy = true; @@ -65,7 +66,9 @@ static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait) if (!wait) return 0; - wait_for_completion(&have_data); + ret = wait_for_completion_killable(&have_data); + if (ret < 0) + return ret; busy = false; @@ -85,7 +88,7 @@ static struct hwrng virtio_hwrng = { .read = virtio_read, }; -static int virtrng_probe(struct virtio_device *vdev) +static int probe_common(struct virtio_device *vdev) { int err; @@ -103,13 +106,37 @@ static int virtrng_probe(struct virtio_device *vdev) return 0; } -static void __devexit virtrng_remove(struct virtio_device *vdev) +static void remove_common(struct virtio_device *vdev) { vdev->config->reset(vdev); + busy = false; hwrng_unregister(&virtio_hwrng); vdev->config->del_vqs(vdev); } +static int virtrng_probe(struct virtio_device *vdev) +{ + return probe_common(vdev); +} + +static void __devexit virtrng_remove(struct virtio_device *vdev) +{ + remove_common(vdev); +} + +#ifdef CONFIG_PM +static int virtrng_freeze(struct virtio_device *vdev) +{ + remove_common(vdev); + return 0; +} + +static int virtrng_restore(struct virtio_device *vdev) +{ + return probe_common(vdev); +} +#endif + static struct virtio_device_id id_table[] = { { VIRTIO_ID_RNG, VIRTIO_DEV_ANY_ID }, { 0 }, @@ -121,6 +148,10 @@ static struct virtio_driver virtio_rng_driver = { .id_table = id_table, .probe = virtrng_probe, .remove = __devexit_p(virtrng_remove), +#ifdef CONFIG_PM + .freeze = virtrng_freeze, + .restore = virtrng_restore, +#endif }; static int __init init(void) |