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>
2013-08-05 20:17:21 +05:30
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/of_address.h>
2013-08-05 20:17:23 +05:30
# include <linux/interrupt.h>
2006-06-26 00:25:03 -07:00
# include <asm/io.h>
2013-08-05 20:17:23 +05:30
# define RNG_REG_STATUS_RDY (1 << 0)
# define RNG_REG_INTACK_RDY_MASK (1 << 0)
# define RNG_REG_INTACK_SHUTDOWN_OFLO_MASK (1 << 1)
# define RNG_SHUTDOWN_OFLO_MASK (1 << 1)
# define RNG_CONTROL_STARTUP_CYCLES_SHIFT 16
# define RNG_CONTROL_STARTUP_CYCLES_MASK (0xffff << 16)
# define RNG_CONTROL_ENABLE_TRNG_SHIFT 10
# define RNG_CONTROL_ENABLE_TRNG_MASK (1 << 10)
# define RNG_CONFIG_MAX_REFIL_CYCLES_SHIFT 16
# define RNG_CONFIG_MAX_REFIL_CYCLES_MASK (0xffff << 16)
# define RNG_CONFIG_MIN_REFIL_CYCLES_SHIFT 0
# define RNG_CONFIG_MIN_REFIL_CYCLES_MASK (0xff << 0)
# define RNG_CONTROL_STARTUP_CYCLES 0xff
# define RNG_CONFIG_MIN_REFIL_CYCLES 0x21
# define RNG_CONFIG_MAX_REFIL_CYCLES 0x22
# define RNG_ALARMCNT_ALARM_TH_SHIFT 0x0
# define RNG_ALARMCNT_ALARM_TH_MASK (0xff << 0)
# define RNG_ALARMCNT_SHUTDOWN_TH_SHIFT 16
# define RNG_ALARMCNT_SHUTDOWN_TH_MASK (0x1f << 16)
# define RNG_ALARM_THRESHOLD 0xff
# define RNG_SHUTDOWN_THRESHOLD 0x4
# define RNG_REG_FROENABLE_MASK 0xffffff
# define RNG_REG_FRODETUNE_MASK 0xffffff
# define OMAP2_RNG_OUTPUT_SIZE 0x4
# define OMAP4_RNG_OUTPUT_SIZE 0x8
enum {
RNG_OUTPUT_L_REG = 0 ,
RNG_OUTPUT_H_REG ,
RNG_STATUS_REG ,
RNG_INTMASK_REG ,
RNG_INTACK_REG ,
RNG_CONTROL_REG ,
RNG_CONFIG_REG ,
RNG_ALARMCNT_REG ,
RNG_FROENABLE_REG ,
RNG_FRODETUNE_REG ,
RNG_ALARMMASK_REG ,
RNG_ALARMSTOP_REG ,
RNG_REV_REG ,
RNG_SYSCONFIG_REG ,
} ;
static const u16 reg_map_omap2 [ ] = {
[ RNG_OUTPUT_L_REG ] = 0x0 ,
[ RNG_STATUS_REG ] = 0x4 ,
[ RNG_CONFIG_REG ] = 0x28 ,
[ RNG_REV_REG ] = 0x3c ,
[ RNG_SYSCONFIG_REG ] = 0x40 ,
} ;
static const u16 reg_map_omap4 [ ] = {
[ RNG_OUTPUT_L_REG ] = 0x0 ,
[ RNG_OUTPUT_H_REG ] = 0x4 ,
[ RNG_STATUS_REG ] = 0x8 ,
[ RNG_INTMASK_REG ] = 0xc ,
[ RNG_INTACK_REG ] = 0x10 ,
[ RNG_CONTROL_REG ] = 0x14 ,
[ RNG_CONFIG_REG ] = 0x18 ,
[ RNG_ALARMCNT_REG ] = 0x1c ,
[ RNG_FROENABLE_REG ] = 0x20 ,
[ RNG_FRODETUNE_REG ] = 0x24 ,
[ RNG_ALARMMASK_REG ] = 0x28 ,
[ RNG_ALARMSTOP_REG ] = 0x2c ,
[ RNG_REV_REG ] = 0x1FE0 ,
[ RNG_SYSCONFIG_REG ] = 0x1FE4 ,
} ;
2006-06-26 00:25:03 -07:00
2013-08-05 20:17:23 +05:30
struct omap_rng_dev ;
2012-09-23 17:28:26 -06:00
/**
2013-08-05 20:17:23 +05:30
* struct omap_rng_pdata - RNG IP block - specific data
* @ regs : Pointer to the register offsets structure .
* @ data_size : No . of bytes in RNG output .
* @ data_present : Callback to determine if data is available .
* @ init : Callback for IP specific initialization sequence .
* @ cleanup : Callback for IP specific cleanup sequence .
2012-09-23 17:28:26 -06:00
*/
2013-08-05 20:17:23 +05:30
struct omap_rng_pdata {
u16 * regs ;
u32 data_size ;
u32 ( * data_present ) ( struct omap_rng_dev * priv ) ;
int ( * init ) ( struct omap_rng_dev * priv ) ;
void ( * cleanup ) ( struct omap_rng_dev * priv ) ;
2012-09-23 17:28:26 -06:00
} ;
2006-06-26 00:25:03 -07:00
2013-08-05 20:17:23 +05:30
struct omap_rng_dev {
void __iomem * base ;
struct device * dev ;
const struct omap_rng_pdata * pdata ;
} ;
static inline u32 omap_rng_read ( struct omap_rng_dev * priv , u16 reg )
{
return __raw_readl ( priv - > base + priv - > pdata - > regs [ reg ] ) ;
}
static inline void omap_rng_write ( struct omap_rng_dev * priv , u16 reg ,
u32 val )
2006-06-26 00:25:03 -07:00
{
2013-08-05 20:17:23 +05:30
__raw_writel ( val , priv - > base + priv - > pdata - > regs [ 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
{
2013-08-05 20:17:23 +05:30
struct omap_rng_dev * priv ;
2007-11-21 12:24:45 +08:00
int data , i ;
2013-08-05 20:17:23 +05:30
priv = ( struct omap_rng_dev * ) rng - > priv ;
2012-09-23 17:28:26 -06:00
2007-11-21 12:24:45 +08:00
for ( i = 0 ; i < 20 ; i + + ) {
2013-08-05 20:17:23 +05:30
data = priv - > pdata - > data_present ( priv ) ;
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 )
{
2013-08-05 20:17:23 +05:30
struct omap_rng_dev * priv ;
u32 data_size , i ;
priv = ( struct omap_rng_dev * ) rng - > priv ;
data_size = priv - > pdata - > data_size ;
for ( i = 0 ; i < data_size / sizeof ( u32 ) ; i + + )
data [ i ] = omap_rng_read ( priv , RNG_OUTPUT_L_REG + i ) ;
if ( priv - > pdata - > regs [ RNG_INTACK_REG ] )
omap_rng_write ( priv , RNG_INTACK_REG , RNG_REG_INTACK_RDY_MASK ) ;
return data_size ;
}
2013-08-20 11:07:53 -07:00
static int omap_rng_init ( struct hwrng * rng )
{
struct omap_rng_dev * priv ;
priv = ( struct omap_rng_dev * ) rng - > priv ;
return priv - > pdata - > init ( priv ) ;
}
static void omap_rng_cleanup ( struct hwrng * rng )
{
struct omap_rng_dev * priv ;
priv = ( struct omap_rng_dev * ) rng - > priv ;
priv - > pdata - > cleanup ( priv ) ;
}
static struct hwrng omap_rng_ops = {
. name = " omap " ,
. data_present = omap_rng_data_present ,
. data_read = omap_rng_data_read ,
. init = omap_rng_init ,
. cleanup = omap_rng_cleanup ,
} ;
static inline u32 omap2_rng_data_present ( struct omap_rng_dev * priv )
{
return omap_rng_read ( priv , RNG_STATUS_REG ) ? 0 : 1 ;
}
static int omap2_rng_init ( struct omap_rng_dev * priv )
{
omap_rng_write ( priv , RNG_SYSCONFIG_REG , 0x1 ) ;
return 0 ;
}
static void omap2_rng_cleanup ( struct omap_rng_dev * priv )
{
omap_rng_write ( priv , RNG_SYSCONFIG_REG , 0x0 ) ;
}
static struct omap_rng_pdata omap2_rng_pdata = {
. regs = ( u16 * ) reg_map_omap2 ,
. data_size = OMAP2_RNG_OUTPUT_SIZE ,
. data_present = omap2_rng_data_present ,
. init = omap2_rng_init ,
. cleanup = omap2_rng_cleanup ,
} ;
# if defined(CONFIG_OF)
static inline u32 omap4_rng_data_present ( struct omap_rng_dev * priv )
{
return omap_rng_read ( priv , RNG_STATUS_REG ) & RNG_REG_STATUS_RDY ;
}
2013-08-05 20:17:23 +05:30
static int omap4_rng_init ( struct omap_rng_dev * priv )
{
u32 val ;
/* Return if RNG is already running. */
2015-03-16 12:54:50 +11:00
if ( omap_rng_read ( priv , RNG_CONTROL_REG ) & RNG_CONTROL_ENABLE_TRNG_MASK )
2013-08-05 20:17:23 +05:30
return 0 ;
val = RNG_CONFIG_MIN_REFIL_CYCLES < < RNG_CONFIG_MIN_REFIL_CYCLES_SHIFT ;
val | = RNG_CONFIG_MAX_REFIL_CYCLES < < RNG_CONFIG_MAX_REFIL_CYCLES_SHIFT ;
omap_rng_write ( priv , RNG_CONFIG_REG , val ) ;
omap_rng_write ( priv , RNG_FRODETUNE_REG , 0x0 ) ;
omap_rng_write ( priv , RNG_FROENABLE_REG , RNG_REG_FROENABLE_MASK ) ;
val = RNG_ALARM_THRESHOLD < < RNG_ALARMCNT_ALARM_TH_SHIFT ;
val | = RNG_SHUTDOWN_THRESHOLD < < RNG_ALARMCNT_SHUTDOWN_TH_SHIFT ;
omap_rng_write ( priv , RNG_ALARMCNT_REG , val ) ;
val = RNG_CONTROL_STARTUP_CYCLES < < RNG_CONTROL_STARTUP_CYCLES_SHIFT ;
val | = RNG_CONTROL_ENABLE_TRNG_MASK ;
omap_rng_write ( priv , RNG_CONTROL_REG , val ) ;
return 0 ;
}
static void omap4_rng_cleanup ( struct omap_rng_dev * priv )
{
int val ;
val = omap_rng_read ( priv , RNG_CONTROL_REG ) ;
val & = ~ RNG_CONTROL_ENABLE_TRNG_MASK ;
2015-03-16 10:19:11 +11:00
omap_rng_write ( priv , RNG_CONTROL_REG , val ) ;
2013-08-05 20:17:23 +05:30
}
static irqreturn_t omap4_rng_irq ( int irq , void * dev_id )
{
struct omap_rng_dev * priv = dev_id ;
u32 fro_detune , fro_enable ;
/*
* Interrupt raised by a fro shutdown threshold , do the following :
* 1. Clear the alarm events .
* 2. De tune the FROs which are shutdown .
* 3. Re enable the shutdown FROs .
*/
omap_rng_write ( priv , RNG_ALARMMASK_REG , 0x0 ) ;
omap_rng_write ( priv , RNG_ALARMSTOP_REG , 0x0 ) ;
fro_enable = omap_rng_read ( priv , RNG_FROENABLE_REG ) ;
fro_detune = ~ fro_enable & RNG_REG_FRODETUNE_MASK ;
fro_detune = fro_detune | omap_rng_read ( priv , RNG_FRODETUNE_REG ) ;
fro_enable = RNG_REG_FROENABLE_MASK ;
2012-09-23 17:28:26 -06:00
2013-08-05 20:17:23 +05:30
omap_rng_write ( priv , RNG_FRODETUNE_REG , fro_detune ) ;
omap_rng_write ( priv , RNG_FROENABLE_REG , fro_enable ) ;
2012-09-23 17:28:26 -06:00
2013-08-05 20:17:23 +05:30
omap_rng_write ( priv , RNG_INTACK_REG , RNG_REG_INTACK_SHUTDOWN_OFLO_MASK ) ;
2006-06-26 00:25:03 -07:00
2013-08-05 20:17:23 +05:30
return IRQ_HANDLED ;
2006-06-26 00:25:03 -07:00
}
2013-08-05 20:17:23 +05:30
static struct omap_rng_pdata omap4_rng_pdata = {
. regs = ( u16 * ) reg_map_omap4 ,
. data_size = OMAP4_RNG_OUTPUT_SIZE ,
. data_present = omap4_rng_data_present ,
. init = omap4_rng_init ,
. cleanup = omap4_rng_cleanup ,
} ;
2013-08-05 20:17:21 +05:30
static const struct of_device_id omap_rng_of_match [ ] = {
2013-08-05 20:17:23 +05:30
{
. compatible = " ti,omap2-rng " ,
. data = & omap2_rng_pdata ,
} ,
{
. compatible = " ti,omap4-rng " ,
. data = & omap4_rng_pdata ,
} ,
2013-08-05 20:17:21 +05:30
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , omap_rng_of_match ) ;
2013-08-05 20:17:23 +05:30
static int of_get_omap_rng_device_details ( struct omap_rng_dev * priv ,
struct platform_device * pdev )
{
const struct of_device_id * match ;
struct device * dev = & pdev - > dev ;
int irq , err ;
match = of_match_device ( of_match_ptr ( omap_rng_of_match ) , dev ) ;
if ( ! match ) {
dev_err ( dev , " no compatible OF match \n " ) ;
return - EINVAL ;
}
priv - > pdata = match - > data ;
if ( of_device_is_compatible ( dev - > of_node , " ti,omap4-rng " ) ) {
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 ) {
dev_err ( dev , " %s: error getting IRQ resource - %d \n " ,
__func__ , irq ) ;
return irq ;
}
err = devm_request_irq ( dev , irq , omap4_rng_irq ,
IRQF_TRIGGER_NONE , dev_name ( dev ) , priv ) ;
if ( err ) {
dev_err ( dev , " unable to request irq %d, err = %d \n " ,
irq , err ) ;
return err ;
}
omap_rng_write ( priv , RNG_INTMASK_REG , RNG_SHUTDOWN_OFLO_MASK ) ;
}
return 0 ;
}
# else
static int of_get_omap_rng_device_details ( struct omap_rng_dev * omap_rng ,
struct platform_device * pdev )
{
return - EINVAL ;
}
2013-08-05 20:17:21 +05:30
# endif
2013-08-05 20:17:23 +05:30
static int get_omap_rng_device_details ( struct omap_rng_dev * omap_rng )
{
/* Only OMAP2/3 can be non-DT */
omap_rng - > pdata = & omap2_rng_pdata ;
return 0 ;
}
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
{
2013-08-05 20:17:23 +05:30
struct omap_rng_dev * priv ;
struct resource * res ;
struct device * dev = & pdev - > dev ;
2006-06-26 00:25:03 -07:00
int ret ;
2013-08-05 20:17:23 +05:30
priv = devm_kzalloc ( dev , sizeof ( struct omap_rng_dev ) , GFP_KERNEL ) ;
2014-04-29 17:15:36 +09:00
if ( ! priv )
2012-09-23 17:28:26 -06:00
return - ENOMEM ;
omap_rng_ops . priv = ( unsigned long ) priv ;
2013-05-29 09:47:29 +09:00
platform_set_drvdata ( pdev , priv ) ;
2013-08-05 20:17:23 +05:30
priv - > dev = dev ;
2006-06-26 00:25:03 -07:00
2013-08-05 20:17:23 +05:30
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
priv - > base = devm_ioremap_resource ( dev , res ) ;
2013-01-21 11:08:59 +01:00
if ( IS_ERR ( priv - > base ) ) {
ret = PTR_ERR ( priv - > base ) ;
2008-09-04 14:07:22 +01:00
goto err_ioremap ;
}
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 ) ;
2013-08-05 20:17:23 +05:30
ret = ( dev - > of_node ) ? of_get_omap_rng_device_details ( priv , pdev ) :
get_omap_rng_device_details ( priv ) ;
if ( ret )
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 " ,
2013-08-05 20:17:23 +05:30
omap_rng_read ( priv , RNG_REV_REG ) ) ;
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 :
2013-08-05 20:17:23 +05:30
dev_err ( dev , " initialization failed. \n " ) ;
2008-09-04 14:07:22 +01:00
return ret ;
2006-06-26 00:25:03 -07:00
}
2015-03-09 10:36:35 -07:00
static int omap_rng_remove ( struct platform_device * pdev )
2006-06-26 00:25:03 -07:00
{
2013-08-05 20:17:23 +05:30
struct omap_rng_dev * 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 ) ;
2013-08-05 20:17:23 +05:30
priv - > pdata - > cleanup ( priv ) ;
2012-09-23 17:28:26 -06:00
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
return 0 ;
}
2015-03-11 14:08:36 -07:00
static int __maybe_unused omap_rng_suspend ( struct device * dev )
2006-06-26 00:25:03 -07:00
{
2013-08-05 20:17:23 +05:30
struct omap_rng_dev * priv = dev_get_drvdata ( dev ) ;
2012-09-23 17:28:26 -06:00
2013-08-05 20:17:23 +05:30
priv - > pdata - > cleanup ( priv ) ;
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 ;
}
2015-03-11 14:08:36 -07:00
static int __maybe_unused omap_rng_resume ( struct device * dev )
2006-06-26 00:25:03 -07:00
{
2013-08-05 20:17:23 +05:30
struct omap_rng_dev * priv = dev_get_drvdata ( dev ) ;
2012-09-23 17:28:26 -06:00
2012-09-23 17:28:26 -06:00
pm_runtime_get_sync ( dev ) ;
2013-08-05 20:17:23 +05:30
priv - > pdata - > init ( priv ) ;
2012-09-23 17:28:26 -06:00
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 ) ;
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 " ,
2015-03-11 14:08:36 -07:00
. pm = & omap_rng_pm ,
2013-08-05 20:17:21 +05:30
. of_match_table = of_match_ptr ( omap_rng_of_match ) ,
2006-08-05 12:14:04 -07:00
} ,
2006-06-26 00:25:03 -07:00
. probe = omap_rng_probe ,
2015-03-09 10:36:35 -07:00
. remove = omap_rng_remove ,
2006-06-26 00:25:03 -07:00
} ;
2013-08-05 20:17:18 +05:30
module_platform_driver ( omap_rng_driver ) ;
MODULE_ALIAS ( " platform:omap_rng " ) ;
2006-06-26 00:25:03 -07:00
MODULE_AUTHOR ( " Deepak Saxena (and others) " ) ;
MODULE_LICENSE ( " GPL " ) ;