2019-05-23 11:14:47 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2015-10-12 09:21:29 +01:00
/*
* Copyright ( c ) 2015 , Daniel Thompson
*/
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/hw_random.h>
# include <linux/io.h>
2018-02-15 14:03:12 +01:00
# include <linux/iopoll.h>
2015-10-12 09:21:29 +01:00
# include <linux/kernel.h>
# include <linux/module.h>
2023-07-28 07:48:27 -06:00
# include <linux/of.h>
2015-10-12 09:21:29 +01:00
# include <linux/of_address.h>
2023-07-28 07:48:27 -06:00
# include <linux/platform_device.h>
2015-10-12 09:21:29 +01:00
# include <linux/pm_runtime.h>
2018-02-15 14:03:08 +01:00
# include <linux/reset.h>
2015-10-12 09:21:29 +01:00
# include <linux/slab.h>
2023-09-21 10:02:55 +02:00
# define RNG_CR 0x00
# define RNG_CR_RNGEN BIT(2)
# define RNG_CR_CED BIT(5)
# define RNG_CR_CONFIG1 GENMASK(11, 8)
# define RNG_CR_NISTC BIT(12)
# define RNG_CR_CONFIG2 GENMASK(15, 13)
2023-09-21 10:02:58 +02:00
# define RNG_CR_CLKDIV_SHIFT 16
# define RNG_CR_CLKDIV GENMASK(19, 16)
2023-09-21 10:02:55 +02:00
# define RNG_CR_CONFIG3 GENMASK(25, 20)
# define RNG_CR_CONDRST BIT(30)
# define RNG_CR_CONFLOCK BIT(31)
# define RNG_CR_ENTROPY_SRC_MASK (RNG_CR_CONFIG1 | RNG_CR_NISTC | RNG_CR_CONFIG2 | RNG_CR_CONFIG3)
2023-09-21 10:02:58 +02:00
# define RNG_CR_CONFIG_MASK (RNG_CR_ENTROPY_SRC_MASK | RNG_CR_CED | RNG_CR_CLKDIV)
2023-09-21 10:02:55 +02:00
2023-09-21 10:02:56 +02:00
# define RNG_SR 0x04
# define RNG_SR_DRDY BIT(0)
# define RNG_SR_CECS BIT(1)
# define RNG_SR_SECS BIT(2)
# define RNG_SR_CEIS BIT(5)
# define RNG_SR_SEIS BIT(6)
2023-09-21 10:02:55 +02:00
# define RNG_DR 0x08
# define RNG_NSCR 0x0C
# define RNG_NSCR_MASK GENMASK(17, 0)
# define RNG_HTCR 0x10
2023-09-21 10:02:57 +02:00
# define RNG_NB_RECOVER_TRIES 3
2023-09-21 10:02:55 +02:00
struct stm32_rng_data {
2023-09-21 10:02:58 +02:00
uint max_clock_rate ;
2023-09-21 10:02:55 +02:00
u32 cr ;
u32 nscr ;
u32 htcr ;
bool has_cond_reset ;
} ;
2015-10-12 09:21:29 +01:00
2023-09-21 10:03:00 +02:00
/**
* struct stm32_rng_config - RNG configuration data
*
* @ cr : RNG configuration . 0 means default hardware RNG configuration
* @ nscr : Noise sources control configuration .
* @ htcr : Health tests configuration .
*/
struct stm32_rng_config {
u32 cr ;
u32 nscr ;
u32 htcr ;
} ;
2015-10-12 09:21:29 +01:00
struct stm32_rng_private {
struct hwrng rng ;
void __iomem * base ;
struct clk * clk ;
2018-02-15 14:03:08 +01:00
struct reset_control * rst ;
2023-09-21 10:03:00 +02:00
struct stm32_rng_config pm_conf ;
2023-09-21 10:02:55 +02:00
const struct stm32_rng_data * data ;
2018-02-15 14:03:10 +01:00
bool ced ;
2023-09-21 10:02:59 +02:00
bool lock_conf ;
2015-10-12 09:21:29 +01:00
} ;
2023-09-21 10:02:56 +02:00
/*
* Extracts from the STM32 RNG specification when RNG supports CONDRST .
*
* When a noise source ( or seed ) error occurs , the RNG stops generating
* random numbers and sets to “ 1 ” both SEIS and SECS bits to indicate
* that a seed error occurred . ( . . . )
*
* 1. Software reset by writing CONDRST at 1 and at 0 ( see bitfield
* description for details ) . This step is needed only if SECS is set .
* Indeed , when SEIS is set and SECS is cleared it means RNG performed
* the reset automatically ( auto - reset ) .
* 2. If SECS was set in step 1 ( no auto - reset ) wait for CONDRST
* to be cleared in the RNG_CR register , then confirm that SEIS is
* cleared in the RNG_SR register . Otherwise just clear SEIS bit in
* the RNG_SR register .
* 3. If SECS was set in step 1 ( no auto - reset ) wait for SECS to be
* cleared by RNG . The random number generation is now back to normal .
*/
static int stm32_rng_conceal_seed_error_cond_reset ( struct stm32_rng_private * priv )
{
struct device * dev = ( struct device * ) priv - > rng . priv ;
u32 sr = readl_relaxed ( priv - > base + RNG_SR ) ;
u32 cr = readl_relaxed ( priv - > base + RNG_CR ) ;
int err ;
if ( sr & RNG_SR_SECS ) {
/* Conceal by resetting the subsystem (step 1.) */
writel_relaxed ( cr | RNG_CR_CONDRST , priv - > base + RNG_CR ) ;
writel_relaxed ( cr & ~ RNG_CR_CONDRST , priv - > base + RNG_CR ) ;
} else {
/* RNG auto-reset (step 2.) */
writel_relaxed ( sr & ~ RNG_SR_SEIS , priv - > base + RNG_SR ) ;
goto end ;
}
err = readl_relaxed_poll_timeout_atomic ( priv - > base + RNG_CR , cr , ! ( cr & RNG_CR_CONDRST ) , 10 ,
100000 ) ;
if ( err ) {
dev_err ( dev , " %s: timeout %x \n " , __func__ , sr ) ;
return err ;
}
/* Check SEIS is cleared (step 2.) */
if ( readl_relaxed ( priv - > base + RNG_SR ) & RNG_SR_SEIS )
return - EINVAL ;
err = readl_relaxed_poll_timeout_atomic ( priv - > base + RNG_SR , sr , ! ( sr & RNG_SR_SECS ) , 10 ,
100000 ) ;
if ( err ) {
dev_err ( dev , " %s: timeout %x \n " , __func__ , sr ) ;
return err ;
}
end :
return 0 ;
}
/*
* Extracts from the STM32 RNG specification , when CONDRST is not supported
*
* When a noise source ( or seed ) error occurs , the RNG stops generating
* random numbers and sets to “ 1 ” both SEIS and SECS bits to indicate
* that a seed error occurred . ( . . . )
*
* The following sequence shall be used to fully recover from a seed
* error after the RNG initialization :
* 1. Clear the SEIS bit by writing it to “ 0 ” .
* 2. Read out 12 words from the RNG_DR register , and discard each of
* them in order to clean the pipeline .
* 3. Confirm that SEIS is still cleared . Random number generation is
* back to normal .
*/
static int stm32_rng_conceal_seed_error_sw_reset ( struct stm32_rng_private * priv )
{
unsigned int i = 0 ;
u32 sr = readl_relaxed ( priv - > base + RNG_SR ) ;
writel_relaxed ( sr & ~ RNG_SR_SEIS , priv - > base + RNG_SR ) ;
for ( i = 12 ; i ! = 0 ; i - - )
( void ) readl_relaxed ( priv - > base + RNG_DR ) ;
if ( readl_relaxed ( priv - > base + RNG_SR ) & RNG_SR_SEIS )
return - EINVAL ;
return 0 ;
}
static int stm32_rng_conceal_seed_error ( struct hwrng * rng )
{
struct stm32_rng_private * priv = container_of ( rng , struct stm32_rng_private , rng ) ;
dev_dbg ( ( struct device * ) priv - > rng . priv , " Concealing seed error \n " ) ;
if ( priv - > data - > has_cond_reset )
return stm32_rng_conceal_seed_error_cond_reset ( priv ) ;
else
return stm32_rng_conceal_seed_error_sw_reset ( priv ) ;
} ;
2015-10-12 09:21:29 +01:00
static int stm32_rng_read ( struct hwrng * rng , void * data , size_t max , bool wait )
{
2023-09-21 10:02:57 +02:00
struct stm32_rng_private * priv = container_of ( rng , struct stm32_rng_private , rng ) ;
unsigned int i = 0 ;
int retval = 0 , err = 0 ;
2015-10-12 09:21:29 +01:00
u32 sr ;
pm_runtime_get_sync ( ( struct device * ) priv - > rng . priv ) ;
2023-09-21 10:02:56 +02:00
if ( readl_relaxed ( priv - > base + RNG_SR ) & RNG_SR_SEIS )
stm32_rng_conceal_seed_error ( rng ) ;
2022-10-12 18:09:24 +02:00
while ( max > = sizeof ( u32 ) ) {
2015-10-12 09:21:29 +01:00
sr = readl_relaxed ( priv - > base + RNG_SR ) ;
2023-09-21 10:02:57 +02:00
/*
* Manage timeout which is based on timer and take
* care of initial delay time when enabling the RNG .
*/
2015-10-12 09:21:29 +01:00
if ( ! sr & & wait ) {
2022-11-08 07:42:40 +01:00
err = readl_relaxed_poll_timeout_atomic ( priv - > base
2018-02-15 14:03:12 +01:00
+ RNG_SR ,
sr , sr ,
10 , 50000 ) ;
2023-09-21 10:02:57 +02:00
if ( err ) {
2018-02-15 14:03:12 +01:00
dev_err ( ( struct device * ) priv - > rng . priv ,
" %s: timeout %x! \n " , __func__ , sr ) ;
2023-09-21 10:02:57 +02:00
break ;
}
} else if ( ! sr ) {
/* The FIFO is being filled up */
break ;
2015-10-12 09:21:29 +01:00
}
2016-05-26 11:34:57 +02:00
if ( sr ! = RNG_SR_DRDY ) {
2023-09-21 10:02:57 +02:00
if ( sr & RNG_SR_SEIS ) {
err = stm32_rng_conceal_seed_error ( rng ) ;
i + + ;
if ( err & & i > RNG_NB_RECOVER_TRIES ) {
dev_err ( ( struct device * ) priv - > rng . priv ,
" Couldn't recover from seed error \n " ) ;
return - ENOTRECOVERABLE ;
}
continue ;
}
if ( WARN_ONCE ( ( sr & RNG_SR_CEIS ) , " RNG clock too slow - %x \n " , sr ) )
2016-05-26 11:34:57 +02:00
writel_relaxed ( 0 , priv - > base + RNG_SR ) ;
}
2015-10-12 09:21:29 +01:00
2023-09-21 10:02:57 +02:00
/* Late seed error case: DR being 0 is an error status */
2015-10-12 09:21:29 +01:00
* ( u32 * ) data = readl_relaxed ( priv - > base + RNG_DR ) ;
2023-09-21 10:02:57 +02:00
if ( ! * ( u32 * ) data ) {
err = stm32_rng_conceal_seed_error ( rng ) ;
i + + ;
if ( err & & i > RNG_NB_RECOVER_TRIES ) {
dev_err ( ( struct device * ) priv - > rng . priv ,
" Couldn't recover from seed error " ) ;
return - ENOTRECOVERABLE ;
}
continue ;
}
2015-10-12 09:21:29 +01:00
2023-09-21 10:02:57 +02:00
i = 0 ;
2015-10-12 09:21:29 +01:00
retval + = sizeof ( u32 ) ;
data + = sizeof ( u32 ) ;
max - = sizeof ( u32 ) ;
}
pm_runtime_mark_last_busy ( ( struct device * ) priv - > rng . priv ) ;
pm_runtime_put_sync_autosuspend ( ( struct device * ) priv - > rng . priv ) ;
return retval | | ! wait ? retval : - EIO ;
}
2023-09-21 10:02:58 +02:00
static uint stm32_rng_clock_freq_restrain ( struct hwrng * rng )
{
struct stm32_rng_private * priv =
container_of ( rng , struct stm32_rng_private , rng ) ;
unsigned long clock_rate = 0 ;
uint clock_div = 0 ;
clock_rate = clk_get_rate ( priv - > clk ) ;
/*
* Get the exponent to apply on the CLKDIV field in RNG_CR register
* No need to handle the case when clock - div > 0xF as it is physically
* impossible
*/
while ( ( clock_rate > > clock_div ) > priv - > data - > max_clock_rate )
clock_div + + ;
pr_debug ( " RNG clk rate : %lu \n " , clk_get_rate ( priv - > clk ) > > clock_div ) ;
return clock_div ;
}
2015-10-12 09:21:29 +01:00
static int stm32_rng_init ( struct hwrng * rng )
{
struct stm32_rng_private * priv =
container_of ( rng , struct stm32_rng_private , rng ) ;
int err ;
2023-09-21 10:02:55 +02:00
u32 reg ;
2015-10-12 09:21:29 +01:00
err = clk_prepare_enable ( priv - > clk ) ;
if ( err )
return err ;
/* clear error indicators */
writel_relaxed ( 0 , priv - > base + RNG_SR ) ;
2023-09-21 10:02:55 +02:00
reg = readl_relaxed ( priv - > base + RNG_CR ) ;
/*
* Keep default RNG configuration if none was specified .
* 0 is an invalid value as it disables all entropy sources .
*/
if ( priv - > data - > has_cond_reset & & priv - > data - > cr ) {
2023-09-21 10:02:58 +02:00
uint clock_div = stm32_rng_clock_freq_restrain ( rng ) ;
2023-09-21 10:02:55 +02:00
reg & = ~ RNG_CR_CONFIG_MASK ;
2023-09-21 10:02:58 +02:00
reg | = RNG_CR_CONDRST | ( priv - > data - > cr & RNG_CR_ENTROPY_SRC_MASK ) |
( clock_div < < RNG_CR_CLKDIV_SHIFT ) ;
2023-09-21 10:02:55 +02:00
if ( priv - > ced )
reg & = ~ RNG_CR_CED ;
else
reg | = RNG_CR_CED ;
writel_relaxed ( reg , priv - > base + RNG_CR ) ;
/* Health tests and noise control registers */
writel_relaxed ( priv - > data - > htcr , priv - > base + RNG_HTCR ) ;
writel_relaxed ( priv - > data - > nscr & RNG_NSCR_MASK , priv - > base + RNG_NSCR ) ;
reg & = ~ RNG_CR_CONDRST ;
reg | = RNG_CR_RNGEN ;
2023-09-21 10:02:59 +02:00
if ( priv - > lock_conf )
reg | = RNG_CR_CONFLOCK ;
2023-09-21 10:02:55 +02:00
writel_relaxed ( reg , priv - > base + RNG_CR ) ;
err = readl_relaxed_poll_timeout_atomic ( priv - > base + RNG_CR , reg ,
( ! ( reg & RNG_CR_CONDRST ) ) ,
10 , 50000 ) ;
if ( err ) {
2023-12-01 16:20:48 +08:00
clk_disable_unprepare ( priv - > clk ) ;
2023-09-21 10:02:55 +02:00
dev_err ( ( struct device * ) priv - > rng . priv ,
" %s: timeout %x! \n " , __func__ , reg ) ;
return - EINVAL ;
}
} else {
/* Handle all RNG versions by checking if conditional reset should be set */
if ( priv - > data - > has_cond_reset )
reg | = RNG_CR_CONDRST ;
if ( priv - > ced )
reg & = ~ RNG_CR_CED ;
else
reg | = RNG_CR_CED ;
writel_relaxed ( reg , priv - > base + RNG_CR ) ;
if ( priv - > data - > has_cond_reset )
reg & = ~ RNG_CR_CONDRST ;
reg | = RNG_CR_RNGEN ;
writel_relaxed ( reg , priv - > base + RNG_CR ) ;
}
err = readl_relaxed_poll_timeout_atomic ( priv - > base + RNG_SR , reg ,
reg & RNG_SR_DRDY ,
10 , 100000 ) ;
if ( err | ( reg & ~ RNG_SR_DRDY ) ) {
clk_disable_unprepare ( priv - > clk ) ;
dev_err ( ( struct device * ) priv - > rng . priv ,
" %s: timeout:%x SR: %x! \n " , __func__ , err , reg ) ;
return - EINVAL ;
}
2015-10-12 09:21:29 +01:00
return 0 ;
}
2023-12-10 23:12:25 +01:00
static void stm32_rng_remove ( struct platform_device * ofdev )
2015-10-12 09:21:29 +01:00
{
2023-09-21 10:02:55 +02:00
pm_runtime_disable ( & ofdev - > dev ) ;
}
2023-09-21 10:03:00 +02:00
static int __maybe_unused stm32_rng_runtime_suspend ( struct device * dev )
2023-09-21 10:02:55 +02:00
{
struct stm32_rng_private * priv = dev_get_drvdata ( dev ) ;
2023-09-21 10:03:00 +02:00
u32 reg ;
2015-10-12 09:21:29 +01:00
2023-09-21 10:02:55 +02:00
reg = readl_relaxed ( priv - > base + RNG_CR ) ;
reg & = ~ RNG_CR_RNGEN ;
writel_relaxed ( reg , priv - > base + RNG_CR ) ;
2015-10-12 09:21:29 +01:00
clk_disable_unprepare ( priv - > clk ) ;
2023-09-21 10:02:55 +02:00
return 0 ;
2015-10-12 09:21:29 +01:00
}
2023-09-21 10:03:00 +02:00
static int __maybe_unused stm32_rng_suspend ( struct device * dev )
{
struct stm32_rng_private * priv = dev_get_drvdata ( dev ) ;
if ( priv - > data - > has_cond_reset ) {
priv - > pm_conf . nscr = readl_relaxed ( priv - > base + RNG_NSCR ) ;
priv - > pm_conf . htcr = readl_relaxed ( priv - > base + RNG_HTCR ) ;
}
/* Do not save that RNG is enabled as it will be handled at resume */
priv - > pm_conf . cr = readl_relaxed ( priv - > base + RNG_CR ) & ~ RNG_CR_RNGEN ;
writel_relaxed ( priv - > pm_conf . cr , priv - > base + RNG_CR ) ;
clk_disable_unprepare ( priv - > clk ) ;
return 0 ;
}
static int __maybe_unused stm32_rng_runtime_resume ( struct device * dev )
2023-09-21 10:02:55 +02:00
{
struct stm32_rng_private * priv = dev_get_drvdata ( dev ) ;
2023-09-21 10:03:00 +02:00
int err ;
u32 reg ;
err = clk_prepare_enable ( priv - > clk ) ;
if ( err )
return err ;
/* Clean error indications */
writel_relaxed ( 0 , priv - > base + RNG_SR ) ;
2023-09-21 10:02:55 +02:00
reg = readl_relaxed ( priv - > base + RNG_CR ) ;
reg | = RNG_CR_RNGEN ;
writel_relaxed ( reg , priv - > base + RNG_CR ) ;
return 0 ;
}
2023-09-21 10:03:00 +02:00
static int __maybe_unused stm32_rng_resume ( struct device * dev )
{
struct stm32_rng_private * priv = dev_get_drvdata ( dev ) ;
int err ;
u32 reg ;
err = clk_prepare_enable ( priv - > clk ) ;
if ( err )
return err ;
/* Clean error indications */
writel_relaxed ( 0 , priv - > base + RNG_SR ) ;
if ( priv - > data - > has_cond_reset ) {
/*
* Correct configuration in bits [ 29 : 4 ] must be set in the same
* access that set RNG_CR_CONDRST bit . Else config setting is
* not taken into account . CONFIGLOCK bit must also be unset but
* it is not handled at the moment .
*/
writel_relaxed ( priv - > pm_conf . cr | RNG_CR_CONDRST , priv - > base + RNG_CR ) ;
writel_relaxed ( priv - > pm_conf . nscr , priv - > base + RNG_NSCR ) ;
writel_relaxed ( priv - > pm_conf . htcr , priv - > base + RNG_HTCR ) ;
reg = readl_relaxed ( priv - > base + RNG_CR ) ;
reg | = RNG_CR_RNGEN ;
reg & = ~ RNG_CR_CONDRST ;
writel_relaxed ( reg , priv - > base + RNG_CR ) ;
err = readl_relaxed_poll_timeout_atomic ( priv - > base + RNG_CR , reg ,
reg & ~ RNG_CR_CONDRST , 10 , 100000 ) ;
if ( err ) {
clk_disable_unprepare ( priv - > clk ) ;
dev_err ( ( struct device * ) priv - > rng . priv ,
" %s: timeout:%x CR: %x! \n " , __func__ , err , reg ) ;
return - EINVAL ;
}
} else {
reg = priv - > pm_conf . cr ;
reg | = RNG_CR_RNGEN ;
writel_relaxed ( reg , priv - > base + RNG_CR ) ;
}
return 0 ;
}
static const struct dev_pm_ops __maybe_unused stm32_rng_pm_ops = {
2023-09-21 10:02:55 +02:00
SET_RUNTIME_PM_OPS ( stm32_rng_runtime_suspend ,
stm32_rng_runtime_resume , NULL )
2023-09-21 10:03:00 +02:00
SET_SYSTEM_SLEEP_PM_OPS ( stm32_rng_suspend ,
stm32_rng_resume )
2023-09-21 10:02:55 +02:00
} ;
static const struct stm32_rng_data stm32mp13_rng_data = {
. has_cond_reset = true ,
2023-09-21 10:02:58 +02:00
. max_clock_rate = 48000000 ,
2023-09-21 10:02:55 +02:00
. cr = 0x00F00D00 ,
. nscr = 0x2B5BB ,
. htcr = 0x969D ,
} ;
static const struct stm32_rng_data stm32_rng_data = {
. has_cond_reset = false ,
2023-09-21 10:02:58 +02:00
. max_clock_rate = 3000000 ,
2023-09-21 10:02:55 +02:00
} ;
static const struct of_device_id stm32_rng_match [ ] = {
{
. compatible = " st,stm32mp13-rng " ,
. data = & stm32mp13_rng_data ,
} ,
{
. compatible = " st,stm32-rng " ,
. data = & stm32_rng_data ,
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , stm32_rng_match ) ;
2015-10-12 09:21:29 +01:00
static int stm32_rng_probe ( struct platform_device * ofdev )
{
struct device * dev = & ofdev - > dev ;
struct device_node * np = ofdev - > dev . of_node ;
struct stm32_rng_private * priv ;
2023-09-21 10:02:54 +02:00
struct resource * res ;
2015-10-12 09:21:29 +01:00
priv = devm_kzalloc ( dev , sizeof ( struct stm32_rng_private ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
2023-09-21 10:02:54 +02:00
priv - > base = devm_platform_get_and_ioremap_resource ( ofdev , 0 , & res ) ;
2015-10-12 09:21:29 +01:00
if ( IS_ERR ( priv - > base ) )
return PTR_ERR ( priv - > base ) ;
priv - > clk = devm_clk_get ( & ofdev - > dev , NULL ) ;
if ( IS_ERR ( priv - > clk ) )
return PTR_ERR ( priv - > clk ) ;
2018-02-15 14:03:08 +01:00
priv - > rst = devm_reset_control_get ( & ofdev - > dev , NULL ) ;
if ( ! IS_ERR ( priv - > rst ) ) {
reset_control_assert ( priv - > rst ) ;
udelay ( 2 ) ;
reset_control_deassert ( priv - > rst ) ;
}
2018-02-15 14:03:10 +01:00
priv - > ced = of_property_read_bool ( np , " clock-error-detect " ) ;
2023-09-21 10:02:59 +02:00
priv - > lock_conf = of_property_read_bool ( np , " st,rng-lock-conf " ) ;
2018-02-15 14:03:10 +01:00
2023-09-21 10:02:55 +02:00
priv - > data = of_device_get_match_data ( dev ) ;
if ( ! priv - > data )
return - ENODEV ;
2015-10-12 09:21:29 +01:00
dev_set_drvdata ( dev , priv ) ;
2020-09-27 21:12:14 +02:00
priv - > rng . name = dev_driver_string ( dev ) ;
priv - > rng . init = stm32_rng_init ;
priv - > rng . read = stm32_rng_read ;
2015-10-12 09:21:29 +01:00
priv - > rng . priv = ( unsigned long ) dev ;
2019-04-01 12:30:46 +02:00
priv - > rng . quality = 900 ;
2015-10-12 09:21:29 +01:00
pm_runtime_set_autosuspend_delay ( dev , 100 ) ;
pm_runtime_use_autosuspend ( dev ) ;
pm_runtime_enable ( dev ) ;
return devm_hwrng_register ( dev , & priv - > rng ) ;
}
static struct platform_driver stm32_rng_driver = {
. driver = {
. name = " stm32-rng " ,
2023-09-21 10:03:00 +02:00
. pm = pm_ptr ( & stm32_rng_pm_ops ) ,
2015-10-12 09:21:29 +01:00
. of_match_table = stm32_rng_match ,
} ,
. probe = stm32_rng_probe ,
2023-12-10 23:12:25 +01:00
. remove_new = stm32_rng_remove ,
2015-10-12 09:21:29 +01:00
} ;
module_platform_driver ( stm32_rng_driver ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Daniel Thompson <daniel.thompson@linaro.org> " ) ;
MODULE_DESCRIPTION ( " STMicroelectronics STM32 RNG device driver " ) ;