/* * File: stm_rng.c * * Hardware Random Number Generator Support for STM STx7109 / STx7200 RNG * (c) Copyright 2008 ST Microelectronics (R&D) Ltd. * * Author: * * ---------------------------------------------------------- * This software may be used and distributed according to the terms * of the GNU General Public License v2, incorporated herein by reference. * */ #include #include #include #include #include #include #include #include #include #include /* * 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. "); MODULE_DESCRIPTION("STM H/W Random Number Generator (RNG) driver"); MODULE_LICENSE("GPL"); module_init(stm_rng_init); module_exit(stm_rng_cleanup);