M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions
+265
View File
@@ -0,0 +1,265 @@
#
# Hardware Random Number Generator (RNG) configuration
#
config HW_RANDOM
tristate "Hardware Random Number Generator Core support"
default m
---help---
Hardware Random Number Generator Core infrastructure.
To compile this driver as a module, choose M here: the
module will be called rng-core. This provides a device
that's usually called /dev/hw_random, and which exposes one
of possibly several hardware random number generators.
These hardware random number generators do not feed directly
into the kernel's random number generator. That is usually
handled by the "rngd" daemon. Documentation/hw_random.txt
has more information.
If unsure, say Y.
config HW_RANDOM_TIMERIOMEM
tristate "Timer IOMEM HW Random Number Generator support"
depends on HW_RANDOM && HAS_IOMEM
---help---
This driver provides kernel-side support for a generic Random
Number Generator used by reading a 'dumb' iomem address that
is to be read no faster than, for example, once a second;
the default FPGA bitstream on the TS-7800 has such functionality.
To compile this driver as a module, choose M here: the
module will be called timeriomem-rng.
If unsure, say Y.
config HW_RANDOM_INTEL
tristate "Intel HW Random Number Generator support"
depends on HW_RANDOM && (X86 || IA64) && PCI
default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
Generator hardware found on Intel i8xx-based motherboards.
To compile this driver as a module, choose M here: the
module will be called intel-rng.
If unsure, say Y.
config HW_RANDOM_AMD
tristate "AMD HW Random Number Generator support"
depends on HW_RANDOM && (X86 || PPC_MAPLE) && PCI
default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
Generator hardware found on AMD 76x-based motherboards.
To compile this driver as a module, choose M here: the
module will be called amd-rng.
If unsure, say Y.
config HW_RANDOM_ATMEL
tristate "Atmel Random Number Generator support"
depends on HW_RANDOM && ARCH_AT91SAM9G45
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_GEODE
tristate "AMD Geode HW Random Number Generator support"
depends on HW_RANDOM && X86_32 && PCI
default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
Generator hardware found on the AMD Geode LX.
To compile this driver as a module, choose M here: the
module will be called geode-rng.
If unsure, say Y.
config HW_RANDOM_N2RNG
tristate "Niagara2 Random Number Generator support"
depends on HW_RANDOM && SPARC64
default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
Generator hardware found on Niagara2 cpus.
To compile this driver as a module, choose M here: the
module will be called n2-rng.
If unsure, say Y.
config HW_RANDOM_VIA
tristate "VIA HW Random Number Generator support"
depends on HW_RANDOM && X86
default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
Generator hardware found on VIA based motherboards.
To compile this driver as a module, choose M here: the
module will be called via-rng.
If unsure, say Y.
config HW_RANDOM_IXP4XX
tristate "Intel IXP4xx NPU HW Random Number Generator support"
depends on HW_RANDOM && ARCH_IXP4XX
default HW_RANDOM
---help---
This driver provides kernel-side support for the Random
Number Generator hardware found on the Intel IXP4xx NPU.
To compile this driver as a module, choose M here: the
module will be called ixp4xx-rng.
If unsure, say Y.
config HW_RANDOM_OMAP
tristate "OMAP Random Number Generator support"
depends on HW_RANDOM && (ARCH_OMAP16XX || ARCH_OMAP2)
default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
Generator hardware found on OMAP16xx and OMAP24xx 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_OCTEON
tristate "Octeon Random Number Generator support"
depends on HW_RANDOM && CPU_CAVIUM_OCTEON
default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
Generator hardware found on Octeon processors.
To compile this driver as a module, choose M here: the
module will be called octeon-rng.
If unsure, say Y.
config HW_RANDOM_PASEMI
tristate "PA Semi HW Random Number Generator support"
depends on HW_RANDOM && PPC_PASEMI
default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
Generator hardware found on PA Semi PWRficient SoCs.
To compile this driver as a module, choose M here: the
module will be called pasemi-rng.
If unsure, say Y.
config HW_RANDOM_VIRTIO
tristate "VirtIO Random Number Generator support"
depends on HW_RANDOM && VIRTIO
---help---
This driver provides kernel-side support for the virtual Random Number
Generator hardware.
To compile this driver as a module, choose M here: the
module will be called virtio-rng. If unsure, say N.
config HW_RANDOM_TX4939
tristate "TX4939 Random Number Generator support"
depends on HW_RANDOM && SOC_TX4939
default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
Generator hardware found on TX4939 SoC.
To compile this driver as a module, choose M here: the
module will be called tx4939-rng.
If unsure, say Y.
config HW_RANDOM_MXC_RNGA
tristate "Freescale i.MX RNGA Random Number Generator"
depends on HW_RANDOM && ARCH_HAS_RNGA
---help---
This driver provides kernel-side support for the Random Number
Generator hardware found on Freescale i.MX processors.
To compile this driver as a module, choose M here: the
module will be called mxc-rnga.
If unsure, say Y.
config HW_RANDOM_NOMADIK
tristate "ST-Ericsson Nomadik Random Number Generator support"
depends on HW_RANDOM && PLAT_NOMADIK
---help---
This driver provides kernel-side support for the Random Number
Generator hardware found on ST-Ericsson SoCs (8815 and 8500).
To compile this driver as a module, choose M here: the
module will be called nomadik-rng.
If unsure, say Y.
config HW_RANDOM_PICOXCELL
tristate "Picochip picoXcell true random number generator support"
depends on HW_RANDOM && ARCH_PICOXCELL && PICOXCELL_PC3X3
---help---
This driver provides kernel-side support for the Random Number
Generator hardware found on Picochip PC3x3 and later devices.
To compile this driver as a module, choose M here: the
module will be called picoxcell-rng.
If unsure, say Y.
config HW_RANDOM_PPC4XX
tristate "PowerPC 4xx generic true random number generator support"
depends on HW_RANDOM && PPC && 4xx
---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 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.
config HW_RANDOM_MSM
tristate "Qualcomm MSM Random Number Generator support"
depends on HW_RANDOM && ARCH_MSM
default n
---help---
This driver provides kernel-side support for the Random Number
Generator hardware found on Qualcomm MSM SoCs.
To compile this driver as a module, choose M here: the
module will be called msm_rng.
If unsure, say Y.
+25
View File
@@ -0,0 +1,25 @@
#
# Makefile for HW Random Number Generator (RNG) device drivers.
#
obj-$(CONFIG_HW_RANDOM) += rng-core.o
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_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_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_PICOXCELL) += picoxcell-rng.o
obj-$(CONFIG_HW_RANDOM_PPC4XX) += ppc4xx-rng.o
obj-$(CONFIG_HW_RANDOM_MSM) += msm_rng.o
+169
View File
@@ -0,0 +1,169 @@
/*
* RNG driver for AMD RNGs
*
* Copyright 2005 (c) MontaVista Software, Inc.
*
* with the majority of the code coming from:
*
* Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
* (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
*
* derived from
*
* Hardware driver for the AMD 768 Random Number Generator (RNG)
* (c) Copyright 2001 Red Hat Inc
*
* derived from
*
* Hardware driver for Intel i810 Random Number Generator (RNG)
* Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.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.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/hw_random.h>
#include <linux/delay.h>
#include <asm/io.h>
#define PFX KBUILD_MODNAME ": "
/*
* Data for PCI driver interface
*
* This data only exists for exporting the supported
* PCI ids via MODULE_DEVICE_TABLE. We do not actually
* register a pci_driver, because someone else might one day
* want to register another driver on the same PCI id.
*/
static const struct pci_device_id pci_tbl[] = {
{ PCI_VDEVICE(AMD, 0x7443), 0, },
{ PCI_VDEVICE(AMD, 0x746b), 0, },
{ 0, }, /* terminate list */
};
MODULE_DEVICE_TABLE(pci, pci_tbl);
static struct pci_dev *amd_pdev;
static int amd_rng_data_present(struct hwrng *rng, int wait)
{
u32 pmbase = (u32)rng->priv;
int data, i;
for (i = 0; i < 20; i++) {
data = !!(inl(pmbase + 0xF4) & 1);
if (data || !wait)
break;
udelay(10);
}
return data;
}
static int amd_rng_data_read(struct hwrng *rng, u32 *data)
{
u32 pmbase = (u32)rng->priv;
*data = inl(pmbase + 0xF0);
return 4;
}
static int amd_rng_init(struct hwrng *rng)
{
u8 rnen;
pci_read_config_byte(amd_pdev, 0x40, &rnen);
rnen |= (1 << 7); /* RNG on */
pci_write_config_byte(amd_pdev, 0x40, rnen);
pci_read_config_byte(amd_pdev, 0x41, &rnen);
rnen |= (1 << 7); /* PMIO enable */
pci_write_config_byte(amd_pdev, 0x41, rnen);
return 0;
}
static void amd_rng_cleanup(struct hwrng *rng)
{
u8 rnen;
pci_read_config_byte(amd_pdev, 0x40, &rnen);
rnen &= ~(1 << 7); /* RNG off */
pci_write_config_byte(amd_pdev, 0x40, rnen);
}
static struct hwrng amd_rng = {
.name = "amd",
.init = amd_rng_init,
.cleanup = amd_rng_cleanup,
.data_present = amd_rng_data_present,
.data_read = amd_rng_data_read,
};
static int __init mod_init(void)
{
int err = -ENODEV;
struct pci_dev *pdev = NULL;
const struct pci_device_id *ent;
u32 pmbase;
for_each_pci_dev(pdev) {
ent = pci_match_id(pci_tbl, pdev);
if (ent)
goto found;
}
/* Device not found. */
goto out;
found:
err = pci_read_config_dword(pdev, 0x58, &pmbase);
if (err)
goto out;
err = -EIO;
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;
printk(KERN_INFO "AMD768 RNG detected\n");
err = hwrng_register(&amd_rng);
if (err) {
printk(KERN_ERR PFX "RNG registering failed (%d)\n",
err);
release_region(pmbase + 0xF0, 8);
goto out;
}
out:
return err;
}
static void __exit mod_exit(void)
{
u32 pmbase = (unsigned long)amd_rng.priv;
release_region(pmbase + 0xF0, 8);
hwrng_unregister(&amd_rng);
}
module_init(mod_init);
module_exit(mod_exit);
MODULE_AUTHOR("The Linux Kernel team");
MODULE_DESCRIPTION("H/W RNG driver for AMD chipsets");
MODULE_LICENSE("GPL");
+148
View File
@@ -0,0 +1,148 @@
/*
* 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_ODATA) & 1) {
*data = readl(trng->base + TRNG_ODATA);
return 4;
} else
return 0;
}
static int atmel_trng_probe(struct platform_device *pdev)
{
struct atmel_trng *trng;
struct resource *res;
int ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -EINVAL;
trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
if (!trng)
return -ENOMEM;
if (!devm_request_mem_region(&pdev->dev, res->start,
resource_size(res), pdev->name))
return -EBUSY;
trng->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!trng->base)
return -EBUSY;
trng->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(trng->clk))
return PTR_ERR(trng->clk);
ret = clk_enable(trng->clk);
if (ret)
goto err_enable;
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);
err_enable:
clk_put(trng->clk);
return ret;
}
static int __devexit 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);
clk_put(trng->clk);
platform_set_drvdata(pdev, NULL);
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 = __devexit_p(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");
+372
View File
@@ -0,0 +1,372 @@
/*
Added support for the AMD Geode LX RNG
(c) Copyright 2004-2005 Advanced Micro Devices, Inc.
derived from
Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
(c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
derived from
Hardware driver for the AMD 768 Random Number Generator (RNG)
(c) Copyright 2001 Red Hat Inc <alan@redhat.com>
derived from
Hardware driver for Intel i810 Random Number Generator (RNG)
Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
Added generic RNG API
Copyright 2006 Michael Buesch <m@bues.ch>
Copyright 2005 (c) MontaVista Software, Inc.
Please read Documentation/hw_random.txt for details on use.
----------------------------------------------------------
This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.
*/
#include <linux/device.h>
#include <linux/hw_random.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#define RNG_MODULE_NAME "hw_random"
#define PFX RNG_MODULE_NAME ": "
#define RNG_MISCDEV_MINOR 183 /* official */
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 inline int hwrng_init(struct hwrng *rng)
{
if (!rng->init)
return 0;
return rng->init(rng);
}
static inline void hwrng_cleanup(struct hwrng *rng)
{
if (rng && rng->cleanup)
rng->cleanup(rng);
}
static int rng_dev_open(struct inode *inode, struct file *filp)
{
/* enforce read-only access to this chrdev */
if ((filp->f_mode & FMODE_READ) == 0)
return -EINVAL;
if (filp->f_mode & FMODE_WRITE)
return -EINVAL;
return 0;
}
static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size,
int wait) {
int present;
if (rng->read)
return rng->read(rng, (void *)buffer, size, wait);
if (rng->data_present)
present = rng->data_present(rng, wait);
else
present = 1;
if (present)
return rng->data_read(rng, (u32 *)buffer);
return 0;
}
static ssize_t rng_dev_read(struct file *filp, char __user *buf,
size_t size, loff_t *offp)
{
ssize_t ret = 0;
int err = 0;
int bytes_read, len;
while (size) {
if (mutex_lock_interruptible(&rng_mutex)) {
err = -ERESTARTSYS;
goto out;
}
if (!current_rng) {
err = -ENODEV;
goto out_unlock;
}
if (!data_avail) {
bytes_read = rng_get_data(current_rng, rng_buffer,
sizeof(rng_buffer),
!(filp->f_flags & O_NONBLOCK));
if (bytes_read < 0) {
err = bytes_read;
goto out_unlock;
}
data_avail = bytes_read;
}
if (!data_avail) {
if (filp->f_flags & O_NONBLOCK) {
err = -EAGAIN;
goto out_unlock;
}
} else {
len = data_avail;
if (len > size)
len = size;
data_avail -= len;
if (copy_to_user(buf + ret, rng_buffer + data_avail,
len)) {
err = -EFAULT;
goto out_unlock;
}
size -= len;
ret += len;
}
mutex_unlock(&rng_mutex);
if (need_resched())
schedule_timeout_interruptible(1);
if (signal_pending(current)) {
err = -ERESTARTSYS;
goto out;
}
}
out:
return ret ? : err;
out_unlock:
mutex_unlock(&rng_mutex);
goto out;
}
static const struct file_operations rng_chrdev_ops = {
.owner = THIS_MODULE,
.open = rng_dev_open,
.read = rng_dev_read,
.llseek = noop_llseek,
};
static struct miscdevice rng_miscdev = {
.minor = RNG_MISCDEV_MINOR,
.name = RNG_MODULE_NAME,
.nodename = "hwrng",
.fops = &rng_chrdev_ops,
};
static ssize_t hwrng_attr_current_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
int err;
struct hwrng *rng;
err = mutex_lock_interruptible(&rng_mutex);
if (err)
return -ERESTARTSYS;
err = -ENODEV;
list_for_each_entry(rng, &rng_list, list) {
if (strcmp(rng->name, buf) == 0) {
if (rng == current_rng) {
err = 0;
break;
}
err = hwrng_init(rng);
if (err)
break;
hwrng_cleanup(current_rng);
current_rng = rng;
err = 0;
break;
}
}
mutex_unlock(&rng_mutex);
return err ? : len;
}
static ssize_t hwrng_attr_current_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int err;
ssize_t ret;
const char *name = "none";
err = mutex_lock_interruptible(&rng_mutex);
if (err)
return -ERESTARTSYS;
if (current_rng)
name = current_rng->name;
ret = snprintf(buf, PAGE_SIZE, "%s\n", name);
mutex_unlock(&rng_mutex);
return ret;
}
static ssize_t hwrng_attr_available_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int err;
ssize_t ret = 0;
struct hwrng *rng;
err = mutex_lock_interruptible(&rng_mutex);
if (err)
return -ERESTARTSYS;
buf[0] = '\0';
list_for_each_entry(rng, &rng_list, list) {
strncat(buf, rng->name, PAGE_SIZE - ret - 1);
ret += strlen(rng->name);
strncat(buf, " ", PAGE_SIZE - ret - 1);
ret++;
}
strncat(buf, "\n", PAGE_SIZE - ret - 1);
ret++;
mutex_unlock(&rng_mutex);
return ret;
}
static DEVICE_ATTR(rng_current, S_IRUGO | S_IWUSR,
hwrng_attr_current_show,
hwrng_attr_current_store);
static DEVICE_ATTR(rng_available, S_IRUGO,
hwrng_attr_available_show,
NULL);
static void unregister_miscdev(void)
{
device_remove_file(rng_miscdev.this_device, &dev_attr_rng_available);
device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current);
misc_deregister(&rng_miscdev);
}
static int register_miscdev(void)
{
int err;
err = misc_register(&rng_miscdev);
if (err)
goto out;
err = device_create_file(rng_miscdev.this_device,
&dev_attr_rng_current);
if (err)
goto err_misc_dereg;
err = device_create_file(rng_miscdev.this_device,
&dev_attr_rng_available);
if (err)
goto err_remove_current;
out:
return err;
err_remove_current:
device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current);
err_misc_dereg:
misc_deregister(&rng_miscdev);
goto out;
}
int hwrng_register(struct hwrng *rng)
{
int must_register_misc;
int err = -EINVAL;
struct hwrng *old_rng, *tmp;
if (rng->name == NULL ||
(rng->data_read == NULL && rng->read == NULL))
goto out;
mutex_lock(&rng_mutex);
/* Must not register two RNGs with the same name. */
err = -EEXIST;
list_for_each_entry(tmp, &rng_list, list) {
if (strcmp(tmp->name, rng->name) == 0)
goto out_unlock;
}
must_register_misc = (current_rng == NULL);
old_rng = current_rng;
if (!old_rng) {
err = hwrng_init(rng);
if (err)
goto out_unlock;
current_rng = rng;
}
err = 0;
if (must_register_misc) {
err = register_miscdev();
if (err) {
if (!old_rng) {
hwrng_cleanup(rng);
current_rng = NULL;
}
goto out_unlock;
}
}
INIT_LIST_HEAD(&rng->list);
list_add_tail(&rng->list, &rng_list);
out_unlock:
mutex_unlock(&rng_mutex);
out:
return err;
}
EXPORT_SYMBOL_GPL(hwrng_register);
void hwrng_unregister(struct hwrng *rng)
{
int err;
mutex_lock(&rng_mutex);
list_del(&rng->list);
if (current_rng == rng) {
hwrng_cleanup(rng);
if (list_empty(&rng_list)) {
current_rng = NULL;
} else {
current_rng = list_entry(rng_list.prev, struct hwrng, list);
err = hwrng_init(current_rng);
if (err)
current_rng = NULL;
}
}
if (list_empty(&rng_list))
unregister_miscdev();
mutex_unlock(&rng_mutex);
}
EXPORT_SYMBOL_GPL(hwrng_unregister);
MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver");
MODULE_LICENSE("GPL");
+139
View File
@@ -0,0 +1,139 @@
/*
* RNG driver for AMD Geode RNGs
*
* Copyright 2005 (c) MontaVista Software, Inc.
*
* with the majority of the code coming from:
*
* Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
* (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
*
* derived from
*
* Hardware driver for the AMD 768 Random Number Generator (RNG)
* (c) Copyright 2001 Red Hat Inc
*
* derived from
*
* Hardware driver for Intel i810 Random Number Generator (RNG)
* Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.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.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/hw_random.h>
#include <linux/delay.h>
#include <asm/io.h>
#define PFX KBUILD_MODNAME ": "
#define GEODE_RNG_DATA_REG 0x50
#define GEODE_RNG_STATUS_REG 0x54
/*
* Data for PCI driver interface
*
* This data only exists for exporting the supported
* PCI ids via MODULE_DEVICE_TABLE. We do not actually
* register a pci_driver, because someone else might one day
* want to register another driver on the same PCI id.
*/
static const struct pci_device_id pci_tbl[] = {
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_LX_AES), 0, },
{ 0, }, /* terminate list */
};
MODULE_DEVICE_TABLE(pci, pci_tbl);
static int geode_rng_data_read(struct hwrng *rng, u32 *data)
{
void __iomem *mem = (void __iomem *)rng->priv;
*data = readl(mem + GEODE_RNG_DATA_REG);
return 4;
}
static int geode_rng_data_present(struct hwrng *rng, int wait)
{
void __iomem *mem = (void __iomem *)rng->priv;
int data, i;
for (i = 0; i < 20; i++) {
data = !!(readl(mem + GEODE_RNG_STATUS_REG));
if (data || !wait)
break;
udelay(10);
}
return data;
}
static struct hwrng geode_rng = {
.name = "geode",
.data_present = geode_rng_data_present,
.data_read = geode_rng_data_read,
};
static int __init mod_init(void)
{
int err = -ENODEV;
struct pci_dev *pdev = NULL;
const struct pci_device_id *ent;
void __iomem *mem;
unsigned long rng_base;
for_each_pci_dev(pdev) {
ent = pci_match_id(pci_tbl, pdev);
if (ent)
goto found;
}
/* Device not found. */
goto out;
found:
rng_base = pci_resource_start(pdev, 0);
if (rng_base == 0)
goto out;
err = -ENOMEM;
mem = ioremap(rng_base, 0x58);
if (!mem)
goto out;
geode_rng.priv = (unsigned long)mem;
printk(KERN_INFO "AMD Geode RNG detected\n");
err = hwrng_register(&geode_rng);
if (err) {
printk(KERN_ERR PFX "RNG registering failed (%d)\n",
err);
goto err_unmap;
}
out:
return err;
err_unmap:
iounmap(mem);
goto out;
}
static void __exit mod_exit(void)
{
void __iomem *mem = (void __iomem *)geode_rng.priv;
hwrng_unregister(&geode_rng);
iounmap(mem);
}
module_init(mod_init);
module_exit(mod_exit);
MODULE_DESCRIPTION("H/W RNG driver for AMD Geode LX CPUs");
MODULE_LICENSE("GPL");
+419
View File
@@ -0,0 +1,419 @@
/*
* RNG driver for Intel RNGs
*
* Copyright 2005 (c) MontaVista Software, Inc.
*
* with the majority of the code coming from:
*
* Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
* (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
*
* derived from
*
* Hardware driver for the AMD 768 Random Number Generator (RNG)
* (c) Copyright 2001 Red Hat Inc
*
* derived from
*
* Hardware driver for Intel i810 Random Number Generator (RNG)
* Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.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.
*/
#include <linux/hw_random.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/stop_machine.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <asm/io.h>
#define PFX KBUILD_MODNAME ": "
/*
* RNG registers
*/
#define INTEL_RNG_HW_STATUS 0
#define INTEL_RNG_PRESENT 0x40
#define INTEL_RNG_ENABLED 0x01
#define INTEL_RNG_STATUS 1
#define INTEL_RNG_DATA_PRESENT 0x01
#define INTEL_RNG_DATA 2
/*
* Magic address at which Intel PCI bridges locate the RNG
*/
#define INTEL_RNG_ADDR 0xFFBC015F
#define INTEL_RNG_ADDR_LEN 3
/*
* LPC bridge PCI config space registers
*/
#define FWH_DEC_EN1_REG_OLD 0xe3
#define FWH_DEC_EN1_REG_NEW 0xd9 /* high byte of 16-bit register */
#define FWH_F8_EN_MASK 0x80
#define BIOS_CNTL_REG_OLD 0x4e
#define BIOS_CNTL_REG_NEW 0xdc
#define BIOS_CNTL_WRITE_ENABLE_MASK 0x01
#define BIOS_CNTL_LOCK_ENABLE_MASK 0x02
/*
* Magic address at which Intel Firmware Hubs get accessed
*/
#define INTEL_FWH_ADDR 0xffff0000
#define INTEL_FWH_ADDR_LEN 2
/*
* Intel Firmware Hub command codes (write to any address inside the device)
*/
#define INTEL_FWH_RESET_CMD 0xff /* aka READ_ARRAY */
#define INTEL_FWH_READ_ID_CMD 0x90
/*
* Intel Firmware Hub Read ID command result addresses
*/
#define INTEL_FWH_MANUFACTURER_CODE_ADDRESS 0x000000
#define INTEL_FWH_DEVICE_CODE_ADDRESS 0x000001
/*
* Intel Firmware Hub Read ID command result values
*/
#define INTEL_FWH_MANUFACTURER_CODE 0x89
#define INTEL_FWH_DEVICE_CODE_8M 0xac
#define INTEL_FWH_DEVICE_CODE_4M 0xad
/*
* Data for PCI driver interface
*
* This data only exists for exporting the supported
* PCI ids via MODULE_DEVICE_TABLE. We do not actually
* register a pci_driver, because someone else might one day
* want to register another driver on the same PCI id.
*/
static const struct pci_device_id pci_tbl[] = {
/* AA
{ PCI_DEVICE(0x8086, 0x2418) }, */
{ PCI_DEVICE(0x8086, 0x2410) }, /* AA */
/* AB
{ PCI_DEVICE(0x8086, 0x2428) }, */
{ PCI_DEVICE(0x8086, 0x2420) }, /* AB */
/* ??
{ PCI_DEVICE(0x8086, 0x2430) }, */
/* BAM, CAM, DBM, FBM, GxM
{ PCI_DEVICE(0x8086, 0x2448) }, */
{ PCI_DEVICE(0x8086, 0x244c) }, /* BAM */
{ PCI_DEVICE(0x8086, 0x248c) }, /* CAM */
{ PCI_DEVICE(0x8086, 0x24cc) }, /* DBM */
{ PCI_DEVICE(0x8086, 0x2641) }, /* FBM */
{ PCI_DEVICE(0x8086, 0x27b9) }, /* GxM */
{ PCI_DEVICE(0x8086, 0x27bd) }, /* GxM DH */
/* BA, CA, DB, Ex, 6300, Fx, 631x/632x, Gx
{ PCI_DEVICE(0x8086, 0x244e) }, */
{ PCI_DEVICE(0x8086, 0x2440) }, /* BA */
{ PCI_DEVICE(0x8086, 0x2480) }, /* CA */
{ PCI_DEVICE(0x8086, 0x24c0) }, /* DB */
{ PCI_DEVICE(0x8086, 0x24d0) }, /* Ex */
{ PCI_DEVICE(0x8086, 0x25a1) }, /* 6300 */
{ PCI_DEVICE(0x8086, 0x2640) }, /* Fx */
{ PCI_DEVICE(0x8086, 0x2670) }, /* 631x/632x */
{ PCI_DEVICE(0x8086, 0x2671) }, /* 631x/632x */
{ PCI_DEVICE(0x8086, 0x2672) }, /* 631x/632x */
{ PCI_DEVICE(0x8086, 0x2673) }, /* 631x/632x */
{ PCI_DEVICE(0x8086, 0x2674) }, /* 631x/632x */
{ PCI_DEVICE(0x8086, 0x2675) }, /* 631x/632x */
{ PCI_DEVICE(0x8086, 0x2676) }, /* 631x/632x */
{ PCI_DEVICE(0x8086, 0x2677) }, /* 631x/632x */
{ PCI_DEVICE(0x8086, 0x2678) }, /* 631x/632x */
{ PCI_DEVICE(0x8086, 0x2679) }, /* 631x/632x */
{ PCI_DEVICE(0x8086, 0x267a) }, /* 631x/632x */
{ PCI_DEVICE(0x8086, 0x267b) }, /* 631x/632x */
{ PCI_DEVICE(0x8086, 0x267c) }, /* 631x/632x */
{ PCI_DEVICE(0x8086, 0x267d) }, /* 631x/632x */
{ PCI_DEVICE(0x8086, 0x267e) }, /* 631x/632x */
{ PCI_DEVICE(0x8086, 0x267f) }, /* 631x/632x */
{ PCI_DEVICE(0x8086, 0x27b8) }, /* Gx */
/* E
{ PCI_DEVICE(0x8086, 0x245e) }, */
{ PCI_DEVICE(0x8086, 0x2450) }, /* E */
{ 0, }, /* terminate list */
};
MODULE_DEVICE_TABLE(pci, pci_tbl);
static __initdata int no_fwh_detect;
module_param(no_fwh_detect, int, 0);
MODULE_PARM_DESC(no_fwh_detect, "Skip FWH detection:\n"
" positive value - skip if FWH space locked read-only\n"
" negative value - skip always");
static inline u8 hwstatus_get(void __iomem *mem)
{
return readb(mem + INTEL_RNG_HW_STATUS);
}
static inline u8 hwstatus_set(void __iomem *mem,
u8 hw_status)
{
writeb(hw_status, mem + INTEL_RNG_HW_STATUS);
return hwstatus_get(mem);
}
static int intel_rng_data_present(struct hwrng *rng, int wait)
{
void __iomem *mem = (void __iomem *)rng->priv;
int data, i;
for (i = 0; i < 20; i++) {
data = !!(readb(mem + INTEL_RNG_STATUS) &
INTEL_RNG_DATA_PRESENT);
if (data || !wait)
break;
udelay(10);
}
return data;
}
static int intel_rng_data_read(struct hwrng *rng, u32 *data)
{
void __iomem *mem = (void __iomem *)rng->priv;
*data = readb(mem + INTEL_RNG_DATA);
return 1;
}
static int intel_rng_init(struct hwrng *rng)
{
void __iomem *mem = (void __iomem *)rng->priv;
u8 hw_status;
int err = -EIO;
hw_status = hwstatus_get(mem);
/* turn RNG h/w on, if it's off */
if ((hw_status & INTEL_RNG_ENABLED) == 0)
hw_status = hwstatus_set(mem, hw_status | INTEL_RNG_ENABLED);
if ((hw_status & INTEL_RNG_ENABLED) == 0) {
printk(KERN_ERR PFX "cannot enable RNG, aborting\n");
goto out;
}
err = 0;
out:
return err;
}
static void intel_rng_cleanup(struct hwrng *rng)
{
void __iomem *mem = (void __iomem *)rng->priv;
u8 hw_status;
hw_status = hwstatus_get(mem);
if (hw_status & INTEL_RNG_ENABLED)
hwstatus_set(mem, hw_status & ~INTEL_RNG_ENABLED);
else
printk(KERN_WARNING PFX "unusual: RNG already disabled\n");
}
static struct hwrng intel_rng = {
.name = "intel",
.init = intel_rng_init,
.cleanup = intel_rng_cleanup,
.data_present = intel_rng_data_present,
.data_read = intel_rng_data_read,
};
struct intel_rng_hw {
struct pci_dev *dev;
void __iomem *mem;
u8 bios_cntl_off;
u8 bios_cntl_val;
u8 fwh_dec_en1_off;
u8 fwh_dec_en1_val;
};
static int __init intel_rng_hw_init(void *_intel_rng_hw)
{
struct intel_rng_hw *intel_rng_hw = _intel_rng_hw;
u8 mfc, dvc;
/* interrupts disabled in stop_machine call */
if (!(intel_rng_hw->fwh_dec_en1_val & FWH_F8_EN_MASK))
pci_write_config_byte(intel_rng_hw->dev,
intel_rng_hw->fwh_dec_en1_off,
intel_rng_hw->fwh_dec_en1_val |
FWH_F8_EN_MASK);
if (!(intel_rng_hw->bios_cntl_val & BIOS_CNTL_WRITE_ENABLE_MASK))
pci_write_config_byte(intel_rng_hw->dev,
intel_rng_hw->bios_cntl_off,
intel_rng_hw->bios_cntl_val |
BIOS_CNTL_WRITE_ENABLE_MASK);
writeb(INTEL_FWH_RESET_CMD, intel_rng_hw->mem);
writeb(INTEL_FWH_READ_ID_CMD, intel_rng_hw->mem);
mfc = readb(intel_rng_hw->mem + INTEL_FWH_MANUFACTURER_CODE_ADDRESS);
dvc = readb(intel_rng_hw->mem + INTEL_FWH_DEVICE_CODE_ADDRESS);
writeb(INTEL_FWH_RESET_CMD, intel_rng_hw->mem);
if (!(intel_rng_hw->bios_cntl_val &
(BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK)))
pci_write_config_byte(intel_rng_hw->dev,
intel_rng_hw->bios_cntl_off,
intel_rng_hw->bios_cntl_val);
if (!(intel_rng_hw->fwh_dec_en1_val & FWH_F8_EN_MASK))
pci_write_config_byte(intel_rng_hw->dev,
intel_rng_hw->fwh_dec_en1_off,
intel_rng_hw->fwh_dec_en1_val);
if (mfc != INTEL_FWH_MANUFACTURER_CODE ||
(dvc != INTEL_FWH_DEVICE_CODE_8M &&
dvc != INTEL_FWH_DEVICE_CODE_4M)) {
printk(KERN_NOTICE PFX "FWH not detected\n");
return -ENODEV;
}
return 0;
}
static int __init intel_init_hw_struct(struct intel_rng_hw *intel_rng_hw,
struct pci_dev *dev)
{
intel_rng_hw->bios_cntl_val = 0xff;
intel_rng_hw->fwh_dec_en1_val = 0xff;
intel_rng_hw->dev = dev;
/* Check for Intel 82802 */
if (dev->device < 0x2640) {
intel_rng_hw->fwh_dec_en1_off = FWH_DEC_EN1_REG_OLD;
intel_rng_hw->bios_cntl_off = BIOS_CNTL_REG_OLD;
} else {
intel_rng_hw->fwh_dec_en1_off = FWH_DEC_EN1_REG_NEW;
intel_rng_hw->bios_cntl_off = BIOS_CNTL_REG_NEW;
}
pci_read_config_byte(dev, intel_rng_hw->fwh_dec_en1_off,
&intel_rng_hw->fwh_dec_en1_val);
pci_read_config_byte(dev, intel_rng_hw->bios_cntl_off,
&intel_rng_hw->bios_cntl_val);
if ((intel_rng_hw->bios_cntl_val &
(BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK))
== BIOS_CNTL_LOCK_ENABLE_MASK) {
static __initdata /*const*/ char warning[] =
KERN_WARNING
PFX "Firmware space is locked read-only. If you can't or\n"
PFX "don't want to disable this in firmware setup, and if\n"
PFX "you are certain that your system has a functional\n"
PFX "RNG, try using the 'no_fwh_detect' option.\n";
if (no_fwh_detect)
return -ENODEV;
printk(warning);
return -EBUSY;
}
intel_rng_hw->mem = ioremap_nocache(INTEL_FWH_ADDR, INTEL_FWH_ADDR_LEN);
if (intel_rng_hw->mem == NULL)
return -EBUSY;
return 0;
}
static int __init mod_init(void)
{
int err = -ENODEV;
int i;
struct pci_dev *dev = NULL;
void __iomem *mem = mem;
u8 hw_status;
struct intel_rng_hw *intel_rng_hw;
for (i = 0; !dev && pci_tbl[i].vendor; ++i)
dev = pci_get_device(pci_tbl[i].vendor, pci_tbl[i].device,
NULL);
if (!dev)
goto out; /* Device not found. */
if (no_fwh_detect < 0) {
pci_dev_put(dev);
goto fwh_done;
}
intel_rng_hw = kmalloc(sizeof(*intel_rng_hw), GFP_KERNEL);
if (!intel_rng_hw) {
pci_dev_put(dev);
goto out;
}
err = intel_init_hw_struct(intel_rng_hw, dev);
if (err) {
pci_dev_put(dev);
kfree(intel_rng_hw);
if (err == -ENODEV)
goto fwh_done;
goto out;
}
/*
* Since the BIOS code/data is going to disappear from its normal
* location with the Read ID command, all activity on the system
* must be stopped until the state is back to normal.
*
* Use stop_machine because IPIs can be blocked by disabling
* interrupts.
*/
err = stop_machine(intel_rng_hw_init, intel_rng_hw, NULL);
pci_dev_put(dev);
iounmap(intel_rng_hw->mem);
kfree(intel_rng_hw);
if (err)
goto out;
fwh_done:
err = -ENOMEM;
mem = ioremap(INTEL_RNG_ADDR, INTEL_RNG_ADDR_LEN);
if (!mem)
goto out;
intel_rng.priv = (unsigned long)mem;
/* Check for Random Number Generator */
err = -ENODEV;
hw_status = hwstatus_get(mem);
if ((hw_status & INTEL_RNG_PRESENT) == 0) {
iounmap(mem);
goto out;
}
printk(KERN_INFO "Intel 82802 RNG detected\n");
err = hwrng_register(&intel_rng);
if (err) {
printk(KERN_ERR PFX "RNG registering failed (%d)\n",
err);
iounmap(mem);
}
out:
return err;
}
static void __exit mod_exit(void)
{
void __iomem *mem = (void __iomem *)intel_rng.priv;
hwrng_unregister(&intel_rng);
iounmap(mem);
}
module_init(mod_init);
module_exit(mod_exit);
MODULE_DESCRIPTION("H/W RNG driver for Intel chipsets");
MODULE_LICENSE("GPL");
@@ -0,0 +1,72 @@
/*
* drivers/char/hw_random/ixp4xx-rng.c
*
* RNG driver for Intel IXP4xx family of NPUs
*
* Author: Deepak Saxena <dsaxena@plexity.net>
*
* Copyright 2005 (c) MontaVista Software, Inc.
*
* Fixes by Michael Buesch
*
* 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/types.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/bitops.h>
#include <linux/hw_random.h>
#include <asm/io.h>
#include <mach/hardware.h>
static int ixp4xx_rng_data_read(struct hwrng *rng, u32 *buffer)
{
void __iomem * rng_base = (void __iomem *)rng->priv;
*buffer = __raw_readl(rng_base);
return 4;
}
static struct hwrng ixp4xx_rng_ops = {
.name = "ixp4xx",
.data_read = ixp4xx_rng_data_read,
};
static int __init ixp4xx_rng_init(void)
{
void __iomem * rng_base;
int err;
rng_base = ioremap(0x70002100, 4);
if (!rng_base)
return -ENOMEM;
ixp4xx_rng_ops.priv = (unsigned long)rng_base;
err = hwrng_register(&ixp4xx_rng_ops);
if (err)
iounmap(rng_base);
return err;
}
static void __exit ixp4xx_rng_exit(void)
{
void __iomem * rng_base = (void __iomem *)ixp4xx_rng_ops.priv;
hwrng_unregister(&ixp4xx_rng_ops);
iounmap(rng_base);
}
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_LICENSE("GPL");
+271
View File
@@ -0,0 +1,271 @@
/*
* Copyright (c) 2011, 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/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/hw_random.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/types.h>
#include <mach/msm_iomap.h>
#include <mach/socinfo.h>
#define DRIVER_NAME "msm_rng"
/* Device specific register offsets */
#define PRNG_DATA_OUT_OFFSET 0x0000
#define PRNG_STATUS_OFFSET 0x0004
#define PRNG_LFSR_CFG_OFFSET 0x0100
#define PRNG_CONFIG_OFFSET 0x0104
/* Device specific register masks and config values */
#define PRNG_LFSR_CFG_MASK 0xFFFF0000
#define PRNG_LFSR_CFG_CLOCKS 0x0000DDDD
#define PRNG_CONFIG_MASK 0xFFFFFFFD
#define PRNG_HW_ENABLE 0x00000002
#define MAX_HW_FIFO_DEPTH 16 /* FIFO is 16 words deep */
#define MAX_HW_FIFO_SIZE (MAX_HW_FIFO_DEPTH * 4) /* FIFO is 32 bits wide */
struct msm_rng_device {
struct platform_device *pdev;
void __iomem *base;
struct clk *prng_clk;
};
static int msm_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
{
struct msm_rng_device *msm_rng_dev;
struct platform_device *pdev;
void __iomem *base;
size_t maxsize;
size_t currsize = 0;
unsigned long val;
unsigned long *retdata = data;
int ret;
msm_rng_dev = (struct msm_rng_device *)rng->priv;
pdev = msm_rng_dev->pdev;
base = msm_rng_dev->base;
/* 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 < 4)
return 0;
/* enable PRNG clock */
ret = clk_prepare_enable(msm_rng_dev->prng_clk);
if (ret) {
dev_err(&pdev->dev, "failed to enable clock in callback\n");
return 0;
}
/* read random data from h/w */
do {
/* check status bit if data is available */
if (!(readl_relaxed(base + PRNG_STATUS_OFFSET) & 0x00000001))
break; /* no data to read so just bail */
/* read FIFO */
val = readl_relaxed(base + PRNG_DATA_OUT_OFFSET);
if (!val)
break; /* no data to read so just bail */
/* write data back to callers pointer */
*(retdata++) = val;
currsize += 4;
/* make sure we stay on 32bit boundary */
if ((maxsize - currsize) < 4)
break;
} while (currsize < maxsize);
/* vote to turn off clock */
clk_disable_unprepare(msm_rng_dev->prng_clk);
return currsize;
}
static struct hwrng msm_rng = {
.name = DRIVER_NAME,
.read = msm_rng_read,
};
static int __devinit msm_rng_enable_hw(struct msm_rng_device *msm_rng_dev)
{
unsigned long val = 0;
unsigned long reg_val = 0;
int ret = 0;
/* Enable the PRNG CLK */
ret = clk_prepare_enable(msm_rng_dev->prng_clk);
if (ret) {
dev_err(&(msm_rng_dev->pdev)->dev,
"failed to enable clock in probe\n");
return -EPERM;
}
/* Enable PRNG h/w only if it is NOT ON */
val = readl_relaxed(msm_rng_dev->base + PRNG_CONFIG_OFFSET) &
PRNG_HW_ENABLE;
/* PRNG H/W is not ON */
if (val != PRNG_HW_ENABLE) {
val = readl_relaxed(msm_rng_dev->base + PRNG_LFSR_CFG_OFFSET);
val &= PRNG_LFSR_CFG_MASK;
val |= PRNG_LFSR_CFG_CLOCKS;
writel_relaxed(val, msm_rng_dev->base + PRNG_LFSR_CFG_OFFSET);
/* The PRNG CONFIG register should be first written */
mb();
reg_val = readl_relaxed(msm_rng_dev->base + PRNG_CONFIG_OFFSET)
& PRNG_CONFIG_MASK;
reg_val |= PRNG_HW_ENABLE;
writel_relaxed(reg_val, msm_rng_dev->base + PRNG_CONFIG_OFFSET);
/* The PRNG clk should be disabled only after we enable the
* PRNG h/w by writing to the PRNG CONFIG register.
*/
mb();
}
clk_disable_unprepare(msm_rng_dev->prng_clk);
return 0;
}
static int __devinit msm_rng_probe(struct platform_device *pdev)
{
struct resource *res;
struct msm_rng_device *msm_rng_dev = NULL;
void __iomem *base = NULL;
int error = 0;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "invalid address\n");
error = -EFAULT;
goto err_exit;
}
msm_rng_dev = kzalloc(sizeof(msm_rng_dev), GFP_KERNEL);
if (!msm_rng_dev) {
dev_err(&pdev->dev, "cannot allocate memory\n");
error = -ENOMEM;
goto err_exit;
}
base = ioremap(res->start, resource_size(res));
if (!base) {
dev_err(&pdev->dev, "ioremap failed\n");
error = -ENOMEM;
goto err_iomap;
}
msm_rng_dev->base = base;
/* create a handle for clock control */
if ((pdev->dev.of_node) && (of_property_read_bool(pdev->dev.of_node,
"qcom,msm-rng-iface-clk")))
msm_rng_dev->prng_clk = clk_get(&pdev->dev,
"iface_clk");
else
msm_rng_dev->prng_clk = clk_get(&pdev->dev, "core_clk");
if (IS_ERR(msm_rng_dev->prng_clk)) {
dev_err(&pdev->dev, "failed to register clock source\n");
error = -EPERM;
goto err_clk_get;
}
/* save away pdev and register driver data */
msm_rng_dev->pdev = pdev;
platform_set_drvdata(pdev, msm_rng_dev);
/* Enable rng h/w */
error = msm_rng_enable_hw(msm_rng_dev);
if (error)
goto rollback_clk;
/* register with hwrng framework */
msm_rng.priv = (unsigned long) msm_rng_dev;
error = hwrng_register(&msm_rng);
if (error) {
dev_err(&pdev->dev, "failed to register hwrng\n");
error = -EPERM;
goto rollback_clk;
}
return 0;
rollback_clk:
clk_put(msm_rng_dev->prng_clk);
err_clk_get:
iounmap(msm_rng_dev->base);
err_iomap:
kfree(msm_rng_dev);
err_exit:
return error;
}
static int __devexit msm_rng_remove(struct platform_device *pdev)
{
struct msm_rng_device *msm_rng_dev = platform_get_drvdata(pdev);
hwrng_unregister(&msm_rng);
clk_put(msm_rng_dev->prng_clk);
iounmap(msm_rng_dev->base);
platform_set_drvdata(pdev, NULL);
kfree(msm_rng_dev);
return 0;
}
static struct of_device_id qrng_match[] = {
{ .compatible = "qcom,msm-rng",
},
{}
};
static struct platform_driver rng_driver = {
.probe = msm_rng_probe,
.remove = __devexit_p(msm_rng_remove),
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = qrng_match,
}
};
static int __init msm_rng_init(void)
{
return platform_driver_register(&rng_driver);
}
module_init(msm_rng_init);
static void __exit msm_rng_exit(void)
{
platform_driver_unregister(&rng_driver);
}
module_exit(msm_rng_exit);
MODULE_AUTHOR("The Linux Foundation");
MODULE_DESCRIPTION("Qualcomm MSM Random Number Driver");
MODULE_LICENSE("GPL v2");
+247
View File
@@ -0,0 +1,247 @@
/*
* RNG driver for Freescale RNGA
*
* Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
* Author: Alan Carvalho de Assis <acassis@gmail.com>
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*
* This driver is based on other RNG drivers.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/hw_random.h>
#include <linux/io.h>
/* RNGA Registers */
#define RNGA_CONTROL 0x00
#define RNGA_STATUS 0x04
#define RNGA_ENTROPY 0x08
#define RNGA_OUTPUT_FIFO 0x0c
#define RNGA_MODE 0x10
#define RNGA_VERIFICATION_CONTROL 0x14
#define RNGA_OSC_CONTROL_COUNTER 0x18
#define RNGA_OSC1_COUNTER 0x1c
#define RNGA_OSC2_COUNTER 0x20
#define RNGA_OSC_COUNTER_STATUS 0x24
/* RNGA Registers Range */
#define RNG_ADDR_RANGE 0x28
/* RNGA Control Register */
#define RNGA_CONTROL_SLEEP 0x00000010
#define RNGA_CONTROL_CLEAR_INT 0x00000008
#define RNGA_CONTROL_MASK_INTS 0x00000004
#define RNGA_CONTROL_HIGH_ASSURANCE 0x00000002
#define RNGA_CONTROL_GO 0x00000001
#define RNGA_STATUS_LEVEL_MASK 0x0000ff00
/* RNGA Status Register */
#define RNGA_STATUS_OSC_DEAD 0x80000000
#define RNGA_STATUS_SLEEP 0x00000010
#define RNGA_STATUS_ERROR_INT 0x00000008
#define RNGA_STATUS_FIFO_UNDERFLOW 0x00000004
#define RNGA_STATUS_LAST_READ_STATUS 0x00000002
#define RNGA_STATUS_SECURITY_VIOLATION 0x00000001
static struct platform_device *rng_dev;
static int mxc_rnga_data_present(struct hwrng *rng)
{
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;
}
static int mxc_rnga_data_read(struct hwrng *rng, u32 * data)
{
int err;
u32 ctrl;
void __iomem *rng_base = (void __iomem *)rng->priv;
/* retrieve a random number from FIFO */
*data = __raw_readl(rng_base + RNGA_OUTPUT_FIFO);
/* some error while reading this random number? */
err = __raw_readl(rng_base + 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);
__raw_writel(ctrl | RNGA_CONTROL_CLEAR_INT,
rng_base + RNGA_CONTROL);
return 0;
} else
return 4;
}
static int mxc_rnga_init(struct hwrng *rng)
{
u32 ctrl, osc;
void __iomem *rng_base = (void __iomem *)rng->priv;
/* wake up */
ctrl = __raw_readl(rng_base + RNGA_CONTROL);
__raw_writel(ctrl & ~RNGA_CONTROL_SLEEP, rng_base + RNGA_CONTROL);
/* verify if oscillator is working */
osc = __raw_readl(rng_base + RNGA_STATUS);
if (osc & RNGA_STATUS_OSC_DEAD) {
dev_err(&rng_dev->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);
return 0;
}
static void mxc_rnga_cleanup(struct hwrng *rng)
{
u32 ctrl;
void __iomem *rng_base = (void __iomem *)rng->priv;
ctrl = __raw_readl(rng_base + RNGA_CONTROL);
/* stop rnga */
__raw_writel(ctrl & ~RNGA_CONTROL_GO, rng_base + 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)) {
dev_err(&pdev->dev, "Could not get rng_clk!\n");
err = PTR_ERR(clk);
goto out;
}
clk_enable(clk);
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;
goto err_ioremap;
}
mxc_rnga.priv = (unsigned long)rng_base;
err = hwrng_register(&mxc_rnga);
if (err) {
dev_err(&pdev->dev, "MXC RNGA registering failed (%d)\n", err);
goto err_register;
}
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);
out:
return err;
}
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);
iounmap(rng_base);
release_mem_region(res->start, resource_size(res));
clk_disable(clk);
clk_put(clk);
return 0;
}
static struct platform_driver mxc_rnga_driver = {
.driver = {
.name = "mxc_rnga",
.owner = THIS_MODULE,
},
.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_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("H/W RNGA driver for i.MX");
MODULE_LICENSE("GPL");
+79
View File
@@ -0,0 +1,79 @@
/* n2-asm.S: Niagara2 RNG hypervisor call assembler.
*
* Copyright (C) 2008 David S. Miller <davem@davemloft.net>
*/
#include <linux/linkage.h>
#include <asm/hypervisor.h>
#include "n2rng.h"
.text
ENTRY(sun4v_rng_get_diag_ctl)
mov HV_FAST_RNG_GET_DIAG_CTL, %o5
ta HV_FAST_TRAP
retl
nop
ENDPROC(sun4v_rng_get_diag_ctl)
ENTRY(sun4v_rng_ctl_read_v1)
mov %o1, %o3
mov %o2, %o4
mov HV_FAST_RNG_CTL_READ, %o5
ta HV_FAST_TRAP
stx %o1, [%o3]
retl
stx %o2, [%o4]
ENDPROC(sun4v_rng_ctl_read_v1)
ENTRY(sun4v_rng_ctl_read_v2)
save %sp, -192, %sp
mov %i0, %o0
mov %i1, %o1
mov HV_FAST_RNG_CTL_READ, %o5
ta HV_FAST_TRAP
stx %o1, [%i2]
stx %o2, [%i3]
stx %o3, [%i4]
stx %o4, [%i5]
ret
restore %g0, %o0, %o0
ENDPROC(sun4v_rng_ctl_read_v2)
ENTRY(sun4v_rng_ctl_write_v1)
mov %o3, %o4
mov HV_FAST_RNG_CTL_WRITE, %o5
ta HV_FAST_TRAP
retl
stx %o1, [%o4]
ENDPROC(sun4v_rng_ctl_write_v1)
ENTRY(sun4v_rng_ctl_write_v2)
mov HV_FAST_RNG_CTL_WRITE, %o5
ta HV_FAST_TRAP
retl
nop
ENDPROC(sun4v_rng_ctl_write_v2)
ENTRY(sun4v_rng_data_read_diag_v1)
mov %o2, %o4
mov HV_FAST_RNG_DATA_READ_DIAG, %o5
ta HV_FAST_TRAP
retl
stx %o1, [%o4]
ENDPROC(sun4v_rng_data_read_diag_v1)
ENTRY(sun4v_rng_data_read_diag_v2)
mov %o3, %o4
mov HV_FAST_RNG_DATA_READ_DIAG, %o5
ta HV_FAST_TRAP
retl
stx %o1, [%o4]
ENDPROC(sun4v_rng_data_read_diag_v2)
ENTRY(sun4v_rng_data_read)
mov %o1, %o4
mov HV_FAST_RNG_DATA_READ, %o5
ta HV_FAST_TRAP
retl
stx %o1, [%o4]
ENDPROC(sun4v_rng_data_read)
+773
View File
@@ -0,0 +1,773 @@
/* n2-drv.c: Niagara-2 RNG driver.
*
* 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>
#include <linux/hw_random.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <asm/hypervisor.h>
#include "n2rng.h"
#define DRV_MODULE_NAME "n2rng"
#define PFX DRV_MODULE_NAME ": "
#define DRV_MODULE_VERSION "0.2"
#define DRV_MODULE_RELDATE "July 27, 2011"
static char version[] __devinitdata =
DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
MODULE_DESCRIPTION("Niagara2 RNG driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_MODULE_VERSION);
/* The Niagara2 RNG provides a 64-bit read-only random number
* register, plus a control register. Access to the RNG is
* virtualized through the hypervisor so that both guests and control
* nodes can access the device.
*
* The entropy source consists of raw entropy sources, each
* constructed from a voltage controlled oscillator whose phase is
* jittered by thermal noise sources.
*
* The oscillator in each of the three raw entropy sources run at
* different frequencies. Normally, all three generator outputs are
* gathered, xored together, and fed into a CRC circuit, the output of
* which is the 64-bit read-only register.
*
* Some time is necessary for all the necessary entropy to build up
* such that a full 64-bits of entropy are available in the register.
* In normal operating mode (RNG_CTL_LFSR is set), the chip implements
* an interlock which blocks register reads until sufficient entropy
* is available.
*
* A control register is provided for adjusting various aspects of RNG
* operation, and to enable diagnostic modes. Each of the three raw
* entropy sources has an enable bit (RNG_CTL_ES{1,2,3}). Also
* provided are fields for controlling the minimum time in cycles
* between read accesses to the register (RNG_CTL_WAIT, this controls
* the interlock described in the previous paragraph).
*
* The standard setting is to have the mode bit (RNG_CTL_LFSR) set,
* all three entropy sources enabled, and the interlock time set
* appropriately.
*
* The CRC polynomial used by the chip is:
*
* P(X) = x64 + x61 + x57 + x56 + x52 + x51 + x50 + x48 + x47 + x46 +
* x43 + x42 + x41 + x39 + x38 + x37 + x35 + x32 + x28 + x25 +
* x22 + x21 + x17 + x15 + x13 + x12 + x11 + x7 + x5 + x + 1
*
* The RNG_CTL_VCO value of each noise cell must be programmed
* separately. This is why 4 control register values must be provided
* to the hypervisor. During a write, the hypervisor writes them all,
* one at a time, to the actual RNG_CTL register. The first three
* values are used to setup the desired RNG_CTL_VCO for each entropy
* source, for example:
*
* control 0: (1 << RNG_CTL_VCO_SHIFT) | RNG_CTL_ES1
* control 1: (2 << RNG_CTL_VCO_SHIFT) | RNG_CTL_ES2
* control 2: (3 << RNG_CTL_VCO_SHIFT) | RNG_CTL_ES3
*
* And then the fourth value sets the final chip state and enables
* desired.
*/
static int n2rng_hv_err_trans(unsigned long hv_err)
{
switch (hv_err) {
case HV_EOK:
return 0;
case HV_EWOULDBLOCK:
return -EAGAIN;
case HV_ENOACCESS:
return -EPERM;
case HV_EIO:
return -EIO;
case HV_EBUSY:
return -EBUSY;
case HV_EBADALIGN:
case HV_ENORADDR:
return -EFAULT;
default:
return -EINVAL;
}
}
static unsigned long n2rng_generic_read_control_v2(unsigned long ra,
unsigned long unit)
{
unsigned long hv_err, state, ticks, watchdog_delta, watchdog_status;
int block = 0, busy = 0;
while (1) {
hv_err = sun4v_rng_ctl_read_v2(ra, unit, &state,
&ticks,
&watchdog_delta,
&watchdog_status);
if (hv_err == HV_EOK)
break;
if (hv_err == HV_EBUSY) {
if (++busy >= N2RNG_BUSY_LIMIT)
break;
udelay(1);
} else if (hv_err == HV_EWOULDBLOCK) {
if (++block >= N2RNG_BLOCK_LIMIT)
break;
__delay(ticks);
} else
break;
}
return hv_err;
}
/* In multi-socket situations, the hypervisor might need to
* queue up the RNG control register write if it's for a unit
* that is on a cpu socket other than the one we are executing on.
*
* We poll here waiting for a successful read of that control
* register to make sure the write has been actually performed.
*/
static unsigned long n2rng_control_settle_v2(struct n2rng *np, int unit)
{
unsigned long ra = __pa(&np->scratch_control[0]);
return n2rng_generic_read_control_v2(ra, unit);
}
static unsigned long n2rng_write_ctl_one(struct n2rng *np, int unit,
unsigned long state,
unsigned long control_ra,
unsigned long watchdog_timeout,
unsigned long *ticks)
{
unsigned long hv_err;
if (np->hvapi_major == 1) {
hv_err = sun4v_rng_ctl_write_v1(control_ra, state,
watchdog_timeout, ticks);
} else {
hv_err = sun4v_rng_ctl_write_v2(control_ra, state,
watchdog_timeout, unit);
if (hv_err == HV_EOK)
hv_err = n2rng_control_settle_v2(np, unit);
*ticks = N2RNG_ACCUM_CYCLES_DEFAULT;
}
return hv_err;
}
static int n2rng_generic_read_data(unsigned long data_ra)
{
unsigned long ticks, hv_err;
int block = 0, hcheck = 0;
while (1) {
hv_err = sun4v_rng_data_read(data_ra, &ticks);
if (hv_err == HV_EOK)
return 0;
if (hv_err == HV_EWOULDBLOCK) {
if (++block >= N2RNG_BLOCK_LIMIT)
return -EWOULDBLOCK;
__delay(ticks);
} else if (hv_err == HV_ENOACCESS) {
return -EPERM;
} else if (hv_err == HV_EIO) {
if (++hcheck >= N2RNG_HCHECK_LIMIT)
return -EIO;
udelay(10000);
} else
return -ENODEV;
}
}
static unsigned long n2rng_read_diag_data_one(struct n2rng *np,
unsigned long unit,
unsigned long data_ra,
unsigned long data_len,
unsigned long *ticks)
{
unsigned long hv_err;
if (np->hvapi_major == 1) {
hv_err = sun4v_rng_data_read_diag_v1(data_ra, data_len, ticks);
} else {
hv_err = sun4v_rng_data_read_diag_v2(data_ra, data_len,
unit, ticks);
if (!*ticks)
*ticks = N2RNG_ACCUM_CYCLES_DEFAULT;
}
return hv_err;
}
static int n2rng_generic_read_diag_data(struct n2rng *np,
unsigned long unit,
unsigned long data_ra,
unsigned long data_len)
{
unsigned long ticks, hv_err;
int block = 0;
while (1) {
hv_err = n2rng_read_diag_data_one(np, unit,
data_ra, data_len,
&ticks);
if (hv_err == HV_EOK)
return 0;
if (hv_err == HV_EWOULDBLOCK) {
if (++block >= N2RNG_BLOCK_LIMIT)
return -EWOULDBLOCK;
__delay(ticks);
} else if (hv_err == HV_ENOACCESS) {
return -EPERM;
} else if (hv_err == HV_EIO) {
return -EIO;
} else
return -ENODEV;
}
}
static int n2rng_generic_write_control(struct n2rng *np,
unsigned long control_ra,
unsigned long unit,
unsigned long state)
{
unsigned long hv_err, ticks;
int block = 0, busy = 0;
while (1) {
hv_err = n2rng_write_ctl_one(np, unit, state, control_ra,
np->wd_timeo, &ticks);
if (hv_err == HV_EOK)
return 0;
if (hv_err == HV_EWOULDBLOCK) {
if (++block >= N2RNG_BLOCK_LIMIT)
return -EWOULDBLOCK;
__delay(ticks);
} else if (hv_err == HV_EBUSY) {
if (++busy >= N2RNG_BUSY_LIMIT)
return -EBUSY;
udelay(1);
} else
return -ENODEV;
}
}
/* Just try to see if we can successfully access the control register
* of the RNG on the domain on which we are currently executing.
*/
static int n2rng_try_read_ctl(struct n2rng *np)
{
unsigned long hv_err;
unsigned long x;
if (np->hvapi_major == 1) {
hv_err = sun4v_rng_get_diag_ctl();
} else {
/* We purposefully give invalid arguments, HV_NOACCESS
* is higher priority than the errors we'd get from
* these other cases, and that's the error we are
* truly interested in.
*/
hv_err = sun4v_rng_ctl_read_v2(0UL, ~0UL, &x, &x, &x, &x);
switch (hv_err) {
case HV_EWOULDBLOCK:
case HV_ENOACCESS:
break;
default:
hv_err = HV_EOK;
break;
}
}
return n2rng_hv_err_trans(hv_err);
}
#define CONTROL_DEFAULT_BASE \
((2 << RNG_CTL_ASEL_SHIFT) | \
(N2RNG_ACCUM_CYCLES_DEFAULT << RNG_CTL_WAIT_SHIFT) | \
RNG_CTL_LFSR)
#define CONTROL_DEFAULT_0 \
(CONTROL_DEFAULT_BASE | \
(1 << RNG_CTL_VCO_SHIFT) | \
RNG_CTL_ES1)
#define CONTROL_DEFAULT_1 \
(CONTROL_DEFAULT_BASE | \
(2 << RNG_CTL_VCO_SHIFT) | \
RNG_CTL_ES2)
#define CONTROL_DEFAULT_2 \
(CONTROL_DEFAULT_BASE | \
(3 << RNG_CTL_VCO_SHIFT) | \
RNG_CTL_ES3)
#define CONTROL_DEFAULT_3 \
(CONTROL_DEFAULT_BASE | \
RNG_CTL_ES1 | RNG_CTL_ES2 | RNG_CTL_ES3)
static void n2rng_control_swstate_init(struct n2rng *np)
{
int i;
np->flags |= N2RNG_FLAG_CONTROL;
np->health_check_sec = N2RNG_HEALTH_CHECK_SEC_DEFAULT;
np->accum_cycles = N2RNG_ACCUM_CYCLES_DEFAULT;
np->wd_timeo = N2RNG_WD_TIMEO_DEFAULT;
for (i = 0; i < np->num_units; i++) {
struct n2rng_unit *up = &np->units[i];
up->control[0] = CONTROL_DEFAULT_0;
up->control[1] = CONTROL_DEFAULT_1;
up->control[2] = CONTROL_DEFAULT_2;
up->control[3] = CONTROL_DEFAULT_3;
}
np->hv_state = HV_RNG_STATE_UNCONFIGURED;
}
static int n2rng_grab_diag_control(struct n2rng *np)
{
int i, busy_count, err = -ENODEV;
busy_count = 0;
for (i = 0; i < 100; i++) {
err = n2rng_try_read_ctl(np);
if (err != -EAGAIN)
break;
if (++busy_count > 100) {
dev_err(&np->op->dev,
"Grab diag control timeout.\n");
return -ENODEV;
}
udelay(1);
}
return err;
}
static int n2rng_init_control(struct n2rng *np)
{
int err = n2rng_grab_diag_control(np);
/* Not in the control domain, that's OK we are only a consumer
* of the RNG data, we don't setup and program it.
*/
if (err == -EPERM)
return 0;
if (err)
return err;
n2rng_control_swstate_init(np);
return 0;
}
static int n2rng_data_read(struct hwrng *rng, u32 *data)
{
struct n2rng *np = (struct n2rng *) rng->priv;
unsigned long ra = __pa(&np->test_data);
int len;
if (!(np->flags & N2RNG_FLAG_READY)) {
len = 0;
} else if (np->flags & N2RNG_FLAG_BUFFER_VALID) {
np->flags &= ~N2RNG_FLAG_BUFFER_VALID;
*data = np->buffer;
len = 4;
} else {
int err = n2rng_generic_read_data(ra);
if (!err) {
np->buffer = np->test_data >> 32;
*data = np->test_data & 0xffffffff;
len = 4;
} else {
dev_err(&np->op->dev, "RNG error, restesting\n");
np->flags &= ~N2RNG_FLAG_READY;
if (!(np->flags & N2RNG_FLAG_SHUTDOWN))
schedule_delayed_work(&np->work, 0);
len = 0;
}
}
return len;
}
/* On a guest node, just make sure we can read random data properly.
* If a control node reboots or reloads it's n2rng driver, this won't
* work during that time. So we have to keep probing until the device
* becomes usable.
*/
static int n2rng_guest_check(struct n2rng *np)
{
unsigned long ra = __pa(&np->test_data);
return n2rng_generic_read_data(ra);
}
static int n2rng_entropy_diag_read(struct n2rng *np, unsigned long unit,
u64 *pre_control, u64 pre_state,
u64 *buffer, unsigned long buf_len,
u64 *post_control, u64 post_state)
{
unsigned long post_ctl_ra = __pa(post_control);
unsigned long pre_ctl_ra = __pa(pre_control);
unsigned long buffer_ra = __pa(buffer);
int err;
err = n2rng_generic_write_control(np, pre_ctl_ra, unit, pre_state);
if (err)
return err;
err = n2rng_generic_read_diag_data(np, unit,
buffer_ra, buf_len);
(void) n2rng_generic_write_control(np, post_ctl_ra, unit,
post_state);
return err;
}
static u64 advance_polynomial(u64 poly, u64 val, int count)
{
int i;
for (i = 0; i < count; i++) {
int highbit_set = ((s64)val < 0);
val <<= 1;
if (highbit_set)
val ^= poly;
}
return val;
}
static int n2rng_test_buffer_find(struct n2rng *np, u64 val)
{
int i, count = 0;
/* Purposefully skip over the first word. */
for (i = 1; i < SELFTEST_BUFFER_WORDS; i++) {
if (np->test_buffer[i] == val)
count++;
}
return count;
}
static void n2rng_dump_test_buffer(struct n2rng *np)
{
int i;
for (i = 0; i < SELFTEST_BUFFER_WORDS; i++)
dev_err(&np->op->dev, "Test buffer slot %d [0x%016llx]\n",
i, np->test_buffer[i]);
}
static int n2rng_check_selftest_buffer(struct n2rng *np, unsigned long unit)
{
u64 val = SELFTEST_VAL;
int err, matches, limit;
matches = 0;
for (limit = 0; limit < SELFTEST_LOOPS_MAX; limit++) {
matches += n2rng_test_buffer_find(np, val);
if (matches >= SELFTEST_MATCH_GOAL)
break;
val = advance_polynomial(SELFTEST_POLY, val, 1);
}
err = 0;
if (limit >= SELFTEST_LOOPS_MAX) {
err = -ENODEV;
dev_err(&np->op->dev, "Selftest failed on unit %lu\n", unit);
n2rng_dump_test_buffer(np);
} else
dev_info(&np->op->dev, "Selftest passed on unit %lu\n", unit);
return err;
}
static int n2rng_control_selftest(struct n2rng *np, unsigned long unit)
{
int err;
np->test_control[0] = (0x2 << RNG_CTL_ASEL_SHIFT);
np->test_control[1] = (0x2 << RNG_CTL_ASEL_SHIFT);
np->test_control[2] = (0x2 << RNG_CTL_ASEL_SHIFT);
np->test_control[3] = ((0x2 << RNG_CTL_ASEL_SHIFT) |
RNG_CTL_LFSR |
((SELFTEST_TICKS - 2) << RNG_CTL_WAIT_SHIFT));
err = n2rng_entropy_diag_read(np, unit, np->test_control,
HV_RNG_STATE_HEALTHCHECK,
np->test_buffer,
sizeof(np->test_buffer),
&np->units[unit].control[0],
np->hv_state);
if (err)
return err;
return n2rng_check_selftest_buffer(np, unit);
}
static int n2rng_control_check(struct n2rng *np)
{
int i;
for (i = 0; i < np->num_units; i++) {
int err = n2rng_control_selftest(np, i);
if (err)
return err;
}
return 0;
}
/* The sanity checks passed, install the final configuration into the
* chip, it's ready to use.
*/
static int n2rng_control_configure_units(struct n2rng *np)
{
int unit, err;
err = 0;
for (unit = 0; unit < np->num_units; unit++) {
struct n2rng_unit *up = &np->units[unit];
unsigned long ctl_ra = __pa(&up->control[0]);
int esrc;
u64 base;
base = ((np->accum_cycles << RNG_CTL_WAIT_SHIFT) |
(2 << RNG_CTL_ASEL_SHIFT) |
RNG_CTL_LFSR);
/* XXX This isn't the best. We should fetch a bunch
* XXX of words using each entropy source combined XXX
* with each VCO setting, and see which combinations
* XXX give the best random data.
*/
for (esrc = 0; esrc < 3; esrc++)
up->control[esrc] = base |
(esrc << RNG_CTL_VCO_SHIFT) |
(RNG_CTL_ES1 << esrc);
up->control[3] = base |
(RNG_CTL_ES1 | RNG_CTL_ES2 | RNG_CTL_ES3);
err = n2rng_generic_write_control(np, ctl_ra, unit,
HV_RNG_STATE_CONFIGURED);
if (err)
break;
}
return err;
}
static void n2rng_work(struct work_struct *work)
{
struct n2rng *np = container_of(work, struct n2rng, work.work);
int err = 0;
if (!(np->flags & N2RNG_FLAG_CONTROL)) {
err = n2rng_guest_check(np);
} else {
preempt_disable();
err = n2rng_control_check(np);
preempt_enable();
if (!err)
err = n2rng_control_configure_units(np);
}
if (!err) {
np->flags |= N2RNG_FLAG_READY;
dev_info(&np->op->dev, "RNG ready\n");
}
if (err && !(np->flags & N2RNG_FLAG_SHUTDOWN))
schedule_delayed_work(&np->work, HZ * 2);
}
static void __devinit n2rng_driver_version(void)
{
static int n2rng_version_printed;
if (n2rng_version_printed++ == 0)
pr_info("%s", version);
}
static const struct of_device_id n2rng_match[];
static int __devinit n2rng_probe(struct platform_device *op)
{
const struct of_device_id *match;
int multi_capable;
int err = -ENOMEM;
struct n2rng *np;
match = of_match_device(n2rng_match, &op->dev);
if (!match)
return -EINVAL;
multi_capable = (match->data != NULL);
n2rng_driver_version();
np = kzalloc(sizeof(*np), GFP_KERNEL);
if (!np)
goto out;
np->op = op;
INIT_DELAYED_WORK(&np->work, n2rng_work);
if (multi_capable)
np->flags |= N2RNG_FLAG_MULTI;
err = -ENODEV;
np->hvapi_major = 2;
if (sun4v_hvapi_register(HV_GRP_RNG,
np->hvapi_major,
&np->hvapi_minor)) {
np->hvapi_major = 1;
if (sun4v_hvapi_register(HV_GRP_RNG,
np->hvapi_major,
&np->hvapi_minor)) {
dev_err(&op->dev, "Cannot register suitable "
"HVAPI version.\n");
goto out_free;
}
}
if (np->flags & N2RNG_FLAG_MULTI) {
if (np->hvapi_major < 2) {
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;
}
np->num_units = of_getintprop_default(op->dev.of_node,
"rng-#units", 0);
if (!np->num_units) {
dev_err(&op->dev, "VF RNG lacks rng-#units property\n");
goto out_hvapi_unregister;
}
} else
np->num_units = 1;
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);
err = -ENOMEM;
if (!np->units)
goto out_hvapi_unregister;
err = n2rng_init_control(np);
if (err)
goto out_free_units;
dev_info(&op->dev, "Found %s RNG, units: %d\n",
((np->flags & N2RNG_FLAG_MULTI) ?
"multi-unit-capable" : "single-unit"),
np->num_units);
np->hwrng.name = "n2rng";
np->hwrng.data_read = n2rng_data_read;
np->hwrng.priv = (unsigned long) np;
err = hwrng_register(&np->hwrng);
if (err)
goto out_free_units;
dev_set_drvdata(&op->dev, 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)
{
struct n2rng *np = dev_get_drvdata(&op->dev);
np->flags |= N2RNG_FLAG_SHUTDOWN;
cancel_delayed_work_sync(&np->work);
hwrng_unregister(&np->hwrng);
sun4v_hvapi_unregister(HV_GRP_RNG);
kfree(np->units);
np->units = NULL;
kfree(np);
dev_set_drvdata(&op->dev, NULL);
return 0;
}
static const struct of_device_id n2rng_match[] = {
{
.name = "random-number-generator",
.compatible = "SUNW,n2-rng",
},
{
.name = "random-number-generator",
.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 platform_driver n2rng_driver = {
.driver = {
.name = "n2rng",
.owner = THIS_MODULE,
.of_match_table = n2rng_match,
},
.probe = n2rng_probe,
.remove = __devexit_p(n2rng_remove),
};
module_platform_driver(n2rng_driver);
+118
View File
@@ -0,0 +1,118 @@
/* n2rng.h: Niagara2 RNG defines.
*
* Copyright (C) 2008 David S. Miller <davem@davemloft.net>
*/
#ifndef _N2RNG_H
#define _N2RNG_H
#define RNG_CTL_WAIT 0x0000000001fffe00ULL /* Minimum wait time */
#define RNG_CTL_WAIT_SHIFT 9
#define RNG_CTL_BYPASS 0x0000000000000100ULL /* VCO voltage source */
#define RNG_CTL_VCO 0x00000000000000c0ULL /* VCO rate control */
#define RNG_CTL_VCO_SHIFT 6
#define RNG_CTL_ASEL 0x0000000000000030ULL /* Analog MUX select */
#define RNG_CTL_ASEL_SHIFT 4
#define RNG_CTL_LFSR 0x0000000000000008ULL /* Use LFSR or plain shift */
#define RNG_CTL_ES3 0x0000000000000004ULL /* Enable entropy source 3 */
#define RNG_CTL_ES2 0x0000000000000002ULL /* Enable entropy source 2 */
#define RNG_CTL_ES1 0x0000000000000001ULL /* Enable entropy source 1 */
#define HV_FAST_RNG_GET_DIAG_CTL 0x130
#define HV_FAST_RNG_CTL_READ 0x131
#define HV_FAST_RNG_CTL_WRITE 0x132
#define HV_FAST_RNG_DATA_READ_DIAG 0x133
#define HV_FAST_RNG_DATA_READ 0x134
#define HV_RNG_STATE_UNCONFIGURED 0
#define HV_RNG_STATE_CONFIGURED 1
#define HV_RNG_STATE_HEALTHCHECK 2
#define HV_RNG_STATE_ERROR 3
#define HV_RNG_NUM_CONTROL 4
#ifndef __ASSEMBLY__
extern unsigned long sun4v_rng_get_diag_ctl(void);
extern unsigned long sun4v_rng_ctl_read_v1(unsigned long ctl_regs_ra,
unsigned long *state,
unsigned long *tick_delta);
extern unsigned long sun4v_rng_ctl_read_v2(unsigned long ctl_regs_ra,
unsigned long unit,
unsigned long *state,
unsigned long *tick_delta,
unsigned long *watchdog,
unsigned long *write_status);
extern unsigned long sun4v_rng_ctl_write_v1(unsigned long ctl_regs_ra,
unsigned long state,
unsigned long write_timeout,
unsigned long *tick_delta);
extern unsigned long sun4v_rng_ctl_write_v2(unsigned long ctl_regs_ra,
unsigned long state,
unsigned long write_timeout,
unsigned long unit);
extern unsigned long sun4v_rng_data_read_diag_v1(unsigned long data_ra,
unsigned long len,
unsigned long *tick_delta);
extern unsigned long sun4v_rng_data_read_diag_v2(unsigned long data_ra,
unsigned long len,
unsigned long unit,
unsigned long *tick_delta);
extern unsigned long sun4v_rng_data_read(unsigned long data_ra,
unsigned long *tick_delta);
struct n2rng_unit {
u64 control[HV_RNG_NUM_CONTROL];
};
struct n2rng {
struct platform_device *op;
unsigned long flags;
#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 */
#define N2RNG_FLAG_BUFFER_VALID 0x00000020 /* u32 buffer holds valid data */
int num_units;
struct n2rng_unit *units;
struct hwrng hwrng;
u32 buffer;
/* Registered hypervisor group API major and minor version. */
unsigned long hvapi_major;
unsigned long hvapi_minor;
struct delayed_work work;
unsigned long hv_state; /* HV_RNG_STATE_foo */
unsigned long health_check_sec;
unsigned long accum_cycles;
unsigned long wd_timeo;
#define N2RNG_HEALTH_CHECK_SEC_DEFAULT 0
#define N2RNG_ACCUM_CYCLES_DEFAULT 2048
#define N2RNG_WD_TIMEO_DEFAULT 0
u64 scratch_control[HV_RNG_NUM_CONTROL];
#define SELFTEST_TICKS 38859
#define SELFTEST_VAL ((u64)0xB8820C7BD387E32C)
#define SELFTEST_POLY ((u64)0x231DCEE91262B8A3)
#define SELFTEST_MATCH_GOAL 6
#define SELFTEST_LOOPS_MAX 40000
#define SELFTEST_BUFFER_WORDS 8
u64 test_data;
u64 test_control[HV_RNG_NUM_CONTROL];
u64 test_buffer[SELFTEST_BUFFER_WORDS];
};
#define N2RNG_BLOCK_LIMIT 60000
#define N2RNG_BUSY_LIMIT 100
#define N2RNG_HCHECK_LIMIT 100
#endif /* !(__ASSEMBLY__) */
#endif /* _N2RNG_H */
+112
View File
@@ -0,0 +1,112 @@
/*
* Nomadik RNG support
* Copyright 2009 Alessandro Rubini
*
* 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.
*/
#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>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/err.h>
static struct clk *rng_clk;
static int nmk_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
{
void __iomem *base = (void __iomem *)rng->priv;
/*
* The register is 32 bits and gives 16 random bits (low half).
* A subsequent read will delay the core for 400ns, so we just read
* once and accept the very unlikely very small delay, even if wait==0.
*/
*(u16 *)data = __raw_readl(base + 8) & 0xffff;
return 2;
}
/* we have at most one RNG per machine, granted */
static struct hwrng nmk_rng = {
.name = "nomadik",
.read = nmk_rng_read,
};
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);
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);
ret = amba_request_regions(dev, dev->dev.init_name);
if (ret)
goto out_clk;
ret = -ENOMEM;
base = ioremap(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;
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;
}
static struct amba_id nmk_rng_ids[] = {
{
.id = 0x000805e1,
.mask = 0x000fffff, /* top bits are rev and cfg: accept all */
},
{0, 0},
};
MODULE_DEVICE_TABLE(amba, nmk_rng_ids);
static struct amba_driver nmk_rng_driver = {
.drv = {
.owner = THIS_MODULE,
.name = "rng",
},
.probe = nmk_rng_probe,
.remove = nmk_rng_remove,
.id_table = nmk_rng_ids,
};
module_amba_driver(nmk_rng_driver);
MODULE_LICENSE("GPL");
+137
View File
@@ -0,0 +1,137 @@
/*
* Hardware Random Number Generator support for Cavium Networks
* Octeon processor family.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2009 Cavium Networks
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/hw_random.h>
#include <linux/io.h>
#include <linux/gfp.h>
#include <asm/octeon/octeon.h>
#include <asm/octeon/cvmx-rnm-defs.h>
struct octeon_rng {
struct hwrng ops;
void __iomem *control_status;
void __iomem *result;
};
static int octeon_rng_init(struct hwrng *rng)
{
union cvmx_rnm_ctl_status ctl;
struct octeon_rng *p = container_of(rng, struct octeon_rng, ops);
ctl.u64 = 0;
ctl.s.ent_en = 1; /* Enable the entropy source. */
ctl.s.rng_en = 1; /* Enable the RNG hardware. */
cvmx_write_csr((u64)p->control_status, ctl.u64);
return 0;
}
static void octeon_rng_cleanup(struct hwrng *rng)
{
union cvmx_rnm_ctl_status ctl;
struct octeon_rng *p = container_of(rng, struct octeon_rng, ops);
ctl.u64 = 0;
/* Disable everything. */
cvmx_write_csr((u64)p->control_status, ctl.u64);
}
static int octeon_rng_data_read(struct hwrng *rng, u32 *data)
{
struct octeon_rng *p = container_of(rng, struct octeon_rng, ops);
*data = cvmx_read64_uint32((u64)p->result);
return sizeof(u32);
}
static int __devinit octeon_rng_probe(struct platform_device *pdev)
{
struct resource *res_ports;
struct resource *res_result;
struct octeon_rng *rng;
int ret;
struct hwrng ops = {
.name = "octeon",
.init = octeon_rng_init,
.cleanup = octeon_rng_cleanup,
.data_read = octeon_rng_data_read
};
rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL);
if (!rng)
return -ENOMEM;
res_ports = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res_ports)
goto err_ports;
res_result = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res_result)
goto err_ports;
rng->control_status = devm_ioremap_nocache(&pdev->dev,
res_ports->start,
sizeof(u64));
if (!rng->control_status)
goto err_ports;
rng->result = devm_ioremap_nocache(&pdev->dev,
res_result->start,
sizeof(u64));
if (!rng->result)
goto err_r;
rng->ops = ops;
dev_set_drvdata(&pdev->dev, &rng->ops);
ret = hwrng_register(&rng->ops);
if (ret)
goto err;
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);
hwrng_unregister(rng);
return 0;
}
static struct platform_driver octeon_rng_driver = {
.driver = {
.name = "octeon_rng",
.owner = THIS_MODULE,
},
.probe = octeon_rng_probe,
.remove = __exit_p(octeon_rng_remove),
};
module_platform_driver(octeon_rng_driver);
MODULE_AUTHOR("David Daney");
MODULE_LICENSE("GPL");
+233
View File
@@ -0,0 +1,233 @@
/*
* omap-rng.c - RNG driver for TI OMAP CPU family
*
* Author: Deepak Saxena <dsaxena@plexity.net>
*
* Copyright 2005 (c) MontaVista Software, Inc.
*
* Mostly based on original driver:
*
* Copyright (C) 2005 Nokia Corporation
* Author: Juha Yrjölä <juha.yrjola@nokia.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.
*/
#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 <asm/io.h>
#include <plat/cpu.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 */
static void __iomem *rng_base;
static struct clk *rng_ick;
static struct platform_device *rng_dev;
static inline u32 omap_rng_read_reg(int reg)
{
return __raw_readl(rng_base + reg);
}
static inline void omap_rng_write_reg(int reg, u32 val)
{
__raw_writel(val, rng_base + reg);
}
static int omap_rng_data_present(struct hwrng *rng, int wait)
{
int data, i;
for (i = 0; i < 20; i++) {
data = omap_rng_read_reg(RNG_STAT_REG) ? 0 : 1;
if (data || !wait)
break;
/* RNG produces data fast enough (2+ MBit/sec, even
* during "rngtest" loads, that these delays don't
* seem to trigger. We *could* use the RNG IRQ, but
* that'd be higher overhead ... so why bother?
*/
udelay(10);
}
return data;
}
static int omap_rng_data_read(struct hwrng *rng, u32 *data)
{
*data = omap_rng_read_reg(RNG_OUT_REG);
return 4;
}
static struct hwrng omap_rng_ops = {
.name = "omap",
.data_present = omap_rng_data_present,
.data_read = omap_rng_data_read,
};
static int __devinit omap_rng_probe(struct platform_device *pdev)
{
struct resource *res;
int ret;
/*
* A bit ugly, and it will never actually happen but there can
* be only one RNG and this catches any bork
*/
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);
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
ret = -ENOENT;
goto err_region;
}
if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
ret = -EBUSY;
goto err_region;
}
dev_set_drvdata(&pdev->dev, res);
rng_base = ioremap(res->start, resource_size(res));
if (!rng_base) {
ret = -ENOMEM;
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;
return 0;
err_register:
iounmap(rng_base);
rng_base = NULL;
err_ioremap:
release_mem_region(res->start, resource_size(res));
err_region:
if (cpu_is_omap24xx()) {
clk_disable(rng_ick);
clk_put(rng_ick);
}
return ret;
}
static int __exit omap_rng_remove(struct platform_device *pdev)
{
struct resource *res = dev_get_drvdata(&pdev->dev);
hwrng_unregister(&omap_rng_ops);
omap_rng_write_reg(RNG_MASK_REG, 0x0);
iounmap(rng_base);
if (cpu_is_omap24xx()) {
clk_disable(rng_ick);
clk_put(rng_ick);
}
release_mem_region(res->start, resource_size(res));
rng_base = NULL;
return 0;
}
#ifdef CONFIG_PM
static int omap_rng_suspend(struct platform_device *pdev, pm_message_t message)
{
omap_rng_write_reg(RNG_MASK_REG, 0x0);
return 0;
}
static int omap_rng_resume(struct platform_device *pdev)
{
omap_rng_write_reg(RNG_MASK_REG, 0x1);
return 0;
}
#else
#define omap_rng_suspend NULL
#define omap_rng_resume 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,
},
.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_AUTHOR("Deepak Saxena (and others)");
MODULE_LICENSE("GPL");
+155
View File
@@ -0,0 +1,155 @@
/*
* Copyright (C) 2006-2007 PA Semi, Inc
*
* Maintained by: Olof Johansson <olof@lixom.net>
*
* Driver for the PWRficient onchip rng
*
* 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/kernel.h>
#include <linux/platform_device.h>
#include <linux/hw_random.h>
#include <linux/delay.h>
#include <linux/of_platform.h>
#include <asm/io.h>
#define SDCRNG_CTL_REG 0x00
#define SDCRNG_CTL_FVLD_M 0x0000f000
#define SDCRNG_CTL_FVLD_S 12
#define SDCRNG_CTL_KSZ 0x00000800
#define SDCRNG_CTL_RSRC_CRG 0x00000010
#define SDCRNG_CTL_RSRC_RRG 0x00000000
#define SDCRNG_CTL_CE 0x00000004
#define SDCRNG_CTL_RE 0x00000002
#define SDCRNG_CTL_DR 0x00000001
#define SDCRNG_CTL_SELECT_RRG_RNG (SDCRNG_CTL_RE | SDCRNG_CTL_RSRC_RRG)
#define SDCRNG_CTL_SELECT_CRG_RNG (SDCRNG_CTL_CE | SDCRNG_CTL_RSRC_CRG)
#define SDCRNG_VAL_REG 0x20
#define MODULE_NAME "pasemi_rng"
static int pasemi_rng_data_present(struct hwrng *rng, int wait)
{
void __iomem *rng_regs = (void __iomem *)rng->priv;
int data, i;
for (i = 0; i < 20; i++) {
data = (in_le32(rng_regs + SDCRNG_CTL_REG)
& SDCRNG_CTL_FVLD_M) ? 1 : 0;
if (data || !wait)
break;
udelay(10);
}
return data;
}
static int pasemi_rng_data_read(struct hwrng *rng, u32 *data)
{
void __iomem *rng_regs = (void __iomem *)rng->priv;
*data = in_le32(rng_regs + SDCRNG_VAL_REG);
return 4;
}
static int pasemi_rng_init(struct hwrng *rng)
{
void __iomem *rng_regs = (void __iomem *)rng->priv;
u32 ctl;
ctl = SDCRNG_CTL_DR | SDCRNG_CTL_SELECT_RRG_RNG | SDCRNG_CTL_KSZ;
out_le32(rng_regs + SDCRNG_CTL_REG, ctl);
out_le32(rng_regs + SDCRNG_CTL_REG, ctl & ~SDCRNG_CTL_DR);
return 0;
}
static void pasemi_rng_cleanup(struct hwrng *rng)
{
void __iomem *rng_regs = (void __iomem *)rng->priv;
u32 ctl;
ctl = SDCRNG_CTL_RE | SDCRNG_CTL_CE;
out_le32(rng_regs + SDCRNG_CTL_REG,
in_le32(rng_regs + SDCRNG_CTL_REG) & ~ctl);
}
static struct hwrng pasemi_rng = {
.name = MODULE_NAME,
.init = pasemi_rng_init,
.cleanup = pasemi_rng_cleanup,
.data_present = pasemi_rng_data_present,
.data_read = pasemi_rng_data_read,
};
static int __devinit rng_probe(struct platform_device *ofdev)
{
void __iomem *rng_regs;
struct device_node *rng_np = ofdev->dev.of_node;
struct resource res;
int err = 0;
err = of_address_to_resource(rng_np, 0, &res);
if (err)
return -ENODEV;
rng_regs = ioremap(res.start, 0x100);
if (!rng_regs)
return -ENOMEM;
pasemi_rng.priv = (unsigned long)rng_regs;
printk(KERN_INFO "Registering PA Semi RNG\n");
err = hwrng_register(&pasemi_rng);
if (err)
iounmap(rng_regs);
return err;
}
static int __devexit rng_remove(struct platform_device *dev)
{
void __iomem *rng_regs = (void __iomem *)pasemi_rng.priv;
hwrng_unregister(&pasemi_rng);
iounmap(rng_regs);
return 0;
}
static struct of_device_id rng_match[] = {
{ .compatible = "1682m-rng", },
{ .compatible = "pasemi,pwrficient-rng", },
{ },
};
static struct platform_driver rng_driver = {
.driver = {
.name = "pasemi-rng",
.owner = THIS_MODULE,
.of_match_table = rng_match,
},
.probe = rng_probe,
.remove = rng_remove,
};
module_platform_driver(rng_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Egor Martovetsky <egor@pasemi.com>");
MODULE_DESCRIPTION("H/W RNG driver for PA Semi processor");
@@ -0,0 +1,198 @@
/*
* Copyright (c) 2010-2011 Picochip Ltd., Jamie Iles
*
* 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.
*
* All enquiries to support@picochip.com
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/hw_random.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#define DATA_REG_OFFSET 0x0200
#define CSR_REG_OFFSET 0x0278
#define CSR_OUT_EMPTY_MASK (1 << 24)
#define CSR_FAULT_MASK (1 << 1)
#define TRNG_BLOCK_RESET_MASK (1 << 0)
#define TAI_REG_OFFSET 0x0380
/*
* The maximum amount of time in microseconds to spend waiting for data if the
* core wants us to wait. The TRNG should generate 32 bits every 320ns so a
* timeout of 20us seems reasonable. The TRNG does builtin tests of the data
* for randomness so we can't always assume there is data present.
*/
#define PICO_TRNG_TIMEOUT 20
static void __iomem *rng_base;
static struct clk *rng_clk;
struct device *rng_dev;
static inline u32 picoxcell_trng_read_csr(void)
{
return __raw_readl(rng_base + CSR_REG_OFFSET);
}
static inline bool picoxcell_trng_is_empty(void)
{
return picoxcell_trng_read_csr() & CSR_OUT_EMPTY_MASK;
}
/*
* Take the random number generator out of reset and make sure the interrupts
* are masked. We shouldn't need to get large amounts of random bytes so just
* poll the status register. The hardware generates 32 bits every 320ns so we
* shouldn't have to wait long enough to warrant waiting for an IRQ.
*/
static void picoxcell_trng_start(void)
{
__raw_writel(0, rng_base + TAI_REG_OFFSET);
__raw_writel(0, rng_base + CSR_REG_OFFSET);
}
static void picoxcell_trng_reset(void)
{
__raw_writel(TRNG_BLOCK_RESET_MASK, rng_base + CSR_REG_OFFSET);
__raw_writel(TRNG_BLOCK_RESET_MASK, rng_base + TAI_REG_OFFSET);
picoxcell_trng_start();
}
/*
* Get some random data from the random number generator. The hw_random core
* layer provides us with locking.
*/
static int picoxcell_trng_read(struct hwrng *rng, void *buf, size_t max,
bool wait)
{
int i;
/* Wait for some data to become available. */
for (i = 0; i < PICO_TRNG_TIMEOUT && picoxcell_trng_is_empty(); ++i) {
if (!wait)
return 0;
udelay(1);
}
if (picoxcell_trng_read_csr() & CSR_FAULT_MASK) {
dev_err(rng_dev, "fault detected, resetting TRNG\n");
picoxcell_trng_reset();
return -EIO;
}
if (i == PICO_TRNG_TIMEOUT)
return 0;
*(u32 *)buf = __raw_readl(rng_base + DATA_REG_OFFSET);
return sizeof(u32);
}
static struct hwrng picoxcell_trng = {
.name = "picoxcell",
.read = picoxcell_trng_read,
};
static int picoxcell_trng_probe(struct platform_device *pdev)
{
int ret;
struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem) {
dev_warn(&pdev->dev, "no memory resource\n");
return -ENOMEM;
}
if (!devm_request_mem_region(&pdev->dev, mem->start, resource_size(mem),
"picoxcell_trng")) {
dev_warn(&pdev->dev, "unable to request io mem\n");
return -EBUSY;
}
rng_base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
if (!rng_base) {
dev_warn(&pdev->dev, "unable to remap io mem\n");
return -ENOMEM;
}
rng_clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(rng_clk)) {
dev_warn(&pdev->dev, "no clk\n");
return PTR_ERR(rng_clk);
}
ret = clk_enable(rng_clk);
if (ret) {
dev_warn(&pdev->dev, "unable to enable clk\n");
goto err_enable;
}
picoxcell_trng_start();
ret = hwrng_register(&picoxcell_trng);
if (ret)
goto err_register;
rng_dev = &pdev->dev;
dev_info(&pdev->dev, "pixoxcell random number generator active\n");
return 0;
err_register:
clk_disable(rng_clk);
err_enable:
clk_put(rng_clk);
return ret;
}
static int __devexit picoxcell_trng_remove(struct platform_device *pdev)
{
hwrng_unregister(&picoxcell_trng);
clk_disable(rng_clk);
clk_put(rng_clk);
return 0;
}
#ifdef CONFIG_PM
static int picoxcell_trng_suspend(struct device *dev)
{
clk_disable(rng_clk);
return 0;
}
static int picoxcell_trng_resume(struct device *dev)
{
return clk_enable(rng_clk);
}
static const struct dev_pm_ops picoxcell_trng_pm_ops = {
.suspend = picoxcell_trng_suspend,
.resume = picoxcell_trng_resume,
};
#endif /* CONFIG_PM */
static struct platform_driver picoxcell_trng_driver = {
.probe = picoxcell_trng_probe,
.remove = __devexit_p(picoxcell_trng_remove),
.driver = {
.name = "picoxcell-trng",
.owner = THIS_MODULE,
#ifdef CONFIG_PM
.pm = &picoxcell_trng_pm_ops,
#endif /* CONFIG_PM */
},
};
module_platform_driver(picoxcell_trng_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jamie Iles");
MODULE_DESCRIPTION("Picochip picoXcell TRNG driver");
+146
View File
@@ -0,0 +1,146 @@
/*
* 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_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 __devinit 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 __devexit 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");
@@ -0,0 +1,156 @@
/*
* drivers/char/hw_random/timeriomem-rng.c
*
* Copyright (C) 2009 Alexander Clouter <alex@digriz.org.uk>
*
* Derived from drivers/char/hw_random/omap-rng.c
* Copyright 2005 (c) MontaVista Software, Inc.
* Author: Deepak Saxena <dsaxena@plexity.net>
*
* 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.
*
* Overview:
* This driver is useful for platforms that have an IO range that provides
* periodic random data from a single IO memory address. All the platform
* has to do is provide the address and 'wait time' that new data becomes
* available.
*
* TODO: add support for reading sizes other than 32bits and masking
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/hw_random.h>
#include <linux/io.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;
static void timeriomem_rng_trigger(unsigned long);
static DEFINE_TIMER(timeriomem_rng_timer, timeriomem_rng_trigger, 0, 0);
/*
* 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;
if (!wait || timeriomem_rng_data->present)
return timeriomem_rng_data->present;
wait_for_completion(&timeriomem_rng_data->completion);
return 1;
}
static int timeriomem_rng_data_read(struct hwrng *rng, u32 *data)
{
unsigned long cur;
s32 delay;
*data = readl(timeriomem_rng_data->address);
if (rng->priv != 0) {
cur = jiffies;
delay = cur - timeriomem_rng_timer.expires;
delay = rng->priv - (delay % rng->priv);
timeriomem_rng_timer.expires = cur + delay;
timeriomem_rng_data->present = 0;
init_completion(&timeriomem_rng_data->completion);
add_timer(&timeriomem_rng_timer);
}
return 4;
}
static void timeriomem_rng_trigger(unsigned long dummy)
{
timeriomem_rng_data->present = 1;
complete(&timeriomem_rng_data->completion);
}
static struct hwrng timeriomem_rng_ops = {
.name = "timeriomem",
.data_present = timeriomem_rng_data_present,
.data_read = timeriomem_rng_data_read,
.priv = 0,
};
static int __devinit timeriomem_rng_probe(struct platform_device *pdev)
{
struct resource *res;
int ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENOENT;
timeriomem_rng_data = pdev->dev.platform_data;
timeriomem_rng_data->address = ioremap(res->start, resource_size(res));
if (!timeriomem_rng_data->address)
return -EIO;
if (timeriomem_rng_data->period != 0
&& usecs_to_jiffies(timeriomem_rng_data->period) > 0) {
timeriomem_rng_timer.expires = jiffies;
timeriomem_rng_ops.priv = usecs_to_jiffies(
timeriomem_rng_data->period);
}
timeriomem_rng_data->present = 1;
ret = hwrng_register(&timeriomem_rng_ops);
if (ret)
goto failed;
dev_info(&pdev->dev, "32bits from 0x%p @ %dus\n",
timeriomem_rng_data->address,
timeriomem_rng_data->period);
return 0;
failed:
dev_err(&pdev->dev, "problem registering\n");
iounmap(timeriomem_rng_data->address);
return ret;
}
static int __devexit timeriomem_rng_remove(struct platform_device *pdev)
{
del_timer_sync(&timeriomem_rng_timer);
hwrng_unregister(&timeriomem_rng_ops);
iounmap(timeriomem_rng_data->address);
return 0;
}
static struct platform_driver timeriomem_rng_driver = {
.driver = {
.name = "timeriomem_rng",
.owner = THIS_MODULE,
},
.probe = timeriomem_rng_probe,
.remove = __devexit_p(timeriomem_rng_remove),
};
module_platform_driver(timeriomem_rng_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alexander Clouter <alex@digriz.org.uk>");
MODULE_DESCRIPTION("Timer IOMEM H/W RNG driver");
+182
View File
@@ -0,0 +1,182 @@
/*
* RNG driver for TX4939 Random Number Generators (RNG)
*
* Copyright (C) 2009 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/hw_random.h>
#include <linux/gfp.h>
#define TX4939_RNG_RCSR 0x00000000
#define TX4939_RNG_ROR(n) (0x00000018 + (n) * 8)
#define TX4939_RNG_RCSR_INTE 0x00000008
#define TX4939_RNG_RCSR_RST 0x00000004
#define TX4939_RNG_RCSR_FIN 0x00000002
#define TX4939_RNG_RCSR_ST 0x00000001
struct tx4939_rng {
struct hwrng rng;
void __iomem *base;
u64 databuf[3];
unsigned int data_avail;
};
static void rng_io_start(void)
{
#ifndef CONFIG_64BIT
/*
* readq is reading a 64-bit register using a 64-bit load. On
* a 32-bit kernel however interrupts or any other processor
* exception would clobber the upper 32-bit of the processor
* register so interrupts need to be disabled.
*/
local_irq_disable();
#endif
}
static void rng_io_end(void)
{
#ifndef CONFIG_64BIT
local_irq_enable();
#endif
}
static u64 read_rng(void __iomem *base, unsigned int offset)
{
return ____raw_readq(base + offset);
}
static void write_rng(u64 val, void __iomem *base, unsigned int offset)
{
return ____raw_writeq(val, base + offset);
}
static int tx4939_rng_data_present(struct hwrng *rng, int wait)
{
struct tx4939_rng *rngdev = container_of(rng, struct tx4939_rng, rng);
int i;
if (rngdev->data_avail)
return rngdev->data_avail;
for (i = 0; i < 20; i++) {
rng_io_start();
if (!(read_rng(rngdev->base, TX4939_RNG_RCSR)
& TX4939_RNG_RCSR_ST)) {
rngdev->databuf[0] =
read_rng(rngdev->base, TX4939_RNG_ROR(0));
rngdev->databuf[1] =
read_rng(rngdev->base, TX4939_RNG_ROR(1));
rngdev->databuf[2] =
read_rng(rngdev->base, TX4939_RNG_ROR(2));
rngdev->data_avail =
sizeof(rngdev->databuf) / sizeof(u32);
/* Start RNG */
write_rng(TX4939_RNG_RCSR_ST,
rngdev->base, TX4939_RNG_RCSR);
wait = 0;
}
rng_io_end();
if (!wait)
break;
/* 90 bus clock cycles by default for generation */
ndelay(90 * 5);
}
return rngdev->data_avail;
}
static int tx4939_rng_data_read(struct hwrng *rng, u32 *buffer)
{
struct tx4939_rng *rngdev = container_of(rng, struct tx4939_rng, rng);
rngdev->data_avail--;
*buffer = *((u32 *)&rngdev->databuf + rngdev->data_avail);
return sizeof(u32);
}
static int __init tx4939_rng_probe(struct platform_device *dev)
{
struct tx4939_rng *rngdev;
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;
rngdev->base = devm_request_and_ioremap(&dev->dev, r);
if (!rngdev->base)
return -EBUSY;
rngdev->rng.name = dev_name(&dev->dev);
rngdev->rng.data_present = tx4939_rng_data_present;
rngdev->rng.data_read = tx4939_rng_data_read;
rng_io_start();
/* Reset RNG */
write_rng(TX4939_RNG_RCSR_RST, rngdev->base, TX4939_RNG_RCSR);
write_rng(0, rngdev->base, TX4939_RNG_RCSR);
/* Start RNG */
write_rng(TX4939_RNG_RCSR_ST, rngdev->base, TX4939_RNG_RCSR);
rng_io_end();
/*
* Drop first two results. From the datasheet:
* The quality of the random numbers generated immediately
* after reset can be insufficient. Therefore, do not use
* random numbers obtained from the first and second
* generations; use the ones from the third or subsequent
* generation.
*/
for (i = 0; i < 2; i++) {
rngdev->data_avail = 0;
if (!tx4939_rng_data_present(&rngdev->rng, 1))
return -EIO;
}
platform_set_drvdata(dev, rngdev);
return hwrng_register(&rngdev->rng);
}
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;
}
static struct platform_driver tx4939_rng_driver = {
.driver = {
.name = "tx4939-rng",
.owner = THIS_MODULE,
},
.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_DESCRIPTION("H/W Random Number Generator (RNG) driver for TX4939");
MODULE_LICENSE("GPL");
+224
View File
@@ -0,0 +1,224 @@
/*
* RNG driver for VIA RNGs
*
* Copyright 2005 (c) MontaVista Software, Inc.
*
* with the majority of the code coming from:
*
* Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
* (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
*
* derived from
*
* Hardware driver for the AMD 768 Random Number Generator (RNG)
* (c) Copyright 2001 Red Hat Inc
*
* derived from
*
* Hardware driver for Intel i810 Random Number Generator (RNG)
* Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.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.
*/
#include <crypto/padlock.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/hw_random.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/msr.h>
#include <asm/cpufeature.h>
#include <asm/i387.h>
enum {
VIA_STRFILT_CNT_SHIFT = 16,
VIA_STRFILT_FAIL = (1 << 15),
VIA_STRFILT_ENABLE = (1 << 14),
VIA_RAWBITS_ENABLE = (1 << 13),
VIA_RNG_ENABLE = (1 << 6),
VIA_NOISESRC1 = (1 << 8),
VIA_NOISESRC2 = (1 << 9),
VIA_XSTORE_CNT_MASK = 0x0F,
VIA_RNG_CHUNK_8 = 0x00, /* 64 rand bits, 64 stored bits */
VIA_RNG_CHUNK_4 = 0x01, /* 32 rand bits, 32 stored bits */
VIA_RNG_CHUNK_4_MASK = 0xFFFFFFFF,
VIA_RNG_CHUNK_2 = 0x02, /* 16 rand bits, 32 stored bits */
VIA_RNG_CHUNK_2_MASK = 0xFFFF,
VIA_RNG_CHUNK_1 = 0x03, /* 8 rand bits, 32 stored bits */
VIA_RNG_CHUNK_1_MASK = 0xFF,
};
/*
* Investigate using the 'rep' prefix to obtain 32 bits of random data
* in one insn. The upside is potentially better performance. The
* downside is that the instruction becomes no longer atomic. Due to
* this, just like familiar issues with /dev/random itself, the worst
* case of a 'rep xstore' could potentially pause a cpu for an
* unreasonably long time. In practice, this condition would likely
* only occur when the hardware is failing. (or so we hope :))
*
* Another possible performance boost may come from simply buffering
* until we have 4 bytes, thus returning a u32 at a time,
* instead of the current u8-at-a-time.
*
* Padlock instructions can generate a spurious DNA fault, so
* we have to call them in the context of irq_ts_save/restore()
*/
static inline u32 xstore(u32 *addr, u32 edx_in)
{
u32 eax_out;
int ts_state;
ts_state = irq_ts_save();
asm(".byte 0x0F,0xA7,0xC0 /* xstore %%edi (addr=%0) */"
: "=m" (*addr), "=a" (eax_out), "+d" (edx_in), "+D" (addr));
irq_ts_restore(ts_state);
return eax_out;
}
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;
int i;
/* We choose the recommended 1-byte-per-instruction RNG rate,
* for greater randomness at the expense of speed. Larger
* values 2, 4, or 8 bytes-per-instruction yield greater
* speed at lesser randomness.
*
* If you change this to another VIA_CHUNK_n, you must also
* change the ->n_bytes values in rng_vendor_ops[] tables.
* VIA_CHUNK_8 requires further code changes.
*
* A copy of MSR_VIA_RNG is placed in eax_out when xstore
* completes.
*/
for (i = 0; i < 20; i++) {
*via_rng_datum = 0; /* paranoia, not really necessary */
bytes_out = xstore(via_rng_datum, VIA_RNG_CHUNK_1);
bytes_out &= VIA_XSTORE_CNT_MASK;
if (bytes_out || !wait)
break;
udelay(10);
}
rng->priv = *via_rng_datum;
return bytes_out ? 1 : 0;
}
static int via_rng_data_read(struct hwrng *rng, u32 *data)
{
u32 via_rng_datum = (u32)rng->priv;
*data = via_rng_datum;
return 1;
}
static int via_rng_init(struct hwrng *rng)
{
struct cpuinfo_x86 *c = &cpu_data(0);
u32 lo, hi, old_lo;
/* VIA Nano CPUs don't have the MSR_VIA_RNG anymore. The RNG
* is always enabled if CPUID rng_en is set. There is no
* RNG configuration like it used to be the case in this
* register */
if ((c->x86 == 6) && (c->x86_model >= 0x0f)) {
if (!cpu_has_xstore_enabled) {
printk(KERN_ERR PFX "can't enable hardware RNG "
"if XSTORE is not enabled\n");
return -ENODEV;
}
return 0;
}
/* Control the RNG via MSR. Tread lightly and pay very close
* close attention to values written, as the reserved fields
* are documented to be "undefined and unpredictable"; but it
* does not say to write them as zero, so I make a guess that
* we restore the values we find in the register.
*/
rdmsr(MSR_VIA_RNG, lo, hi);
old_lo = lo;
lo &= ~(0x7f << VIA_STRFILT_CNT_SHIFT);
lo &= ~VIA_XSTORE_CNT_MASK;
lo &= ~(VIA_STRFILT_ENABLE | VIA_STRFILT_FAIL | VIA_RAWBITS_ENABLE);
lo |= VIA_RNG_ENABLE;
lo |= VIA_NOISESRC1;
/* Enable secondary noise source on CPUs where it is present. */
/* Nehemiah stepping 8 and higher */
if ((c->x86_model == 9) && (c->x86_mask > 7))
lo |= VIA_NOISESRC2;
/* Esther */
if (c->x86_model >= 10)
lo |= VIA_NOISESRC2;
if (lo != old_lo)
wrmsr(MSR_VIA_RNG, lo, hi);
/* perhaps-unnecessary sanity check; remove after testing if
unneeded */
rdmsr(MSR_VIA_RNG, lo, hi);
if ((lo & VIA_RNG_ENABLE) == 0) {
printk(KERN_ERR PFX "cannot enable VIA C3 RNG, aborting\n");
return -ENODEV;
}
return 0;
}
static struct hwrng via_rng = {
.name = "via",
.init = via_rng_init,
.data_present = via_rng_data_present,
.data_read = via_rng_data_read,
};
static int __init mod_init(void)
{
int err;
if (!cpu_has_xstore)
return -ENODEV;
printk(KERN_INFO "VIA RNG detected\n");
err = hwrng_register(&via_rng);
if (err) {
printk(KERN_ERR PFX "RNG registering failed (%d)\n",
err);
goto out;
}
out:
return err;
}
static void __exit mod_exit(void)
{
hwrng_unregister(&via_rng);
}
module_init(mod_init);
module_exit(mod_exit);
MODULE_DESCRIPTION("H/W RNG driver for VIA CPU with PadLock");
MODULE_LICENSE("GPL");
+140
View File
@@ -0,0 +1,140 @@
/*
* Randomness driver for virtio
* Copyright (C) 2007, 2008 Rusty Russell 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/err.h>
#include <linux/hw_random.h>
#include <linux/scatterlist.h>
#include <linux/spinlock.h>
#include <linux/virtio.h>
#include <linux/virtio_rng.h>
#include <linux/module.h>
static struct virtqueue *vq;
static unsigned int data_avail;
static DECLARE_COMPLETION(have_data);
static bool busy;
static void random_recv_done(struct virtqueue *vq)
{
/* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */
if (!virtqueue_get_buf(vq, &data_avail))
return;
complete(&have_data);
}
/* The host will fill any buffer we give it with sweet, sweet randomness. */
static void register_buffer(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, GFP_KERNEL) < 0)
BUG();
virtqueue_kick(vq);
}
static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait)
{
if (!busy) {
busy = true;
init_completion(&have_data);
register_buffer(buf, size);
}
if (!wait)
return 0;
wait_for_completion(&have_data);
busy = false;
return data_avail;
}
static void virtio_cleanup(struct hwrng *rng)
{
if (busy)
wait_for_completion(&have_data);
}
static struct hwrng virtio_hwrng = {
.name = "virtio",
.cleanup = virtio_cleanup,
.read = virtio_read,
};
static int virtrng_probe(struct virtio_device *vdev)
{
int err;
/* We expect a single virtqueue. */
vq = virtio_find_single_vq(vdev, random_recv_done, "input");
if (IS_ERR(vq))
return PTR_ERR(vq);
err = hwrng_register(&virtio_hwrng);
if (err) {
vdev->config->del_vqs(vdev);
return err;
}
return 0;
}
static void __devexit virtrng_remove(struct virtio_device *vdev)
{
vdev->config->reset(vdev);
hwrng_unregister(&virtio_hwrng);
vdev->config->del_vqs(vdev);
}
static struct virtio_device_id id_table[] = {
{ VIRTIO_ID_RNG, VIRTIO_DEV_ANY_ID },
{ 0 },
};
static struct virtio_driver virtio_rng_driver = {
.driver.name = KBUILD_MODNAME,
.driver.owner = THIS_MODULE,
.id_table = id_table,
.probe = virtrng_probe,
.remove = __devexit_p(virtrng_remove),
};
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_DEVICE_TABLE(virtio, id_table);
MODULE_DESCRIPTION("Virtio random number driver");
MODULE_LICENSE("GPL");