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>
# 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>
2012-09-23 17:28:26 -06:00
# include <linux/slab.h>
2012-09-23 17:28:26 -06:00
# include <linux/pm_runtime.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 */
2012-09-23 17:28:26 -06:00
/**
* struct omap_rng_private_data - RNG IP block - specific data
* @ base : virtual address of the beginning of the RNG IP block registers
* @ mem_res : struct resource * for the IP block registers physical memory
*/
struct omap_rng_private_data {
void __iomem * base ;
struct resource * mem_res ;
} ;
2006-06-26 00:25:03 -07:00
2012-09-23 17:28:26 -06:00
static inline u32 omap_rng_read_reg ( struct omap_rng_private_data * priv , int reg )
2006-06-26 00:25:03 -07:00
{
2012-09-23 17:28:26 -06:00
return __raw_readl ( priv - > base + reg ) ;
2006-06-26 00:25:03 -07:00
}
2012-09-23 17:28:26 -06:00
static inline void omap_rng_write_reg ( struct omap_rng_private_data * priv ,
int reg , u32 val )
2006-06-26 00:25:03 -07:00
{
2012-09-23 17:28:26 -06:00
__raw_writel ( val , priv - > base + reg ) ;
2006-06-26 00:25:03 -07:00
}
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
{
2012-09-23 17:28:26 -06:00
struct omap_rng_private_data * priv ;
2007-11-21 12:24:45 +08:00
int data , i ;
2012-09-23 17:28:26 -06:00
priv = ( struct omap_rng_private_data * ) rng - > priv ;
2007-11-21 12:24:45 +08:00
for ( i = 0 ; i < 20 ; i + + ) {
2012-09-23 17:28:26 -06:00
data = omap_rng_read_reg ( priv , RNG_STAT_REG ) ? 0 : 1 ;
2007-11-21 12:24:45 +08:00
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 )
{
2012-09-23 17:28:26 -06:00
struct omap_rng_private_data * priv ;
priv = ( struct omap_rng_private_data * ) rng - > priv ;
* data = omap_rng_read_reg ( priv , RNG_OUT_REG ) ;
2006-06-26 00:25:03 -07:00
2012-09-23 17:28:26 -06:00
return sizeof ( u32 ) ;
2006-06-26 00:25:03 -07:00
}
static struct hwrng omap_rng_ops = {
. name = " omap " ,
. data_present = omap_rng_data_present ,
. data_read = omap_rng_data_read ,
} ;
2012-12-21 15:12:08 -08:00
static int omap_rng_probe ( struct platform_device * pdev )
2006-06-26 00:25:03 -07:00
{
2012-09-23 17:28:26 -06:00
struct omap_rng_private_data * priv ;
2006-06-26 00:25:03 -07:00
int ret ;
2012-09-23 17:28:26 -06:00
priv = kzalloc ( sizeof ( struct omap_rng_private_data ) , GFP_KERNEL ) ;
if ( ! priv ) {
dev_err ( & pdev - > dev , " could not allocate memory \n " ) ;
return - ENOMEM ;
} ;
omap_rng_ops . priv = ( unsigned long ) priv ;
2013-05-29 09:47:29 +09:00
platform_set_drvdata ( pdev , priv ) ;
2006-06-26 00:25:03 -07:00
2012-09-23 17:28:26 -06:00
priv - > mem_res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-01-21 11:08:59 +01:00
priv - > base = devm_ioremap_resource ( & pdev - > dev , priv - > mem_res ) ;
if ( IS_ERR ( priv - > base ) ) {
ret = PTR_ERR ( priv - > base ) ;
2008-09-04 14:07:22 +01:00
goto err_ioremap ;
}
2013-05-29 09:47:29 +09:00
platform_set_drvdata ( pdev , priv ) ;
2006-06-26 00:25:03 -07:00
2012-09-23 17:28:26 -06:00
pm_runtime_enable ( & pdev - > dev ) ;
pm_runtime_get_sync ( & pdev - > dev ) ;
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 " ,
2012-09-23 17:28:26 -06:00
omap_rng_read_reg ( priv , RNG_REV_REG ) ) ;
omap_rng_write_reg ( priv , RNG_MASK_REG , 0x1 ) ;
2006-06-26 00:25:03 -07:00
return 0 ;
2008-09-04 14:07:22 +01:00
err_register :
2012-09-23 17:28:26 -06:00
priv - > base = NULL ;
2012-09-23 17:28:26 -06:00
pm_runtime_disable ( & pdev - > dev ) ;
2008-09-04 14:07:22 +01:00
err_ioremap :
2012-09-23 17:28:26 -06:00
kfree ( priv ) ;
2008-09-04 14:07:22 +01:00
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
{
2013-05-29 09:47:29 +09:00
struct omap_rng_private_data * priv = platform_get_drvdata ( pdev ) ;
2012-09-23 17:28:26 -06:00
2006-06-26 00:25:03 -07:00
hwrng_unregister ( & omap_rng_ops ) ;
2012-09-23 17:28:26 -06:00
omap_rng_write_reg ( priv , RNG_MASK_REG , 0x0 ) ;
2012-09-23 17:28:26 -06:00
pm_runtime_put_sync ( & pdev - > dev ) ;
pm_runtime_disable ( & pdev - > dev ) ;
2006-06-26 00:25:03 -07:00
2012-09-23 17:28:26 -06:00
release_mem_region ( priv - > mem_res - > start , resource_size ( priv - > mem_res ) ) ;
kfree ( priv ) ;
2006-06-26 00:25:03 -07:00
return 0 ;
}
2012-08-04 07:11:34 +00:00
# ifdef CONFIG_PM_SLEEP
2006-06-26 00:25:03 -07:00
2012-07-06 19:08:53 +02:00
static int omap_rng_suspend ( struct device * dev )
2006-06-26 00:25:03 -07:00
{
2012-09-23 17:28:26 -06:00
struct omap_rng_private_data * priv = dev_get_drvdata ( dev ) ;
omap_rng_write_reg ( priv , RNG_MASK_REG , 0x0 ) ;
2012-09-23 17:28:26 -06:00
pm_runtime_put_sync ( dev ) ;
2012-09-23 17:28:26 -06:00
2006-06-26 00:25:03 -07:00
return 0 ;
}
2012-07-06 19:08:53 +02:00
static int omap_rng_resume ( struct device * dev )
2006-06-26 00:25:03 -07:00
{
2012-09-23 17:28:26 -06:00
struct omap_rng_private_data * priv = dev_get_drvdata ( dev ) ;
2012-09-23 17:28:26 -06:00
pm_runtime_get_sync ( dev ) ;
2012-09-23 17:28:26 -06:00
omap_rng_write_reg ( priv , RNG_MASK_REG , 0x1 ) ;
2006-08-05 12:14:04 -07:00
return 0 ;
2006-06-26 00:25:03 -07:00
}
2012-07-06 19:08:53 +02:00
static SIMPLE_DEV_PM_OPS ( omap_rng_pm , omap_rng_suspend , omap_rng_resume ) ;
# define OMAP_RNG_PM (&omap_rng_pm)
2006-06-26 00:25:03 -07:00
# else
2012-07-06 19:08:53 +02:00
# define OMAP_RNG_PM NULL
2006-06-26 00:25:03 -07:00
# 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 ,
2012-07-06 19:08:53 +02:00
. pm = OMAP_RNG_PM ,
2006-08-05 12:14:04 -07:00
} ,
2006-06-26 00:25:03 -07:00
. probe = omap_rng_probe ,
. remove = __exit_p ( omap_rng_remove ) ,
} ;
static int __init omap_rng_init ( void )
{
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 " ) ;