2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2016-04-13 18:11:28 +08:00
/*
* Copyright ( C ) 2016 HiSilicon Co . , Ltd .
*/
# include <linux/err.h>
# include <linux/kernel.h>
# include <linux/hw_random.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/platform_device.h>
# include <linux/random.h>
# define RNG_SEED 0x0
# define RNG_CTRL 0x4
# define RNG_SEED_SEL BIT(2)
# define RNG_RING_EN BIT(1)
# define RNG_EN BIT(0)
# define RNG_RAN_NUM 0x10
# define RNG_PHY_SEED 0x14
# define to_hisi_rng(p) container_of(p, struct hisi_rng, rng)
static int seed_sel ;
module_param ( seed_sel , int , S_IRUGO ) ;
MODULE_PARM_DESC ( seed_sel , " Auto reload seed. 0, use LFSR(default); 1, use ring oscillator. " ) ;
struct hisi_rng {
void __iomem * base ;
struct hwrng rng ;
} ;
static int hisi_rng_init ( struct hwrng * rng )
{
struct hisi_rng * hrng = to_hisi_rng ( rng ) ;
int val = RNG_EN ;
u32 seed ;
/* get a random number as initial seed */
get_random_bytes ( & seed , sizeof ( seed ) ) ;
writel_relaxed ( seed , hrng - > base + RNG_SEED ) ;
/**
* The seed is reload periodically , there are two choice
* of seeds , default seed using the value from LFSR , or
* will use seed generated by ring oscillator .
*/
if ( seed_sel = = 1 )
val | = RNG_RING_EN | RNG_SEED_SEL ;
writel_relaxed ( val , hrng - > base + RNG_CTRL ) ;
return 0 ;
}
static void hisi_rng_cleanup ( struct hwrng * rng )
{
struct hisi_rng * hrng = to_hisi_rng ( rng ) ;
writel_relaxed ( 0 , hrng - > base + RNG_CTRL ) ;
}
static int hisi_rng_read ( struct hwrng * rng , void * buf , size_t max , bool wait )
{
struct hisi_rng * hrng = to_hisi_rng ( rng ) ;
u32 * data = buf ;
* data = readl_relaxed ( hrng - > base + RNG_RAN_NUM ) ;
return 4 ;
}
static int hisi_rng_probe ( struct platform_device * pdev )
{
struct hisi_rng * rng ;
int ret ;
rng = devm_kzalloc ( & pdev - > dev , sizeof ( * rng ) , GFP_KERNEL ) ;
if ( ! rng )
return - ENOMEM ;
platform_set_drvdata ( pdev , rng ) ;
2019-10-16 18:46:12 +08:00
rng - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
2016-04-13 18:11:28 +08:00
if ( IS_ERR ( rng - > base ) )
return PTR_ERR ( rng - > base ) ;
rng - > rng . name = pdev - > name ;
rng - > rng . init = hisi_rng_init ;
rng - > rng . cleanup = hisi_rng_cleanup ;
rng - > rng . read = hisi_rng_read ;
ret = devm_hwrng_register ( & pdev - > dev , & rng - > rng ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to register hwrng \n " ) ;
return ret ;
}
return 0 ;
}
2020-06-29 18:03:57 +10:00
static const struct of_device_id hisi_rng_dt_ids [ ] __maybe_unused = {
2016-04-13 18:11:28 +08:00
{ . compatible = " hisilicon,hip04-rng " } ,
{ . compatible = " hisilicon,hip05-rng " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , hisi_rng_dt_ids ) ;
static struct platform_driver hisi_rng_driver = {
. probe = hisi_rng_probe ,
. driver = {
. name = " hisi-rng " ,
. of_match_table = of_match_ptr ( hisi_rng_dt_ids ) ,
} ,
} ;
module_platform_driver ( hisi_rng_driver ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Kefeng Wang <wangkefeng.wang@huawei> " ) ;
MODULE_DESCRIPTION ( " Hisilicon random number generator driver " ) ;