2019-05-27 09:55:08 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2009-04-02 19:38:41 +04:00
/*
* RNG driver for Freescale RNGA
*
* Copyright 2008 - 2009 Freescale Semiconductor , Inc . All Rights Reserved .
* Author : Alan Carvalho de Assis < acassis @ gmail . com >
*/
/*
*
* This driver is based on other RNG drivers .
*/
# include <linux/clk.h>
2012-06-13 20:15:34 +04:00
# include <linux/delay.h>
2018-03-06 01:21:00 +03:00
# include <linux/hw_random.h>
2009-04-02 19:38:41 +04:00
# include <linux/io.h>
2018-03-06 01:21:00 +03:00
# include <linux/module.h>
# include <linux/of.h>
# include <linux/platform_device.h>
2009-04-02 19:38:41 +04:00
/* RNGA Registers */
# define RNGA_CONTROL 0x00
# define RNGA_STATUS 0x04
# define RNGA_ENTROPY 0x08
# define RNGA_OUTPUT_FIFO 0x0c
# define RNGA_MODE 0x10
# define RNGA_VERIFICATION_CONTROL 0x14
# define RNGA_OSC_CONTROL_COUNTER 0x18
# define RNGA_OSC1_COUNTER 0x1c
# define RNGA_OSC2_COUNTER 0x20
# define RNGA_OSC_COUNTER_STATUS 0x24
/* RNGA Registers Range */
# define RNG_ADDR_RANGE 0x28
/* RNGA Control Register */
# define RNGA_CONTROL_SLEEP 0x00000010
# define RNGA_CONTROL_CLEAR_INT 0x00000008
# define RNGA_CONTROL_MASK_INTS 0x00000004
# define RNGA_CONTROL_HIGH_ASSURANCE 0x00000002
# define RNGA_CONTROL_GO 0x00000001
# define RNGA_STATUS_LEVEL_MASK 0x0000ff00
/* RNGA Status Register */
# define RNGA_STATUS_OSC_DEAD 0x80000000
# define RNGA_STATUS_SLEEP 0x00000010
# define RNGA_STATUS_ERROR_INT 0x00000008
# define RNGA_STATUS_FIFO_UNDERFLOW 0x00000004
# define RNGA_STATUS_LAST_READ_STATUS 0x00000002
# define RNGA_STATUS_SECURITY_VIOLATION 0x00000001
2012-09-05 01:35:22 +04:00
struct mxc_rng {
struct device * dev ;
struct hwrng rng ;
void __iomem * mem ;
struct clk * clk ;
} ;
2009-04-02 19:38:41 +04:00
2012-06-13 20:15:34 +04:00
static int mxc_rnga_data_present ( struct hwrng * rng , int wait )
2009-04-02 19:38:41 +04:00
{
2012-06-13 20:15:34 +04:00
int i ;
2012-09-05 01:35:22 +04:00
struct mxc_rng * mxc_rng = container_of ( rng , struct mxc_rng , rng ) ;
2012-06-13 20:15:34 +04:00
for ( i = 0 ; i < 20 ; i + + ) {
/* how many random numbers are in FIFO? [0-16] */
2012-09-05 01:35:22 +04:00
int level = ( __raw_readl ( mxc_rng - > mem + RNGA_STATUS ) &
2012-06-13 20:15:34 +04:00
RNGA_STATUS_LEVEL_MASK ) > > 8 ;
if ( level | | ! wait )
return ! ! level ;
udelay ( 10 ) ;
}
return 0 ;
2009-04-02 19:38:41 +04:00
}
static int mxc_rnga_data_read ( struct hwrng * rng , u32 * data )
{
int err ;
u32 ctrl ;
2012-09-05 01:35:22 +04:00
struct mxc_rng * mxc_rng = container_of ( rng , struct mxc_rng , rng ) ;
2009-04-02 19:38:41 +04:00
/* retrieve a random number from FIFO */
2012-09-05 01:35:22 +04:00
* data = __raw_readl ( mxc_rng - > mem + RNGA_OUTPUT_FIFO ) ;
2009-04-02 19:38:41 +04:00
/* some error while reading this random number? */
2012-09-05 01:35:22 +04:00
err = __raw_readl ( mxc_rng - > mem + RNGA_STATUS ) & RNGA_STATUS_ERROR_INT ;
2009-04-02 19:38:41 +04:00
/* if error: clear error interrupt, but doesn't return random number */
if ( err ) {
2012-09-05 01:35:22 +04:00
dev_dbg ( mxc_rng - > dev , " Error while reading random number! \n " ) ;
ctrl = __raw_readl ( mxc_rng - > mem + RNGA_CONTROL ) ;
2009-04-02 19:38:41 +04:00
__raw_writel ( ctrl | RNGA_CONTROL_CLEAR_INT ,
2012-09-05 01:35:22 +04:00
mxc_rng - > mem + RNGA_CONTROL ) ;
2009-04-02 19:38:41 +04:00
return 0 ;
} else
return 4 ;
}
static int mxc_rnga_init ( struct hwrng * rng )
{
u32 ctrl , osc ;
2012-09-05 01:35:22 +04:00
struct mxc_rng * mxc_rng = container_of ( rng , struct mxc_rng , rng ) ;
2009-04-02 19:38:41 +04:00
/* wake up */
2012-09-05 01:35:22 +04:00
ctrl = __raw_readl ( mxc_rng - > mem + RNGA_CONTROL ) ;
__raw_writel ( ctrl & ~ RNGA_CONTROL_SLEEP , mxc_rng - > mem + RNGA_CONTROL ) ;
2009-04-02 19:38:41 +04:00
/* verify if oscillator is working */
2012-09-05 01:35:22 +04:00
osc = __raw_readl ( mxc_rng - > mem + RNGA_STATUS ) ;
2009-04-02 19:38:41 +04:00
if ( osc & RNGA_STATUS_OSC_DEAD ) {
2012-09-05 01:35:22 +04:00
dev_err ( mxc_rng - > dev , " RNGA Oscillator is dead! \n " ) ;
2009-04-02 19:38:41 +04:00
return - ENODEV ;
}
/* go running */
2012-09-05 01:35:22 +04:00
ctrl = __raw_readl ( mxc_rng - > mem + RNGA_CONTROL ) ;
__raw_writel ( ctrl | RNGA_CONTROL_GO , mxc_rng - > mem + RNGA_CONTROL ) ;
2009-04-02 19:38:41 +04:00
return 0 ;
}
static void mxc_rnga_cleanup ( struct hwrng * rng )
{
u32 ctrl ;
2012-09-05 01:35:22 +04:00
struct mxc_rng * mxc_rng = container_of ( rng , struct mxc_rng , rng ) ;
2009-04-02 19:38:41 +04:00
2012-09-05 01:35:22 +04:00
ctrl = __raw_readl ( mxc_rng - > mem + RNGA_CONTROL ) ;
2009-04-02 19:38:41 +04:00
/* stop rnga */
2012-09-05 01:35:22 +04:00
__raw_writel ( ctrl & ~ RNGA_CONTROL_GO , mxc_rng - > mem + RNGA_CONTROL ) ;
2009-04-02 19:38:41 +04:00
}
static int __init mxc_rnga_probe ( struct platform_device * pdev )
{
2015-09-13 02:19:51 +03:00
int err ;
2012-09-05 01:35:22 +04:00
struct mxc_rng * mxc_rng ;
2015-09-13 02:19:53 +03:00
mxc_rng = devm_kzalloc ( & pdev - > dev , sizeof ( * mxc_rng ) , GFP_KERNEL ) ;
2012-09-05 01:35:22 +04:00
if ( ! mxc_rng )
return - ENOMEM ;
mxc_rng - > dev = & pdev - > dev ;
mxc_rng - > rng . name = " mxc-rnga " ;
mxc_rng - > rng . init = mxc_rnga_init ;
2020-09-27 22:12:17 +03:00
mxc_rng - > rng . cleanup = mxc_rnga_cleanup ;
mxc_rng - > rng . data_present = mxc_rnga_data_present ;
mxc_rng - > rng . data_read = mxc_rnga_data_read ;
2012-09-05 01:35:22 +04:00
mxc_rng - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( mxc_rng - > clk ) ) {
2009-04-02 19:38:41 +04:00
dev_err ( & pdev - > dev , " Could not get rng_clk! \n " ) ;
2015-09-13 02:19:50 +03:00
return PTR_ERR ( mxc_rng - > clk ) ;
2009-04-02 19:38:41 +04:00
}
2013-07-21 21:41:38 +04:00
err = clk_prepare_enable ( mxc_rng - > clk ) ;
if ( err )
2015-09-13 02:19:50 +03:00
return err ;
2009-04-02 19:38:41 +04:00
2019-07-17 12:04:38 +03:00
mxc_rng - > mem = devm_platform_ioremap_resource ( pdev , 0 ) ;
2013-03-13 07:57:27 +04:00
if ( IS_ERR ( mxc_rng - > mem ) ) {
err = PTR_ERR ( mxc_rng - > mem ) ;
2009-04-02 19:38:41 +04:00
goto err_ioremap ;
}
2012-09-05 01:35:22 +04:00
err = hwrng_register ( & mxc_rng - > rng ) ;
2009-04-02 19:38:41 +04:00
if ( err ) {
dev_err ( & pdev - > dev , " MXC RNGA registering failed (%d) \n " , err ) ;
2012-09-05 01:35:22 +04:00
goto err_ioremap ;
2009-04-02 19:38:41 +04:00
}
return 0 ;
err_ioremap :
2012-09-05 01:35:22 +04:00
clk_disable_unprepare ( mxc_rng - > clk ) ;
2009-04-02 19:38:41 +04:00
return err ;
}
static int __exit mxc_rnga_remove ( struct platform_device * pdev )
{
2012-09-05 01:35:22 +04:00
struct mxc_rng * mxc_rng = platform_get_drvdata ( pdev ) ;
2009-04-02 19:38:41 +04:00
2012-09-05 01:35:22 +04:00
hwrng_unregister ( & mxc_rng - > rng ) ;
2009-04-02 19:38:41 +04:00
2012-09-05 01:35:22 +04:00
clk_disable_unprepare ( mxc_rng - > clk ) ;
2009-04-02 19:38:41 +04:00
return 0 ;
}
2018-03-06 01:21:00 +03:00
static const struct of_device_id mxc_rnga_of_match [ ] = {
{ . compatible = " fsl,imx21-rnga " , } ,
{ . compatible = " fsl,imx31-rnga " , } ,
{ /* sentinel */ } ,
} ;
MODULE_DEVICE_TABLE ( of , mxc_rnga_of_match ) ;
2009-04-02 19:38:41 +04:00
static struct platform_driver mxc_rnga_driver = {
. driver = {
2018-03-06 01:21:00 +03:00
. name = " mxc_rnga " ,
. of_match_table = mxc_rnga_of_match ,
} ,
2009-04-02 19:38:41 +04:00
. remove = __exit_p ( mxc_rnga_remove ) ,
} ;
2013-03-14 21:09:33 +04:00
module_platform_driver_probe ( mxc_rnga_driver , mxc_rnga_probe ) ;
2009-04-02 19:38:41 +04:00
MODULE_AUTHOR ( " Freescale Semiconductor, Inc. " ) ;
MODULE_DESCRIPTION ( " H/W RNGA driver for i.MX " ) ;
MODULE_LICENSE ( " GPL " ) ;