2013-06-18 23:47:13 +02: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/kernel.h>
# include <linux/slab.h>
# include <linux/fmc.h>
# include <linux/ipmi-fru.h>
/* The fru parser is both user and kernel capable: it needs alloc */
void * fru_alloc ( size_t size )
{
return kzalloc ( size , GFP_KERNEL ) ;
}
/* The actual match function */
int fmc_match ( struct device * dev , struct device_driver * drv )
{
struct fmc_driver * fdrv = to_fmc_driver ( drv ) ;
struct fmc_device * fdev = to_fmc_device ( dev ) ;
struct fmc_fru_id * fid ;
int i , matched = 0 ;
/* This currently only matches the EEPROM (FRU id) */
fid = fdrv - > id_table . fru_id ;
if ( ! fid ) {
dev_warn ( & fdev - > dev , " Driver has no ID: matches all \n " ) ;
matched = 1 ;
} else {
if ( ! fdev - > id . manufacturer | | ! fdev - > id . product_name )
return 0 ; /* the device has no FRU information */
for ( i = 0 ; i < fdrv - > id_table . fru_id_nr ; i + + , fid + + ) {
if ( fid - > manufacturer & &
strcmp ( fid - > manufacturer , fdev - > id . manufacturer ) )
continue ;
if ( fid - > product_name & &
strcmp ( fid - > product_name , fdev - > id . product_name ) )
continue ;
matched = 1 ;
break ;
}
}
/* FIXME: match SDB contents */
return matched ;
}
/* This function creates ID info for a newly registered device */
int fmc_fill_id_info ( struct fmc_device * fmc )
{
struct fru_common_header * h ;
struct fru_board_info_area * bia ;
int ret , allocated = 0 ;
/* If we know the eeprom length, try to read it off the device */
if ( fmc - > eeprom_len & & ! fmc - > eeprom ) {
fmc - > eeprom = kzalloc ( fmc - > eeprom_len , GFP_KERNEL ) ;
if ( ! fmc - > eeprom )
return - ENOMEM ;
allocated = 1 ;
2017-07-18 08:32:53 +02:00
ret = fmc_read_ee ( fmc , 0 , fmc - > eeprom , fmc - > eeprom_len ) ;
2013-06-18 23:47:13 +02:00
if ( ret < 0 )
goto out ;
}
/* If no eeprom, continue with other matches */
if ( ! fmc - > eeprom )
return 0 ;
dev_info ( fmc - > hwdev , " mezzanine %i \n " , fmc - > slot_id ) ; /* header */
/* So we have the eeprom: parse the FRU part (if any) */
h = ( void * ) fmc - > eeprom ;
if ( h - > format ! = 1 ) {
pr_info ( " EEPROM has no FRU information \n " ) ;
goto out ;
}
if ( ! fru_header_cksum_ok ( h ) ) {
pr_info ( " FRU: wrong header checksum \n " ) ;
goto out ;
}
bia = fru_get_board_area ( h ) ;
if ( ! fru_bia_cksum_ok ( bia ) ) {
pr_info ( " FRU: wrong board area checksum \n " ) ;
goto out ;
}
fmc - > id . manufacturer = fru_get_board_manufacturer ( h ) ;
fmc - > id . product_name = fru_get_product_name ( h ) ;
pr_info ( " Manufacturer: %s \n " , fmc - > id . manufacturer ) ;
pr_info ( " Product name: %s \n " , fmc - > id . product_name ) ;
/* Create the short name (FIXME: look in sdb as well) */
fmc - > mezzanine_name = kstrdup ( fmc - > id . product_name , GFP_KERNEL ) ;
out :
if ( allocated ) {
kfree ( fmc - > eeprom ) ;
fmc - > eeprom = NULL ;
}
return 0 ; /* no error: let other identification work */
}
/* Some ID data is allocated using fru_alloc() above, so release it */
void fmc_free_id_info ( struct fmc_device * fmc )
{
kfree ( fmc - > mezzanine_name ) ;
kfree ( fmc - > id . manufacturer ) ;
kfree ( fmc - > id . product_name ) ;
}