Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
93
drivers/char/hw_random/Kconfig
Normal file
93
drivers/char/hw_random/Kconfig
Normal file
@@ -0,0 +1,93 @@
|
||||
#
|
||||
# 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.
|
||||
|
||||
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 && 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_GEODE
|
||||
tristate "AMD Geode HW Random Number Generator support"
|
||||
depends on HW_RANDOM && X86 && 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_VIA
|
||||
tristate "VIA HW Random Number Generator support"
|
||||
depends on HW_RANDOM && X86_32
|
||||
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_OMAP24XX)
|
||||
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.
|
||||
12
drivers/char/hw_random/Makefile
Normal file
12
drivers/char/hw_random/Makefile
Normal file
@@ -0,0 +1,12 @@
|
||||
#
|
||||
# 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_INTEL) += intel-rng.o
|
||||
obj-$(CONFIG_HW_RANDOM_AMD) += amd-rng.o
|
||||
obj-$(CONFIG_HW_RANDOM_GEODE) += geode-rng.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
|
||||
152
drivers/char/hw_random/amd-rng.c
Normal file
152
drivers/char/hw_random/amd-rng.c
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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 <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>
|
||||
*
|
||||
* 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 <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[] = {
|
||||
{ 0x1022, 0x7443, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
|
||||
{ 0x1022, 0x746b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 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)
|
||||
{
|
||||
u32 pmbase = (u32)rng->priv;
|
||||
|
||||
return !!(inl(pmbase + 0xF4) & 1);
|
||||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit mod_exit(void)
|
||||
{
|
||||
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");
|
||||
355
drivers/char/hw_random/core.c
Normal file
355
drivers/char/hw_random/core.c
Normal file
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
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 <mbuesch@freenet.de>
|
||||
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 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 inline int hwrng_data_present(struct hwrng *rng)
|
||||
{
|
||||
if (!rng->data_present)
|
||||
return 1;
|
||||
return rng->data_present(rng);
|
||||
}
|
||||
|
||||
static inline int hwrng_data_read(struct hwrng *rng, u32 *data)
|
||||
{
|
||||
return rng->data_read(rng, data);
|
||||
}
|
||||
|
||||
|
||||
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 ssize_t rng_dev_read(struct file *filp, char __user *buf,
|
||||
size_t size, loff_t *offp)
|
||||
{
|
||||
u32 data;
|
||||
ssize_t ret = 0;
|
||||
int i, err = 0;
|
||||
int data_present;
|
||||
int bytes_read;
|
||||
|
||||
while (size) {
|
||||
err = -ERESTARTSYS;
|
||||
if (mutex_lock_interruptible(&rng_mutex))
|
||||
goto out;
|
||||
if (!current_rng) {
|
||||
mutex_unlock(&rng_mutex);
|
||||
err = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
if (filp->f_flags & O_NONBLOCK) {
|
||||
data_present = hwrng_data_present(current_rng);
|
||||
} else {
|
||||
/* Some RNG require some time between data_reads to gather
|
||||
* new entropy. Poll it.
|
||||
*/
|
||||
for (i = 0; i < 20; i++) {
|
||||
data_present = hwrng_data_present(current_rng);
|
||||
if (data_present)
|
||||
break;
|
||||
udelay(10);
|
||||
}
|
||||
}
|
||||
bytes_read = 0;
|
||||
if (data_present)
|
||||
bytes_read = hwrng_data_read(current_rng, &data);
|
||||
mutex_unlock(&rng_mutex);
|
||||
|
||||
err = -EAGAIN;
|
||||
if (!bytes_read && (filp->f_flags & O_NONBLOCK))
|
||||
goto out;
|
||||
|
||||
err = -EFAULT;
|
||||
while (bytes_read && size) {
|
||||
if (put_user((u8)data, buf++))
|
||||
goto out;
|
||||
size--;
|
||||
ret++;
|
||||
bytes_read--;
|
||||
data >>= 8;
|
||||
}
|
||||
|
||||
if (need_resched())
|
||||
schedule_timeout_interruptible(1);
|
||||
err = -ERESTARTSYS;
|
||||
if (signal_pending(current))
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
return ret ? : err;
|
||||
}
|
||||
|
||||
|
||||
static const struct file_operations rng_chrdev_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = rng_dev_open,
|
||||
.read = rng_dev_read,
|
||||
};
|
||||
|
||||
static struct miscdevice rng_miscdev = {
|
||||
.minor = RNG_MISCDEV_MINOR,
|
||||
.name = RNG_MODULE_NAME,
|
||||
.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)
|
||||
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");
|
||||
132
drivers/char/hw_random/geode-rng.c
Normal file
132
drivers/char/hw_random/geode-rng.c
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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 <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>
|
||||
*
|
||||
* 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 <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_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LX_AES,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 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)
|
||||
{
|
||||
void __iomem *mem = (void __iomem *)rng->priv;
|
||||
|
||||
return !!(readl(mem + GEODE_RNG_STATUS_REG));
|
||||
}
|
||||
|
||||
|
||||
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");
|
||||
385
drivers/char/hw_random/intel-rng.c
Normal file
385
drivers/char/hw_random/intel-rng.c
Normal file
@@ -0,0 +1,385 @@
|
||||
/*
|
||||
* 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 <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>
|
||||
*
|
||||
* 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 <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)
|
||||
{
|
||||
void __iomem *mem = (void __iomem *)rng->priv;
|
||||
|
||||
return !!(readb(mem + INTEL_RNG_STATUS) & INTEL_RNG_DATA_PRESENT);
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static char __initdata waitflag;
|
||||
|
||||
static void __init intel_init_wait(void *unused)
|
||||
{
|
||||
while (waitflag)
|
||||
cpu_relax();
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __init mod_init(void)
|
||||
{
|
||||
int err = -ENODEV;
|
||||
unsigned i;
|
||||
struct pci_dev *dev = NULL;
|
||||
void __iomem *mem;
|
||||
unsigned long flags;
|
||||
u8 bios_cntl_off, fwh_dec_en1_off;
|
||||
u8 bios_cntl_val = 0xff, fwh_dec_en1_val = 0xff;
|
||||
u8 hw_status, mfc, dvc;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* Check for Intel 82802 */
|
||||
if (dev->device < 0x2640) {
|
||||
fwh_dec_en1_off = FWH_DEC_EN1_REG_OLD;
|
||||
bios_cntl_off = BIOS_CNTL_REG_OLD;
|
||||
} else {
|
||||
fwh_dec_en1_off = FWH_DEC_EN1_REG_NEW;
|
||||
bios_cntl_off = BIOS_CNTL_REG_NEW;
|
||||
}
|
||||
|
||||
pci_read_config_byte(dev, fwh_dec_en1_off, &fwh_dec_en1_val);
|
||||
pci_read_config_byte(dev, bios_cntl_off, &bios_cntl_val);
|
||||
|
||||
if ((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"
|
||||
KERN_WARNING PFX "don't want to disable this in firmware setup, and if\n"
|
||||
KERN_WARNING PFX "you are certain that your system has a functional\n"
|
||||
KERN_WARNING PFX "RNG, try using the 'no_fwh_detect' option.\n";
|
||||
|
||||
pci_dev_put(dev);
|
||||
if (no_fwh_detect)
|
||||
goto fwh_done;
|
||||
printk(warning);
|
||||
err = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mem = ioremap_nocache(INTEL_FWH_ADDR, INTEL_FWH_ADDR_LEN);
|
||||
if (mem == NULL) {
|
||||
pci_dev_put(dev);
|
||||
err = -EBUSY;
|
||||
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.
|
||||
*/
|
||||
#ifdef CONFIG_SMP
|
||||
set_mb(waitflag, 1);
|
||||
if (smp_call_function(intel_init_wait, NULL, 1, 0) != 0) {
|
||||
set_mb(waitflag, 0);
|
||||
pci_dev_put(dev);
|
||||
printk(KERN_ERR PFX "cannot run on all processors\n");
|
||||
err = -EAGAIN;
|
||||
goto err_unmap;
|
||||
}
|
||||
#endif
|
||||
local_irq_save(flags);
|
||||
|
||||
if (!(fwh_dec_en1_val & FWH_F8_EN_MASK))
|
||||
pci_write_config_byte(dev,
|
||||
fwh_dec_en1_off,
|
||||
fwh_dec_en1_val | FWH_F8_EN_MASK);
|
||||
if (!(bios_cntl_val & BIOS_CNTL_WRITE_ENABLE_MASK))
|
||||
pci_write_config_byte(dev,
|
||||
bios_cntl_off,
|
||||
bios_cntl_val | BIOS_CNTL_WRITE_ENABLE_MASK);
|
||||
|
||||
writeb(INTEL_FWH_RESET_CMD, mem);
|
||||
writeb(INTEL_FWH_READ_ID_CMD, mem);
|
||||
mfc = readb(mem + INTEL_FWH_MANUFACTURER_CODE_ADDRESS);
|
||||
dvc = readb(mem + INTEL_FWH_DEVICE_CODE_ADDRESS);
|
||||
writeb(INTEL_FWH_RESET_CMD, mem);
|
||||
|
||||
if (!(bios_cntl_val &
|
||||
(BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK)))
|
||||
pci_write_config_byte(dev, bios_cntl_off, bios_cntl_val);
|
||||
if (!(fwh_dec_en1_val & FWH_F8_EN_MASK))
|
||||
pci_write_config_byte(dev, fwh_dec_en1_off, fwh_dec_en1_val);
|
||||
|
||||
local_irq_restore(flags);
|
||||
#ifdef CONFIG_SMP
|
||||
/* Tell other CPUs to resume. */
|
||||
set_mb(waitflag, 0);
|
||||
#endif
|
||||
|
||||
iounmap(mem);
|
||||
pci_dev_put(dev);
|
||||
|
||||
if (mfc != INTEL_FWH_MANUFACTURER_CODE ||
|
||||
(dvc != INTEL_FWH_DEVICE_CODE_8M &&
|
||||
dvc != INTEL_FWH_DEVICE_CODE_4M)) {
|
||||
printk(KERN_ERR PFX "FWH not detected\n");
|
||||
err = -ENODEV;
|
||||
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)
|
||||
goto err_unmap;
|
||||
|
||||
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);
|
||||
goto err_unmap;
|
||||
}
|
||||
out:
|
||||
return err;
|
||||
|
||||
err_unmap:
|
||||
iounmap(mem);
|
||||
goto out;
|
||||
}
|
||||
|
||||
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");
|
||||
72
drivers/char/hw_random/ixp4xx-rng.c
Normal file
72
drivers/char/hw_random/ixp4xx-rng.c
Normal file
@@ -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 <asm/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");
|
||||
205
drivers/char/hw_random/omap-rng.c
Normal file
205
drivers/char/hw_random/omap-rng.c
Normal file
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* drivers/char/hw_random/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<72><6A><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.
|
||||
*
|
||||
* TODO:
|
||||
*
|
||||
* - Make status updated be interrupt driven so we don't poll
|
||||
*
|
||||
*/
|
||||
|
||||
#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 <asm/io.h>
|
||||
|
||||
#define RNG_OUT_REG 0x00 /* Output register */
|
||||
#define RNG_STAT_REG 0x04 /* Status register
|
||||
[0] = STAT_BUSY */
|
||||
#define RNG_ALARM_REG 0x24 /* Alarm register
|
||||
[7:0] = ALARM_COUNTER */
|
||||
#define RNG_CONFIG_REG 0x28 /* Configuration register
|
||||
[11:6] = RESET_COUNT
|
||||
[5:3] = RING2_DELAY
|
||||
[2:0] = RING1_DELAY */
|
||||
#define RNG_REV_REG 0x3c /* Revision register
|
||||
[7:0] = REV_NB */
|
||||
#define RNG_MASK_REG 0x40 /* Mask and reset register
|
||||
[2] = IT_EN
|
||||
[1] = SOFTRESET
|
||||
[0] = AUTOIDLE */
|
||||
#define RNG_SYSSTATUS 0x44 /* System status
|
||||
[0] = RESETDONE */
|
||||
|
||||
static void __iomem *rng_base;
|
||||
static struct clk *rng_ick;
|
||||
static struct platform_device *rng_dev;
|
||||
|
||||
static u32 omap_rng_read_reg(int reg)
|
||||
{
|
||||
return __raw_readl(rng_base + reg);
|
||||
}
|
||||
|
||||
static void omap_rng_write_reg(int reg, u32 val)
|
||||
{
|
||||
__raw_writel(val, rng_base + reg);
|
||||
}
|
||||
|
||||
/* REVISIT: Does the status bit really work on 16xx? */
|
||||
static int omap_rng_data_present(struct hwrng *rng)
|
||||
{
|
||||
return omap_rng_read_reg(RNG_STAT_REG) ? 0 : 1;
|
||||
}
|
||||
|
||||
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 __init omap_rng_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res, *mem;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* A bit ugly, and it will never actually happen but there can
|
||||
* be only one RNG and this catches any bork
|
||||
*/
|
||||
BUG_ON(rng_dev);
|
||||
|
||||
if (cpu_is_omap24xx()) {
|
||||
rng_ick = clk_get(NULL, "rng_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)
|
||||
return -ENOENT;
|
||||
|
||||
mem = request_mem_region(res->start, res->end - res->start + 1,
|
||||
pdev->name);
|
||||
if (mem == NULL)
|
||||
return -EBUSY;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, mem);
|
||||
rng_base = (u32 __iomem *)io_p2v(res->start);
|
||||
|
||||
ret = hwrng_register(&omap_rng_ops);
|
||||
if (ret) {
|
||||
release_resource(mem);
|
||||
rng_base = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int __exit omap_rng_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *mem = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
hwrng_unregister(&omap_rng_ops);
|
||||
|
||||
omap_rng_write_reg(RNG_MASK_REG, 0x0);
|
||||
|
||||
if (cpu_is_omap24xx()) {
|
||||
clk_disable(rng_ick);
|
||||
clk_put(rng_ick);
|
||||
}
|
||||
|
||||
release_resource(mem);
|
||||
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
|
||||
|
||||
|
||||
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");
|
||||
183
drivers/char/hw_random/via-rng.c
Normal file
183
drivers/char/hw_random/via-rng.c
Normal file
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* 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 <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>
|
||||
*
|
||||
* 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 <asm/io.h>
|
||||
#include <asm/msr.h>
|
||||
#include <asm/cpufeature.h>
|
||||
|
||||
|
||||
#define PFX KBUILD_MODNAME ": "
|
||||
|
||||
|
||||
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_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.
|
||||
*/
|
||||
|
||||
static inline u32 xstore(u32 *addr, u32 edx_in)
|
||||
{
|
||||
u32 eax_out;
|
||||
|
||||
asm(".byte 0x0F,0xA7,0xC0 /* xstore %%edi (addr=%0) */"
|
||||
:"=m"(*addr), "=a"(eax_out)
|
||||
:"D"(addr), "d"(edx_in));
|
||||
|
||||
return eax_out;
|
||||
}
|
||||
|
||||
static int via_rng_data_present(struct hwrng *rng)
|
||||
{
|
||||
u32 bytes_out;
|
||||
u32 *via_rng_datum = (u32 *)(&rng->priv);
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
*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 == 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
u32 lo, hi, old_lo;
|
||||
|
||||
/* 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;
|
||||
|
||||
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 chipsets");
|
||||
MODULE_LICENSE("GPL");
|
||||
Reference in New Issue
Block a user