191 lines
4.1 KiB
C
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);
|