2011-10-06 21:56:30 +04:00
/*
* Copyright ( c ) 2011 Peter Korsgaard < jacmet @ sunsite . dk >
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*/
# include <linux/kernel.h>
# include <linux/module.h>
2018-06-20 08:47:28 +03:00
# include <linux/mod_devicetable.h>
2011-10-06 21:56:30 +04:00
# include <linux/slab.h>
# include <linux/err.h>
# include <linux/clk.h>
# include <linux/io.h>
2022-02-21 10:59:22 +03:00
# include <linux/iopoll.h>
2011-10-06 21:56:30 +04:00
# include <linux/hw_random.h>
2023-07-28 16:48:27 +03:00
# include <linux/of.h>
2011-10-06 21:56:30 +04:00
# include <linux/platform_device.h>
2022-02-21 10:59:27 +03:00
# include <linux/pm_runtime.h>
2011-10-06 21:56:30 +04:00
# define TRNG_CR 0x00
2019-11-04 14:54:57 +03:00
# define TRNG_MR 0x04
2011-10-06 21:56:30 +04:00
# define TRNG_ISR 0x1c
2022-02-21 10:59:22 +03:00
# define TRNG_ISR_DATRDY BIT(0)
2011-10-06 21:56:30 +04:00
# define TRNG_ODATA 0x50
# define TRNG_KEY 0x524e4700 /* RNG */
2019-11-04 14:54:57 +03:00
# define TRNG_HALFR BIT(0) /* generate RN every 168 cycles */
struct atmel_trng_data {
bool has_half_rate ;
} ;
2011-10-06 21:56:30 +04:00
struct atmel_trng {
struct clk * clk ;
void __iomem * base ;
struct hwrng rng ;
2022-02-21 10:59:25 +03:00
bool has_half_rate ;
2011-10-06 21:56:30 +04:00
} ;
2022-02-21 10:59:22 +03:00
static bool atmel_trng_wait_ready ( struct atmel_trng * trng , bool wait )
{
int ready ;
ready = readl ( trng - > base + TRNG_ISR ) & TRNG_ISR_DATRDY ;
if ( ! ready & & wait )
readl_poll_timeout ( trng - > base + TRNG_ISR , ready ,
ready & TRNG_ISR_DATRDY , 1000 , 20000 ) ;
return ! ! ready ;
}
2011-10-06 21:56:30 +04:00
static int atmel_trng_read ( struct hwrng * rng , void * buf , size_t max ,
bool wait )
{
struct atmel_trng * trng = container_of ( rng , struct atmel_trng , rng ) ;
u32 * data = buf ;
2022-02-21 10:59:22 +03:00
int ret ;
2011-10-06 21:56:30 +04:00
2022-02-21 10:59:27 +03:00
ret = pm_runtime_get_sync ( ( struct device * ) trng - > rng . priv ) ;
if ( ret < 0 ) {
pm_runtime_put_sync ( ( struct device * ) trng - > rng . priv ) ;
return ret ;
}
2022-02-21 10:59:22 +03:00
ret = atmel_trng_wait_ready ( trng , wait ) ;
if ( ! ret )
goto out ;
* data = readl ( trng - > base + TRNG_ODATA ) ;
/*
* ensure data ready is only set again AFTER the next data word is ready
* in case it got set between checking ISR and reading ODATA , so we
* don ' t risk re - reading the same word
*/
readl ( trng - > base + TRNG_ISR ) ;
ret = 4 ;
out :
2022-02-21 10:59:27 +03:00
pm_runtime_mark_last_busy ( ( struct device * ) trng - > rng . priv ) ;
pm_runtime_put_sync_autosuspend ( ( struct device * ) trng - > rng . priv ) ;
2022-02-21 10:59:22 +03:00
return ret ;
2011-10-06 21:56:30 +04:00
}
2022-02-21 10:59:25 +03:00
static int atmel_trng_init ( struct atmel_trng * trng )
2016-10-28 11:00:46 +03:00
{
2022-02-21 10:59:25 +03:00
unsigned long rate ;
int ret ;
ret = clk_prepare_enable ( trng - > clk ) ;
if ( ret )
return ret ;
if ( trng - > has_half_rate ) {
rate = clk_get_rate ( trng - > clk ) ;
/* if peripheral clk is above 100MHz, set HALFR */
if ( rate > 100000000 )
writel ( TRNG_HALFR , trng - > base + TRNG_MR ) ;
}
2016-10-28 11:00:46 +03:00
writel ( TRNG_KEY | 1 , trng - > base + TRNG_CR ) ;
2022-02-21 10:59:25 +03:00
return 0 ;
2016-10-28 11:00:46 +03:00
}
2022-02-21 10:59:24 +03:00
static void atmel_trng_cleanup ( struct atmel_trng * trng )
2016-10-28 11:00:46 +03:00
{
writel ( TRNG_KEY , trng - > base + TRNG_CR ) ;
2022-02-21 10:59:25 +03:00
clk_disable_unprepare ( trng - > clk ) ;
2016-10-28 11:00:46 +03:00
}
2011-10-06 21:56:30 +04:00
static int atmel_trng_probe ( struct platform_device * pdev )
{
struct atmel_trng * trng ;
2019-11-04 14:54:57 +03:00
const struct atmel_trng_data * data ;
2011-10-06 21:56:30 +04:00
int ret ;
trng = devm_kzalloc ( & pdev - > dev , sizeof ( * trng ) , GFP_KERNEL ) ;
if ( ! trng )
return - ENOMEM ;
2019-10-16 13:46:09 +03:00
trng - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
2014-02-12 09:17:08 +04:00
if ( IS_ERR ( trng - > base ) )
return PTR_ERR ( trng - > base ) ;
2011-10-06 21:56:30 +04:00
2014-02-27 09:00:09 +04:00
trng - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
2011-10-06 21:56:30 +04:00
if ( IS_ERR ( trng - > clk ) )
return PTR_ERR ( trng - > clk ) ;
2019-11-04 14:54:57 +03:00
data = of_device_get_match_data ( & pdev - > dev ) ;
if ( ! data )
return - ENODEV ;
2022-02-21 10:59:25 +03:00
trng - > has_half_rate = data - > has_half_rate ;
trng - > rng . name = pdev - > name ;
trng - > rng . read = atmel_trng_read ;
2022-02-21 10:59:27 +03:00
trng - > rng . priv = ( unsigned long ) & pdev - > dev ;
platform_set_drvdata ( pdev , trng ) ;
2011-10-06 21:56:30 +04:00
2022-02-21 10:59:27 +03:00
# ifndef CONFIG_PM
2022-02-21 10:59:25 +03:00
ret = atmel_trng_init ( trng ) ;
2011-10-06 21:56:30 +04:00
if ( ret )
2014-02-27 09:00:09 +04:00
return ret ;
2022-02-21 10:59:27 +03:00
# endif
2011-10-06 21:56:30 +04:00
2022-02-21 10:59:27 +03:00
pm_runtime_set_autosuspend_delay ( & pdev - > dev , 100 ) ;
pm_runtime_use_autosuspend ( & pdev - > dev ) ;
pm_runtime_enable ( & pdev - > dev ) ;
2011-10-06 21:56:30 +04:00
2022-02-21 10:59:27 +03:00
ret = devm_hwrng_register ( & pdev - > dev , & trng - > rng ) ;
if ( ret ) {
pm_runtime_disable ( & pdev - > dev ) ;
pm_runtime_set_suspended ( & pdev - > dev ) ;
# ifndef CONFIG_PM
atmel_trng_cleanup ( trng ) ;
# endif
}
2011-10-06 21:56:30 +04:00
return ret ;
}
2012-11-19 22:26:26 +04:00
static int atmel_trng_remove ( struct platform_device * pdev )
2011-10-06 21:56:30 +04:00
{
struct atmel_trng * trng = platform_get_drvdata ( pdev ) ;
2022-02-21 10:59:24 +03:00
atmel_trng_cleanup ( trng ) ;
2022-02-21 10:59:27 +03:00
pm_runtime_disable ( & pdev - > dev ) ;
pm_runtime_set_suspended ( & pdev - > dev ) ;
2011-10-06 21:56:30 +04:00
return 0 ;
}
2022-02-21 10:59:27 +03:00
static int __maybe_unused atmel_trng_runtime_suspend ( struct device * dev )
2011-10-06 21:56:30 +04:00
{
struct atmel_trng * trng = dev_get_drvdata ( dev ) ;
2022-02-21 10:59:24 +03:00
atmel_trng_cleanup ( trng ) ;
2011-10-06 21:56:30 +04:00
return 0 ;
}
2022-02-21 10:59:27 +03:00
static int __maybe_unused atmel_trng_runtime_resume ( struct device * dev )
2011-10-06 21:56:30 +04:00
{
struct atmel_trng * trng = dev_get_drvdata ( dev ) ;
2016-10-28 11:00:46 +03:00
2022-02-21 10:59:25 +03:00
return atmel_trng_init ( trng ) ;
2011-10-06 21:56:30 +04:00
}
2022-02-21 10:59:26 +03:00
static const struct dev_pm_ops __maybe_unused atmel_trng_pm_ops = {
2022-02-21 10:59:27 +03:00
SET_RUNTIME_PM_OPS ( atmel_trng_runtime_suspend ,
atmel_trng_runtime_resume , NULL )
SET_SYSTEM_SLEEP_PM_OPS ( pm_runtime_force_suspend ,
pm_runtime_force_resume )
2011-10-06 21:56:30 +04:00
} ;
2019-11-04 14:54:57 +03:00
static const struct atmel_trng_data at91sam9g45_config = {
. has_half_rate = false ,
} ;
static const struct atmel_trng_data sam9x60_config = {
. has_half_rate = true ,
} ;
2014-11-20 12:43:23 +03:00
static const struct of_device_id atmel_trng_dt_ids [ ] = {
2019-11-04 14:54:57 +03:00
{
. compatible = " atmel,at91sam9g45-trng " ,
. data = & at91sam9g45_config ,
} , {
. compatible = " microchip,sam9x60-trng " ,
. data = & sam9x60_config ,
} , {
/* sentinel */
}
2014-11-20 12:43:23 +03:00
} ;
MODULE_DEVICE_TABLE ( of , atmel_trng_dt_ids ) ;
2011-10-06 21:56:30 +04:00
static struct platform_driver atmel_trng_driver = {
. probe = atmel_trng_probe ,
2012-12-22 03:12:08 +04:00
. remove = atmel_trng_remove ,
2011-10-06 21:56:30 +04:00
. driver = {
. name = " atmel-trng " ,
2022-02-21 10:59:26 +03:00
. pm = pm_ptr ( & atmel_trng_pm_ops ) ,
2014-11-20 12:43:23 +03:00
. of_match_table = atmel_trng_dt_ids ,
2011-10-06 21:56:30 +04:00
} ,
} ;
2011-11-26 17:11:06 +04:00
module_platform_driver ( atmel_trng_driver ) ;
2011-10-06 21:56:30 +04:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Peter Korsgaard <jacmet@sunsite.dk> " ) ;
MODULE_DESCRIPTION ( " Atmel true random number generator driver " ) ;