2012-07-24 16:33:11 +02:00
/*
* Broadcom BCM63xx Random Number Generator support
*
* Copyright ( C ) 2011 , Florian Fainelli < florian @ openwrt . org >
* Copyright ( C ) 2009 , Broadcom Corporation
*
*/
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/io.h>
# include <linux/err.h>
# include <linux/clk.h>
# include <linux/platform_device.h>
# include <linux/hw_random.h>
2015-02-16 18:09:14 -08:00
# define RNG_CTRL 0x00
# define RNG_EN (1 << 0)
# define RNG_STAT 0x04
# define RNG_AVAIL_MASK (0xff000000)
# define RNG_DATA 0x08
# define RNG_THRES 0x0c
# define RNG_MASK 0x10
2012-07-24 16:33:11 +02:00
struct bcm63xx_rng_priv {
2015-03-12 14:00:03 -07:00
struct hwrng rng ;
2012-07-24 16:33:11 +02:00
struct clk * clk ;
void __iomem * regs ;
} ;
2015-03-12 14:00:03 -07:00
# define to_rng_priv(rng) container_of(rng, struct bcm63xx_rng_priv, rng)
2012-07-24 16:33:11 +02:00
static int bcm63xx_rng_init ( struct hwrng * rng )
{
struct bcm63xx_rng_priv * priv = to_rng_priv ( rng ) ;
u32 val ;
2015-03-12 14:00:03 -07:00
int error ;
error = clk_prepare_enable ( priv - > clk ) ;
if ( error )
return error ;
2012-07-24 16:33:11 +02:00
2015-02-16 18:09:13 -08:00
val = __raw_readl ( priv - > regs + RNG_CTRL ) ;
2012-07-24 16:33:11 +02:00
val | = RNG_EN ;
2015-02-16 18:09:13 -08:00
__raw_writel ( val , priv - > regs + RNG_CTRL ) ;
2012-07-24 16:33:11 +02:00
return 0 ;
}
static void bcm63xx_rng_cleanup ( struct hwrng * rng )
{
struct bcm63xx_rng_priv * priv = to_rng_priv ( rng ) ;
u32 val ;
2015-02-16 18:09:13 -08:00
val = __raw_readl ( priv - > regs + RNG_CTRL ) ;
2012-07-24 16:33:11 +02:00
val & = ~ RNG_EN ;
2015-02-16 18:09:13 -08:00
__raw_writel ( val , priv - > regs + RNG_CTRL ) ;
2015-03-12 14:00:03 -07:00
2015-05-02 12:08:42 +02:00
clk_disable_unprepare ( priv - > clk ) ;
2012-07-24 16:33:11 +02:00
}
static int bcm63xx_rng_data_present ( struct hwrng * rng , int wait )
{
struct bcm63xx_rng_priv * priv = to_rng_priv ( rng ) ;
2015-02-16 18:09:13 -08:00
return __raw_readl ( priv - > regs + RNG_STAT ) & RNG_AVAIL_MASK ;
2012-07-24 16:33:11 +02:00
}
static int bcm63xx_rng_data_read ( struct hwrng * rng , u32 * data )
{
struct bcm63xx_rng_priv * priv = to_rng_priv ( rng ) ;
2015-02-16 18:09:13 -08:00
* data = __raw_readl ( priv - > regs + RNG_DATA ) ;
2012-07-24 16:33:11 +02:00
return 4 ;
}
2012-12-21 15:12:08 -08:00
static int bcm63xx_rng_probe ( struct platform_device * pdev )
2012-07-24 16:33:11 +02:00
{
struct resource * r ;
struct clk * clk ;
int ret ;
struct bcm63xx_rng_priv * priv ;
struct hwrng * rng ;
r = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! r ) {
dev_err ( & pdev - > dev , " no iomem resource \n " ) ;
2015-03-12 14:00:03 -07:00
return - ENXIO ;
2012-07-24 16:33:11 +02:00
}
2015-02-16 18:09:16 -08:00
priv = devm_kzalloc ( & pdev - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
2015-03-12 14:00:03 -07:00
if ( ! priv )
return - ENOMEM ;
priv - > rng . name = pdev - > name ;
priv - > rng . init = bcm63xx_rng_init ;
priv - > rng . cleanup = bcm63xx_rng_cleanup ;
2015-05-02 12:08:42 +02:00
priv - > rng . data_present = bcm63xx_rng_data_present ;
2015-03-12 14:00:03 -07:00
priv - > rng . data_read = bcm63xx_rng_data_read ;
priv - > clk = devm_clk_get ( & pdev - > dev , " ipsec " ) ;
if ( IS_ERR ( priv - > clk ) ) {
2015-05-02 12:08:42 +02:00
ret = PTR_ERR ( priv - > clk ) ;
dev_err ( & pdev - > dev , " no clock for device: %d \n " , ret ) ;
return ret ;
2012-07-24 16:33:11 +02:00
}
if ( ! devm_request_mem_region ( & pdev - > dev , r - > start ,
resource_size ( r ) , pdev - > name ) ) {
dev_err ( & pdev - > dev , " request mem failed " ) ;
2015-03-12 14:00:03 -07:00
return - EBUSY ;
2012-07-24 16:33:11 +02:00
}
priv - > regs = devm_ioremap_nocache ( & pdev - > dev , r - > start ,
resource_size ( r ) ) ;
if ( ! priv - > regs ) {
dev_err ( & pdev - > dev , " ioremap failed " ) ;
2015-03-12 14:00:03 -07:00
return - ENOMEM ;
2012-07-24 16:33:11 +02:00
}
2015-05-02 12:08:42 +02:00
ret = devm_hwrng_register ( & pdev - > dev , & priv - > rng ) ;
if ( ret ) {
2015-03-12 14:00:03 -07:00
dev_err ( & pdev - > dev , " failed to register rng device: %d \n " ,
2015-05-02 12:08:42 +02:00
ret ) ;
return ret ;
2012-07-24 16:33:11 +02:00
}
dev_info ( & pdev - > dev , " registered RNG driver \n " ) ;
return 0 ;
}
static struct platform_driver bcm63xx_rng_driver = {
. probe = bcm63xx_rng_probe ,
. driver = {
. name = " bcm63xx-rng " ,
} ,
} ;
module_platform_driver ( bcm63xx_rng_driver ) ;
MODULE_AUTHOR ( " Florian Fainelli <florian@openwrt.org> " ) ;
MODULE_DESCRIPTION ( " Broadcom BCM63xx RNG driver " ) ;
MODULE_LICENSE ( " GPL " ) ;