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 >
*/
/*
* The code contained herein is licensed under the GNU General Public
* License . You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations :
*
* http : //www.opensource.org/licenses/gpl-license.html
* http : //www.gnu.org/copyleft/gpl.html
*
* This driver is based on other RNG drivers .
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/clk.h>
# include <linux/err.h>
# include <linux/ioport.h>
# include <linux/platform_device.h>
# include <linux/hw_random.h>
2012-06-13 20:15:34 +04:00
# include <linux/delay.h>
2009-04-02 19:38:41 +04:00
# include <linux/io.h>
/* 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 )
{
int err = - ENODEV ;
struct resource * res , * mem ;
2012-09-05 01:35:22 +04:00
struct mxc_rng * mxc_rng ;
mxc_rng = devm_kzalloc ( & pdev - > dev , sizeof ( struct mxc_rng ) ,
GFP_KERNEL ) ;
if ( ! mxc_rng )
return - ENOMEM ;
mxc_rng - > dev = & pdev - > dev ;
mxc_rng - > rng . name = " mxc-rnga " ;
mxc_rng - > rng . init = mxc_rnga_init ;
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 ,
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 " ) ;
2012-09-05 01:35:22 +04:00
err = PTR_ERR ( mxc_rng - > clk ) ;
2009-04-02 19:38:41 +04:00
goto out ;
}
2012-09-05 01:35:22 +04:00
clk_prepare_enable ( mxc_rng - > clk ) ;
2009-04-02 19:38:41 +04:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
err = - ENOENT ;
goto err_region ;
}
mem = request_mem_region ( res - > start , resource_size ( res ) , pdev - > name ) ;
if ( mem = = NULL ) {
err = - EBUSY ;
goto err_region ;
}
2012-09-05 01:35:22 +04:00
mxc_rng - > mem = ioremap ( res - > start , resource_size ( res ) ) ;
if ( ! mxc_rng - > mem ) {
2009-04-02 19:38:41 +04:00
err = - ENOMEM ;
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
}
dev_info ( & pdev - > dev , " MXC RNGA Registered. \n " ) ;
return 0 ;
err_ioremap :
release_mem_region ( res - > start , resource_size ( res ) ) ;
err_region :
2012-09-05 01:35:22 +04:00
clk_disable_unprepare ( mxc_rng - > clk ) ;
2009-04-02 19:38:41 +04:00
out :
return err ;
}
static int __exit mxc_rnga_remove ( struct platform_device * pdev )
{
struct resource * res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
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
iounmap ( mxc_rng - > mem ) ;
2009-04-02 19:38:41 +04:00
release_mem_region ( res - > start , resource_size ( res ) ) ;
2012-09-05 01:35:22 +04:00
clk_disable_unprepare ( mxc_rng - > clk ) ;
2009-04-02 19:38:41 +04:00
return 0 ;
}
static struct platform_driver mxc_rnga_driver = {
. driver = {
. name = " mxc_rnga " ,
. owner = THIS_MODULE ,
} ,
. 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 " ) ;