2013-06-19 01:47:56 +04:00
/*
* Copyright ( C ) 2012 CERN ( www . cern . ch )
* Author : Alessandro Rubini < rubini @ gnudd . com >
*
* Released according to the GNU GPL , version 2 or any later version .
*
* This work is part of the White Rabbit project , a research effort led
* by CERN , the European Institute for Nuclear Research .
*/
# include <linux/module.h>
# include <linux/string.h>
# include <linux/firmware.h>
# include <linux/init.h>
# include <linux/fmc.h>
# include <asm/unaligned.h>
/*
* This module uses the firmware loader to program the whole or part
* of the FMC eeprom . The meat is in the _run functions . However , no
* default file name is provided , to avoid accidental mishaps . Also ,
* you must pass the busid argument
*/
static struct fmc_driver fwe_drv ;
FMC_PARAM_BUSID ( fwe_drv ) ;
/* The "file=" is like the generic "gateware=" used elsewhere */
static char * fwe_file [ FMC_MAX_CARDS ] ;
static int fwe_file_n ;
2014-02-26 03:01:41 +04:00
module_param_array_named ( file , fwe_file , charp , & fwe_file_n , 0444 ) ;
2013-06-19 01:47:56 +04:00
static int fwe_run_tlv ( struct fmc_device * fmc , const struct firmware * fw ,
int write )
{
const uint8_t * p = fw - > data ;
int len = fw - > size ;
uint16_t thislen , thisaddr ;
int err ;
/* format is: 'w' addr16 len16 data... */
while ( len > 5 ) {
thisaddr = get_unaligned_le16 ( p + 1 ) ;
thislen = get_unaligned_le16 ( p + 3 ) ;
if ( p [ 0 ] ! = ' w ' | | thislen + 5 > len ) {
dev_err ( & fmc - > dev , " invalid tlv at offset %ti \n " ,
p - fw - > data ) ;
return - EINVAL ;
}
err = 0 ;
if ( write ) {
dev_info ( & fmc - > dev , " write %i bytes at 0x%04x \n " ,
thislen , thisaddr ) ;
err = fmc - > op - > write_ee ( fmc , thisaddr , p + 5 , thislen ) ;
}
if ( err < 0 ) {
dev_err ( & fmc - > dev , " write failure @0x%04x \n " ,
thisaddr ) ;
return err ;
}
p + = 5 + thislen ;
len - = 5 + thislen ;
}
if ( write )
dev_info ( & fmc - > dev , " write_eeprom: success \n " ) ;
return 0 ;
}
static int fwe_run_bin ( struct fmc_device * fmc , const struct firmware * fw )
{
int ret ;
dev_info ( & fmc - > dev , " programming %zi bytes \n " , fw - > size ) ;
ret = fmc - > op - > write_ee ( fmc , 0 , ( void * ) fw - > data , fw - > size ) ;
if ( ret < 0 ) {
dev_info ( & fmc - > dev , " write_eeprom: error %i \n " , ret ) ;
return ret ;
}
dev_info ( & fmc - > dev , " write_eeprom: success \n " ) ;
return 0 ;
}
static int fwe_run ( struct fmc_device * fmc , const struct firmware * fw , char * s )
{
char * last4 = s + strlen ( s ) - 4 ;
int err ;
if ( ! strcmp ( last4 , " .bin " ) )
return fwe_run_bin ( fmc , fw ) ;
if ( ! strcmp ( last4 , " .tlv " ) ) {
err = fwe_run_tlv ( fmc , fw , 0 ) ;
if ( ! err )
err = fwe_run_tlv ( fmc , fw , 1 ) ;
return err ;
}
dev_err ( & fmc - > dev , " invalid file name \" %s \" \n " , s ) ;
return - EINVAL ;
}
/*
* Programming is done at probe time . Morever , only those listed with
* busid = are programmed .
* card is probed for , only one is programmed . Unfortunately , it ' s
* difficult to know in advance when probing the first card if others
* are there .
*/
2013-08-12 07:35:46 +04:00
static int fwe_probe ( struct fmc_device * fmc )
2013-06-19 01:47:56 +04:00
{
int err , index = 0 ;
const struct firmware * fw ;
struct device * dev = & fmc - > dev ;
char * s ;
if ( ! fwe_drv . busid_n ) {
dev_err ( dev , " %s: no busid passed, refusing all cards \n " ,
KBUILD_MODNAME ) ;
return - ENODEV ;
}
if ( fmc - > op - > validate )
index = fmc - > op - > validate ( fmc , & fwe_drv ) ;
if ( index < 0 ) {
pr_err ( " %s: refusing device \" %s \" \n " , KBUILD_MODNAME ,
dev_name ( dev ) ) ;
return - ENODEV ;
}
if ( index > = fwe_file_n ) {
pr_err ( " %s: no filename for device index %i \n " ,
KBUILD_MODNAME , index ) ;
return - ENODEV ;
}
s = fwe_file [ index ] ;
if ( ! s ) {
pr_err ( " %s: no filename for \" %s \" not programming \n " ,
KBUILD_MODNAME , dev_name ( dev ) ) ;
return - ENOENT ;
}
err = request_firmware ( & fw , s , dev ) ;
if ( err < 0 ) {
dev_err ( & fmc - > dev , " request firmware \" %s \" : error %i \n " ,
s , err ) ;
return err ;
}
fwe_run ( fmc , fw , s ) ;
release_firmware ( fw ) ;
return 0 ;
}
2013-08-12 07:35:46 +04:00
static int fwe_remove ( struct fmc_device * fmc )
2013-06-19 01:47:56 +04:00
{
return 0 ;
}
static struct fmc_driver fwe_drv = {
. version = FMC_VERSION ,
. driver . name = KBUILD_MODNAME ,
. probe = fwe_probe ,
. remove = fwe_remove ,
/* no table, as the current match just matches everything */
} ;
static int fwe_init ( void )
{
int ret ;
ret = fmc_driver_register ( & fwe_drv ) ;
return ret ;
}
static void fwe_exit ( void )
{
fmc_driver_unregister ( & fwe_drv ) ;
}
module_init ( fwe_init ) ;
module_exit ( fwe_exit ) ;
MODULE_LICENSE ( " GPL " ) ;