diff options
Diffstat (limited to 'drivers/char/hw_random')
26 files changed, 2320 insertions, 505 deletions
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index d31483c5488..836b061ced3 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -2,7 +2,7 @@  # Hardware Random Number Generator (RNG) configuration  # -config HW_RANDOM +menuconfig HW_RANDOM  	tristate "Hardware Random Number Generator Core support"  	default m  	---help--- @@ -20,9 +20,11 @@ config HW_RANDOM  	  If unsure, say Y. +if HW_RANDOM +  config HW_RANDOM_TIMERIOMEM  	tristate "Timer IOMEM HW Random Number Generator support" -	depends on HW_RANDOM && HAS_IOMEM +	depends on HAS_IOMEM  	---help---  	  This driver provides kernel-side support for a generic Random  	  Number Generator used by reading a 'dumb' iomem address that @@ -36,7 +38,7 @@ config HW_RANDOM_TIMERIOMEM  config HW_RANDOM_INTEL  	tristate "Intel HW Random Number Generator support" -	depends on HW_RANDOM && (X86 || IA64) && PCI +	depends on (X86 || IA64) && PCI  	default HW_RANDOM  	---help---  	  This driver provides kernel-side support for the Random Number @@ -49,7 +51,7 @@ config HW_RANDOM_INTEL  config HW_RANDOM_AMD  	tristate "AMD HW Random Number Generator support" -	depends on HW_RANDOM && X86 && PCI +	depends on (X86 || PPC_MAPLE) && PCI  	default HW_RANDOM  	---help---  	  This driver provides kernel-side support for the Random Number @@ -60,9 +62,48 @@ config HW_RANDOM_AMD  	  If unsure, say Y. +config HW_RANDOM_ATMEL +	tristate "Atmel Random Number Generator support" +	depends on ARCH_AT91 && HAVE_CLK +	default HW_RANDOM +	---help--- +	  This driver provides kernel-side support for the Random Number +	  Generator hardware found on Atmel AT91 devices. + +	  To compile this driver as a module, choose M here: the +	  module will be called atmel-rng. + +	  If unsure, say Y. + +config HW_RANDOM_BCM63XX +	tristate "Broadcom BCM63xx Random Number Generator support" +	depends on 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_BCM2835 +	tristate "Broadcom BCM2835 Random Number Generator support" +	depends on ARCH_BCM2835 +	default HW_RANDOM +	---help--- +	  This driver provides kernel-side support for the Random Number +	  Generator hardware found on the Broadcom BCM2835 SoCs. + +	  To compile this driver as a module, choose M here: the +	  module will be called bcm2835-rng + +	  If unsure, say Y. +  config HW_RANDOM_GEODE  	tristate "AMD Geode HW Random Number Generator support" -	depends on HW_RANDOM && X86_32 && PCI +	depends on X86_32 && PCI  	default HW_RANDOM  	---help---  	  This driver provides kernel-side support for the Random Number @@ -75,7 +116,7 @@ config HW_RANDOM_GEODE  config HW_RANDOM_N2RNG  	tristate "Niagara2 Random Number Generator support" -	depends on HW_RANDOM && SPARC64 +	depends on SPARC64  	default HW_RANDOM  	---help---  	  This driver provides kernel-side support for the Random Number @@ -88,7 +129,7 @@ config HW_RANDOM_N2RNG  config HW_RANDOM_VIA  	tristate "VIA HW Random Number Generator support" -	depends on HW_RANDOM && X86 +	depends on X86  	default HW_RANDOM  	---help---  	  This driver provides kernel-side support for the Random Number @@ -100,12 +141,12 @@ config HW_RANDOM_VIA  	  If unsure, say Y.  config HW_RANDOM_IXP4XX -	tristate "Intel IXP4xx NPU HW Random Number Generator support" -	depends on HW_RANDOM && ARCH_IXP4XX +	tristate "Intel IXP4xx NPU HW Pseudo-Random Number Generator support" +	depends on ARCH_IXP4XX  	default HW_RANDOM  	---help--- -	  This driver provides kernel-side support for the Random -	  Number Generator hardware found on the Intel IXP4xx NPU. +	  This driver provides kernel-side support for the Pseudo-Random +	  Number Generator hardware found on the Intel IXP45x/46x NPU.  	  To compile this driver as a module, choose M here: the  	  module will be called ixp4xx-rng. @@ -114,21 +155,34 @@ config HW_RANDOM_IXP4XX  config HW_RANDOM_OMAP  	tristate "OMAP Random Number Generator support" -	depends on HW_RANDOM && (ARCH_OMAP16XX || ARCH_OMAP2) +	depends on ARCH_OMAP16XX || ARCH_OMAP2PLUS  	default HW_RANDOM   	---help---   	  This driver provides kernel-side support for the Random Number -	  Generator hardware found on OMAP16xx and OMAP24xx multimedia -	  processors. +	  Generator hardware found on OMAP16xx, OMAP2/3/4/5 and AM33xx/AM43xx +	  multimedia processors.  	  To compile this driver as a module, choose M here: the  	  module will be called omap-rng.   	  If unsure, say Y. +config HW_RANDOM_OMAP3_ROM +	tristate "OMAP3 ROM Random Number Generator support" +	depends on ARCH_OMAP3 +	default HW_RANDOM +	---help--- +	  This driver provides kernel-side support for the Random Number +	  Generator hardware found on OMAP34xx processors. + +	  To compile this driver as a module, choose M here: the +	  module will be called omap3-rom-rng. + +	  If unsure, say Y. +  config HW_RANDOM_OCTEON  	tristate "Octeon Random Number Generator support" -	depends on HW_RANDOM && CPU_CAVIUM_OCTEON +	depends on CAVIUM_OCTEON_SOC  	default HW_RANDOM  	---help---  	  This driver provides kernel-side support for the Random Number @@ -141,7 +195,7 @@ config HW_RANDOM_OCTEON  config HW_RANDOM_PASEMI  	tristate "PA Semi HW Random Number Generator support" -	depends on HW_RANDOM && PPC_PASEMI +	depends on PPC_PASEMI  	default HW_RANDOM  	---help---  	  This driver provides kernel-side support for the Random Number @@ -154,7 +208,7 @@ config HW_RANDOM_PASEMI  config HW_RANDOM_VIRTIO  	tristate "VirtIO Random Number Generator support" -	depends on HW_RANDOM && VIRTIO +	depends on VIRTIO  	---help---  	  This driver provides kernel-side support for the virtual Random Number  	  Generator hardware. @@ -164,7 +218,7 @@ config HW_RANDOM_VIRTIO  config HW_RANDOM_TX4939  	tristate "TX4939 Random Number Generator support" -	depends on HW_RANDOM && SOC_TX4939 +	depends on SOC_TX4939  	default HW_RANDOM  	---help---  	  This driver provides kernel-side support for the Random Number @@ -177,7 +231,8 @@ config HW_RANDOM_TX4939  config HW_RANDOM_MXC_RNGA  	tristate "Freescale i.MX RNGA Random Number Generator" -	depends on HW_RANDOM && ARCH_HAS_RNGA +	depends on ARCH_HAS_RNGA +	default HW_RANDOM  	---help---  	  This driver provides kernel-side support for the Random Number  	  Generator hardware found on Freescale i.MX processors. @@ -189,7 +244,8 @@ config HW_RANDOM_MXC_RNGA  config HW_RANDOM_NOMADIK  	tristate "ST-Ericsson Nomadik Random Number Generator support" -	depends on HW_RANDOM && PLAT_NOMADIK +	depends on ARCH_NOMADIK +	default HW_RANDOM  	---help---  	  This driver provides kernel-side support for the Random Number  	  Generator hardware found on ST-Ericsson SoCs (8815 and 8500). @@ -198,3 +254,98 @@ config HW_RANDOM_NOMADIK  	  module will be called nomadik-rng.  	  If unsure, say Y. + +config HW_RANDOM_PPC4XX +	tristate "PowerPC 4xx generic true random number generator support" +	depends on PPC && 4xx +	default HW_RANDOM +	---help--- +	 This driver provides the kernel-side support for the TRNG hardware +	 found in the security function of some PowerPC 4xx SoCs. + +	 To compile this driver as a module, choose M here: the +	 module will be called ppc4xx-rng. + +	 If unsure, say N. + +config HW_RANDOM_PSERIES +	tristate "pSeries HW Random Number Generator support" +	depends on PPC64 && IBMVIO +	default HW_RANDOM +	---help--- +	  This driver provides kernel-side support for the Random Number +	  Generator hardware found on POWER7+ machines and above + +	  To compile this driver as a module, choose M here: the +	  module will be called pseries-rng. + +	  If unsure, say Y. + +config HW_RANDOM_POWERNV +	tristate "PowerNV Random Number Generator support" +	depends on PPC_POWERNV +	default HW_RANDOM +	---help--- +	  This is the driver for Random Number Generator hardware found +	  in POWER7+ and above machines for PowerNV platform. + +	  To compile this driver as a module, choose M here: the +	  module will be called powernv-rng. + +	  If unsure, say Y. + +config HW_RANDOM_EXYNOS +	tristate "EXYNOS HW random number generator support" +	depends on ARCH_EXYNOS +	default HW_RANDOM +	---help--- +	  This driver provides kernel-side support for the Random Number +	  Generator hardware found on EXYNOS SOCs. + +	  To compile this driver as a module, choose M here: the +	  module will be called exynos-rng. + +	  If unsure, say Y. + +config HW_RANDOM_TPM +	tristate "TPM HW Random Number Generator support" +	depends on TCG_TPM +	default HW_RANDOM +	---help--- +	  This driver provides kernel-side support for the Random Number +	  Generator in the Trusted Platform Module + +	  To compile this driver as a module, choose M here: the +	  module will be called tpm-rng. + +	  If unsure, say Y. + +config HW_RANDOM_MSM +	tristate "Qualcomm SoCs Random Number Generator support" +	depends on HW_RANDOM && ARCH_QCOM +	default HW_RANDOM +	---help--- +	  This driver provides kernel-side support for the Random Number +	  Generator hardware found on Qualcomm SoCs. + +	  To compile this driver as a module, choose M here. the +	  module will be called msm-rng. + +	  If unsure, say Y. + +endif # HW_RANDOM + +config UML_RANDOM +	depends on UML +	tristate "Hardware random number generator" +	help +	  This option enables UML's "hardware" random number generator.  It +	  attaches itself to the host's /dev/random, supplying as much entropy +	  as the host has, rather than the small amount the UML gets from its +	  own drivers.  It registers itself as a standard hardware random number +	  generator, major 10, minor 183, and the canonical device name is +	  /dev/hwrng. +	  The way to make use of this is to install the rng-tools package +	  (check your distro, or download from +	  http://sourceforge.net/projects/gkernel/).  rngd periodically reads +	  /dev/hwrng and injects the entropy into /dev/random. diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index 4273308aa1e..199ed283e14 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -7,15 +7,25 @@ rng-core-y := core.o  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  obj-$(CONFIG_HW_RANDOM_VIA) += via-rng.o  obj-$(CONFIG_HW_RANDOM_IXP4XX) += ixp4xx-rng.o  obj-$(CONFIG_HW_RANDOM_OMAP) += omap-rng.o +obj-$(CONFIG_HW_RANDOM_OMAP3_ROM) += omap3-rom-rng.o  obj-$(CONFIG_HW_RANDOM_PASEMI) += pasemi-rng.o  obj-$(CONFIG_HW_RANDOM_VIRTIO) += virtio-rng.o  obj-$(CONFIG_HW_RANDOM_TX4939) += tx4939-rng.o  obj-$(CONFIG_HW_RANDOM_MXC_RNGA) += mxc-rnga.o  obj-$(CONFIG_HW_RANDOM_OCTEON) += octeon-rng.o  obj-$(CONFIG_HW_RANDOM_NOMADIK) += nomadik-rng.o +obj-$(CONFIG_HW_RANDOM_PPC4XX) += ppc4xx-rng.o +obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o +obj-$(CONFIG_HW_RANDOM_POWERNV) += powernv-rng.o +obj-$(CONFIG_HW_RANDOM_EXYNOS)	+= exynos-rng.o +obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o +obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o +obj-$(CONFIG_HW_RANDOM_MSM) += msm-rng.o diff --git a/drivers/char/hw_random/amd-rng.c b/drivers/char/hw_random/amd-rng.c index 0d8c5788b8e..c6af038682f 100644 --- a/drivers/char/hw_random/amd-rng.c +++ b/drivers/char/hw_random/amd-rng.c @@ -133,6 +133,12 @@ found:  	pmbase &= 0x0000FF00;  	if (pmbase == 0)  		goto out; +	if (!request_region(pmbase + 0xF0, 8, "AMD HWRNG")) { +		dev_err(&pdev->dev, "AMD HWRNG region 0x%x already in use!\n", +			pmbase + 0xF0); +		err = -EBUSY; +		goto out; +	}  	amd_rng.priv = (unsigned long)pmbase;  	amd_pdev = pdev; @@ -141,6 +147,7 @@ found:  	if (err) {  		printk(KERN_ERR PFX "RNG registering failed (%d)\n",  		       err); +		release_region(pmbase + 0xF0, 8);  		goto out;  	}  out: @@ -149,6 +156,8 @@ out:  static void __exit mod_exit(void)  { +	u32 pmbase = (unsigned long)amd_rng.priv; +	release_region(pmbase + 0xF0, 8);  	hwrng_unregister(&amd_rng);  } diff --git a/drivers/char/hw_random/atmel-rng.c b/drivers/char/hw_random/atmel-rng.c new file mode 100644 index 00000000000..851bc7e20ad --- /dev/null +++ b/drivers/char/hw_random/atmel-rng.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2011 Peter Korsgaard <jacmet@sunsite.dk> + * + * This file is licensed under  the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/hw_random.h> +#include <linux/platform_device.h> + +#define TRNG_CR		0x00 +#define TRNG_ISR	0x1c +#define TRNG_ODATA	0x50 + +#define TRNG_KEY	0x524e4700 /* RNG */ + +struct atmel_trng { +	struct clk *clk; +	void __iomem *base; +	struct hwrng rng; +}; + +static int atmel_trng_read(struct hwrng *rng, void *buf, size_t max, +			   bool wait) +{ +	struct atmel_trng *trng = container_of(rng, struct atmel_trng, rng); +	u32 *data = buf; + +	/* data ready? */ +	if (readl(trng->base + TRNG_ISR) & 1) { +		*data = readl(trng->base + TRNG_ODATA); +		/* +		  ensure data ready is only set again AFTER the next data +		  word is ready in case it got set between checking ISR +		  and reading ODATA, so we don't risk re-reading the +		  same word +		*/ +		readl(trng->base + TRNG_ISR); +		return 4; +	} else +		return 0; +} + +static int atmel_trng_probe(struct platform_device *pdev) +{ +	struct atmel_trng *trng; +	struct resource *res; +	int ret; + +	trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL); +	if (!trng) +		return -ENOMEM; + +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	trng->base = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(trng->base)) +		return PTR_ERR(trng->base); + +	trng->clk = devm_clk_get(&pdev->dev, NULL); +	if (IS_ERR(trng->clk)) +		return PTR_ERR(trng->clk); + +	ret = clk_enable(trng->clk); +	if (ret) +		return ret; + +	writel(TRNG_KEY | 1, trng->base + TRNG_CR); +	trng->rng.name = pdev->name; +	trng->rng.read = atmel_trng_read; + +	ret = hwrng_register(&trng->rng); +	if (ret) +		goto err_register; + +	platform_set_drvdata(pdev, trng); + +	return 0; + +err_register: +	clk_disable(trng->clk); +	return ret; +} + +static int atmel_trng_remove(struct platform_device *pdev) +{ +	struct atmel_trng *trng = platform_get_drvdata(pdev); + +	hwrng_unregister(&trng->rng); + +	writel(TRNG_KEY, trng->base + TRNG_CR); +	clk_disable(trng->clk); + +	return 0; +} + +#ifdef CONFIG_PM +static int atmel_trng_suspend(struct device *dev) +{ +	struct atmel_trng *trng = dev_get_drvdata(dev); + +	clk_disable(trng->clk); + +	return 0; +} + +static int atmel_trng_resume(struct device *dev) +{ +	struct atmel_trng *trng = dev_get_drvdata(dev); + +	return clk_enable(trng->clk); +} + +static const struct dev_pm_ops atmel_trng_pm_ops = { +	.suspend	= atmel_trng_suspend, +	.resume		= atmel_trng_resume, +}; +#endif /* CONFIG_PM */ + +static struct platform_driver atmel_trng_driver = { +	.probe		= atmel_trng_probe, +	.remove		= atmel_trng_remove, +	.driver		= { +		.name	= "atmel-trng", +		.owner	= THIS_MODULE, +#ifdef CONFIG_PM +		.pm	= &atmel_trng_pm_ops, +#endif /* CONFIG_PM */ +	}, +}; + +module_platform_driver(atmel_trng_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>"); +MODULE_DESCRIPTION("Atmel true random number generator driver"); diff --git a/drivers/char/hw_random/bcm2835-rng.c b/drivers/char/hw_random/bcm2835-rng.c new file mode 100644 index 00000000000..e900961cdd2 --- /dev/null +++ b/drivers/char/hw_random/bcm2835-rng.c @@ -0,0 +1,112 @@ +/** + * Copyright (c) 2010-2012 Broadcom. All rights reserved. + * Copyright (c) 2013 Lubomir Rintel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License ("GPL") + * version 2, as published by the Free Software Foundation. + */ + +#include <linux/hw_random.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/printk.h> + +#define RNG_CTRL	0x0 +#define RNG_STATUS	0x4 +#define RNG_DATA	0x8 + +/* enable rng */ +#define RNG_RBGEN	0x1 + +/* the initial numbers generated are "less random" so will be discarded */ +#define RNG_WARMUP_COUNT 0x40000 + +static int bcm2835_rng_read(struct hwrng *rng, void *buf, size_t max, +			       bool wait) +{ +	void __iomem *rng_base = (void __iomem *)rng->priv; + +	while ((__raw_readl(rng_base + RNG_STATUS) >> 24) == 0) { +		if (!wait) +			return 0; +		cpu_relax(); +	} + +	*(u32 *)buf = __raw_readl(rng_base + RNG_DATA); +	return sizeof(u32); +} + +static struct hwrng bcm2835_rng_ops = { +	.name	= "bcm2835", +	.read	= bcm2835_rng_read, +}; + +static int bcm2835_rng_probe(struct platform_device *pdev) +{ +	struct device *dev = &pdev->dev; +	struct device_node *np = dev->of_node; +	void __iomem *rng_base; +	int err; + +	/* map peripheral */ +	rng_base = of_iomap(np, 0); +	if (!rng_base) { +		dev_err(dev, "failed to remap rng regs"); +		return -ENODEV; +	} +	bcm2835_rng_ops.priv = (unsigned long)rng_base; + +	/* set warm-up count & enable */ +	__raw_writel(RNG_WARMUP_COUNT, rng_base + RNG_STATUS); +	__raw_writel(RNG_RBGEN, rng_base + RNG_CTRL); + +	/* register driver */ +	err = hwrng_register(&bcm2835_rng_ops); +	if (err) { +		dev_err(dev, "hwrng registration failed\n"); +		iounmap(rng_base); +	} else +		dev_info(dev, "hwrng registered\n"); + +	return err; +} + +static int bcm2835_rng_remove(struct platform_device *pdev) +{ +	void __iomem *rng_base = (void __iomem *)bcm2835_rng_ops.priv; + +	/* disable rng hardware */ +	__raw_writel(0, rng_base + RNG_CTRL); + +	/* unregister driver */ +	hwrng_unregister(&bcm2835_rng_ops); +	iounmap(rng_base); + +	return 0; +} + +static const struct of_device_id bcm2835_rng_of_match[] = { +	{ .compatible = "brcm,bcm2835-rng", }, +	{}, +}; +MODULE_DEVICE_TABLE(of, bcm2835_rng_of_match); + +static struct platform_driver bcm2835_rng_driver = { +	.driver = { +		.name = "bcm2835-rng", +		.owner = THIS_MODULE, +		.of_match_table = bcm2835_rng_of_match, +	}, +	.probe		= bcm2835_rng_probe, +	.remove		= bcm2835_rng_remove, +}; +module_platform_driver(bcm2835_rng_driver); + +MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>"); +MODULE_DESCRIPTION("BCM2835 Random Number Generator (RNG) driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/char/hw_random/bcm63xx-rng.c b/drivers/char/hw_random/bcm63xx-rng.c new file mode 100644 index 00000000000..36581ea562c --- /dev/null +++ b/drivers/char/hw_random/bcm63xx-rng.c @@ -0,0 +1,173 @@ +/* + * 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 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: +	kfree(rng); +out_free_priv: +	kfree(priv); +out: +	return ret; +} + +static int 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); + +	return 0; +} + +static struct platform_driver bcm63xx_rng_driver = { +	.probe		= bcm63xx_rng_probe, +	.remove		= 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/core.c b/drivers/char/hw_random/core.c index 788da05190c..c4419ea1ab0 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -19,7 +19,7 @@  	Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>  	Added generic RNG API -	Copyright 2006 Michael Buesch <mbuesch@freenet.de> +	Copyright 2006 Michael Buesch <m@bues.ch>  	Copyright 2005 (c) MontaVista Software, Inc.  	Please read Documentation/hw_random.txt for details on use. @@ -37,10 +37,10 @@  #include <linux/kernel.h>  #include <linux/fs.h>  #include <linux/sched.h> -#include <linux/smp_lock.h> -#include <linux/init.h>  #include <linux/miscdevice.h>  #include <linux/delay.h> +#include <linux/slab.h> +#include <linux/random.h>  #include <asm/uaccess.h> @@ -53,14 +53,43 @@ static struct hwrng *current_rng;  static LIST_HEAD(rng_list);  static DEFINE_MUTEX(rng_mutex);  static int data_avail; -static u8 rng_buffer[SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES] -	__cacheline_aligned; +static u8 *rng_buffer; + +static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size, +			       int wait); + +static size_t rng_buffer_size(void) +{ +	return SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES; +} + +static void add_early_randomness(struct hwrng *rng) +{ +	unsigned char bytes[16]; +	int bytes_read; + +	/* +	 * Currently only virtio-rng cannot return data during device +	 * probe, and that's handled in virtio-rng.c itself.  If there +	 * are more such devices, this call to rng_get_data can be +	 * made conditional here instead of doing it per-device. +	 */ +	bytes_read = rng_get_data(rng, bytes, sizeof(bytes), 1); +	if (bytes_read > 0) +		add_device_randomness(bytes, bytes_read); +}  static inline int hwrng_init(struct hwrng *rng)  { -	if (!rng->init) -		return 0; -	return rng->init(rng); +	if (rng->init) { +		int ret; + +		ret =  rng->init(rng); +		if (ret) +			return ret; +	} +	add_early_randomness(rng); +	return 0;  }  static inline void hwrng_cleanup(struct hwrng *rng) @@ -117,7 +146,7 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf,  		if (!data_avail) {  			bytes_read = rng_get_data(current_rng, rng_buffer, -				sizeof(rng_buffer), +				rng_buffer_size(),  				!(filp->f_flags & O_NONBLOCK));  			if (bytes_read < 0) {  				err = bytes_read; @@ -298,7 +327,6 @@ err_misc_dereg:  int hwrng_register(struct hwrng *rng)  { -	int must_register_misc;  	int err = -EINVAL;  	struct hwrng *old_rng, *tmp; @@ -308,6 +336,14 @@ int hwrng_register(struct hwrng *rng)  	mutex_lock(&rng_mutex); +	/* kmalloc makes this safe for virt_to_page() in virtio_rng.c */ +	err = -ENOMEM; +	if (!rng_buffer) { +		rng_buffer = kmalloc(rng_buffer_size(), GFP_KERNEL); +		if (!rng_buffer) +			goto out_unlock; +	} +  	/* Must not register two RNGs with the same name. */  	err = -EEXIST;  	list_for_each_entry(tmp, &rng_list, list) { @@ -315,7 +351,6 @@ int hwrng_register(struct hwrng *rng)  			goto out_unlock;  	} -	must_register_misc = (current_rng == NULL);  	old_rng = current_rng;  	if (!old_rng) {  		err = hwrng_init(rng); @@ -324,18 +359,28 @@ int hwrng_register(struct hwrng *rng)  		current_rng = rng;  	}  	err = 0; -	if (must_register_misc) { +	if (!old_rng) {  		err = register_miscdev();  		if (err) { -			if (!old_rng) { -				hwrng_cleanup(rng); -				current_rng = NULL; -			} +			hwrng_cleanup(rng); +			current_rng = NULL;  			goto out_unlock;  		}  	}  	INIT_LIST_HEAD(&rng->list);  	list_add_tail(&rng->list, &rng_list); + +	if (old_rng && !rng->init) { +		/* +		 * Use a new device's input to add some randomness to +		 * the system.  If this rng device isn't going to be +		 * used right away, its init function hasn't been +		 * called yet; so only use the randomness from devices +		 * that don't need an init callback. +		 */ +		add_early_randomness(rng); +	} +  out_unlock:  	mutex_unlock(&rng_mutex);  out: @@ -368,6 +413,15 @@ void hwrng_unregister(struct hwrng *rng)  }  EXPORT_SYMBOL_GPL(hwrng_unregister); +static void __exit hwrng_exit(void) +{ +	mutex_lock(&rng_mutex); +	BUG_ON(current_rng); +	kfree(rng_buffer); +	mutex_unlock(&rng_mutex); +} + +module_exit(hwrng_exit);  MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver");  MODULE_LICENSE("GPL"); diff --git a/drivers/char/hw_random/exynos-rng.c b/drivers/char/hw_random/exynos-rng.c new file mode 100644 index 00000000000..9f8277cc44b --- /dev/null +++ b/drivers/char/hw_random/exynos-rng.c @@ -0,0 +1,183 @@ +/* + * exynos-rng.c - Random Number Generator driver for the exynos + * + * Copyright (C) 2012 Samsung Electronics + * Jonghwa Lee <jonghwa3.lee@smasung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + */ + +#include <linux/hw_random.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/pm_runtime.h> +#include <linux/err.h> + +#define EXYNOS_PRNG_STATUS_OFFSET	0x10 +#define EXYNOS_PRNG_SEED_OFFSET		0x140 +#define EXYNOS_PRNG_OUT1_OFFSET		0x160 +#define SEED_SETTING_DONE		BIT(1) +#define PRNG_START			0x18 +#define PRNG_DONE			BIT(5) +#define EXYNOS_AUTOSUSPEND_DELAY	100 + +struct exynos_rng { +	struct device *dev; +	struct hwrng rng; +	void __iomem *mem; +	struct clk *clk; +}; + +static u32 exynos_rng_readl(struct exynos_rng *rng, u32 offset) +{ +	return	__raw_readl(rng->mem + offset); +} + +static void exynos_rng_writel(struct exynos_rng *rng, u32 val, u32 offset) +{ +	__raw_writel(val, rng->mem + offset); +} + +static int exynos_init(struct hwrng *rng) +{ +	struct exynos_rng *exynos_rng = container_of(rng, +						struct exynos_rng, rng); +	int i; +	int ret = 0; + +	pm_runtime_get_sync(exynos_rng->dev); + +	for (i = 0 ; i < 5 ; i++) +		exynos_rng_writel(exynos_rng, jiffies, +				EXYNOS_PRNG_SEED_OFFSET + 4*i); + +	if (!(exynos_rng_readl(exynos_rng, EXYNOS_PRNG_STATUS_OFFSET) +						 & SEED_SETTING_DONE)) +		ret = -EIO; + +	pm_runtime_put_noidle(exynos_rng->dev); + +	return ret; +} + +static int exynos_read(struct hwrng *rng, void *buf, +					size_t max, bool wait) +{ +	struct exynos_rng *exynos_rng = container_of(rng, +						struct exynos_rng, rng); +	u32 *data = buf; + +	pm_runtime_get_sync(exynos_rng->dev); + +	exynos_rng_writel(exynos_rng, PRNG_START, 0); + +	while (!(exynos_rng_readl(exynos_rng, +			EXYNOS_PRNG_STATUS_OFFSET) & PRNG_DONE)) +		cpu_relax(); + +	exynos_rng_writel(exynos_rng, PRNG_DONE, EXYNOS_PRNG_STATUS_OFFSET); + +	*data = exynos_rng_readl(exynos_rng, EXYNOS_PRNG_OUT1_OFFSET); + +	pm_runtime_mark_last_busy(exynos_rng->dev); +	pm_runtime_autosuspend(exynos_rng->dev); + +	return 4; +} + +static int exynos_rng_probe(struct platform_device *pdev) +{ +	struct exynos_rng *exynos_rng; +	struct resource *res; + +	exynos_rng = devm_kzalloc(&pdev->dev, sizeof(struct exynos_rng), +					GFP_KERNEL); +	if (!exynos_rng) +		return -ENOMEM; + +	exynos_rng->dev = &pdev->dev; +	exynos_rng->rng.name = "exynos"; +	exynos_rng->rng.init =	exynos_init; +	exynos_rng->rng.read = exynos_read; +	exynos_rng->clk = devm_clk_get(&pdev->dev, "secss"); +	if (IS_ERR(exynos_rng->clk)) { +		dev_err(&pdev->dev, "Couldn't get clock.\n"); +		return -ENOENT; +	} + +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	exynos_rng->mem = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(exynos_rng->mem)) +		return PTR_ERR(exynos_rng->mem); + +	platform_set_drvdata(pdev, exynos_rng); + +	pm_runtime_set_autosuspend_delay(&pdev->dev, EXYNOS_AUTOSUSPEND_DELAY); +	pm_runtime_use_autosuspend(&pdev->dev); +	pm_runtime_enable(&pdev->dev); + +	return hwrng_register(&exynos_rng->rng); +} + +static int exynos_rng_remove(struct platform_device *pdev) +{ +	struct exynos_rng *exynos_rng = platform_get_drvdata(pdev); + +	hwrng_unregister(&exynos_rng->rng); + +	return 0; +} + +#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_RUNTIME) +static int exynos_rng_runtime_suspend(struct device *dev) +{ +	struct platform_device *pdev = to_platform_device(dev); +	struct exynos_rng *exynos_rng = platform_get_drvdata(pdev); + +	clk_disable_unprepare(exynos_rng->clk); + +	return 0; +} + +static int exynos_rng_runtime_resume(struct device *dev) +{ +	struct platform_device *pdev = to_platform_device(dev); +	struct exynos_rng *exynos_rng = platform_get_drvdata(pdev); + +	return clk_prepare_enable(exynos_rng->clk); +} +#endif + +static UNIVERSAL_DEV_PM_OPS(exynos_rng_pm_ops, exynos_rng_runtime_suspend, +					exynos_rng_runtime_resume, NULL); + +static struct platform_driver exynos_rng_driver = { +	.driver		= { +		.name	= "exynos-rng", +		.owner	= THIS_MODULE, +		.pm	= &exynos_rng_pm_ops, +	}, +	.probe		= exynos_rng_probe, +	.remove		= exynos_rng_remove, +}; + +module_platform_driver(exynos_rng_driver); + +MODULE_DESCRIPTION("EXYNOS 4 H/W Random Number Generator driver"); +MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/hw_random/ixp4xx-rng.c b/drivers/char/hw_random/ixp4xx-rng.c index 263567f5f39..beec1627db3 100644 --- a/drivers/char/hw_random/ixp4xx-rng.c +++ b/drivers/char/hw_random/ixp4xx-rng.c @@ -45,6 +45,9 @@ static int __init ixp4xx_rng_init(void)  	void __iomem * rng_base;  	int err; +	if (!cpu_is_ixp46x()) /* includes IXP455 */ +		return -ENOSYS; +  	rng_base = ioremap(0x70002100, 4);  	if (!rng_base)  		return -ENOMEM; @@ -68,5 +71,5 @@ module_init(ixp4xx_rng_init);  module_exit(ixp4xx_rng_exit);  MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net>"); -MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver for IXP4xx"); +MODULE_DESCRIPTION("H/W Pseudo-Random Number Generator (RNG) driver for IXP45x/46x");  MODULE_LICENSE("GPL"); diff --git a/drivers/char/hw_random/msm-rng.c b/drivers/char/hw_random/msm-rng.c new file mode 100644 index 00000000000..148521e51dc --- /dev/null +++ b/drivers/char/hw_random/msm-rng.c @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + */ +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/hw_random.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.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 { +	void __iomem *base; +	struct clk *clk; +	struct hwrng hwrng; +}; + +#define to_msm_rng(p)	container_of(p, struct msm_rng, hwrng) + +static int msm_rng_enable(struct hwrng *hwrng, int enable) +{ +	struct msm_rng *rng = to_msm_rng(hwrng); +	u32 val; +	int ret; + +	ret = clk_prepare_enable(rng->clk); +	if (ret) +		return ret; + +	if (enable) { +		/* Enable PRNG only if it is not already enabled */ +		val = readl_relaxed(rng->base + PRNG_CONFIG); +		if (val & PRNG_CONFIG_HW_ENABLE) +			goto already_enabled; + +		val = readl_relaxed(rng->base + PRNG_LFSR_CFG); +		val &= ~PRNG_LFSR_CFG_MASK; +		val |= PRNG_LFSR_CFG_CLOCKS; +		writel(val, rng->base + PRNG_LFSR_CFG); + +		val = readl_relaxed(rng->base + PRNG_CONFIG); +		val |= PRNG_CONFIG_HW_ENABLE; +		writel(val, rng->base + PRNG_CONFIG); +	} else { +		val = readl_relaxed(rng->base + PRNG_CONFIG); +		val &= ~PRNG_CONFIG_HW_ENABLE; +		writel(val, rng->base + PRNG_CONFIG); +	} + +already_enabled: +	clk_disable_unprepare(rng->clk); +	return 0; +} + +static int msm_rng_read(struct hwrng *hwrng, void *data, size_t max, bool wait) +{ +	struct msm_rng *rng = to_msm_rng(hwrng); +	size_t currsize = 0; +	u32 *retdata = data; +	size_t maxsize; +	int ret; +	u32 val; + +	/* calculate max size bytes to transfer back to caller */ +	maxsize = min_t(size_t, MAX_HW_FIFO_SIZE, max); + +	/* no room for word data */ +	if (maxsize < WORD_SZ) +		return 0; + +	ret = clk_prepare_enable(rng->clk); +	if (ret) +		return ret; + +	/* read random data from hardware */ +	do { +		val = readl_relaxed(rng->base + PRNG_STATUS); +		if (!(val & PRNG_STATUS_DATA_AVAIL)) +			break; + +		val = readl_relaxed(rng->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); + +	clk_disable_unprepare(rng->clk); + +	return currsize; +} + +static int msm_rng_init(struct hwrng *hwrng) +{ +	return msm_rng_enable(hwrng, 1); +} + +static void msm_rng_cleanup(struct hwrng *hwrng) +{ +	msm_rng_enable(hwrng, 0); +} + +static int msm_rng_probe(struct platform_device *pdev) +{ +	struct resource *res; +	struct msm_rng *rng; +	int ret; + +	rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); +	if (!rng) +		return -ENOMEM; + +	platform_set_drvdata(pdev, rng); + +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	rng->base = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(rng->base)) +		return PTR_ERR(rng->base); + +	rng->clk = devm_clk_get(&pdev->dev, "core"); +	if (IS_ERR(rng->clk)) +		return PTR_ERR(rng->clk); + +	rng->hwrng.name = KBUILD_MODNAME, +	rng->hwrng.init = msm_rng_init, +	rng->hwrng.cleanup = msm_rng_cleanup, +	rng->hwrng.read = msm_rng_read, + +	ret = hwrng_register(&rng->hwrng); +	if (ret) { +		dev_err(&pdev->dev, "failed to register hwrng\n"); +		return ret; +	} + +	return 0; +} + +static int msm_rng_remove(struct platform_device *pdev) +{ +	struct msm_rng *rng = platform_get_drvdata(pdev); + +	hwrng_unregister(&rng->hwrng); +	return 0; +} + +static const struct of_device_id msm_rng_of_match[] = { +	{ .compatible = "qcom,prng", }, +	{} +}; +MODULE_DEVICE_TABLE(of, msm_rng_of_match); + +static struct platform_driver msm_rng_driver = { +	.probe = msm_rng_probe, +	.remove = msm_rng_remove, +	.driver = { +		.name = KBUILD_MODNAME, +		.owner = THIS_MODULE, +		.of_match_table = of_match_ptr(msm_rng_of_match), +	} +}; +module_platform_driver(msm_rng_driver); + +MODULE_ALIAS("platform:" KBUILD_MODNAME); +MODULE_AUTHOR("The Linux Foundation"); +MODULE_DESCRIPTION("Qualcomm MSM random number generator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/char/hw_random/mxc-rnga.c b/drivers/char/hw_random/mxc-rnga.c index 187c6be80f4..6a86b6f56af 100644 --- a/drivers/char/hw_random/mxc-rnga.c +++ b/drivers/char/hw_random/mxc-rnga.c @@ -24,6 +24,7 @@  #include <linux/ioport.h>  #include <linux/platform_device.h>  #include <linux/hw_random.h> +#include <linux/delay.h>  #include <linux/io.h>  /* RNGA Registers */ @@ -58,38 +59,47 @@  #define RNGA_STATUS_LAST_READ_STATUS	0x00000002  #define RNGA_STATUS_SECURITY_VIOLATION	0x00000001 -static struct platform_device *rng_dev; +struct mxc_rng { +	struct device *dev; +	struct hwrng rng; +	void __iomem *mem; +	struct clk *clk; +}; -static int mxc_rnga_data_present(struct hwrng *rng) +static int mxc_rnga_data_present(struct hwrng *rng, int wait)  { -	int level; -	void __iomem *rng_base = (void __iomem *)rng->priv; - -	/* how many random numbers is in FIFO? [0-16] */ -	level = ((__raw_readl(rng_base + RNGA_STATUS) & -			RNGA_STATUS_LEVEL_MASK) >> 8); - -	return level > 0 ? 1 : 0; +	int i; +	struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng); + +	for (i = 0; i < 20; i++) { +		/* how many random numbers are in FIFO? [0-16] */ +		int level = (__raw_readl(mxc_rng->mem + RNGA_STATUS) & +				RNGA_STATUS_LEVEL_MASK) >> 8; +		if (level || !wait) +			return !!level; +		udelay(10); +	} +	return 0;  }  static int mxc_rnga_data_read(struct hwrng *rng, u32 * data)  {  	int err;  	u32 ctrl; -	void __iomem *rng_base = (void __iomem *)rng->priv; +	struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng);  	/* retrieve a random number from FIFO */ -	*data = __raw_readl(rng_base + RNGA_OUTPUT_FIFO); +	*data = __raw_readl(mxc_rng->mem + RNGA_OUTPUT_FIFO);  	/* some error while reading this random number? */ -	err = __raw_readl(rng_base + RNGA_STATUS) & RNGA_STATUS_ERROR_INT; +	err = __raw_readl(mxc_rng->mem + RNGA_STATUS) & RNGA_STATUS_ERROR_INT;  	/* if error: clear error interrupt, but doesn't return random number */  	if (err) { -		dev_dbg(&rng_dev->dev, "Error while reading random number!\n"); -		ctrl = __raw_readl(rng_base + RNGA_CONTROL); +		dev_dbg(mxc_rng->dev, "Error while reading random number!\n"); +		ctrl = __raw_readl(mxc_rng->mem + RNGA_CONTROL);  		__raw_writel(ctrl | RNGA_CONTROL_CLEAR_INT, -					rng_base + RNGA_CONTROL); +					mxc_rng->mem + RNGA_CONTROL);  		return 0;  	} else  		return 4; @@ -98,22 +108,22 @@ static int mxc_rnga_data_read(struct hwrng *rng, u32 * data)  static int mxc_rnga_init(struct hwrng *rng)  {  	u32 ctrl, osc; -	void __iomem *rng_base = (void __iomem *)rng->priv; +	struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng);  	/* wake up */ -	ctrl = __raw_readl(rng_base + RNGA_CONTROL); -	__raw_writel(ctrl & ~RNGA_CONTROL_SLEEP, rng_base + RNGA_CONTROL); +	ctrl = __raw_readl(mxc_rng->mem + RNGA_CONTROL); +	__raw_writel(ctrl & ~RNGA_CONTROL_SLEEP, mxc_rng->mem + RNGA_CONTROL);  	/* verify if oscillator is working */ -	osc = __raw_readl(rng_base + RNGA_STATUS); +	osc = __raw_readl(mxc_rng->mem + RNGA_STATUS);  	if (osc & RNGA_STATUS_OSC_DEAD) { -		dev_err(&rng_dev->dev, "RNGA Oscillator is dead!\n"); +		dev_err(mxc_rng->dev, "RNGA Oscillator is dead!\n");  		return -ENODEV;  	}  	/* go running */ -	ctrl = __raw_readl(rng_base + RNGA_CONTROL); -	__raw_writel(ctrl | RNGA_CONTROL_GO, rng_base + RNGA_CONTROL); +	ctrl = __raw_readl(mxc_rng->mem + RNGA_CONTROL); +	__raw_writel(ctrl | RNGA_CONTROL_GO, mxc_rng->mem + RNGA_CONTROL);  	return 0;  } @@ -121,83 +131,62 @@ static int mxc_rnga_init(struct hwrng *rng)  static void mxc_rnga_cleanup(struct hwrng *rng)  {  	u32 ctrl; -	void __iomem *rng_base = (void __iomem *)rng->priv; +	struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng); -	ctrl = __raw_readl(rng_base + RNGA_CONTROL); +	ctrl = __raw_readl(mxc_rng->mem + RNGA_CONTROL);  	/* stop rnga */ -	__raw_writel(ctrl & ~RNGA_CONTROL_GO, rng_base + RNGA_CONTROL); +	__raw_writel(ctrl & ~RNGA_CONTROL_GO, mxc_rng->mem + RNGA_CONTROL);  } -static struct hwrng mxc_rnga = { -	.name = "mxc-rnga", -	.init = mxc_rnga_init, -	.cleanup = mxc_rnga_cleanup, -	.data_present = mxc_rnga_data_present, -	.data_read = mxc_rnga_data_read -}; -  static int __init mxc_rnga_probe(struct platform_device *pdev)  {  	int err = -ENODEV; -	struct clk *clk; -	struct resource *res, *mem; -	void __iomem *rng_base = NULL; - -	if (rng_dev) -		return -EBUSY; - -	clk = clk_get(&pdev->dev, "rng"); -	if (IS_ERR(clk)) { +	struct resource *res; +	struct mxc_rng *mxc_rng; + +	mxc_rng = devm_kzalloc(&pdev->dev, sizeof(struct mxc_rng), +					GFP_KERNEL); +	if (!mxc_rng) +		return -ENOMEM; + +	mxc_rng->dev = &pdev->dev; +	mxc_rng->rng.name = "mxc-rnga"; +	mxc_rng->rng.init = mxc_rnga_init; +	mxc_rng->rng.cleanup = mxc_rnga_cleanup, +	mxc_rng->rng.data_present = mxc_rnga_data_present, +	mxc_rng->rng.data_read = mxc_rnga_data_read, + +	mxc_rng->clk = devm_clk_get(&pdev->dev, NULL); +	if (IS_ERR(mxc_rng->clk)) {  		dev_err(&pdev->dev, "Could not get rng_clk!\n"); -		err = PTR_ERR(clk); +		err = PTR_ERR(mxc_rng->clk);  		goto out;  	} -	clk_enable(clk); +	err = clk_prepare_enable(mxc_rng->clk); +	if (err) +		goto out;  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	if (!res) { -		err = -ENOENT; -		goto err_region; -	} - -	mem = request_mem_region(res->start, resource_size(res), pdev->name); -	if (mem == NULL) { -		err = -EBUSY; -		goto err_region; -	} - -	rng_base = ioremap(res->start, resource_size(res)); -	if (!rng_base) { -		err = -ENOMEM; +	mxc_rng->mem = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(mxc_rng->mem)) { +		err = PTR_ERR(mxc_rng->mem);  		goto err_ioremap;  	} -	mxc_rnga.priv = (unsigned long)rng_base; - -	err = hwrng_register(&mxc_rnga); +	err = hwrng_register(&mxc_rng->rng);  	if (err) {  		dev_err(&pdev->dev, "MXC RNGA registering failed (%d)\n", err); -		goto err_register; +		goto err_ioremap;  	} -	rng_dev = pdev; -  	dev_info(&pdev->dev, "MXC RNGA Registered.\n");  	return 0; -err_register: -	iounmap(rng_base); -	rng_base = NULL; -  err_ioremap: -	release_mem_region(res->start, resource_size(res)); - -err_region: -	clk_disable(clk); -	clk_put(clk); +	clk_disable_unprepare(mxc_rng->clk);  out:  	return err; @@ -205,18 +194,11 @@ out:  static int __exit mxc_rnga_remove(struct platform_device *pdev)  { -	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	void __iomem *rng_base = (void __iomem *)mxc_rnga.priv; -	struct clk *clk = clk_get(&pdev->dev, "rng"); - -	hwrng_unregister(&mxc_rnga); +	struct mxc_rng *mxc_rng = platform_get_drvdata(pdev); -	iounmap(rng_base); +	hwrng_unregister(&mxc_rng->rng); -	release_mem_region(res->start, resource_size(res)); - -	clk_disable(clk); -	clk_put(clk); +	clk_disable_unprepare(mxc_rng->clk);  	return 0;  } @@ -229,18 +211,7 @@ static struct platform_driver mxc_rnga_driver = {  	.remove = __exit_p(mxc_rnga_remove),  }; -static int __init mod_init(void) -{ -	return platform_driver_probe(&mxc_rnga_driver, mxc_rnga_probe); -} - -static void __exit mod_exit(void) -{ -	platform_driver_unregister(&mxc_rnga_driver); -} - -module_init(mod_init); -module_exit(mod_exit); +module_platform_driver_probe(mxc_rnga_driver, mxc_rnga_probe);  MODULE_AUTHOR("Freescale Semiconductor, Inc.");  MODULE_DESCRIPTION("H/W RNGA driver for i.MX"); diff --git a/drivers/char/hw_random/n2-drv.c b/drivers/char/hw_random/n2-drv.c index a3f5e381e74..292a5889f67 100644 --- a/drivers/char/hw_random/n2-drv.c +++ b/drivers/char/hw_random/n2-drv.c @@ -1,13 +1,12 @@  /* n2-drv.c: Niagara-2 RNG driver.   * - * Copyright (C) 2008 David S. Miller <davem@davemloft.net> + * Copyright (C) 2008, 2011 David S. Miller <davem@davemloft.net>   */  #include <linux/kernel.h>  #include <linux/module.h>  #include <linux/types.h>  #include <linux/delay.h> -#include <linux/init.h>  #include <linux/slab.h>  #include <linux/workqueue.h>  #include <linux/preempt.h> @@ -22,10 +21,10 @@  #define DRV_MODULE_NAME		"n2rng"  #define PFX DRV_MODULE_NAME	": " -#define DRV_MODULE_VERSION	"0.1" -#define DRV_MODULE_RELDATE	"May 15, 2008" +#define DRV_MODULE_VERSION	"0.2" +#define DRV_MODULE_RELDATE	"July 27, 2011" -static char version[] __devinitdata = +static char version[] =  	DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";  MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); @@ -611,7 +610,7 @@ static void n2rng_work(struct work_struct *work)  		schedule_delayed_work(&np->work, HZ * 2);  } -static void __devinit n2rng_driver_version(void) +static void n2rng_driver_version(void)  {  	static int n2rng_version_printed; @@ -619,24 +618,29 @@ static void __devinit n2rng_driver_version(void)  		pr_info("%s", version);  } -static int __devinit n2rng_probe(struct platform_device *op, -				 const struct of_device_id *match) +static const struct of_device_id n2rng_match[]; +static int n2rng_probe(struct platform_device *op)  { -	int victoria_falls = (match->data != NULL); +	const struct of_device_id *match; +	int multi_capable;  	int err = -ENOMEM;  	struct n2rng *np; -	n2rng_driver_version(); +	match = of_match_device(n2rng_match, &op->dev); +	if (!match) +		return -EINVAL; +	multi_capable = (match->data != NULL); -	np = kzalloc(sizeof(*np), GFP_KERNEL); +	n2rng_driver_version(); +	np = devm_kzalloc(&op->dev, sizeof(*np), GFP_KERNEL);  	if (!np)  		goto out;  	np->op = op;  	INIT_DELAYED_WORK(&np->work, n2rng_work); -	if (victoria_falls) -		np->flags |= N2RNG_FLAG_VF; +	if (multi_capable) +		np->flags |= N2RNG_FLAG_MULTI;  	err = -ENODEV;  	np->hvapi_major = 2; @@ -649,14 +653,14 @@ static int __devinit n2rng_probe(struct platform_device *op,  					 &np->hvapi_minor)) {  			dev_err(&op->dev, "Cannot register suitable "  				"HVAPI version.\n"); -			goto out_free; +			goto out;  		}  	} -	if (np->flags & N2RNG_FLAG_VF) { +	if (np->flags & N2RNG_FLAG_MULTI) {  		if (np->hvapi_major < 2) { -			dev_err(&op->dev, "VF RNG requires HVAPI major " -				"version 2 or later, got %lu\n", +			dev_err(&op->dev, "multi-unit-capable RNG requires " +				"HVAPI major version 2 or later, got %lu\n",  				np->hvapi_major);  			goto out_hvapi_unregister;  		} @@ -672,19 +676,20 @@ static int __devinit n2rng_probe(struct platform_device *op,  	dev_info(&op->dev, "Registered RNG HVAPI major %lu minor %lu\n",  		 np->hvapi_major, np->hvapi_minor); -	np->units = kzalloc(sizeof(struct n2rng_unit) * np->num_units, -			    GFP_KERNEL); +	np->units = devm_kzalloc(&op->dev, +				 sizeof(struct n2rng_unit) * np->num_units, +				 GFP_KERNEL);  	err = -ENOMEM;  	if (!np->units)  		goto out_hvapi_unregister;  	err = n2rng_init_control(np);  	if (err) -		goto out_free_units; +		goto out_hvapi_unregister;  	dev_info(&op->dev, "Found %s RNG, units: %d\n", -		 ((np->flags & N2RNG_FLAG_VF) ? -		  "Victoria Falls" : "Niagara2"), +		 ((np->flags & N2RNG_FLAG_MULTI) ? +		  "multi-unit-capable" : "single-unit"),  		 np->num_units);  	np->hwrng.name = "n2rng"; @@ -693,30 +698,24 @@ static int __devinit n2rng_probe(struct platform_device *op,  	err = hwrng_register(&np->hwrng);  	if (err) -		goto out_free_units; +		goto out_hvapi_unregister; -	dev_set_drvdata(&op->dev, np); +	platform_set_drvdata(op, np);  	schedule_delayed_work(&np->work, 0);  	return 0; -out_free_units: -	kfree(np->units); -	np->units = NULL; -  out_hvapi_unregister:  	sun4v_hvapi_unregister(HV_GRP_RNG); -out_free: -	kfree(np);  out:  	return err;  } -static int __devexit n2rng_remove(struct platform_device *op) +static int n2rng_remove(struct platform_device *op)  { -	struct n2rng *np = dev_get_drvdata(&op->dev); +	struct n2rng *np = platform_get_drvdata(op);  	np->flags |= N2RNG_FLAG_SHUTDOWN; @@ -726,13 +725,6 @@ static int __devexit n2rng_remove(struct platform_device *op)  	sun4v_hvapi_unregister(HV_GRP_RNG); -	kfree(np->units); -	np->units = NULL; - -	kfree(np); - -	dev_set_drvdata(&op->dev, NULL); -  	return 0;  } @@ -746,29 +738,23 @@ static const struct of_device_id n2rng_match[] = {  		.compatible	= "SUNW,vf-rng",  		.data		= (void *) 1,  	}, +	{ +		.name		= "random-number-generator", +		.compatible	= "SUNW,kt-rng", +		.data		= (void *) 1, +	},  	{},  };  MODULE_DEVICE_TABLE(of, n2rng_match); -static struct of_platform_driver n2rng_driver = { +static struct platform_driver n2rng_driver = {  	.driver = {  		.name = "n2rng",  		.owner = THIS_MODULE,  		.of_match_table = n2rng_match,  	},  	.probe		= n2rng_probe, -	.remove		= __devexit_p(n2rng_remove), +	.remove		= n2rng_remove,  }; -static int __init n2rng_init(void) -{ -	return of_register_platform_driver(&n2rng_driver); -} - -static void __exit n2rng_exit(void) -{ -	of_unregister_platform_driver(&n2rng_driver); -} - -module_init(n2rng_init); -module_exit(n2rng_exit); +module_platform_driver(n2rng_driver); diff --git a/drivers/char/hw_random/n2rng.h b/drivers/char/hw_random/n2rng.h index 4bea07f3097..f244ac89087 100644 --- a/drivers/char/hw_random/n2rng.h +++ b/drivers/char/hw_random/n2rng.h @@ -68,7 +68,7 @@ struct n2rng {  	struct platform_device	*op;  	unsigned long		flags; -#define N2RNG_FLAG_VF		0x00000001 /* Victoria Falls RNG, else N2 */ +#define N2RNG_FLAG_MULTI	0x00000001 /* Multi-unit capable RNG */  #define N2RNG_FLAG_CONTROL	0x00000002 /* Operating in control domain */  #define N2RNG_FLAG_READY	0x00000008 /* Ready for hw-rng layer      */  #define N2RNG_FLAG_SHUTDOWN	0x00000010 /* Driver unregistering        */ diff --git a/drivers/char/hw_random/nomadik-rng.c b/drivers/char/hw_random/nomadik-rng.c index a348c7e9aa0..9c858157724 100644 --- a/drivers/char/hw_random/nomadik-rng.c +++ b/drivers/char/hw_random/nomadik-rng.c @@ -10,7 +10,6 @@  #include <linux/kernel.h>  #include <linux/module.h> -#include <linux/init.h>  #include <linux/device.h>  #include <linux/amba/bus.h>  #include <linux/hw_random.h> @@ -39,50 +38,46 @@ static struct hwrng nmk_rng = {  	.read		= nmk_rng_read,  }; -static int nmk_rng_probe(struct amba_device *dev, struct amba_id *id) +static int nmk_rng_probe(struct amba_device *dev, const struct amba_id *id)  {  	void __iomem *base;  	int ret; -	rng_clk = clk_get(&dev->dev, NULL); +	rng_clk = devm_clk_get(&dev->dev, NULL);  	if (IS_ERR(rng_clk)) {  		dev_err(&dev->dev, "could not get rng clock\n");  		ret = PTR_ERR(rng_clk);  		return ret;  	} -	clk_enable(rng_clk); +	clk_prepare_enable(rng_clk);  	ret = amba_request_regions(dev, dev->dev.init_name);  	if (ret) -		return ret; +		goto out_clk;  	ret = -ENOMEM; -	base = ioremap(dev->res.start, resource_size(&dev->res)); +	base = devm_ioremap(&dev->dev, dev->res.start, +			    resource_size(&dev->res));  	if (!base)  		goto out_release;  	nmk_rng.priv = (unsigned long)base;  	ret = hwrng_register(&nmk_rng);  	if (ret) -		goto out_unmap; +		goto out_release;  	return 0; -out_unmap: -	iounmap(base);  out_release:  	amba_release_regions(dev); +out_clk:  	clk_disable(rng_clk); -	clk_put(rng_clk);  	return ret;  }  static int nmk_rng_remove(struct amba_device *dev)  { -	void __iomem *base = (void __iomem *)nmk_rng.priv;  	hwrng_unregister(&nmk_rng); -	iounmap(base);  	amba_release_regions(dev);  	clk_disable(rng_clk); -	clk_put(rng_clk);  	return 0;  } @@ -94,6 +89,8 @@ static struct amba_id nmk_rng_ids[] = {  	{0, 0},  }; +MODULE_DEVICE_TABLE(amba, nmk_rng_ids); +  static struct amba_driver nmk_rng_driver = {  	.drv = {  		.owner = THIS_MODULE, @@ -104,17 +101,6 @@ static struct amba_driver nmk_rng_driver = {  	.id_table = nmk_rng_ids,  }; -static int __init nmk_rng_init(void) -{ -	return amba_driver_register(&nmk_rng_driver); -} - -static void __devexit nmk_rng_exit(void) -{ -	amba_driver_unregister(&nmk_rng_driver); -} - -module_init(nmk_rng_init); -module_exit(nmk_rng_exit); +module_amba_driver(nmk_rng_driver);  MODULE_LICENSE("GPL"); diff --git a/drivers/char/hw_random/octeon-rng.c b/drivers/char/hw_random/octeon-rng.c index 9cd0feca318..b5cc3420c65 100644 --- a/drivers/char/hw_random/octeon-rng.c +++ b/drivers/char/hw_random/octeon-rng.c @@ -10,7 +10,6 @@   */  #include <linux/module.h> -#include <linux/init.h>  #include <linux/platform_device.h>  #include <linux/device.h>  #include <linux/hw_random.h> @@ -56,7 +55,7 @@ static int octeon_rng_data_read(struct hwrng *rng, u32 *data)  	return sizeof(u32);  } -static int __devinit octeon_rng_probe(struct platform_device *pdev) +static int octeon_rng_probe(struct platform_device *pdev)  {  	struct resource *res_ports;  	struct resource *res_result; @@ -75,47 +74,40 @@ static int __devinit octeon_rng_probe(struct platform_device *pdev)  	res_ports = platform_get_resource(pdev, IORESOURCE_MEM, 0);  	if (!res_ports) -		goto err_ports; +		return -ENOENT;  	res_result = platform_get_resource(pdev, IORESOURCE_MEM, 1);  	if (!res_result) -		goto err_ports; +		return -ENOENT;  	rng->control_status = devm_ioremap_nocache(&pdev->dev,  						   res_ports->start,  						   sizeof(u64));  	if (!rng->control_status) -		goto err_ports; +		return -ENOENT;  	rng->result = devm_ioremap_nocache(&pdev->dev,  					   res_result->start,  					   sizeof(u64));  	if (!rng->result) -		goto err_r; +		return -ENOENT;  	rng->ops = ops; -	dev_set_drvdata(&pdev->dev, &rng->ops); +	platform_set_drvdata(pdev, &rng->ops);  	ret = hwrng_register(&rng->ops);  	if (ret) -		goto err; +		return -ENOENT;  	dev_info(&pdev->dev, "Octeon Random Number Generator\n");  	return 0; -err: -	devm_iounmap(&pdev->dev, rng->control_status); -err_r: -	devm_iounmap(&pdev->dev, rng->result); -err_ports: -	devm_kfree(&pdev->dev, rng); -	return -ENOENT;  }  static int __exit octeon_rng_remove(struct platform_device *pdev)  { -	struct hwrng *rng = dev_get_drvdata(&pdev->dev); +	struct hwrng *rng = platform_get_drvdata(pdev);  	hwrng_unregister(rng); @@ -131,18 +123,7 @@ static struct platform_driver octeon_rng_driver = {  	.remove		= __exit_p(octeon_rng_remove),  }; -static int __init octeon_rng_mod_init(void) -{ -	return platform_driver_register(&octeon_rng_driver); -} - -static void __exit octeon_rng_mod_exit(void) -{ -	platform_driver_unregister(&octeon_rng_driver); -} - -module_init(octeon_rng_mod_init); -module_exit(octeon_rng_mod_exit); +module_platform_driver(octeon_rng_driver);  MODULE_AUTHOR("David Daney");  MODULE_LICENSE("GPL"); diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c index 06aad0831c7..f66ea258382 100644 --- a/drivers/char/hw_random/omap-rng.c +++ b/drivers/char/hw_random/omap-rng.c @@ -18,52 +18,137 @@  #include <linux/module.h>  #include <linux/init.h>  #include <linux/random.h> -#include <linux/clk.h>  #include <linux/err.h>  #include <linux/platform_device.h>  #include <linux/hw_random.h>  #include <linux/delay.h> +#include <linux/slab.h> +#include <linux/pm_runtime.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/interrupt.h>  #include <asm/io.h> -#define RNG_OUT_REG		0x00		/* Output register */ -#define RNG_STAT_REG		0x04		/* Status register -							[0] = STAT_BUSY */ -#define RNG_ALARM_REG		0x24		/* Alarm register -							[7:0] = ALARM_COUNTER */ -#define RNG_CONFIG_REG		0x28		/* Configuration register -							[11:6] = RESET_COUNT -							[5:3]  = RING2_DELAY -							[2:0]  = RING1_DELAY */ -#define RNG_REV_REG		0x3c		/* Revision register -							[7:0] = REV_NB */ -#define RNG_MASK_REG		0x40		/* Mask and reset register -							[2] = IT_EN -							[1] = SOFTRESET -							[0] = AUTOIDLE */ -#define RNG_SYSSTATUS		0x44		/* System status -							[0] = RESETDONE */ +#define RNG_REG_STATUS_RDY			(1 << 0) + +#define RNG_REG_INTACK_RDY_MASK			(1 << 0) +#define RNG_REG_INTACK_SHUTDOWN_OFLO_MASK	(1 << 1) +#define RNG_SHUTDOWN_OFLO_MASK			(1 << 1) + +#define RNG_CONTROL_STARTUP_CYCLES_SHIFT	16 +#define RNG_CONTROL_STARTUP_CYCLES_MASK		(0xffff << 16) +#define RNG_CONTROL_ENABLE_TRNG_SHIFT		10 +#define RNG_CONTROL_ENABLE_TRNG_MASK		(1 << 10) + +#define RNG_CONFIG_MAX_REFIL_CYCLES_SHIFT	16 +#define RNG_CONFIG_MAX_REFIL_CYCLES_MASK	(0xffff << 16) +#define RNG_CONFIG_MIN_REFIL_CYCLES_SHIFT	0 +#define RNG_CONFIG_MIN_REFIL_CYCLES_MASK	(0xff << 0) + +#define RNG_CONTROL_STARTUP_CYCLES		0xff +#define RNG_CONFIG_MIN_REFIL_CYCLES		0x21 +#define RNG_CONFIG_MAX_REFIL_CYCLES		0x22 + +#define RNG_ALARMCNT_ALARM_TH_SHIFT		0x0 +#define RNG_ALARMCNT_ALARM_TH_MASK		(0xff << 0) +#define RNG_ALARMCNT_SHUTDOWN_TH_SHIFT		16 +#define RNG_ALARMCNT_SHUTDOWN_TH_MASK		(0x1f << 16) +#define RNG_ALARM_THRESHOLD			0xff +#define RNG_SHUTDOWN_THRESHOLD			0x4 + +#define RNG_REG_FROENABLE_MASK			0xffffff +#define RNG_REG_FRODETUNE_MASK			0xffffff + +#define OMAP2_RNG_OUTPUT_SIZE			0x4 +#define OMAP4_RNG_OUTPUT_SIZE			0x8 + +enum { +	RNG_OUTPUT_L_REG = 0, +	RNG_OUTPUT_H_REG, +	RNG_STATUS_REG, +	RNG_INTMASK_REG, +	RNG_INTACK_REG, +	RNG_CONTROL_REG, +	RNG_CONFIG_REG, +	RNG_ALARMCNT_REG, +	RNG_FROENABLE_REG, +	RNG_FRODETUNE_REG, +	RNG_ALARMMASK_REG, +	RNG_ALARMSTOP_REG, +	RNG_REV_REG, +	RNG_SYSCONFIG_REG, +}; + +static const u16 reg_map_omap2[] = { +	[RNG_OUTPUT_L_REG]	= 0x0, +	[RNG_STATUS_REG]	= 0x4, +	[RNG_CONFIG_REG]	= 0x28, +	[RNG_REV_REG]		= 0x3c, +	[RNG_SYSCONFIG_REG]	= 0x40, +}; -static void __iomem *rng_base; -static struct clk *rng_ick; -static struct platform_device *rng_dev; +static const u16 reg_map_omap4[] = { +	[RNG_OUTPUT_L_REG]	= 0x0, +	[RNG_OUTPUT_H_REG]	= 0x4, +	[RNG_STATUS_REG]	= 0x8, +	[RNG_INTMASK_REG]	= 0xc, +	[RNG_INTACK_REG]	= 0x10, +	[RNG_CONTROL_REG]	= 0x14, +	[RNG_CONFIG_REG]	= 0x18, +	[RNG_ALARMCNT_REG]	= 0x1c, +	[RNG_FROENABLE_REG]	= 0x20, +	[RNG_FRODETUNE_REG]	= 0x24, +	[RNG_ALARMMASK_REG]	= 0x28, +	[RNG_ALARMSTOP_REG]	= 0x2c, +	[RNG_REV_REG]		= 0x1FE0, +	[RNG_SYSCONFIG_REG]	= 0x1FE4, +}; + +struct omap_rng_dev; +/** + * struct omap_rng_pdata - RNG IP block-specific data + * @regs: Pointer to the register offsets structure. + * @data_size: No. of bytes in RNG output. + * @data_present: Callback to determine if data is available. + * @init: Callback for IP specific initialization sequence. + * @cleanup: Callback for IP specific cleanup sequence. + */ +struct omap_rng_pdata { +	u16	*regs; +	u32	data_size; +	u32	(*data_present)(struct omap_rng_dev *priv); +	int	(*init)(struct omap_rng_dev *priv); +	void	(*cleanup)(struct omap_rng_dev *priv); +}; -static inline u32 omap_rng_read_reg(int reg) +struct omap_rng_dev { +	void __iomem			*base; +	struct device			*dev; +	const struct omap_rng_pdata	*pdata; +}; + +static inline u32 omap_rng_read(struct omap_rng_dev *priv, u16 reg)  { -	return __raw_readl(rng_base + reg); +	return __raw_readl(priv->base + priv->pdata->regs[reg]);  } -static inline void omap_rng_write_reg(int reg, u32 val) +static inline void omap_rng_write(struct omap_rng_dev *priv, u16 reg, +				      u32 val)  { -	__raw_writel(val, rng_base + reg); +	__raw_writel(val, priv->base + priv->pdata->regs[reg]);  }  static int omap_rng_data_present(struct hwrng *rng, int wait)  { +	struct omap_rng_dev *priv;  	int data, i; +	priv = (struct omap_rng_dev *)rng->priv; +  	for (i = 0; i < 20; i++) { -		data = omap_rng_read_reg(RNG_STAT_REG) ? 0 : 1; +		data = priv->pdata->data_present(priv);  		if (data || !wait)  			break;  		/* RNG produces data fast enough (2+ MBit/sec, even @@ -78,154 +163,308 @@ static int omap_rng_data_present(struct hwrng *rng, int wait)  static int omap_rng_data_read(struct hwrng *rng, u32 *data)  { -	*data = omap_rng_read_reg(RNG_OUT_REG); +	struct omap_rng_dev *priv; +	u32 data_size, i; + +	priv = (struct omap_rng_dev *)rng->priv; +	data_size = priv->pdata->data_size; + +	for (i = 0; i < data_size / sizeof(u32); i++) +		data[i] = omap_rng_read(priv, RNG_OUTPUT_L_REG + i); + +	if (priv->pdata->regs[RNG_INTACK_REG]) +		omap_rng_write(priv, RNG_INTACK_REG, RNG_REG_INTACK_RDY_MASK); +	return data_size; +} + +static int omap_rng_init(struct hwrng *rng) +{ +	struct omap_rng_dev *priv; + +	priv = (struct omap_rng_dev *)rng->priv; +	return priv->pdata->init(priv); +} + +static void omap_rng_cleanup(struct hwrng *rng) +{ +	struct omap_rng_dev *priv; -	return 4; +	priv = (struct omap_rng_dev *)rng->priv; +	priv->pdata->cleanup(priv);  }  static struct hwrng omap_rng_ops = {  	.name		= "omap",  	.data_present	= omap_rng_data_present,  	.data_read	= omap_rng_data_read, +	.init		= omap_rng_init, +	.cleanup	= omap_rng_cleanup,  }; -static int __devinit omap_rng_probe(struct platform_device *pdev) +static inline u32 omap2_rng_data_present(struct omap_rng_dev *priv)  { -	struct resource *res, *mem; -	int ret; +	return omap_rng_read(priv, RNG_STATUS_REG) ? 0 : 1; +} + +static int omap2_rng_init(struct omap_rng_dev *priv) +{ +	omap_rng_write(priv, RNG_SYSCONFIG_REG, 0x1); +	return 0; +} + +static void omap2_rng_cleanup(struct omap_rng_dev *priv) +{ +	omap_rng_write(priv, RNG_SYSCONFIG_REG, 0x0); +} + +static struct omap_rng_pdata omap2_rng_pdata = { +	.regs		= (u16 *)reg_map_omap2, +	.data_size	= OMAP2_RNG_OUTPUT_SIZE, +	.data_present	= omap2_rng_data_present, +	.init		= omap2_rng_init, +	.cleanup	= omap2_rng_cleanup, +}; + +#if defined(CONFIG_OF) +static inline u32 omap4_rng_data_present(struct omap_rng_dev *priv) +{ +	return omap_rng_read(priv, RNG_STATUS_REG) & RNG_REG_STATUS_RDY; +} + +static int omap4_rng_init(struct omap_rng_dev *priv) +{ +	u32 val; + +	/* Return if RNG is already running. */ +	if (omap_rng_read(priv, RNG_CONFIG_REG) & RNG_CONTROL_ENABLE_TRNG_MASK) +		return 0; + +	val = RNG_CONFIG_MIN_REFIL_CYCLES << RNG_CONFIG_MIN_REFIL_CYCLES_SHIFT; +	val |= RNG_CONFIG_MAX_REFIL_CYCLES << RNG_CONFIG_MAX_REFIL_CYCLES_SHIFT; +	omap_rng_write(priv, RNG_CONFIG_REG, val); + +	omap_rng_write(priv, RNG_FRODETUNE_REG, 0x0); +	omap_rng_write(priv, RNG_FROENABLE_REG, RNG_REG_FROENABLE_MASK); +	val = RNG_ALARM_THRESHOLD << RNG_ALARMCNT_ALARM_TH_SHIFT; +	val |= RNG_SHUTDOWN_THRESHOLD << RNG_ALARMCNT_SHUTDOWN_TH_SHIFT; +	omap_rng_write(priv, RNG_ALARMCNT_REG, val); + +	val = RNG_CONTROL_STARTUP_CYCLES << RNG_CONTROL_STARTUP_CYCLES_SHIFT; +	val |= RNG_CONTROL_ENABLE_TRNG_MASK; +	omap_rng_write(priv, RNG_CONTROL_REG, val); + +	return 0; +} + +static void omap4_rng_cleanup(struct omap_rng_dev *priv) +{ +	int val; + +	val = omap_rng_read(priv, RNG_CONTROL_REG); +	val &= ~RNG_CONTROL_ENABLE_TRNG_MASK; +	omap_rng_write(priv, RNG_CONFIG_REG, val); +} + +static irqreturn_t omap4_rng_irq(int irq, void *dev_id) +{ +	struct omap_rng_dev *priv = dev_id; +	u32 fro_detune, fro_enable;  	/* -	 * A bit ugly, and it will never actually happen but there can -	 * be only one RNG and this catches any bork +	 * Interrupt raised by a fro shutdown threshold, do the following: +	 * 1. Clear the alarm events. +	 * 2. De tune the FROs which are shutdown. +	 * 3. Re enable the shutdown FROs.  	 */ -	if (rng_dev) -		return -EBUSY; - -	if (cpu_is_omap24xx()) { -		rng_ick = clk_get(&pdev->dev, "ick"); -		if (IS_ERR(rng_ick)) { -			dev_err(&pdev->dev, "Could not get rng_ick\n"); -			ret = PTR_ERR(rng_ick); -			return ret; -		} else -			clk_enable(rng_ick); -	} +	omap_rng_write(priv, RNG_ALARMMASK_REG, 0x0); +	omap_rng_write(priv, RNG_ALARMSTOP_REG, 0x0); -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	fro_enable = omap_rng_read(priv, RNG_FROENABLE_REG); +	fro_detune = ~fro_enable & RNG_REG_FRODETUNE_MASK; +	fro_detune = fro_detune | omap_rng_read(priv, RNG_FRODETUNE_REG); +	fro_enable = RNG_REG_FROENABLE_MASK; -	if (!res) -		return -ENOENT; +	omap_rng_write(priv, RNG_FRODETUNE_REG, fro_detune); +	omap_rng_write(priv, RNG_FROENABLE_REG, fro_enable); -	mem = request_mem_region(res->start, resource_size(res), -				 pdev->name); -	if (mem == NULL) { -		ret = -EBUSY; -		goto err_region; +	omap_rng_write(priv, RNG_INTACK_REG, RNG_REG_INTACK_SHUTDOWN_OFLO_MASK); + +	return IRQ_HANDLED; +} + +static struct omap_rng_pdata omap4_rng_pdata = { +	.regs		= (u16 *)reg_map_omap4, +	.data_size	= OMAP4_RNG_OUTPUT_SIZE, +	.data_present	= omap4_rng_data_present, +	.init		= omap4_rng_init, +	.cleanup	= omap4_rng_cleanup, +}; + +static const struct of_device_id omap_rng_of_match[] = { +		{ +			.compatible	= "ti,omap2-rng", +			.data		= &omap2_rng_pdata, +		}, +		{ +			.compatible	= "ti,omap4-rng", +			.data		= &omap4_rng_pdata, +		}, +		{}, +}; +MODULE_DEVICE_TABLE(of, omap_rng_of_match); + +static int of_get_omap_rng_device_details(struct omap_rng_dev *priv, +					  struct platform_device *pdev) +{ +	const struct of_device_id *match; +	struct device *dev = &pdev->dev; +	int irq, err; + +	match = of_match_device(of_match_ptr(omap_rng_of_match), dev); +	if (!match) { +		dev_err(dev, "no compatible OF match\n"); +		return -EINVAL; +	} +	priv->pdata = match->data; + +	if (of_device_is_compatible(dev->of_node, "ti,omap4-rng")) { +		irq = platform_get_irq(pdev, 0); +		if (irq < 0) { +			dev_err(dev, "%s: error getting IRQ resource - %d\n", +				__func__, irq); +			return irq; +		} + +		err = devm_request_irq(dev, irq, omap4_rng_irq, +				       IRQF_TRIGGER_NONE, dev_name(dev), priv); +		if (err) { +			dev_err(dev, "unable to request irq %d, err = %d\n", +				irq, err); +			return err; +		} +		omap_rng_write(priv, RNG_INTMASK_REG, RNG_SHUTDOWN_OFLO_MASK);  	} +	return 0; +} +#else +static int of_get_omap_rng_device_details(struct omap_rng_dev *omap_rng, +					  struct platform_device *pdev) +{ +	return -EINVAL; +} +#endif + +static int get_omap_rng_device_details(struct omap_rng_dev *omap_rng) +{ +	/* Only OMAP2/3 can be non-DT */ +	omap_rng->pdata = &omap2_rng_pdata; +	return 0; +} -	dev_set_drvdata(&pdev->dev, mem); -	rng_base = ioremap(res->start, resource_size(res)); -	if (!rng_base) { -		ret = -ENOMEM; +static int omap_rng_probe(struct platform_device *pdev) +{ +	struct omap_rng_dev *priv; +	struct resource *res; +	struct device *dev = &pdev->dev; +	int ret; + +	priv = devm_kzalloc(dev, sizeof(struct omap_rng_dev), GFP_KERNEL); +	if (!priv) +		return -ENOMEM; + +	omap_rng_ops.priv = (unsigned long)priv; +	platform_set_drvdata(pdev, priv); +	priv->dev = dev; + +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	priv->base = devm_ioremap_resource(dev, res); +	if (IS_ERR(priv->base)) { +		ret = PTR_ERR(priv->base);  		goto err_ioremap;  	} +	pm_runtime_enable(&pdev->dev); +	pm_runtime_get_sync(&pdev->dev); + +	ret = (dev->of_node) ? of_get_omap_rng_device_details(priv, pdev) : +				get_omap_rng_device_details(priv); +	if (ret) +		goto err_ioremap; +  	ret = hwrng_register(&omap_rng_ops);  	if (ret)  		goto err_register;  	dev_info(&pdev->dev, "OMAP Random Number Generator ver. %02x\n", -		omap_rng_read_reg(RNG_REV_REG)); -	omap_rng_write_reg(RNG_MASK_REG, 0x1); - -	rng_dev = pdev; +		 omap_rng_read(priv, RNG_REV_REG));  	return 0;  err_register: -	iounmap(rng_base); -	rng_base = NULL; +	priv->base = NULL; +	pm_runtime_disable(&pdev->dev);  err_ioremap: -	release_resource(mem); -err_region: -	if (cpu_is_omap24xx()) { -		clk_disable(rng_ick); -		clk_put(rng_ick); -	} +	dev_err(dev, "initialization failed.\n");  	return ret;  }  static int __exit omap_rng_remove(struct platform_device *pdev)  { -	struct resource *mem = dev_get_drvdata(&pdev->dev); +	struct omap_rng_dev *priv = platform_get_drvdata(pdev);  	hwrng_unregister(&omap_rng_ops); -	omap_rng_write_reg(RNG_MASK_REG, 0x0); +	priv->pdata->cleanup(priv); -	iounmap(rng_base); - -	if (cpu_is_omap24xx()) { -		clk_disable(rng_ick); -		clk_put(rng_ick); -	} - -	release_resource(mem); -	rng_base = NULL; +	pm_runtime_put_sync(&pdev->dev); +	pm_runtime_disable(&pdev->dev);  	return 0;  } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP -static int omap_rng_suspend(struct platform_device *pdev, pm_message_t message) +static int omap_rng_suspend(struct device *dev)  { -	omap_rng_write_reg(RNG_MASK_REG, 0x0); +	struct omap_rng_dev *priv = dev_get_drvdata(dev); + +	priv->pdata->cleanup(priv); +	pm_runtime_put_sync(dev); +  	return 0;  } -static int omap_rng_resume(struct platform_device *pdev) +static int omap_rng_resume(struct device *dev)  { -	omap_rng_write_reg(RNG_MASK_REG, 0x1); +	struct omap_rng_dev *priv = dev_get_drvdata(dev); + +	pm_runtime_get_sync(dev); +	priv->pdata->init(priv); +  	return 0;  } +static SIMPLE_DEV_PM_OPS(omap_rng_pm, omap_rng_suspend, omap_rng_resume); +#define	OMAP_RNG_PM	(&omap_rng_pm) +  #else -#define	omap_rng_suspend	NULL -#define	omap_rng_resume		NULL +#define	OMAP_RNG_PM	NULL  #endif -/* work with hotplug and coldplug */ -MODULE_ALIAS("platform:omap_rng"); -  static struct platform_driver omap_rng_driver = {  	.driver = {  		.name		= "omap_rng",  		.owner		= THIS_MODULE, +		.pm		= OMAP_RNG_PM, +		.of_match_table = of_match_ptr(omap_rng_of_match),  	},  	.probe		= omap_rng_probe,  	.remove		= __exit_p(omap_rng_remove), -	.suspend	= omap_rng_suspend, -	.resume		= omap_rng_resume  }; -static int __init omap_rng_init(void) -{ -	if (!cpu_is_omap16xx() && !cpu_is_omap24xx()) -		return -ENODEV; - -	return platform_driver_register(&omap_rng_driver); -} - -static void __exit omap_rng_exit(void) -{ -	platform_driver_unregister(&omap_rng_driver); -} - -module_init(omap_rng_init); -module_exit(omap_rng_exit); - +module_platform_driver(omap_rng_driver); +MODULE_ALIAS("platform:omap_rng");  MODULE_AUTHOR("Deepak Saxena (and others)");  MODULE_LICENSE("GPL"); diff --git a/drivers/char/hw_random/omap3-rom-rng.c b/drivers/char/hw_random/omap3-rom-rng.c new file mode 100644 index 00000000000..6f2eaffed62 --- /dev/null +++ b/drivers/char/hw_random/omap3-rom-rng.c @@ -0,0 +1,140 @@ +/* + * omap3-rom-rng.c - RNG driver for TI OMAP3 CPU family + * + * Copyright (C) 2009 Nokia Corporation + * Author: Juha Yrjola <juha.yrjola@solidboot.com> + * + * Copyright (C) 2013 Pali Rohár <pali.rohar@gmail.com> + * + * This file is licensed under  the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/random.h> +#include <linux/hw_random.h> +#include <linux/timer.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/platform_device.h> + +#define RNG_RESET			0x01 +#define RNG_GEN_PRNG_HW_INIT		0x02 +#define RNG_GEN_HW			0x08 + +/* param1: ptr, param2: count, param3: flag */ +static u32 (*omap3_rom_rng_call)(u32, u32, u32); + +static struct timer_list idle_timer; +static int rng_idle; +static struct clk *rng_clk; + +static void omap3_rom_rng_idle(unsigned long data) +{ +	int r; + +	r = omap3_rom_rng_call(0, 0, RNG_RESET); +	if (r != 0) { +		pr_err("reset failed: %d\n", r); +		return; +	} +	clk_disable_unprepare(rng_clk); +	rng_idle = 1; +} + +static int omap3_rom_rng_get_random(void *buf, unsigned int count) +{ +	u32 r; +	u32 ptr; + +	del_timer_sync(&idle_timer); +	if (rng_idle) { +		clk_prepare_enable(rng_clk); +		r = omap3_rom_rng_call(0, 0, RNG_GEN_PRNG_HW_INIT); +		if (r != 0) { +			clk_disable_unprepare(rng_clk); +			pr_err("HW init failed: %d\n", r); +			return -EIO; +		} +		rng_idle = 0; +	} + +	ptr = virt_to_phys(buf); +	r = omap3_rom_rng_call(ptr, count, RNG_GEN_HW); +	mod_timer(&idle_timer, jiffies + msecs_to_jiffies(500)); +	if (r != 0) +		return -EINVAL; +	return 0; +} + +static int omap3_rom_rng_data_present(struct hwrng *rng, int wait) +{ +	return 1; +} + +static int omap3_rom_rng_data_read(struct hwrng *rng, u32 *data) +{ +	int r; + +	r = omap3_rom_rng_get_random(data, 4); +	if (r < 0) +		return r; +	return 4; +} + +static struct hwrng omap3_rom_rng_ops = { +	.name		= "omap3-rom", +	.data_present	= omap3_rom_rng_data_present, +	.data_read	= omap3_rom_rng_data_read, +}; + +static int omap3_rom_rng_probe(struct platform_device *pdev) +{ +	pr_info("initializing\n"); + +	omap3_rom_rng_call = pdev->dev.platform_data; +	if (!omap3_rom_rng_call) { +		pr_err("omap3_rom_rng_call is NULL\n"); +		return -EINVAL; +	} + +	setup_timer(&idle_timer, omap3_rom_rng_idle, 0); +	rng_clk = devm_clk_get(&pdev->dev, "ick"); +	if (IS_ERR(rng_clk)) { +		pr_err("unable to get RNG clock\n"); +		return PTR_ERR(rng_clk); +	} + +	/* Leave the RNG in reset state. */ +	clk_prepare_enable(rng_clk); +	omap3_rom_rng_idle(0); + +	return hwrng_register(&omap3_rom_rng_ops); +} + +static int omap3_rom_rng_remove(struct platform_device *pdev) +{ +	hwrng_unregister(&omap3_rom_rng_ops); +	clk_disable_unprepare(rng_clk); +	return 0; +} + +static struct platform_driver omap3_rom_rng_driver = { +	.driver = { +		.name		= "omap3-rom-rng", +		.owner		= THIS_MODULE, +	}, +	.probe		= omap3_rom_rng_probe, +	.remove		= omap3_rom_rng_remove, +}; + +module_platform_driver(omap3_rom_rng_driver); + +MODULE_ALIAS("platform:omap3-rom-rng"); +MODULE_AUTHOR("Juha Yrjola"); +MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/hw_random/pasemi-rng.c b/drivers/char/hw_random/pasemi-rng.c index a31c830ca8c..c66279bb6ef 100644 --- a/drivers/char/hw_random/pasemi-rng.c +++ b/drivers/char/hw_random/pasemi-rng.c @@ -24,6 +24,7 @@  #include <linux/platform_device.h>  #include <linux/hw_random.h>  #include <linux/delay.h> +#include <linux/of_address.h>  #include <linux/of_platform.h>  #include <asm/io.h> @@ -94,8 +95,7 @@ static struct hwrng pasemi_rng = {  	.data_read	= pasemi_rng_data_read,  }; -static int __devinit rng_probe(struct platform_device *ofdev, -			       const struct of_device_id *match) +static int rng_probe(struct platform_device *ofdev)  {  	void __iomem *rng_regs;  	struct device_node *rng_np = ofdev->dev.of_node; @@ -123,7 +123,7 @@ static int __devinit rng_probe(struct platform_device *ofdev,  	return err;  } -static int __devexit rng_remove(struct platform_device *dev) +static int rng_remove(struct platform_device *dev)  {  	void __iomem *rng_regs = (void __iomem *)pasemi_rng.priv; @@ -139,7 +139,7 @@ static struct of_device_id rng_match[] = {  	{ },  }; -static struct of_platform_driver rng_driver = { +static struct platform_driver rng_driver = {  	.driver = {  		.name = "pasemi-rng",  		.owner = THIS_MODULE, @@ -149,17 +149,7 @@ static struct of_platform_driver rng_driver = {  	.remove		= rng_remove,  }; -static int __init rng_init(void) -{ -	return of_register_platform_driver(&rng_driver); -} -module_init(rng_init); - -static void __exit rng_exit(void) -{ -	of_unregister_platform_driver(&rng_driver); -} -module_exit(rng_exit); +module_platform_driver(rng_driver);  MODULE_LICENSE("GPL");  MODULE_AUTHOR("Egor Martovetsky <egor@pasemi.com>"); diff --git a/drivers/char/hw_random/powernv-rng.c b/drivers/char/hw_random/powernv-rng.c new file mode 100644 index 00000000000..3f4f6320456 --- /dev/null +++ b/drivers/char/hw_random/powernv-rng.c @@ -0,0 +1,81 @@ +/* + * Copyright 2013 Michael Ellerman, Guo Chao, IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/random.h> +#include <linux/hw_random.h> + +static int powernv_rng_read(struct hwrng *rng, void *data, size_t max, bool wait) +{ +	unsigned long *buf; +	int i, len; + +	/* We rely on rng_buffer_size() being >= sizeof(unsigned long) */ +	len = max / sizeof(unsigned long); + +	buf = (unsigned long *)data; + +	for (i = 0; i < len; i++) +		powernv_get_random_long(buf++); + +	return len * sizeof(unsigned long); +} + +static struct hwrng powernv_hwrng = { +	.name = "powernv-rng", +	.read = powernv_rng_read, +}; + +static int powernv_rng_remove(struct platform_device *pdev) +{ +	hwrng_unregister(&powernv_hwrng); + +	return 0; +} + +static int powernv_rng_probe(struct platform_device *pdev) +{ +	int rc; + +	rc = hwrng_register(&powernv_hwrng); +	if (rc) { +		/* We only register one device, ignore any others */ +		if (rc == -EEXIST) +			rc = -ENODEV; + +		return rc; +	} + +	pr_info("Registered powernv hwrng.\n"); + +	return 0; +} + +static struct of_device_id powernv_rng_match[] = { +	{ .compatible	= "ibm,power-rng",}, +	{}, +}; +MODULE_DEVICE_TABLE(of, powernv_rng_match); + +static struct platform_driver powernv_rng_driver = { +	.driver = { +		.name = "powernv_rng", +		.of_match_table = powernv_rng_match, +	}, +	.probe	= powernv_rng_probe, +	.remove = powernv_rng_remove, +}; +module_platform_driver(powernv_rng_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Bare metal HWRNG driver for POWER7+ and above"); diff --git a/drivers/char/hw_random/ppc4xx-rng.c b/drivers/char/hw_random/ppc4xx-rng.c new file mode 100644 index 00000000000..521f76b0934 --- /dev/null +++ b/drivers/char/hw_random/ppc4xx-rng.c @@ -0,0 +1,147 @@ +/* + * Generic PowerPC 44x RNG driver + * + * Copyright 2011 IBM Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; version 2 of the License. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/hw_random.h> +#include <linux/delay.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <asm/io.h> + +#define PPC4XX_TRNG_DEV_CTRL 0x60080 + +#define PPC4XX_TRNGE 0x00020000 +#define PPC4XX_TRNG_CTRL 0x0008 +#define PPC4XX_TRNG_CTRL_DALM 0x20 +#define PPC4XX_TRNG_STAT 0x0004 +#define PPC4XX_TRNG_STAT_B 0x1 +#define PPC4XX_TRNG_DATA 0x0000 + +#define MODULE_NAME "ppc4xx_rng" + +static int ppc4xx_rng_data_present(struct hwrng *rng, int wait) +{ +	void __iomem *rng_regs = (void __iomem *) rng->priv; +	int busy, i, present = 0; + +	for (i = 0; i < 20; i++) { +		busy = (in_le32(rng_regs + PPC4XX_TRNG_STAT) & PPC4XX_TRNG_STAT_B); +		if (!busy || !wait) { +			present = 1; +			break; +		} +		udelay(10); +	} +	return present; +} + +static int ppc4xx_rng_data_read(struct hwrng *rng, u32 *data) +{ +	void __iomem *rng_regs = (void __iomem *) rng->priv; +	*data = in_le32(rng_regs + PPC4XX_TRNG_DATA); +	return 4; +} + +static int ppc4xx_rng_enable(int enable) +{ +	struct device_node *ctrl; +	void __iomem *ctrl_reg; +	int err = 0; +	u32 val; + +	/* Find the main crypto device node and map it to turn the TRNG on */ +	ctrl = of_find_compatible_node(NULL, NULL, "amcc,ppc4xx-crypto"); +	if (!ctrl) +		return -ENODEV; + +	ctrl_reg = of_iomap(ctrl, 0); +	if (!ctrl_reg) { +		err = -ENODEV; +		goto out; +	} + +	val = in_le32(ctrl_reg + PPC4XX_TRNG_DEV_CTRL); + +	if (enable) +		val |= PPC4XX_TRNGE; +	else +		val = val & ~PPC4XX_TRNGE; + +	out_le32(ctrl_reg + PPC4XX_TRNG_DEV_CTRL, val); +	iounmap(ctrl_reg); + +out: +	of_node_put(ctrl); + +	return err; +} + +static struct hwrng ppc4xx_rng = { +	.name = MODULE_NAME, +	.data_present = ppc4xx_rng_data_present, +	.data_read = ppc4xx_rng_data_read, +}; + +static int ppc4xx_rng_probe(struct platform_device *dev) +{ +	void __iomem *rng_regs; +	int err = 0; + +	rng_regs = of_iomap(dev->dev.of_node, 0); +	if (!rng_regs) +		return -ENODEV; + +	err = ppc4xx_rng_enable(1); +	if (err) +		return err; + +	out_le32(rng_regs + PPC4XX_TRNG_CTRL, PPC4XX_TRNG_CTRL_DALM); +	ppc4xx_rng.priv = (unsigned long) rng_regs; + +	err = hwrng_register(&ppc4xx_rng); + +	return err; +} + +static int ppc4xx_rng_remove(struct platform_device *dev) +{ +	void __iomem *rng_regs = (void __iomem *) ppc4xx_rng.priv; + +	hwrng_unregister(&ppc4xx_rng); +	ppc4xx_rng_enable(0); +	iounmap(rng_regs); + +	return 0; +} + +static struct of_device_id ppc4xx_rng_match[] = { +	{ .compatible = "ppc4xx-rng", }, +	{ .compatible = "amcc,ppc460ex-rng", }, +	{ .compatible = "amcc,ppc440epx-rng", }, +	{}, +}; + +static struct platform_driver ppc4xx_rng_driver = { +	.driver = { +		.name = MODULE_NAME, +		.owner = THIS_MODULE, +		.of_match_table = ppc4xx_rng_match, +	}, +	.probe = ppc4xx_rng_probe, +	.remove = ppc4xx_rng_remove, +}; + +module_platform_driver(ppc4xx_rng_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Josh Boyer <jwboyer@linux.vnet.ibm.com>"); +MODULE_DESCRIPTION("HW RNG driver for PPC 4xx processors"); diff --git a/drivers/char/hw_random/pseries-rng.c b/drivers/char/hw_random/pseries-rng.c new file mode 100644 index 00000000000..ab7ffdec0ec --- /dev/null +++ b/drivers/char/hw_random/pseries-rng.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2010 Michael Neuling IBM Corporation + * + * Driver for the pseries hardware RNG for POWER7+ and above + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/hw_random.h> +#include <asm/vio.h> + + +static int pseries_rng_data_read(struct hwrng *rng, u32 *data) +{ +	int rc; + +	rc = plpar_hcall(H_RANDOM, (unsigned long *)data); +	if (rc != H_SUCCESS) { +		pr_err_ratelimited("H_RANDOM call failed %d\n", rc); +		return -EIO; +	} + +	/* The hypervisor interface returns 64 bits */ +	return 8; +} + +/** + * pseries_rng_get_desired_dma - Return desired DMA allocate for CMO operations + * + * This is a required function for a driver to operate in a CMO environment + * but this device does not make use of DMA allocations, return 0. + * + * Return value: + *	Number of bytes of IO data the driver will need to perform well -> 0 + */ +static unsigned long pseries_rng_get_desired_dma(struct vio_dev *vdev) +{ +	return 0; +}; + +static struct hwrng pseries_rng = { +	.name		= KBUILD_MODNAME, +	.data_read	= pseries_rng_data_read, +}; + +static int __init pseries_rng_probe(struct vio_dev *dev, +		const struct vio_device_id *id) +{ +	return hwrng_register(&pseries_rng); +} + +static int __exit pseries_rng_remove(struct vio_dev *dev) +{ +	hwrng_unregister(&pseries_rng); +	return 0; +} + +static struct vio_device_id pseries_rng_driver_ids[] = { +	{ "ibm,random-v1", "ibm,random"}, +	{ "", "" } +}; +MODULE_DEVICE_TABLE(vio, pseries_rng_driver_ids); + +static struct vio_driver pseries_rng_driver = { +	.name = KBUILD_MODNAME, +	.probe = pseries_rng_probe, +	.remove = pseries_rng_remove, +	.get_desired_dma = pseries_rng_get_desired_dma, +	.id_table = pseries_rng_driver_ids +}; + +static int __init rng_init(void) +{ +	printk(KERN_INFO "Registering IBM pSeries RNG driver\n"); +	return vio_register_driver(&pseries_rng_driver); +} + +module_init(rng_init); + +static void __exit rng_exit(void) +{ +	vio_unregister_driver(&pseries_rng_driver); +} +module_exit(rng_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michael Neuling <mikey@neuling.org>"); +MODULE_DESCRIPTION("H/W RNG driver for IBM pSeries processors"); diff --git a/drivers/char/hw_random/timeriomem-rng.c b/drivers/char/hw_random/timeriomem-rng.c index a94e930575f..b6ab9ac3f34 100644 --- a/drivers/char/hw_random/timeriomem-rng.c +++ b/drivers/char/hw_random/timeriomem-rng.c @@ -23,145 +23,191 @@  #include <linux/module.h>  #include <linux/kernel.h>  #include <linux/platform_device.h> +#include <linux/of.h>  #include <linux/hw_random.h>  #include <linux/io.h> +#include <linux/slab.h>  #include <linux/timeriomem-rng.h>  #include <linux/jiffies.h>  #include <linux/sched.h>  #include <linux/timer.h>  #include <linux/completion.h> -static struct timeriomem_rng_data *timeriomem_rng_data; +struct timeriomem_rng_private_data { +	void __iomem		*io_base; +	unsigned int		expires; +	unsigned int		period; +	unsigned int		present:1; -static void timeriomem_rng_trigger(unsigned long); -static DEFINE_TIMER(timeriomem_rng_timer, timeriomem_rng_trigger, 0, 0); +	struct timer_list	timer; +	struct completion	completion; + +	struct hwrng		timeriomem_rng_ops; +}; + +#define to_rng_priv(rng) \ +		((struct timeriomem_rng_private_data *)rng->priv)  /*   * have data return 1, however return 0 if we have nothing   */  static int timeriomem_rng_data_present(struct hwrng *rng, int wait)  { -	if (rng->priv == 0) -		return 1; +	struct timeriomem_rng_private_data *priv = to_rng_priv(rng); -	if (!wait || timeriomem_rng_data->present) -		return timeriomem_rng_data->present; +	if (!wait || priv->present) +		return priv->present; -	wait_for_completion(&timeriomem_rng_data->completion); +	wait_for_completion(&priv->completion);  	return 1;  }  static int timeriomem_rng_data_read(struct hwrng *rng, u32 *data)  { +	struct timeriomem_rng_private_data *priv = to_rng_priv(rng);  	unsigned long cur;  	s32 delay; -	*data = readl(timeriomem_rng_data->address); +	*data = readl(priv->io_base); -	if (rng->priv != 0) { -		cur = jiffies; +	cur = jiffies; -		delay = cur - timeriomem_rng_timer.expires; -		delay = rng->priv - (delay % rng->priv); +	delay = cur - priv->expires; +	delay = priv->period - (delay % priv->period); -		timeriomem_rng_timer.expires = cur + delay; -		timeriomem_rng_data->present = 0; +	priv->expires = cur + delay; +	priv->present = 0; -		init_completion(&timeriomem_rng_data->completion); -		add_timer(&timeriomem_rng_timer); -	} +	reinit_completion(&priv->completion); +	mod_timer(&priv->timer, priv->expires);  	return 4;  } -static void timeriomem_rng_trigger(unsigned long dummy) +static void timeriomem_rng_trigger(unsigned long data)  { -	timeriomem_rng_data->present = 1; -	complete(&timeriomem_rng_data->completion); -} +	struct timeriomem_rng_private_data *priv +			= (struct timeriomem_rng_private_data *)data; -static struct hwrng timeriomem_rng_ops = { -	.name		= "timeriomem", -	.data_present	= timeriomem_rng_data_present, -	.data_read	= timeriomem_rng_data_read, -	.priv		= 0, -}; +	priv->present = 1; +	complete(&priv->completion); +} -static int __devinit timeriomem_rng_probe(struct platform_device *pdev) +static int timeriomem_rng_probe(struct platform_device *pdev)  { +	struct timeriomem_rng_data *pdata = pdev->dev.platform_data; +	struct timeriomem_rng_private_data *priv;  	struct resource *res; -	int ret; +	int err = 0; +	int period; -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (!pdev->dev.of_node && !pdata) { +		dev_err(&pdev->dev, "timeriomem_rng_data is missing\n"); +		return -EINVAL; +	} +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  	if (!res) -		return -ENOENT; +		return -ENXIO; + +	if (res->start % 4 != 0 || resource_size(res) != 4) { +		dev_err(&pdev->dev, +			"address must be four bytes wide and aligned\n"); +		return -EINVAL; +	} + +	/* Allocate memory for the device structure (and zero it) */ +	priv = devm_kzalloc(&pdev->dev, +			sizeof(struct timeriomem_rng_private_data), GFP_KERNEL); +	if (!priv) +		return -ENOMEM; + +	platform_set_drvdata(pdev, priv); + +	if (pdev->dev.of_node) { +		int i; + +		if (!of_property_read_u32(pdev->dev.of_node, +						"period", &i)) +			period = i; +		else { +			dev_err(&pdev->dev, "missing period\n"); +			return -EINVAL; +		} +	} else { +		period = pdata->period; +	} -	timeriomem_rng_data = pdev->dev.platform_data; +	priv->period = usecs_to_jiffies(period); +	if (priv->period < 1) { +		dev_err(&pdev->dev, "period is less than one jiffy\n"); +		return -EINVAL; +	} + +	priv->expires	= jiffies; +	priv->present	= 1; -	timeriomem_rng_data->address = ioremap(res->start, -						res->end - res->start + 1); -	if (!timeriomem_rng_data->address) -		return -EIO; +	init_completion(&priv->completion); +	complete(&priv->completion); -	if (timeriomem_rng_data->period != 0 -		&& usecs_to_jiffies(timeriomem_rng_data->period) > 0) { -		timeriomem_rng_timer.expires = jiffies; +	setup_timer(&priv->timer, timeriomem_rng_trigger, (unsigned long)priv); -		timeriomem_rng_ops.priv = usecs_to_jiffies( -						timeriomem_rng_data->period); +	priv->timeriomem_rng_ops.name		= dev_name(&pdev->dev); +	priv->timeriomem_rng_ops.data_present	= timeriomem_rng_data_present; +	priv->timeriomem_rng_ops.data_read	= timeriomem_rng_data_read; +	priv->timeriomem_rng_ops.priv		= (unsigned long)priv; + +	priv->io_base = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(priv->io_base)) { +		err = PTR_ERR(priv->io_base); +		goto out_timer;  	} -	timeriomem_rng_data->present = 1; -	ret = hwrng_register(&timeriomem_rng_ops); -	if (ret) -		goto failed; +	err = hwrng_register(&priv->timeriomem_rng_ops); +	if (err) { +		dev_err(&pdev->dev, "problem registering\n"); +		goto out_timer; +	}  	dev_info(&pdev->dev, "32bits from 0x%p @ %dus\n", -			timeriomem_rng_data->address, -			timeriomem_rng_data->period); +			priv->io_base, period);  	return 0; -failed: -	dev_err(&pdev->dev, "problem registering\n"); -	iounmap(timeriomem_rng_data->address); - -	return ret; +out_timer: +	del_timer_sync(&priv->timer); +	return err;  } -static int __devexit timeriomem_rng_remove(struct platform_device *pdev) +static int timeriomem_rng_remove(struct platform_device *pdev)  { -	del_timer_sync(&timeriomem_rng_timer); -	hwrng_unregister(&timeriomem_rng_ops); +	struct timeriomem_rng_private_data *priv = platform_get_drvdata(pdev); + +	hwrng_unregister(&priv->timeriomem_rng_ops); -	iounmap(timeriomem_rng_data->address); +	del_timer_sync(&priv->timer);  	return 0;  } +static const struct of_device_id timeriomem_rng_match[] = { +	{ .compatible = "timeriomem_rng" }, +	{}, +}; +MODULE_DEVICE_TABLE(of, timeriomem_rng_match); +  static struct platform_driver timeriomem_rng_driver = {  	.driver = {  		.name		= "timeriomem_rng",  		.owner		= THIS_MODULE, +		.of_match_table	= timeriomem_rng_match,  	},  	.probe		= timeriomem_rng_probe, -	.remove		= __devexit_p(timeriomem_rng_remove), +	.remove		= timeriomem_rng_remove,  }; -static int __init timeriomem_rng_init(void) -{ -	return platform_driver_register(&timeriomem_rng_driver); -} - -static void __exit timeriomem_rng_exit(void) -{ -	platform_driver_unregister(&timeriomem_rng_driver); -} - -module_init(timeriomem_rng_init); -module_exit(timeriomem_rng_exit); +module_platform_driver(timeriomem_rng_driver);  MODULE_LICENSE("GPL");  MODULE_AUTHOR("Alexander Clouter <alex@digriz.org.uk>"); diff --git a/drivers/char/hw_random/tpm-rng.c b/drivers/char/hw_random/tpm-rng.c new file mode 100644 index 00000000000..d6d448266f0 --- /dev/null +++ b/drivers/char/hw_random/tpm-rng.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 Kent Yoder IBM Corporation + * + * HWRNG interfaces to pull RNG data from a TPM + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/hw_random.h> +#include <linux/tpm.h> + +#define MODULE_NAME "tpm-rng" + +static int tpm_rng_read(struct hwrng *rng, void *data, size_t max, bool wait) +{ +	return tpm_get_random(TPM_ANY_NUM, data, max); +} + +static struct hwrng tpm_rng = { +	.name = MODULE_NAME, +	.read = tpm_rng_read, +}; + +static int __init rng_init(void) +{ +	return hwrng_register(&tpm_rng); +} +module_init(rng_init); + +static void __exit rng_exit(void) +{ +	hwrng_unregister(&tpm_rng); +} +module_exit(rng_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Kent Yoder <key@linux.vnet.ibm.com>"); +MODULE_DESCRIPTION("RNG driver for TPM devices"); diff --git a/drivers/char/hw_random/tx4939-rng.c b/drivers/char/hw_random/tx4939-rng.c index 0bc0cb70210..09c5fbea2b9 100644 --- a/drivers/char/hw_random/tx4939-rng.c +++ b/drivers/char/hw_random/tx4939-rng.c @@ -7,6 +7,7 @@   * License.  See the file "COPYING" in the main directory of this archive   * for more details.   */ +#include <linux/err.h>  #include <linux/module.h>  #include <linux/kernel.h>  #include <linux/init.h> @@ -109,18 +110,13 @@ static int __init tx4939_rng_probe(struct platform_device *dev)  	struct resource *r;  	int i; -	r = platform_get_resource(dev, IORESOURCE_MEM, 0); -	if (!r) -		return -EBUSY;  	rngdev = devm_kzalloc(&dev->dev, sizeof(*rngdev), GFP_KERNEL);  	if (!rngdev)  		return -ENOMEM; -	if (!devm_request_mem_region(&dev->dev, r->start, resource_size(r), -				     dev_name(&dev->dev))) -		return -EBUSY; -	rngdev->base = devm_ioremap(&dev->dev, r->start, resource_size(r)); -	if (!rngdev->base) -		return -EBUSY; +	r = platform_get_resource(dev, IORESOURCE_MEM, 0); +	rngdev->base = devm_ioremap_resource(&dev->dev, r); +	if (IS_ERR(rngdev->base)) +		return PTR_ERR(rngdev->base);  	rngdev->rng.name = dev_name(&dev->dev);  	rngdev->rng.data_present = tx4939_rng_data_present; @@ -156,7 +152,6 @@ static int __exit tx4939_rng_remove(struct platform_device *dev)  	struct tx4939_rng *rngdev = platform_get_drvdata(dev);  	hwrng_unregister(&rngdev->rng); -	platform_set_drvdata(dev, NULL);  	return 0;  } @@ -168,18 +163,7 @@ static struct platform_driver tx4939_rng_driver = {  	.remove = tx4939_rng_remove,  }; -static int __init tx4939rng_init(void) -{ -	return platform_driver_probe(&tx4939_rng_driver, tx4939_rng_probe); -} - -static void __exit tx4939rng_exit(void) -{ -	platform_driver_unregister(&tx4939_rng_driver); -} - -module_init(tx4939rng_init); -module_exit(tx4939rng_exit); +module_platform_driver_probe(tx4939_rng_driver, tx4939_rng_probe);  MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver for TX4939");  MODULE_LICENSE("GPL"); diff --git a/drivers/char/hw_random/via-rng.c b/drivers/char/hw_random/via-rng.c index 794aacb715c..de5a6dcfb3e 100644 --- a/drivers/char/hw_random/via-rng.c +++ b/drivers/char/hw_random/via-rng.c @@ -24,17 +24,18 @@   * warranty of any kind, whether express or implied.   */ +#include <crypto/padlock.h>  #include <linux/module.h>  #include <linux/kernel.h>  #include <linux/hw_random.h>  #include <linux/delay.h> +#include <asm/cpu_device_id.h>  #include <asm/io.h>  #include <asm/msr.h>  #include <asm/cpufeature.h>  #include <asm/i387.h> -#define PFX	KBUILD_MODNAME ": "  enum { @@ -81,8 +82,7 @@ static inline u32 xstore(u32 *addr, u32 edx_in)  	ts_state = irq_ts_save();  	asm(".byte 0x0F,0xA7,0xC0 /* xstore %%edi (addr=%0) */" -		:"=m"(*addr), "=a"(eax_out) -		:"D"(addr), "d"(edx_in)); +		: "=m" (*addr), "=a" (eax_out), "+d" (edx_in), "+D" (addr));  	irq_ts_restore(ts_state);  	return eax_out; @@ -90,8 +90,10 @@ static inline u32 xstore(u32 *addr, u32 edx_in)  static int via_rng_data_present(struct hwrng *rng, int wait)  { +	char buf[16 + PADLOCK_ALIGNMENT - STACK_ALIGN] __attribute__ +		((aligned(STACK_ALIGN))); +	u32 *via_rng_datum = (u32 *)PTR_ALIGN(&buf[0], PADLOCK_ALIGNMENT);  	u32 bytes_out; -	u32 *via_rng_datum = (u32 *)(&rng->priv);  	int i;  	/* We choose the recommended 1-byte-per-instruction RNG rate, @@ -115,6 +117,7 @@ static int via_rng_data_present(struct hwrng *rng, int wait)  			break;  		udelay(10);  	} +	rng->priv = *via_rng_datum;  	return bytes_out ? 1 : 0;  } @@ -218,5 +221,11 @@ static void __exit mod_exit(void)  module_init(mod_init);  module_exit(mod_exit); +static struct x86_cpu_id __maybe_unused via_rng_cpu_id[] = { +	X86_FEATURE_MATCH(X86_FEATURE_XSTORE), +	{} +}; +  MODULE_DESCRIPTION("H/W RNG driver for VIA CPU with PadLock");  MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(x86cpu, via_rng_cpu_id); diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index 75f1cbd61c1..e9b15bc18b4 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -23,92 +23,167 @@  #include <linux/spinlock.h>  #include <linux/virtio.h>  #include <linux/virtio_rng.h> +#include <linux/module.h> + +static DEFINE_IDA(rng_index_ida); + +struct virtrng_info { +	struct virtio_device *vdev; +	struct hwrng hwrng; +	struct virtqueue *vq; +	unsigned int data_avail; +	struct completion have_data; +	bool busy; +	char name[25]; +	int index; +}; -static struct virtqueue *vq; -static unsigned int data_avail; -static DECLARE_COMPLETION(have_data); -static bool busy; +static bool probe_done;  static void random_recv_done(struct virtqueue *vq)  { +	struct virtrng_info *vi = vq->vdev->priv; +  	/* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */ -	if (!virtqueue_get_buf(vq, &data_avail)) +	if (!virtqueue_get_buf(vi->vq, &vi->data_avail))  		return; -	complete(&have_data); +	complete(&vi->have_data);  }  /* The host will fill any buffer we give it with sweet, sweet randomness. */ -static void register_buffer(u8 *buf, size_t size) +static void register_buffer(struct virtrng_info *vi, u8 *buf, size_t size)  {  	struct scatterlist sg;  	sg_init_one(&sg, buf, size);  	/* There should always be room for one buffer. */ -	if (virtqueue_add_buf(vq, &sg, 0, 1, buf) < 0) -		BUG(); +	virtqueue_add_inbuf(vi->vq, &sg, 1, buf, GFP_KERNEL); -	virtqueue_kick(vq); +	virtqueue_kick(vi->vq);  }  static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait)  { +	int ret; +	struct virtrng_info *vi = (struct virtrng_info *)rng->priv; + +	/* +	 * Don't ask host for data till we're setup.  This call can +	 * happen during hwrng_register(), after commit d9e7972619. +	 */ +	if (unlikely(!probe_done)) +		return 0; -	if (!busy) { -		busy = true; -		init_completion(&have_data); -		register_buffer(buf, size); +	if (!vi->busy) { +		vi->busy = true; +		init_completion(&vi->have_data); +		register_buffer(vi, buf, size);  	}  	if (!wait)  		return 0; -	wait_for_completion(&have_data); +	ret = wait_for_completion_killable(&vi->have_data); +	if (ret < 0) +		return ret; -	busy = false; +	vi->busy = false; -	return data_avail; +	return vi->data_avail;  }  static void virtio_cleanup(struct hwrng *rng)  { -	if (busy) -		wait_for_completion(&have_data); +	struct virtrng_info *vi = (struct virtrng_info *)rng->priv; + +	if (vi->busy) +		wait_for_completion(&vi->have_data);  } +static int probe_common(struct virtio_device *vdev) +{ +	int err, index; +	struct virtrng_info *vi = NULL; -static struct hwrng virtio_hwrng = { -	.name		= "virtio", -	.cleanup	= virtio_cleanup, -	.read		= virtio_read, -}; +	vi = kzalloc(sizeof(struct virtrng_info), GFP_KERNEL); +	if (!vi) +		return -ENOMEM; -static int virtrng_probe(struct virtio_device *vdev) -{ -	int err; +	vi->index = index = ida_simple_get(&rng_index_ida, 0, 0, GFP_KERNEL); +	if (index < 0) { +		kfree(vi); +		return index; +	} +	sprintf(vi->name, "virtio_rng.%d", index); +	init_completion(&vi->have_data); + +	vi->hwrng = (struct hwrng) { +		.read = virtio_read, +		.cleanup = virtio_cleanup, +		.priv = (unsigned long)vi, +		.name = vi->name, +	}; +	vdev->priv = vi;  	/* We expect a single virtqueue. */ -	vq = virtio_find_single_vq(vdev, random_recv_done, "input"); -	if (IS_ERR(vq)) -		return PTR_ERR(vq); +	vi->vq = virtio_find_single_vq(vdev, random_recv_done, "input"); +	if (IS_ERR(vi->vq)) { +		err = PTR_ERR(vi->vq); +		vi->vq = NULL; +		kfree(vi); +		ida_simple_remove(&rng_index_ida, index); +		return err; +	} -	err = hwrng_register(&virtio_hwrng); +	err = hwrng_register(&vi->hwrng);  	if (err) {  		vdev->config->del_vqs(vdev); +		vi->vq = NULL; +		kfree(vi); +		ida_simple_remove(&rng_index_ida, index);  		return err;  	} +	probe_done = true;  	return 0;  } -static void __devexit virtrng_remove(struct virtio_device *vdev) +static void remove_common(struct virtio_device *vdev)  { +	struct virtrng_info *vi = vdev->priv;  	vdev->config->reset(vdev); -	hwrng_unregister(&virtio_hwrng); +	vi->busy = false; +	hwrng_unregister(&vi->hwrng);  	vdev->config->del_vqs(vdev); +	ida_simple_remove(&rng_index_ida, vi->index); +	kfree(vi); +} + +static int virtrng_probe(struct virtio_device *vdev) +{ +	return probe_common(vdev);  } +static void virtrng_remove(struct virtio_device *vdev) +{ +	remove_common(vdev); +} + +#ifdef CONFIG_PM_SLEEP +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 }, @@ -119,21 +194,14 @@ static struct virtio_driver virtio_rng_driver = {  	.driver.owner =	THIS_MODULE,  	.id_table =	id_table,  	.probe =	virtrng_probe, -	.remove =	__devexit_p(virtrng_remove), +	.remove =	virtrng_remove, +#ifdef CONFIG_PM_SLEEP +	.freeze =	virtrng_freeze, +	.restore =	virtrng_restore, +#endif  }; -static int __init init(void) -{ -	return register_virtio_driver(&virtio_rng_driver); -} - -static void __exit fini(void) -{ -	unregister_virtio_driver(&virtio_rng_driver); -} -module_init(init); -module_exit(fini); - +module_virtio_driver(virtio_rng_driver);  MODULE_DEVICE_TABLE(virtio, id_table);  MODULE_DESCRIPTION("Virtio random number driver");  MODULE_LICENSE("GPL");  | 
