2015-10-12 09:21:29 +01:00
/*
* Copyright ( c ) 2015 , Daniel Thompson
*
* This file is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
*
* This file is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# 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>
# include <linux/of_address.h>
# include <linux/of_platform.h>
# 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>
# define RNG_CR 0x00
# define RNG_CR_RNGEN BIT(2)
2018-02-15 14:03:10 +01:00
# define RNG_CR_CED BIT(5)
2015-10-12 09:21:29 +01:00
# define RNG_SR 0x04
# define RNG_SR_SEIS BIT(6)
# define RNG_SR_CEIS BIT(5)
# define RNG_SR_DRDY BIT(0)
# define RNG_DR 0x08
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 ;
2018-02-15 14:03:10 +01:00
bool ced ;
2015-10-12 09:21:29 +01:00
} ;
static int stm32_rng_read ( struct hwrng * rng , void * data , size_t max , bool wait )
{
struct stm32_rng_private * priv =
container_of ( rng , struct stm32_rng_private , rng ) ;
u32 sr ;
int retval = 0 ;
pm_runtime_get_sync ( ( struct device * ) priv - > rng . priv ) ;
while ( max > sizeof ( u32 ) ) {
sr = readl_relaxed ( priv - > base + RNG_SR ) ;
2018-02-15 14:03:12 +01:00
/* Manage timeout which is based on timer and take */
/* care of initial delay time when enabling rng */
2015-10-12 09:21:29 +01:00
if ( ! sr & & wait ) {
2018-02-15 14:03:12 +01:00
retval = readl_relaxed_poll_timeout_atomic ( priv - > base
+ RNG_SR ,
sr , sr ,
10 , 50000 ) ;
if ( retval )
dev_err ( ( struct device * ) priv - > rng . priv ,
" %s: timeout %x! \n " , __func__ , sr ) ;
2015-10-12 09:21:29 +01:00
}
/* If error detected or data not ready... */
2016-05-26 11:34:57 +02:00
if ( sr ! = RNG_SR_DRDY ) {
if ( WARN_ONCE ( sr & ( RNG_SR_SEIS | RNG_SR_CEIS ) ,
" bad RNG status - %x \n " , sr ) )
writel_relaxed ( 0 , priv - > base + RNG_SR ) ;
2015-10-12 09:21:29 +01:00
break ;
2016-05-26 11:34:57 +02:00
}
2015-10-12 09:21:29 +01:00
* ( u32 * ) data = readl_relaxed ( priv - > base + RNG_DR ) ;
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 ;
}
static int stm32_rng_init ( struct hwrng * rng )
{
struct stm32_rng_private * priv =
container_of ( rng , struct stm32_rng_private , rng ) ;
int err ;
err = clk_prepare_enable ( priv - > clk ) ;
if ( err )
return err ;
2018-02-15 14:03:10 +01:00
if ( priv - > ced )
writel_relaxed ( RNG_CR_RNGEN , priv - > base + RNG_CR ) ;
else
writel_relaxed ( RNG_CR_RNGEN | RNG_CR_CED ,
priv - > base + RNG_CR ) ;
2015-10-12 09:21:29 +01:00
/* clear error indicators */
writel_relaxed ( 0 , priv - > base + RNG_SR ) ;
return 0 ;
}
static void stm32_rng_cleanup ( struct hwrng * rng )
{
struct stm32_rng_private * priv =
container_of ( rng , struct stm32_rng_private , rng ) ;
writel_relaxed ( 0 , priv - > base + RNG_CR ) ;
clk_disable_unprepare ( priv - > clk ) ;
}
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 ;
struct resource res ;
int err ;
priv = devm_kzalloc ( dev , sizeof ( struct stm32_rng_private ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
err = of_address_to_resource ( np , 0 , & res ) ;
if ( err )
return err ;
priv - > base = devm_ioremap_resource ( dev , & res ) ;
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 " ) ;
2015-10-12 09:21:29 +01:00
dev_set_drvdata ( dev , priv ) ;
priv - > rng . name = dev_driver_string ( dev ) ,
# ifndef CONFIG_PM
priv - > rng . init = stm32_rng_init ,
priv - > rng . cleanup = stm32_rng_cleanup ,
# endif
priv - > rng . read = stm32_rng_read ,
priv - > rng . priv = ( unsigned long ) dev ;
pm_runtime_set_autosuspend_delay ( dev , 100 ) ;
pm_runtime_use_autosuspend ( dev ) ;
pm_runtime_enable ( dev ) ;
return devm_hwrng_register ( dev , & priv - > rng ) ;
}
# ifdef CONFIG_PM
static int stm32_rng_runtime_suspend ( struct device * dev )
{
2015-10-14 17:04:55 +01:00
struct stm32_rng_private * priv = dev_get_drvdata ( dev ) ;
2015-10-12 09:21:29 +01:00
stm32_rng_cleanup ( & priv - > rng ) ;
return 0 ;
}
static int stm32_rng_runtime_resume ( struct device * dev )
{
2015-10-14 17:04:55 +01:00
struct stm32_rng_private * priv = dev_get_drvdata ( dev ) ;
2015-10-12 09:21:29 +01:00
return stm32_rng_init ( & priv - > rng ) ;
}
# endif
2018-04-23 17:04:26 +02:00
static const struct dev_pm_ops stm32_rng_pm_ops = {
SET_RUNTIME_PM_OPS ( stm32_rng_runtime_suspend ,
stm32_rng_runtime_resume , NULL )
SET_SYSTEM_SLEEP_PM_OPS ( pm_runtime_force_suspend ,
pm_runtime_force_resume )
} ;
2015-10-12 09:21:29 +01:00
static const struct of_device_id stm32_rng_match [ ] = {
{
. compatible = " st,stm32-rng " ,
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , stm32_rng_match ) ;
static struct platform_driver stm32_rng_driver = {
. driver = {
. name = " stm32-rng " ,
. pm = & stm32_rng_pm_ops ,
. of_match_table = stm32_rng_match ,
} ,
. probe = stm32_rng_probe ,
} ;
module_platform_driver ( stm32_rng_driver ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Daniel Thompson <daniel.thompson@linaro.org> " ) ;
MODULE_DESCRIPTION ( " STMicroelectronics STM32 RNG device driver " ) ;