2013-06-19 01:47:13 +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 .
*/
2013-06-12 11:13:25 +04:00
# include <linux/kernel.h>
# include <linux/module.h>
2013-06-19 01:47:13 +04:00
# include <linux/slab.h>
2013-06-12 11:13:25 +04:00
# include <linux/init.h>
# include <linux/device.h>
2013-06-19 01:47:13 +04:00
# include <linux/fmc.h>
2017-07-18 09:33:03 +03:00
# include <linux/fmc-sdb.h>
# include "fmc-private.h"
2013-06-19 01:47:13 +04:00
static int fmc_check_version ( unsigned long version , const char * name )
{
if ( __FMC_MAJOR ( version ) ! = FMC_MAJOR ) {
pr_err ( " %s: \" %s \" has wrong major (has %li, expected %i) \n " ,
__func__ , name , __FMC_MAJOR ( version ) , FMC_MAJOR ) ;
return - EINVAL ;
}
if ( __FMC_MINOR ( version ) ! = FMC_MINOR )
pr_info ( " %s: \" %s \" has wrong minor (has %li, expected %i) \n " ,
__func__ , name , __FMC_MINOR ( version ) , FMC_MINOR ) ;
return 0 ;
}
static int fmc_uevent ( struct device * dev , struct kobj_uevent_env * env )
{
/* struct fmc_device *fdev = to_fmc_device(dev); */
/* FIXME: The MODALIAS */
add_uevent_var ( env , " MODALIAS=%s " , " fmc " ) ;
return 0 ;
}
static int fmc_probe ( struct device * dev )
{
struct fmc_driver * fdrv = to_fmc_driver ( dev - > driver ) ;
struct fmc_device * fdev = to_fmc_device ( dev ) ;
return fdrv - > probe ( fdev ) ;
}
static int fmc_remove ( struct device * dev )
{
struct fmc_driver * fdrv = to_fmc_driver ( dev - > driver ) ;
struct fmc_device * fdev = to_fmc_device ( dev ) ;
return fdrv - > remove ( fdev ) ;
}
static void fmc_shutdown ( struct device * dev )
{
/* not implemented but mandatory */
}
2013-06-12 11:13:25 +04:00
static struct bus_type fmc_bus_type = {
. name = " fmc " ,
2013-06-19 01:47:13 +04:00
. match = fmc_match ,
. uevent = fmc_uevent ,
. probe = fmc_probe ,
. remove = fmc_remove ,
. shutdown = fmc_shutdown ,
2013-06-12 11:13:25 +04:00
} ;
2013-06-19 01:47:13 +04:00
static void fmc_release ( struct device * dev )
{
struct fmc_device * fmc = container_of ( dev , struct fmc_device , dev ) ;
kfree ( fmc ) ;
}
/*
* The eeprom is exported in sysfs , through a binary attribute
*/
static ssize_t fmc_read_eeprom ( struct file * file , struct kobject * kobj ,
struct bin_attribute * bin_attr ,
char * buf , loff_t off , size_t count )
{
struct device * dev ;
struct fmc_device * fmc ;
int eelen ;
dev = container_of ( kobj , struct device , kobj ) ;
fmc = container_of ( dev , struct fmc_device , dev ) ;
eelen = fmc - > eeprom_len ;
if ( off > eelen )
return - ESPIPE ;
if ( off = = eelen )
return 0 ; /* EOF */
if ( off + count > eelen )
count = eelen - off ;
memcpy ( buf , fmc - > eeprom + off , count ) ;
return count ;
}
2014-02-22 12:11:12 +04:00
static ssize_t fmc_write_eeprom ( struct file * file , struct kobject * kobj ,
struct bin_attribute * bin_attr ,
char * buf , loff_t off , size_t count )
{
struct device * dev ;
struct fmc_device * fmc ;
dev = container_of ( kobj , struct device , kobj ) ;
fmc = container_of ( dev , struct fmc_device , dev ) ;
return fmc - > op - > write_ee ( fmc , off , buf , count ) ;
}
2013-06-19 01:47:13 +04:00
static struct bin_attribute fmc_eeprom_attr = {
2014-02-22 12:11:12 +04:00
. attr = { . name = " eeprom " , . mode = S_IRUGO | S_IWUSR , } ,
2013-06-19 01:47:13 +04:00
. size = 8192 , /* more or less standard */
. read = fmc_read_eeprom ,
2014-02-22 12:11:12 +04:00
. write = fmc_write_eeprom ,
2013-06-19 01:47:13 +04:00
} ;
2017-07-18 09:32:53 +03:00
int fmc_irq_request ( struct fmc_device * fmc , irq_handler_t h ,
char * name , int flags )
{
if ( fmc - > op - > irq_request )
return fmc - > op - > irq_request ( fmc , h , name , flags ) ;
return - EPERM ;
}
EXPORT_SYMBOL ( fmc_irq_request ) ;
void fmc_irq_free ( struct fmc_device * fmc )
{
if ( fmc - > op - > irq_free )
fmc - > op - > irq_free ( fmc ) ;
}
EXPORT_SYMBOL ( fmc_irq_free ) ;
void fmc_irq_ack ( struct fmc_device * fmc )
{
if ( likely ( fmc - > op - > irq_ack ) )
fmc - > op - > irq_ack ( fmc ) ;
}
EXPORT_SYMBOL ( fmc_irq_ack ) ;
int fmc_validate ( struct fmc_device * fmc , struct fmc_driver * drv )
{
if ( fmc - > op - > validate )
return fmc - > op - > validate ( fmc , drv ) ;
return - EPERM ;
}
EXPORT_SYMBOL ( fmc_validate ) ;
int fmc_gpio_config ( struct fmc_device * fmc , struct fmc_gpio * gpio , int ngpio )
{
if ( fmc - > op - > gpio_config )
return fmc - > op - > gpio_config ( fmc , gpio , ngpio ) ;
return - EPERM ;
}
EXPORT_SYMBOL ( fmc_gpio_config ) ;
int fmc_read_ee ( struct fmc_device * fmc , int pos , void * d , int l )
{
if ( fmc - > op - > read_ee )
return fmc - > op - > read_ee ( fmc , pos , d , l ) ;
return - EPERM ;
}
EXPORT_SYMBOL ( fmc_read_ee ) ;
int fmc_write_ee ( struct fmc_device * fmc , int pos , const void * d , int l )
{
if ( fmc - > op - > write_ee )
return fmc - > op - > write_ee ( fmc , pos , d , l ) ;
return - EPERM ;
}
EXPORT_SYMBOL ( fmc_write_ee ) ;
2013-06-19 01:47:13 +04:00
/*
* Functions for client modules follow
*/
int fmc_driver_register ( struct fmc_driver * drv )
{
if ( fmc_check_version ( drv - > version , drv - > driver . name ) )
return - EINVAL ;
drv - > driver . bus = & fmc_bus_type ;
return driver_register ( & drv - > driver ) ;
}
EXPORT_SYMBOL ( fmc_driver_register ) ;
void fmc_driver_unregister ( struct fmc_driver * drv )
{
driver_unregister ( & drv - > driver ) ;
}
EXPORT_SYMBOL ( fmc_driver_unregister ) ;
/*
* When a device set is registered , all eeproms must be read
* and all FRUs must be parsed
*/
int fmc_device_register_n ( struct fmc_device * * devs , int n )
{
struct fmc_device * fmc , * * devarray ;
uint32_t device_id ;
int i , ret = 0 ;
if ( n < 1 )
return 0 ;
/* Check the version of the first data structure (function prints) */
if ( fmc_check_version ( devs [ 0 ] - > version , devs [ 0 ] - > carrier_name ) )
return - EINVAL ;
devarray = kmemdup ( devs , n * sizeof ( * devs ) , GFP_KERNEL ) ;
if ( ! devarray )
return - ENOMEM ;
/* Make all other checks before continuing, for all devices */
for ( i = 0 ; i < n ; i + + ) {
fmc = devarray [ i ] ;
if ( ! fmc - > hwdev ) {
pr_err ( " %s: device nr. %i has no hwdev pointer \n " ,
__func__ , i ) ;
ret = - EINVAL ;
break ;
}
2014-01-30 16:05:09 +04:00
if ( fmc - > flags & FMC_DEVICE_NO_MEZZANINE ) {
2013-06-19 01:47:13 +04:00
dev_info ( fmc - > hwdev , " absent mezzanine in slot %d \n " ,
fmc - > slot_id ) ;
continue ;
}
if ( ! fmc - > eeprom ) {
dev_err ( fmc - > hwdev , " no eeprom provided for slot %i \n " ,
fmc - > slot_id ) ;
ret = - EINVAL ;
}
if ( ! fmc - > eeprom_addr ) {
dev_err ( fmc - > hwdev , " no eeprom_addr for slot %i \n " ,
fmc - > slot_id ) ;
ret = - EINVAL ;
}
if ( ! fmc - > carrier_name | | ! fmc - > carrier_data | |
! fmc - > device_id ) {
dev_err ( fmc - > hwdev ,
" deivce nr %i: carrier name, "
" data or dev_id not set \n " , i ) ;
ret = - EINVAL ;
}
if ( ret )
break ;
}
if ( ret ) {
kfree ( devarray ) ;
return ret ;
}
/* Validation is ok. Now init and register the devices */
for ( i = 0 ; i < n ; i + + ) {
fmc = devarray [ i ] ;
fmc - > nr_slots = n ; /* each slot must know how many are there */
fmc - > devarray = devarray ;
device_initialize ( & fmc - > dev ) ;
fmc - > dev . release = fmc_release ;
fmc - > dev . parent = fmc - > hwdev ;
/* Fill the identification stuff (may fail) */
fmc_fill_id_info ( fmc ) ;
fmc - > dev . bus = & fmc_bus_type ;
/* Name from mezzanine info or carrier info. Or 0,1,2.. */
device_id = fmc - > device_id ;
if ( ! fmc - > mezzanine_name )
dev_set_name ( & fmc - > dev , " fmc-%04x " , device_id ) ;
else
dev_set_name ( & fmc - > dev , " %s-%04x " , fmc - > mezzanine_name ,
device_id ) ;
ret = device_add ( & fmc - > dev ) ;
if ( ret < 0 ) {
dev_err ( fmc - > hwdev , " Slot %i: Failed in registering "
" \" %s \" \n " , fmc - > slot_id , fmc - > dev . kobj . name ) ;
goto out ;
}
ret = sysfs_create_bin_file ( & fmc - > dev . kobj , & fmc_eeprom_attr ) ;
if ( ret < 0 ) {
dev_err ( & fmc - > dev , " Failed in registering eeprom \n " ) ;
goto out1 ;
}
/* This device went well, give information to the user */
fmc_dump_eeprom ( fmc ) ;
2017-07-18 09:33:03 +03:00
fmc_debug_init ( fmc ) ;
2013-06-19 01:47:13 +04:00
}
return 0 ;
out1 :
device_del ( & fmc - > dev ) ;
out :
fmc_free_id_info ( fmc ) ;
put_device ( & fmc - > dev ) ;
kfree ( devarray ) ;
for ( i - - ; i > = 0 ; i - - ) {
2017-07-18 09:33:03 +03:00
fmc_debug_exit ( devs [ i ] ) ;
2013-06-19 01:47:13 +04:00
sysfs_remove_bin_file ( & devs [ i ] - > dev . kobj , & fmc_eeprom_attr ) ;
device_del ( & devs [ i ] - > dev ) ;
fmc_free_id_info ( devs [ i ] ) ;
put_device ( & devs [ i ] - > dev ) ;
}
return ret ;
}
EXPORT_SYMBOL ( fmc_device_register_n ) ;
int fmc_device_register ( struct fmc_device * fmc )
{
return fmc_device_register_n ( & fmc , 1 ) ;
}
EXPORT_SYMBOL ( fmc_device_register ) ;
void fmc_device_unregister_n ( struct fmc_device * * devs , int n )
{
int i ;
if ( n < 1 )
return ;
/* Free devarray first, not used by the later loop */
kfree ( devs [ 0 ] - > devarray ) ;
for ( i = 0 ; i < n ; i + + ) {
2017-07-18 09:33:03 +03:00
fmc_debug_exit ( devs [ i ] ) ;
2013-06-19 01:47:13 +04:00
sysfs_remove_bin_file ( & devs [ i ] - > dev . kobj , & fmc_eeprom_attr ) ;
device_del ( & devs [ i ] - > dev ) ;
fmc_free_id_info ( devs [ i ] ) ;
put_device ( & devs [ i ] - > dev ) ;
}
}
EXPORT_SYMBOL ( fmc_device_unregister_n ) ;
void fmc_device_unregister ( struct fmc_device * fmc )
{
fmc_device_unregister_n ( & fmc , 1 ) ;
}
EXPORT_SYMBOL ( fmc_device_unregister ) ;
/* Init and exit are trivial */
2013-06-12 11:13:25 +04:00
static int fmc_init ( void )
{
return bus_register ( & fmc_bus_type ) ;
}
static void fmc_exit ( void )
{
bus_unregister ( & fmc_bus_type ) ;
}
module_init ( fmc_init ) ;
module_exit ( fmc_exit ) ;
MODULE_LICENSE ( " GPL " ) ;