2006-06-26 00:25:03 -07:00
/*
2008-04-16 19:24:42 +08:00
* omap - rng . c - RNG driver for TI OMAP CPU family
2006-06-26 00:25:03 -07:00
*
* Author : Deepak Saxena < dsaxena @ plexity . net >
*
* Copyright 2005 ( c ) MontaVista Software , Inc .
*
* Mostly based on original driver :
*
* Copyright ( C ) 2005 Nokia Corporation
2007-10-19 23:21:04 +02:00
* Author : Juha Yrjölä < juha . yrjola @ nokia . com >
2006-06-26 00:25:03 -07:00
*
* 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>
2006-08-05 12:14:04 -07:00
# include <linux/clk.h>
2006-06-26 00:25:03 -07:00
# include <linux/err.h>
2006-08-05 12:14:04 -07:00
# include <linux/platform_device.h>
2006-06-26 00:25:03 -07:00
# include <linux/hw_random.h>
2007-11-21 12:24:45 +08:00
# include <linux/delay.h>
2006-06-26 00:25:03 -07:00
# 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 ;
2006-08-05 12:14:04 -07:00
static struct platform_device * rng_dev ;
2006-06-26 00:25:03 -07:00
2008-04-16 19:24:42 +08:00
static inline u32 omap_rng_read_reg ( int reg )
2006-06-26 00:25:03 -07:00
{
return __raw_readl ( rng_base + reg ) ;
}
2008-04-16 19:24:42 +08:00
static inline void omap_rng_write_reg ( int reg , u32 val )
2006-06-26 00:25:03 -07:00
{
__raw_writel ( val , rng_base + reg ) ;
}
2007-11-21 12:24:45 +08:00
static int omap_rng_data_present ( struct hwrng * rng , int wait )
2006-06-26 00:25:03 -07:00
{
2007-11-21 12:24:45 +08:00
int data , i ;
for ( i = 0 ; i < 20 ; i + + ) {
data = omap_rng_read_reg ( RNG_STAT_REG ) ? 0 : 1 ;
if ( data | | ! wait )
break ;
2008-04-16 19:24:42 +08:00
/* 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 ?
*/
2007-11-21 12:24:45 +08:00
udelay ( 10 ) ;
}
return data ;
2006-06-26 00:25:03 -07:00
}
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 ,
} ;
2009-03-29 15:47:06 +08:00
static int __devinit omap_rng_probe ( struct platform_device * pdev )
2006-06-26 00:25:03 -07:00
{
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
*/
2008-04-16 19:24:42 +08:00
if ( rng_dev )
return - EBUSY ;
2006-06-26 00:25:03 -07:00
2006-08-05 12:14:04 -07:00
if ( cpu_is_omap24xx ( ) ) {
2009-01-19 20:58:56 +00:00
rng_ick = clk_get ( & pdev - > dev , " ick " ) ;
2006-06-26 00:25:03 -07:00
if ( IS_ERR ( rng_ick ) ) {
2006-08-05 12:14:04 -07:00
dev_err ( & pdev - > dev , " Could not get rng_ick \n " ) ;
2006-06-26 00:25:03 -07:00
ret = PTR_ERR ( rng_ick ) ;
return ret ;
2006-08-05 12:14:04 -07:00
} else
clk_enable ( rng_ick ) ;
2006-06-26 00:25:03 -07:00
}
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 ) ;
2008-09-04 14:07:22 +01:00
if ( mem = = NULL ) {
ret = - EBUSY ;
goto err_region ;
}
2006-06-26 00:25:03 -07:00
2006-08-05 12:14:04 -07:00
dev_set_drvdata ( & pdev - > dev , mem ) ;
2008-09-04 14:07:22 +01:00
rng_base = ioremap ( res - > start , res - > end - res - > start + 1 ) ;
if ( ! rng_base ) {
ret = - ENOMEM ;
goto err_ioremap ;
}
2006-06-26 00:25:03 -07:00
ret = hwrng_register ( & omap_rng_ops ) ;
2008-09-04 14:07:22 +01:00
if ( ret )
goto err_register ;
2006-06-26 00:25:03 -07:00
2006-08-05 12:14:04 -07:00
dev_info ( & pdev - > dev , " OMAP Random Number Generator ver. %02x \n " ,
2006-06-26 00:25:03 -07:00
omap_rng_read_reg ( RNG_REV_REG ) ) ;
omap_rng_write_reg ( RNG_MASK_REG , 0x1 ) ;
2006-08-05 12:14:04 -07:00
rng_dev = pdev ;
2006-06-26 00:25:03 -07:00
return 0 ;
2008-09-04 14:07:22 +01:00
err_register :
iounmap ( rng_base ) ;
rng_base = NULL ;
err_ioremap :
release_resource ( mem ) ;
err_region :
if ( cpu_is_omap24xx ( ) ) {
clk_disable ( rng_ick ) ;
clk_put ( rng_ick ) ;
}
return ret ;
2006-06-26 00:25:03 -07:00
}
2006-08-05 12:14:04 -07:00
static int __exit omap_rng_remove ( struct platform_device * pdev )
2006-06-26 00:25:03 -07:00
{
2006-08-05 12:14:04 -07:00
struct resource * mem = dev_get_drvdata ( & pdev - > dev ) ;
2006-06-26 00:25:03 -07:00
hwrng_unregister ( & omap_rng_ops ) ;
omap_rng_write_reg ( RNG_MASK_REG , 0x0 ) ;
2008-09-04 14:07:22 +01:00
iounmap ( rng_base ) ;
2006-06-26 00:25:03 -07:00
if ( cpu_is_omap24xx ( ) ) {
2006-08-05 12:14:04 -07:00
clk_disable ( rng_ick ) ;
2006-06-26 00:25:03 -07:00
clk_put ( rng_ick ) ;
}
release_resource ( mem ) ;
rng_base = NULL ;
return 0 ;
}
# ifdef CONFIG_PM
2006-08-05 12:14:04 -07:00
static int omap_rng_suspend ( struct platform_device * pdev , pm_message_t message )
2006-06-26 00:25:03 -07:00
{
omap_rng_write_reg ( RNG_MASK_REG , 0x0 ) ;
return 0 ;
}
2006-08-05 12:14:04 -07:00
static int omap_rng_resume ( struct platform_device * pdev )
2006-06-26 00:25:03 -07:00
{
omap_rng_write_reg ( RNG_MASK_REG , 0x1 ) ;
2006-08-05 12:14:04 -07:00
return 0 ;
2006-06-26 00:25:03 -07:00
}
# else
# define omap_rng_suspend NULL
# define omap_rng_resume NULL
# endif
2008-04-16 19:24:42 +08:00
/* work with hotplug and coldplug */
MODULE_ALIAS ( " platform:omap_rng " ) ;
2006-06-26 00:25:03 -07:00
2006-08-05 12:14:04 -07:00
static struct platform_driver omap_rng_driver = {
. driver = {
. name = " omap_rng " ,
. owner = THIS_MODULE ,
} ,
2006-06-26 00:25:03 -07:00
. 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 ;
2006-08-05 12:14:04 -07:00
return platform_driver_register ( & omap_rng_driver ) ;
2006-06-26 00:25:03 -07:00
}
static void __exit omap_rng_exit ( void )
{
2006-08-05 12:14:04 -07:00
platform_driver_unregister ( & omap_rng_driver ) ;
2006-06-26 00:25:03 -07:00
}
module_init ( omap_rng_init ) ;
module_exit ( omap_rng_exit ) ;
MODULE_AUTHOR ( " Deepak Saxena (and others) " ) ;
MODULE_LICENSE ( " GPL " ) ;