satip-axe/kernel/drivers/stm/rng.c
2015-03-26 17:24:57 +01:00

191 lines
4.1 KiB
C

/*
* File: stm_rng.c
*
* Hardware Random Number Generator Support for STM STx7109 / STx7200 RNG
* (c) Copyright 2008 ST Microelectronics (R&D) Ltd.
*
* Author: <carl.shaw@st.com>
*
* ----------------------------------------------------------
* This software may be used and distributed according to the terms
* of the GNU General Public License v2, incorporated herein by reference.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/random.h>
#include <linux/timer.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/io.h>
/*
* core module and version information
*/
#define RNG_VERSION "1.0"
#define RNG_MODULE_NAME "stm-rng"
#define RNG_DRIVER_NAME RNG_MODULE_NAME " hardware driver " RNG_VERSION
#define PFX RNG_MODULE_NAME ": "
/* Debug Macros */
/* #define DEBUG */
#ifdef DEBUG
#define DPRINTK(fmt, args...) \
printk(KERN_DEBUG PFX "%s: " fmt, __FUNCTION__ , ## args)
#else
#define DPRINTK(fmt, args...)
#endif
/*
* Defines
*/
/* per-cpu buffer size in 16 bit words */
#define STM_RNG_BUFFSIZE 512
/* register offsets */
#define STM_RNG_STATUS_REG 0x20
#define STM_RNG_DATA_REG 0x24
/*
* Local variables
*/
struct timer_list stm_rng_timer;
static void __iomem *stm_rng_base;
static unsigned short *stm_rng_buffer;
static unsigned long stm_rng_bufcnt;
static spinlock_t stm_rng_spinlock = SPIN_LOCK_UNLOCKED;
/*
* The real work is done by the poll function below.
* The timer should fire every 10 ms
*/
static void stm_rng_poll(unsigned long arg)
{
u32 val;
mod_timer(&stm_rng_timer, jiffies + (HZ / 100));
spin_lock(&stm_rng_spinlock);
/* data OK ? */
val = readl(stm_rng_base + STM_RNG_STATUS_REG);
if ((val & 3) == 0) {
/* Get random number and add to our entropy pool */
val = readl(stm_rng_base + STM_RNG_DATA_REG);
stm_rng_buffer[stm_rng_bufcnt] =
(unsigned short)(val & 0x0000ffff);
stm_rng_bufcnt++;
if (stm_rng_bufcnt == STM_RNG_BUFFSIZE) {
DPRINTK("adding RNG data to /dev/random\n");
add_random_data((char *)stm_rng_buffer,
STM_RNG_BUFFSIZE*sizeof(short));
stm_rng_bufcnt = 0;
}
}
spin_unlock(&stm_rng_spinlock);
}
/*
* Platform bus support
*/
static int __init stm_rng_probe(struct platform_device *rng_device)
{
struct resource *res;
if (!rng_device->name) {
pr_err(PFX "Device probe failed. "
"Check your kernel SoC config!!\n");
return -ENODEV;
}
res = platform_get_resource(rng_device, IORESOURCE_MEM, 0);
if (!res) {
pr_err(PFX "RNG config not found. "
"Check your kernel SoC config!!\n");
return -ENODEV;
}
DPRINTK("RNG physical base address = 0x%08x\n", res->start);
if (res->start == 0) {
pr_err(PFX "RNG base address undefined. "
"Check your SoC config!!\n");
return -ENODEV;
}
stm_rng_base = ioremap(res->start, 0x28);
if (stm_rng_base == NULL) {
pr_err(PFX "Cannot ioremap RNG memory\n");
return -EBUSY;
}
stm_rng_buffer = kmalloc(STM_RNG_BUFFSIZE * sizeof(short), GFP_KERNEL);
if (stm_rng_buffer == NULL) {
pr_err(PFX "Cannot allocate entropy words buffer\n");
return -ENOMEM;
}
stm_rng_bufcnt = 0;
init_timer(&stm_rng_timer);
stm_rng_timer.function = stm_rng_poll;
stm_rng_timer.expires = jiffies + (HZ / 100);
add_timer(&stm_rng_timer);
pr_info(RNG_DRIVER_NAME " configured\n");
return 0;
}
static int stm_rng_remove(struct platform_device *dev)
{
del_timer_sync(&stm_rng_timer);
iounmap(stm_rng_base);
kfree(stm_rng_buffer);
return 0;
}
static struct platform_driver stm_rng_driver = {
.driver.name = "stm-rng",
.driver.owner = THIS_MODULE,
.probe = stm_rng_probe,
.remove = stm_rng_remove,
};
/*
* rng_init - initialize RNG poll timer
*/
static int __init stm_rng_init(void)
{
return platform_driver_register(&stm_rng_driver);
}
/*
* rng_init - shutdown RNG module
*/
static void __exit stm_rng_cleanup(void)
{
platform_driver_unregister(&stm_rng_driver);
}
MODULE_AUTHOR("ST Microelectronics R&D Ltd. <carl.shaw@st.com>");
MODULE_DESCRIPTION("STM H/W Random Number Generator (RNG) driver");
MODULE_LICENSE("GPL");
module_init(stm_rng_init);
module_exit(stm_rng_cleanup);