2006-06-26 11:25:00 +04:00
/*
* RNG driver for AMD RNGs
*
* Copyright 2005 ( c ) MontaVista Software , Inc .
*
* with the majority of the code coming from :
*
* Hardware driver for the Intel / AMD / VIA Random Number Generators ( RNG )
* ( c ) Copyright 2003 Red Hat Inc < jgarzik @ redhat . com >
*
* derived from
*
* Hardware driver for the AMD 768 Random Number Generator ( RNG )
2008-10-27 18:10:23 +03:00
* ( c ) Copyright 2001 Red Hat Inc
2006-06-26 11:25:00 +04:00
*
* derived from
*
* Hardware driver for Intel i810 Random Number Generator ( RNG )
* Copyright 2000 , 2001 Jeff Garzik < jgarzik @ pobox . com >
* Copyright 2000 , 2001 Philipp Rumpf < prumpf @ mandrakesoft . com >
*
* 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 .
*/
2016-08-26 14:11:32 +03:00
# include <linux/delay.h>
# include <linux/hw_random.h>
2006-06-26 11:25:00 +04:00
# include <linux/kernel.h>
2016-08-26 14:11:32 +03:00
# include <linux/module.h>
2006-06-26 11:25:00 +04:00
# include <linux/pci.h>
2016-08-26 14:11:31 +03:00
# define DRV_NAME "AMD768-HWRNG"
2006-06-26 11:25:00 +04:00
2016-08-26 14:11:35 +03:00
# define RNGDATA 0x00
# define RNGDONE 0x04
# define PMBASE_OFFSET 0xF0
# define PMBASE_SIZE 8
2006-06-26 11:25:00 +04:00
/*
* Data for PCI driver interface
*
* This data only exists for exporting the supported
* PCI ids via MODULE_DEVICE_TABLE . We do not actually
* register a pci_driver , because someone else might one day
* want to register another driver on the same PCI id .
*/
static const struct pci_device_id pci_tbl [ ] = {
2009-06-25 09:50:53 +04:00
{ PCI_VDEVICE ( AMD , 0x7443 ) , 0 , } ,
{ PCI_VDEVICE ( AMD , 0x746b ) , 0 , } ,
2006-06-26 11:25:00 +04:00
{ 0 , } , /* terminate list */
} ;
MODULE_DEVICE_TABLE ( pci , pci_tbl ) ;
2016-08-26 14:11:34 +03:00
struct amd768_priv {
2016-08-26 14:11:35 +03:00
void __iomem * iobase ;
2016-08-26 14:11:34 +03:00
struct pci_dev * pcidev ;
u32 pmbase ;
} ;
2006-06-26 11:25:00 +04:00
2007-11-21 07:24:45 +03:00
static int amd_rng_data_present ( struct hwrng * rng , int wait )
2006-06-26 11:25:00 +04:00
{
2016-08-26 14:11:34 +03:00
struct amd768_priv * priv = ( struct amd768_priv * ) rng - > priv ;
2007-11-21 07:24:45 +03:00
int data , i ;
for ( i = 0 ; i < 20 ; i + + ) {
2016-08-26 14:11:35 +03:00
data = ! ! ( ioread32 ( priv - > iobase + RNGDONE ) & 1 ) ;
2007-11-21 07:24:45 +03:00
if ( data | | ! wait )
break ;
udelay ( 10 ) ;
}
return data ;
2006-06-26 11:25:00 +04:00
}
static int amd_rng_data_read ( struct hwrng * rng , u32 * data )
{
2016-08-26 14:11:34 +03:00
struct amd768_priv * priv = ( struct amd768_priv * ) rng - > priv ;
2006-06-26 11:25:00 +04:00
2016-08-26 14:11:35 +03:00
* data = ioread32 ( priv - > iobase + RNGDATA ) ;
2006-06-26 11:25:00 +04:00
return 4 ;
}
static int amd_rng_init ( struct hwrng * rng )
{
2016-08-26 14:11:34 +03:00
struct amd768_priv * priv = ( struct amd768_priv * ) rng - > priv ;
2006-06-26 11:25:00 +04:00
u8 rnen ;
2016-08-26 14:11:34 +03:00
pci_read_config_byte ( priv - > pcidev , 0x40 , & rnen ) ;
2016-08-26 14:11:30 +03:00
rnen | = BIT ( 7 ) ; /* RNG on */
2016-08-26 14:11:34 +03:00
pci_write_config_byte ( priv - > pcidev , 0x40 , rnen ) ;
2006-06-26 11:25:00 +04:00
2016-08-26 14:11:34 +03:00
pci_read_config_byte ( priv - > pcidev , 0x41 , & rnen ) ;
2016-08-26 14:11:30 +03:00
rnen | = BIT ( 7 ) ; /* PMIO enable */
2016-08-26 14:11:34 +03:00
pci_write_config_byte ( priv - > pcidev , 0x41 , rnen ) ;
2006-06-26 11:25:00 +04:00
return 0 ;
}
static void amd_rng_cleanup ( struct hwrng * rng )
{
2016-08-26 14:11:34 +03:00
struct amd768_priv * priv = ( struct amd768_priv * ) rng - > priv ;
2006-06-26 11:25:00 +04:00
u8 rnen ;
2016-08-26 14:11:34 +03:00
pci_read_config_byte ( priv - > pcidev , 0x40 , & rnen ) ;
2016-08-26 14:11:30 +03:00
rnen & = ~ BIT ( 7 ) ; /* RNG off */
2016-08-26 14:11:34 +03:00
pci_write_config_byte ( priv - > pcidev , 0x40 , rnen ) ;
2006-06-26 11:25:00 +04:00
}
static struct hwrng amd_rng = {
. name = " amd " ,
. init = amd_rng_init ,
. cleanup = amd_rng_cleanup ,
. data_present = amd_rng_data_present ,
. data_read = amd_rng_data_read ,
} ;
static int __init mod_init ( void )
{
int err = - ENODEV ;
struct pci_dev * pdev = NULL ;
const struct pci_device_id * ent ;
u32 pmbase ;
2016-08-26 14:11:34 +03:00
struct amd768_priv * priv ;
2006-06-26 11:25:00 +04:00
for_each_pci_dev ( pdev ) {
ent = pci_match_id ( pci_tbl , pdev ) ;
if ( ent )
goto found ;
}
/* Device not found. */
2016-08-26 14:11:34 +03:00
return - ENODEV ;
2006-06-26 11:25:00 +04:00
found :
err = pci_read_config_dword ( pdev , 0x58 , & pmbase ) ;
if ( err )
2016-08-26 14:11:34 +03:00
return err ;
2006-06-26 11:25:00 +04:00
pmbase & = 0x0000FF00 ;
if ( pmbase = = 0 )
2016-08-26 14:11:34 +03:00
return - EIO ;
priv = kzalloc ( sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
2016-08-26 14:11:35 +03:00
if ( ! request_region ( pmbase + PMBASE_OFFSET , PMBASE_SIZE , DRV_NAME ) ) {
2016-08-26 14:11:31 +03:00
dev_err ( & pdev - > dev , DRV_NAME " region 0x%x already in use! \n " ,
2011-04-27 23:21:15 +04:00
pmbase + 0xF0 ) ;
err = - EBUSY ;
goto out ;
}
2016-08-26 14:11:35 +03:00
priv - > iobase = ioport_map ( pmbase + PMBASE_OFFSET , PMBASE_SIZE ) ;
if ( ! priv - > iobase ) {
pr_err ( DRV_NAME " Cannot map ioport \n " ) ;
err = - EINVAL ;
goto err_iomap ;
}
2016-08-26 14:11:34 +03:00
amd_rng . priv = ( unsigned long ) priv ;
priv - > pmbase = pmbase ;
priv - > pcidev = pdev ;
2006-06-26 11:25:00 +04:00
2016-08-26 14:11:31 +03:00
pr_info ( DRV_NAME " detected \n " ) ;
2006-06-26 11:25:00 +04:00
err = hwrng_register ( & amd_rng ) ;
if ( err ) {
2016-08-26 14:11:31 +03:00
pr_err ( DRV_NAME " registering failed (%d) \n " , err ) ;
2016-08-26 14:11:35 +03:00
goto err_hwrng ;
2006-06-26 11:25:00 +04:00
}
2016-08-26 14:11:34 +03:00
return 0 ;
2016-08-26 14:11:35 +03:00
err_hwrng :
ioport_unmap ( priv - > iobase ) ;
err_iomap :
release_region ( pmbase + PMBASE_OFFSET , PMBASE_SIZE ) ;
2006-06-26 11:25:00 +04:00
out :
2016-08-26 14:11:34 +03:00
kfree ( priv ) ;
2006-06-26 11:25:00 +04:00
return err ;
}
static void __exit mod_exit ( void )
{
2016-08-26 14:11:34 +03:00
struct amd768_priv * priv ;
priv = ( struct amd768_priv * ) amd_rng . priv ;
2016-08-26 14:11:29 +03:00
2006-06-26 11:25:00 +04:00
hwrng_unregister ( & amd_rng ) ;
2016-08-26 14:11:33 +03:00
2016-08-26 14:11:35 +03:00
ioport_unmap ( priv - > iobase ) ;
release_region ( priv - > pmbase + PMBASE_OFFSET , PMBASE_SIZE ) ;
2016-08-26 14:11:34 +03:00
kfree ( priv ) ;
2006-06-26 11:25:00 +04:00
}
2007-01-11 10:15:43 +03:00
module_init ( mod_init ) ;
2006-06-26 11:25:00 +04:00
module_exit ( mod_exit ) ;
MODULE_AUTHOR ( " The Linux Kernel team " ) ;
MODULE_DESCRIPTION ( " H/W RNG driver for AMD chipsets " ) ;
MODULE_LICENSE ( " GPL " ) ;