2010-04-07 11:43:00 +04:00
/*
* Janz CMOD - IO MODULbus Carrier Board PCI Driver
*
* Copyright ( c ) 2010 Ira W . Snyder < iws @ ovro . caltech . edu >
*
* Lots of inspiration and code was copied from drivers / mfd / sm501 . c
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/pci.h>
# include <linux/interrupt.h>
# include <linux/delay.h>
# include <linux/platform_device.h>
2010-08-05 09:57:14 +04:00
# include <linux/slab.h>
2010-04-07 11:43:00 +04:00
# include <linux/mfd/core.h>
# include <linux/mfd/janz.h>
# define DRV_NAME "janz-cmodio"
/* Size of each MODULbus module in PCI BAR4 */
# define CMODIO_MODULBUS_SIZE 0x200
/* Maximum number of MODULbus modules on a CMOD-IO carrier board */
# define CMODIO_MAX_MODULES 4
/* Module Parameters */
static unsigned int num_modules = CMODIO_MAX_MODULES ;
2012-01-13 03:02:16 +04:00
static char * modules [ CMODIO_MAX_MODULES ] = {
2010-04-07 11:43:00 +04:00
" empty " , " empty " , " empty " , " empty " ,
} ;
module_param_array ( modules , charp , & num_modules , S_IRUGO ) ;
MODULE_PARM_DESC ( modules , " MODULbus modules attached to the carrier board " ) ;
/* Unique Device Id */
static unsigned int cmodio_id ;
struct cmodio_device {
/* Parent PCI device */
struct pci_dev * pdev ;
/* PLX control registers */
struct janz_cmodio_onboard_regs __iomem * ctrl ;
/* hex switch position */
u8 hex ;
/* mfd-core API */
struct mfd_cell cells [ CMODIO_MAX_MODULES ] ;
struct resource resources [ 3 * CMODIO_MAX_MODULES ] ;
struct janz_platform_data pdata [ CMODIO_MAX_MODULES ] ;
} ;
/*
* Subdevices using the mfd - core API
*/
2012-11-19 22:23:04 +04:00
static int cmodio_setup_subdevice ( struct cmodio_device * priv ,
2010-04-07 11:43:00 +04:00
char * name , unsigned int devno ,
unsigned int modno )
{
struct janz_platform_data * pdata ;
struct mfd_cell * cell ;
struct resource * res ;
struct pci_dev * pci ;
pci = priv - > pdev ;
cell = & priv - > cells [ devno ] ;
res = & priv - > resources [ devno * 3 ] ;
pdata = & priv - > pdata [ devno ] ;
cell - > name = name ;
cell - > resources = res ;
cell - > num_resources = 3 ;
/* Setup the subdevice ID -- must be unique */
cell - > id = cmodio_id + + ;
/* Add platform data */
pdata - > modno = modno ;
2011-04-06 18:02:25 +04:00
cell - > platform_data = pdata ;
cell - > pdata_size = sizeof ( * pdata ) ;
2010-04-07 11:43:00 +04:00
/* MODULbus registers -- PCI BAR3 is big-endian MODULbus access */
res - > flags = IORESOURCE_MEM ;
res - > parent = & pci - > resource [ 3 ] ;
res - > start = pci - > resource [ 3 ] . start + ( CMODIO_MODULBUS_SIZE * modno ) ;
res - > end = res - > start + CMODIO_MODULBUS_SIZE - 1 ;
res + + ;
/* PLX Control Registers -- PCI BAR4 is interrupt and other registers */
res - > flags = IORESOURCE_MEM ;
res - > parent = & pci - > resource [ 4 ] ;
res - > start = pci - > resource [ 4 ] . start ;
res - > end = pci - > resource [ 4 ] . end ;
res + + ;
/*
* IRQ
*
* The start and end fields are used as an offset to the irq_base
* parameter passed into the mfd_add_devices ( ) function call . All
* devices share the same IRQ .
*/
res - > flags = IORESOURCE_IRQ ;
res - > parent = NULL ;
res - > start = 0 ;
res - > end = 0 ;
res + + ;
return 0 ;
}
/* Probe each submodule using kernel parameters */
2012-11-19 22:23:04 +04:00
static int cmodio_probe_submodules ( struct cmodio_device * priv )
2010-04-07 11:43:00 +04:00
{
struct pci_dev * pdev = priv - > pdev ;
unsigned int num_probed = 0 ;
char * name ;
int i ;
for ( i = 0 ; i < num_modules ; i + + ) {
name = modules [ i ] ;
if ( ! strcmp ( name , " " ) | | ! strcmp ( name , " empty " ) )
continue ;
dev_dbg ( & priv - > pdev - > dev , " MODULbus %d: name %s \n " , i , name ) ;
cmodio_setup_subdevice ( priv , name , num_probed , i ) ;
num_probed + + ;
}
/* print an error message if no modules were probed */
if ( num_probed = = 0 ) {
dev_err ( & priv - > pdev - > dev , " no MODULbus modules specified, "
" please set the ``modules'' kernel "
" parameter according to your "
" hardware configuration \n " ) ;
return - ENODEV ;
}
return mfd_add_devices ( & pdev - > dev , 0 , priv - > cells ,
2012-09-11 11:16:36 +04:00
num_probed , NULL , pdev - > irq , NULL ) ;
2010-04-07 11:43:00 +04:00
}
/*
* SYSFS Attributes
*/
static ssize_t mbus_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct cmodio_device * priv = dev_get_drvdata ( dev ) ;
return snprintf ( buf , PAGE_SIZE , " %x \n " , priv - > hex ) ;
}
static DEVICE_ATTR ( modulbus_number , S_IRUGO , mbus_show , NULL ) ;
static struct attribute * cmodio_sysfs_attrs [ ] = {
& dev_attr_modulbus_number . attr ,
NULL ,
} ;
static const struct attribute_group cmodio_sysfs_attr_group = {
. attrs = cmodio_sysfs_attrs ,
} ;
/*
* PCI Driver
*/
2012-11-19 22:23:04 +04:00
static int cmodio_pci_probe ( struct pci_dev * dev ,
2010-04-07 11:43:00 +04:00
const struct pci_device_id * id )
{
struct cmodio_device * priv ;
int ret ;
2013-05-23 19:25:15 +04:00
priv = devm_kzalloc ( & dev - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
2010-04-07 11:43:00 +04:00
if ( ! priv ) {
dev_err ( & dev - > dev , " unable to allocate private data \n " ) ;
2013-05-23 19:25:15 +04:00
return - ENOMEM ;
2010-04-07 11:43:00 +04:00
}
pci_set_drvdata ( dev , priv ) ;
priv - > pdev = dev ;
/* Hardware Initialization */
ret = pci_enable_device ( dev ) ;
if ( ret ) {
dev_err ( & dev - > dev , " unable to enable device \n " ) ;
2013-05-23 19:25:15 +04:00
return ret ;
2010-04-07 11:43:00 +04:00
}
pci_set_master ( dev ) ;
ret = pci_request_regions ( dev , DRV_NAME ) ;
if ( ret ) {
dev_err ( & dev - > dev , " unable to request regions \n " ) ;
goto out_pci_disable_device ;
}
/* Onboard configuration registers */
priv - > ctrl = pci_ioremap_bar ( dev , 4 ) ;
if ( ! priv - > ctrl ) {
dev_err ( & dev - > dev , " unable to remap onboard regs \n " ) ;
ret = - ENOMEM ;
goto out_pci_release_regions ;
}
/* Read the hex switch on the carrier board */
priv - > hex = ioread8 ( & priv - > ctrl - > int_enable ) ;
/* Add the MODULbus number (hex switch value) to the device's sysfs */
ret = sysfs_create_group ( & dev - > dev . kobj , & cmodio_sysfs_attr_group ) ;
if ( ret ) {
dev_err ( & dev - > dev , " unable to create sysfs attributes \n " ) ;
goto out_unmap_ctrl ;
}
/*
* Disable all interrupt lines , each submodule will enable its
* own interrupt line if needed
*/
iowrite8 ( 0xf , & priv - > ctrl - > int_disable ) ;
/* Register drivers for all submodules */
ret = cmodio_probe_submodules ( priv ) ;
if ( ret ) {
dev_err ( & dev - > dev , " unable to probe submodules \n " ) ;
goto out_sysfs_remove_group ;
}
return 0 ;
out_sysfs_remove_group :
sysfs_remove_group ( & dev - > dev . kobj , & cmodio_sysfs_attr_group ) ;
out_unmap_ctrl :
iounmap ( priv - > ctrl ) ;
out_pci_release_regions :
pci_release_regions ( dev ) ;
out_pci_disable_device :
pci_disable_device ( dev ) ;
2013-05-23 19:25:15 +04:00
2010-04-07 11:43:00 +04:00
return ret ;
}
2012-11-19 22:26:01 +04:00
static void cmodio_pci_remove ( struct pci_dev * dev )
2010-04-07 11:43:00 +04:00
{
struct cmodio_device * priv = pci_get_drvdata ( dev ) ;
mfd_remove_devices ( & dev - > dev ) ;
sysfs_remove_group ( & dev - > dev . kobj , & cmodio_sysfs_attr_group ) ;
iounmap ( priv - > ctrl ) ;
pci_release_regions ( dev ) ;
pci_disable_device ( dev ) ;
}
# define PCI_VENDOR_ID_JANZ 0x13c3
/* The list of devices that this module will support */
2013-12-03 03:15:39 +04:00
static const struct pci_device_id cmodio_pci_ids [ ] = {
2010-04-07 11:43:00 +04:00
{ PCI_VENDOR_ID_PLX , PCI_DEVICE_ID_PLX_9030 , PCI_VENDOR_ID_JANZ , 0x0101 } ,
{ PCI_VENDOR_ID_PLX , PCI_DEVICE_ID_PLX_9050 , PCI_VENDOR_ID_JANZ , 0x0100 } ,
{ 0 , }
} ;
MODULE_DEVICE_TABLE ( pci , cmodio_pci_ids ) ;
static struct pci_driver cmodio_pci_driver = {
. name = DRV_NAME ,
. id_table = cmodio_pci_ids ,
. probe = cmodio_pci_probe ,
2012-11-19 22:20:24 +04:00
. remove = cmodio_pci_remove ,
2010-04-07 11:43:00 +04:00
} ;
2012-04-03 05:09:19 +04:00
module_pci_driver ( cmodio_pci_driver ) ;
2010-04-07 11:43:00 +04:00
MODULE_AUTHOR ( " Ira W. Snyder <iws@ovro.caltech.edu> " ) ;
MODULE_DESCRIPTION ( " Janz CMOD-IO PCI MODULbus Carrier Board Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;