2018-11-10 17:51:16 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
2013-03-28 10:19:38 +04:00
* Copyright ( c ) 2010 - 2012 Broadcom . All rights reserved .
* Copyright ( c ) 2013 Lubomir Rintel
*/
# include <linux/hw_random.h>
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/of_address.h>
# include <linux/of_platform.h>
# include <linux/platform_device.h>
# include <linux/printk.h>
2017-11-08 03:44:44 +03:00
# include <linux/clk.h>
2013-03-28 10:19:38 +04:00
# define RNG_CTRL 0x0
# define RNG_STATUS 0x4
# define RNG_DATA 0x8
2016-05-27 13:10:39 +03:00
# define RNG_INT_MASK 0x10
2013-03-28 10:19:38 +04:00
/* enable rng */
# define RNG_RBGEN 0x1
/* the initial numbers generated are "less random" so will be discarded */
# define RNG_WARMUP_COUNT 0x40000
2016-05-27 13:10:39 +03:00
# define RNG_INT_OFF 0x1
2017-11-08 03:44:39 +03:00
struct bcm2835_rng_priv {
struct hwrng rng ;
void __iomem * base ;
2017-11-08 03:44:43 +03:00
bool mask_interrupts ;
2017-11-08 03:44:44 +03:00
struct clk * clk ;
2017-11-08 03:44:39 +03:00
} ;
static inline struct bcm2835_rng_priv * to_rng_priv ( struct hwrng * rng )
{
return container_of ( rng , struct bcm2835_rng_priv , rng ) ;
}
2017-11-08 03:44:45 +03:00
static inline u32 rng_readl ( struct bcm2835_rng_priv * priv , u32 offset )
{
2017-11-08 03:44:46 +03:00
/* MIPS chips strapped for BE will automagically configure the
* peripheral registers for CPU - native byte order .
*/
if ( IS_ENABLED ( CONFIG_MIPS ) & & IS_ENABLED ( CONFIG_CPU_BIG_ENDIAN ) )
return __raw_readl ( priv - > base + offset ) ;
else
return readl ( priv - > base + offset ) ;
2017-11-08 03:44:45 +03:00
}
static inline void rng_writel ( struct bcm2835_rng_priv * priv , u32 val ,
u32 offset )
{
2017-11-08 03:44:46 +03:00
if ( IS_ENABLED ( CONFIG_MIPS ) & & IS_ENABLED ( CONFIG_CPU_BIG_ENDIAN ) )
__raw_writel ( val , priv - > base + offset ) ;
else
writel ( val , priv - > base + offset ) ;
2017-11-08 03:44:45 +03:00
}
2013-03-28 10:19:38 +04:00
static int bcm2835_rng_read ( struct hwrng * rng , void * buf , size_t max ,
bool wait )
{
2017-11-08 03:44:39 +03:00
struct bcm2835_rng_priv * priv = to_rng_priv ( rng ) ;
2016-05-27 13:10:41 +03:00
u32 max_words = max / sizeof ( u32 ) ;
u32 num_words , count ;
2013-03-28 10:19:38 +04:00
2017-11-08 03:44:45 +03:00
while ( ( rng_readl ( priv , RNG_STATUS ) > > 24 ) = = 0 ) {
2013-03-28 10:19:38 +04:00
if ( ! wait )
return 0 ;
cpu_relax ( ) ;
}
2017-11-08 03:44:45 +03:00
num_words = rng_readl ( priv , RNG_STATUS ) > > 24 ;
2016-05-27 13:10:41 +03:00
if ( num_words > max_words )
num_words = max_words ;
for ( count = 0 ; count < num_words ; count + + )
2017-11-08 03:44:45 +03:00
( ( u32 * ) buf ) [ count ] = rng_readl ( priv , RNG_DATA ) ;
2016-05-27 13:10:41 +03:00
return num_words * sizeof ( u32 ) ;
2013-03-28 10:19:38 +04:00
}
2017-11-08 03:44:40 +03:00
static int bcm2835_rng_init ( struct hwrng * rng )
{
struct bcm2835_rng_priv * priv = to_rng_priv ( rng ) ;
2017-11-08 03:44:44 +03:00
int ret = 0 ;
2017-11-08 03:44:43 +03:00
u32 val ;
2017-11-08 03:44:44 +03:00
if ( ! IS_ERR ( priv - > clk ) ) {
ret = clk_prepare_enable ( priv - > clk ) ;
if ( ret )
return ret ;
}
2017-11-08 03:44:43 +03:00
if ( priv - > mask_interrupts ) {
/* mask the interrupt */
2017-11-08 03:44:45 +03:00
val = rng_readl ( priv , RNG_INT_MASK ) ;
2017-11-08 03:44:43 +03:00
val | = RNG_INT_OFF ;
2017-11-08 03:44:45 +03:00
rng_writel ( priv , val , RNG_INT_MASK ) ;
2017-11-08 03:44:43 +03:00
}
2017-11-08 03:44:40 +03:00
/* set warm-up count & enable */
2017-11-08 03:44:45 +03:00
rng_writel ( priv , RNG_WARMUP_COUNT , RNG_STATUS ) ;
rng_writel ( priv , RNG_RBGEN , RNG_CTRL ) ;
2017-11-08 03:44:40 +03:00
2017-11-08 03:44:44 +03:00
return ret ;
2017-11-08 03:44:40 +03:00
}
2017-11-08 03:44:41 +03:00
static void bcm2835_rng_cleanup ( struct hwrng * rng )
{
struct bcm2835_rng_priv * priv = to_rng_priv ( rng ) ;
/* disable rng hardware */
2017-11-08 03:44:45 +03:00
rng_writel ( priv , 0 , RNG_CTRL ) ;
2017-11-08 03:44:44 +03:00
if ( ! IS_ERR ( priv - > clk ) )
clk_disable_unprepare ( priv - > clk ) ;
2017-11-08 03:44:41 +03:00
}
2017-11-08 03:44:43 +03:00
struct bcm2835_rng_of_data {
bool mask_interrupts ;
} ;
static const struct bcm2835_rng_of_data nsp_rng_of_data = {
. mask_interrupts = true ,
} ;
2016-05-27 13:10:39 +03:00
static const struct of_device_id bcm2835_rng_of_match [ ] = {
{ . compatible = " brcm,bcm2835-rng " } ,
2017-11-08 03:44:43 +03:00
{ . compatible = " brcm,bcm-nsp-rng " , . data = & nsp_rng_of_data } ,
{ . compatible = " brcm,bcm5301x-rng " , . data = & nsp_rng_of_data } ,
2017-11-08 03:44:48 +03:00
{ . compatible = " brcm,bcm6368-rng " } ,
2016-05-27 13:10:39 +03:00
{ } ,
} ;
2013-03-28 10:19:38 +04:00
static int bcm2835_rng_probe ( struct platform_device * pdev )
{
2017-11-08 03:44:43 +03:00
const struct bcm2835_rng_of_data * of_data ;
2013-03-28 10:19:38 +04:00
struct device * dev = & pdev - > dev ;
2016-05-27 13:10:39 +03:00
const struct of_device_id * rng_id ;
2017-11-08 03:44:39 +03:00
struct bcm2835_rng_priv * priv ;
2013-03-28 10:19:38 +04:00
int err ;
2017-11-08 03:44:39 +03:00
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
platform_set_drvdata ( pdev , priv ) ;
2013-03-28 10:19:38 +04:00
/* map peripheral */
2019-10-16 13:46:10 +03:00
priv - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
2018-01-17 14:40:34 +03:00
if ( IS_ERR ( priv - > base ) )
2017-11-08 03:44:39 +03:00
return PTR_ERR ( priv - > base ) ;
2017-11-08 03:44:44 +03:00
/* Clock is optional on most platforms */
priv - > clk = devm_clk_get ( dev , NULL ) ;
2020-02-04 04:37:45 +03:00
if ( PTR_ERR ( priv - > clk ) = = - EPROBE_DEFER )
2018-02-12 23:11:36 +03:00
return - EPROBE_DEFER ;
2017-11-08 03:44:44 +03:00
2017-11-08 03:44:48 +03:00
priv - > rng . name = pdev - > name ;
2017-11-08 03:44:40 +03:00
priv - > rng . init = bcm2835_rng_init ;
2017-11-08 03:44:39 +03:00
priv - > rng . read = bcm2835_rng_read ;
2017-11-08 03:44:41 +03:00
priv - > rng . cleanup = bcm2835_rng_cleanup ;
2013-03-28 10:19:38 +04:00
2019-02-19 15:16:08 +03:00
if ( dev_of_node ( dev ) ) {
2020-06-29 11:04:00 +03:00
rng_id = of_match_node ( bcm2835_rng_of_match , dev - > of_node ) ;
2019-02-19 15:16:08 +03:00
if ( ! rng_id )
return - EINVAL ;
/* Check for rng init function, execute it */
of_data = rng_id - > data ;
if ( of_data )
priv - > mask_interrupts = of_data - > mask_interrupts ;
}
2016-05-27 13:10:39 +03:00
2013-03-28 10:19:38 +04:00
/* register driver */
2017-11-08 03:44:42 +03:00
err = devm_hwrng_register ( dev , & priv - > rng ) ;
2017-11-08 03:44:38 +03:00
if ( err )
2013-03-28 10:19:38 +04:00
dev_err ( dev , " hwrng registration failed \n " ) ;
2017-11-08 03:44:38 +03:00
else
2013-03-28 10:19:38 +04:00
dev_info ( dev , " hwrng registered \n " ) ;
return err ;
}
MODULE_DEVICE_TABLE ( of , bcm2835_rng_of_match ) ;
2020-07-01 23:09:46 +03:00
static const struct platform_device_id bcm2835_rng_devtype [ ] = {
2017-11-08 03:44:48 +03:00
{ . name = " bcm2835-rng " } ,
{ . name = " bcm63xx-rng " } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( platform , bcm2835_rng_devtype ) ;
2013-03-28 10:19:38 +04:00
static struct platform_driver bcm2835_rng_driver = {
. driver = {
. name = " bcm2835-rng " ,
. of_match_table = bcm2835_rng_of_match ,
} ,
. probe = bcm2835_rng_probe ,
2017-11-08 03:44:48 +03:00
. id_table = bcm2835_rng_devtype ,
2013-03-28 10:19:38 +04:00
} ;
module_platform_driver ( bcm2835_rng_driver ) ;
MODULE_AUTHOR ( " Lubomir Rintel <lkundrak@v3.sk> " ) ;
MODULE_DESCRIPTION ( " BCM2835 Random Number Generator (RNG) driver " ) ;
2013-04-23 17:39:50 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;