2006-01-19 00:41:50 +03:00
/*
2005-04-17 02:20:36 +04:00
Copyright ( c ) 2001 , 2002 Christer Weinigel < wingel @ nano - system . com >
National Semiconductor SCx200 ACCESS . bus support
2006-01-19 00:48:26 +03:00
Also supports the AMD CS5535 and AMD CS5536
2006-01-19 00:41:50 +03:00
2005-04-17 02:20:36 +04:00
Based on i2c - keywest . c which is :
Copyright ( c ) 2001 Benjamin Herrenschmidt < benh @ kernel . crashing . org >
Copyright ( c ) 2000 Philip Edelbrock < phil @ stimpy . netroedge . com >
2006-01-19 00:41:50 +03:00
2005-04-17 02:20:36 +04:00
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 .
2006-01-19 00:41:50 +03:00
2005-04-17 02:20:36 +04:00
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
General Public License for more details .
2006-01-19 00:41:50 +03:00
2005-04-17 02:20:36 +04:00
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/module.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/i2c.h>
# include <linux/pci.h>
# include <linux/delay.h>
2006-01-19 01:17:01 +03:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
# include <asm/io.h>
# include <linux/scx200.h>
# define NAME "scx200_acb"
MODULE_AUTHOR ( " Christer Weinigel <wingel@nano-system.com> " ) ;
MODULE_DESCRIPTION ( " NatSemi SCx200 ACCESS.bus Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
# define MAX_DEVICES 4
static int base [ MAX_DEVICES ] = { 0x820 , 0x840 } ;
module_param_array ( base , int , NULL , 0 ) ;
MODULE_PARM_DESC ( base , " Base addresses for the ACCESS.bus controllers " ) ;
2006-01-19 00:52:06 +03:00
# define POLL_TIMEOUT (HZ / 5)
2005-04-17 02:20:36 +04:00
enum scx200_acb_state {
state_idle ,
state_address ,
state_command ,
state_repeat_start ,
state_quick ,
state_read ,
state_write ,
} ;
static const char * scx200_acb_state_name [ ] = {
" idle " ,
" address " ,
" command " ,
" repeat_start " ,
" quick " ,
" read " ,
" write " ,
} ;
/* Physical interface */
2006-01-19 00:41:50 +03:00
struct scx200_acb_iface {
2005-04-17 02:20:36 +04:00
struct scx200_acb_iface * next ;
struct i2c_adapter adapter ;
unsigned base ;
2006-01-19 01:17:01 +03:00
struct mutex mutex ;
2005-04-17 02:20:36 +04:00
/* State machine data */
enum scx200_acb_state state ;
int result ;
u8 address_byte ;
u8 command ;
u8 * ptr ;
char needs_reset ;
unsigned len ;
2006-06-12 23:44:28 +04:00
/* PCI device info */
struct pci_dev * pdev ;
int bar ;
2005-04-17 02:20:36 +04:00
} ;
/* Register Definitions */
# define ACBSDA (iface->base + 0)
# define ACBST (iface->base + 1)
# define ACBST_SDAST 0x40 /* SDA Status */
2006-01-19 00:41:50 +03:00
# define ACBST_BER 0x20
2005-04-17 02:20:36 +04:00
# define ACBST_NEGACK 0x10 /* Negative Acknowledge */
# define ACBST_STASTR 0x08 /* Stall After Start */
# define ACBST_MASTER 0x02
# define ACBCST (iface->base + 2)
# define ACBCST_BB 0x02
# define ACBCTL1 (iface->base + 3)
# define ACBCTL1_STASTRE 0x80
# define ACBCTL1_NMINTE 0x40
2006-01-19 00:41:50 +03:00
# define ACBCTL1_ACK 0x10
# define ACBCTL1_STOP 0x02
# define ACBCTL1_START 0x01
2005-04-17 02:20:36 +04:00
# define ACBADDR (iface->base + 4)
# define ACBCTL2 (iface->base + 5)
# define ACBCTL2_ENABLE 0x01
/************************************************************************/
static void scx200_acb_machine ( struct scx200_acb_iface * iface , u8 status )
{
const char * errmsg ;
2006-01-19 00:43:10 +03:00
dev_dbg ( & iface - > adapter . dev , " state %s, status = 0x%02x \n " ,
scx200_acb_state_name [ iface - > state ] , status ) ;
2005-04-17 02:20:36 +04:00
if ( status & ACBST_BER ) {
errmsg = " bus error " ;
goto error ;
}
if ( ! ( status & ACBST_MASTER ) ) {
errmsg = " not master " ;
goto error ;
}
2006-01-19 00:44:04 +03:00
if ( status & ACBST_NEGACK ) {
dev_dbg ( & iface - > adapter . dev , " negative ack in state %s \n " ,
scx200_acb_state_name [ iface - > state ] ) ;
iface - > state = state_idle ;
iface - > result = - ENXIO ;
outb ( inb ( ACBCTL1 ) | ACBCTL1_STOP , ACBCTL1 ) ;
outb ( ACBST_STASTR | ACBST_NEGACK , ACBST ) ;
2006-04-29 00:53:30 +04:00
/* Reset the status register */
outb ( 0 , ACBST ) ;
2006-01-19 00:44:04 +03:00
return ;
}
2005-04-17 02:20:36 +04:00
switch ( iface - > state ) {
case state_idle :
dev_warn ( & iface - > adapter . dev , " interrupt in idle state \n " ) ;
break ;
case state_address :
/* Do a pointer write first */
outb ( iface - > address_byte & ~ 1 , ACBSDA ) ;
iface - > state = state_command ;
break ;
case state_command :
outb ( iface - > command , ACBSDA ) ;
if ( iface - > address_byte & 1 )
iface - > state = state_repeat_start ;
else
iface - > state = state_write ;
break ;
case state_repeat_start :
outb ( inb ( ACBCTL1 ) | ACBCTL1_START , ACBCTL1 ) ;
/* fallthrough */
2006-01-19 00:41:50 +03:00
2005-04-17 02:20:36 +04:00
case state_quick :
if ( iface - > address_byte & 1 ) {
2006-01-19 00:41:50 +03:00
if ( iface - > len = = 1 )
2005-04-17 02:20:36 +04:00
outb ( inb ( ACBCTL1 ) | ACBCTL1_ACK , ACBCTL1 ) ;
else
outb ( inb ( ACBCTL1 ) & ~ ACBCTL1_ACK , ACBCTL1 ) ;
outb ( iface - > address_byte , ACBSDA ) ;
iface - > state = state_read ;
} else {
outb ( iface - > address_byte , ACBSDA ) ;
iface - > state = state_write ;
}
break ;
case state_read :
2006-07-01 19:05:12 +04:00
/* Set ACK if _next_ byte will be the last one */
if ( iface - > len = = 2 )
2005-04-17 02:20:36 +04:00
outb ( inb ( ACBCTL1 ) | ACBCTL1_ACK , ACBCTL1 ) ;
else
outb ( inb ( ACBCTL1 ) & ~ ACBCTL1_ACK , ACBCTL1 ) ;
2006-07-01 19:05:12 +04:00
if ( iface - > len = = 1 ) {
2005-04-17 02:20:36 +04:00
iface - > result = 0 ;
iface - > state = state_idle ;
outb ( inb ( ACBCTL1 ) | ACBCTL1_STOP , ACBCTL1 ) ;
}
2006-07-01 19:05:12 +04:00
* iface - > ptr + + = inb ( ACBSDA ) ;
- - iface - > len ;
2005-04-17 02:20:36 +04:00
break ;
case state_write :
if ( iface - > len = = 0 ) {
iface - > result = 0 ;
iface - > state = state_idle ;
outb ( inb ( ACBCTL1 ) | ACBCTL1_STOP , ACBCTL1 ) ;
break ;
}
2006-01-19 00:41:50 +03:00
2005-04-17 02:20:36 +04:00
outb ( * iface - > ptr + + , ACBSDA ) ;
- - iface - > len ;
2006-01-19 00:41:50 +03:00
2005-04-17 02:20:36 +04:00
break ;
}
return ;
error :
dev_err ( & iface - > adapter . dev , " %s in state %s \n " , errmsg ,
scx200_acb_state_name [ iface - > state ] ) ;
iface - > state = state_idle ;
iface - > result = - EIO ;
iface - > needs_reset = 1 ;
}
static void scx200_acb_poll ( struct scx200_acb_iface * iface )
{
2006-01-19 00:44:04 +03:00
u8 status ;
2005-04-17 02:20:36 +04:00
unsigned long timeout ;
timeout = jiffies + POLL_TIMEOUT ;
2006-08-05 23:15:19 +04:00
while ( 1 ) {
2005-04-17 02:20:36 +04:00
status = inb ( ACBST ) ;
2006-04-29 00:53:30 +04:00
/* Reset the status register to avoid the hang */
outb ( 0 , ACBST ) ;
2005-04-17 02:20:36 +04:00
if ( ( status & ( ACBST_SDAST | ACBST_BER | ACBST_NEGACK ) ) ! = 0 ) {
scx200_acb_machine ( iface , status ) ;
return ;
}
2006-08-05 23:15:19 +04:00
if ( time_after ( jiffies , timeout ) )
break ;
cpu_relax ( ) ;
cond_resched ( ) ;
2005-04-17 02:20:36 +04:00
}
2006-01-19 00:44:04 +03:00
dev_err ( & iface - > adapter . dev , " timeout in state %s \n " ,
scx200_acb_state_name [ iface - > state ] ) ;
iface - > state = state_idle ;
iface - > result = - EIO ;
iface - > needs_reset = 1 ;
2005-04-17 02:20:36 +04:00
}
static void scx200_acb_reset ( struct scx200_acb_iface * iface )
{
/* Disable the ACCESS.bus device and Configure the SCL
2006-01-19 00:41:50 +03:00
frequency : 16 clock cycles */
2005-04-17 02:20:36 +04:00
outb ( 0x70 , ACBCTL2 ) ;
/* Polling mode */
outb ( 0 , ACBCTL1 ) ;
/* Disable slave address */
outb ( 0 , ACBADDR ) ;
/* Enable the ACCESS.bus device */
outb ( inb ( ACBCTL2 ) | ACBCTL2_ENABLE , ACBCTL2 ) ;
/* Free STALL after START */
outb ( inb ( ACBCTL1 ) & ~ ( ACBCTL1_STASTRE | ACBCTL1_NMINTE ) , ACBCTL1 ) ;
/* Send a STOP */
outb ( inb ( ACBCTL1 ) | ACBCTL1_STOP , ACBCTL1 ) ;
/* Clear BER, NEGACK and STASTR bits */
outb ( ACBST_BER | ACBST_NEGACK | ACBST_STASTR , ACBST ) ;
/* Clear BB bit */
outb ( inb ( ACBCST ) | ACBCST_BB , ACBCST ) ;
}
static s32 scx200_acb_smbus_xfer ( struct i2c_adapter * adapter ,
2006-01-19 00:41:50 +03:00
u16 address , unsigned short flags ,
char rw , u8 command , int size ,
union i2c_smbus_data * data )
2005-04-17 02:20:36 +04:00
{
struct scx200_acb_iface * iface = i2c_get_adapdata ( adapter ) ;
int len ;
u8 * buffer ;
u16 cur_word ;
int rc ;
switch ( size ) {
case I2C_SMBUS_QUICK :
2006-01-19 00:41:50 +03:00
len = 0 ;
buffer = NULL ;
break ;
2005-04-17 02:20:36 +04:00
case I2C_SMBUS_BYTE :
2006-01-19 00:44:04 +03:00
len = 1 ;
buffer = rw ? & data - > byte : & command ;
2006-01-19 00:41:50 +03:00
break ;
2005-04-17 02:20:36 +04:00
case I2C_SMBUS_BYTE_DATA :
2006-01-19 00:41:50 +03:00
len = 1 ;
buffer = & data - > byte ;
break ;
2005-04-17 02:20:36 +04:00
case I2C_SMBUS_WORD_DATA :
len = 2 ;
2006-01-19 00:41:50 +03:00
cur_word = cpu_to_le16 ( data - > word ) ;
buffer = ( u8 * ) & cur_word ;
2005-04-17 02:20:36 +04:00
break ;
2006-01-19 00:41:50 +03:00
2006-07-01 19:06:43 +04:00
case I2C_SMBUS_I2C_BLOCK_DATA :
2006-01-19 00:41:50 +03:00
len = data - > block [ 0 ] ;
2006-07-01 19:06:43 +04:00
if ( len = = 0 | | len > I2C_SMBUS_BLOCK_MAX )
return - EINVAL ;
2006-01-19 00:41:50 +03:00
buffer = & data - > block [ 1 ] ;
2005-04-17 02:20:36 +04:00
break ;
2006-01-19 00:41:50 +03:00
2005-04-17 02:20:36 +04:00
default :
2006-01-19 00:41:50 +03:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2006-01-19 00:43:10 +03:00
dev_dbg ( & adapter - > dev ,
" size=%d, address=0x%x, command=0x%x, len=%d, read=%d \n " ,
size , address , command , len , rw ) ;
2005-04-17 02:20:36 +04:00
if ( ! len & & rw = = I2C_SMBUS_READ ) {
2006-01-19 00:43:10 +03:00
dev_dbg ( & adapter - > dev , " zero length read \n " ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
2006-01-19 01:17:01 +03:00
mutex_lock ( & iface - > mutex ) ;
2005-04-17 02:20:36 +04:00
2006-01-19 00:44:04 +03:00
iface - > address_byte = ( address < < 1 ) | rw ;
2005-04-17 02:20:36 +04:00
iface - > command = command ;
iface - > ptr = buffer ;
iface - > len = len ;
iface - > result = - EINVAL ;
iface - > needs_reset = 0 ;
outb ( inb ( ACBCTL1 ) | ACBCTL1_START , ACBCTL1 ) ;
if ( size = = I2C_SMBUS_QUICK | | size = = I2C_SMBUS_BYTE )
iface - > state = state_quick ;
else
iface - > state = state_address ;
while ( iface - > state ! = state_idle )
scx200_acb_poll ( iface ) ;
if ( iface - > needs_reset )
scx200_acb_reset ( iface ) ;
rc = iface - > result ;
2006-01-19 01:17:01 +03:00
mutex_unlock ( & iface - > mutex ) ;
2005-04-17 02:20:36 +04:00
if ( rc = = 0 & & size = = I2C_SMBUS_WORD_DATA & & rw = = I2C_SMBUS_READ )
2006-01-19 00:41:50 +03:00
data - > word = le16_to_cpu ( cur_word ) ;
2005-04-17 02:20:36 +04:00
# ifdef DEBUG
2006-01-19 00:43:10 +03:00
dev_dbg ( & adapter - > dev , " transfer done, result: %d " , rc ) ;
2005-04-17 02:20:36 +04:00
if ( buffer ) {
int i ;
printk ( " data: " ) ;
for ( i = 0 ; i < len ; + + i )
printk ( " %02x " , buffer [ i ] ) ;
}
printk ( " \n " ) ;
# endif
return rc ;
}
static u32 scx200_acb_func ( struct i2c_adapter * adapter )
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
2006-07-01 19:06:43 +04:00
I2C_FUNC_SMBUS_I2C_BLOCK ;
2005-04-17 02:20:36 +04:00
}
/* For now, we only handle combined mode (smbus) */
2006-09-04 00:39:46 +04:00
static const struct i2c_algorithm scx200_acb_algorithm = {
2005-04-17 02:20:36 +04:00
. smbus_xfer = scx200_acb_smbus_xfer ,
. functionality = scx200_acb_func ,
} ;
static struct scx200_acb_iface * scx200_acb_list ;
2007-07-12 16:12:28 +04:00
static DEFINE_MUTEX ( scx200_acb_list_mutex ) ;
2005-04-17 02:20:36 +04:00
2006-06-12 23:46:04 +04:00
static __init int scx200_acb_probe ( struct scx200_acb_iface * iface )
2005-04-17 02:20:36 +04:00
{
u8 val ;
/* Disable the ACCESS.bus device and Configure the SCL
2006-01-19 00:41:50 +03:00
frequency : 16 clock cycles */
2005-04-17 02:20:36 +04:00
outb ( 0x70 , ACBCTL2 ) ;
if ( inb ( ACBCTL2 ) ! = 0x70 ) {
2006-01-19 00:43:10 +03:00
pr_debug ( NAME " : ACBCTL2 readback failed \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENXIO ;
}
outb ( inb ( ACBCTL1 ) | ACBCTL1_NMINTE , ACBCTL1 ) ;
val = inb ( ACBCTL1 ) ;
if ( val ) {
2006-01-19 00:43:10 +03:00
pr_debug ( NAME " : disabled, but ACBCTL1=0x%02x \n " ,
val ) ;
2005-04-17 02:20:36 +04:00
return - ENXIO ;
}
outb ( inb ( ACBCTL2 ) | ACBCTL2_ENABLE , ACBCTL2 ) ;
outb ( inb ( ACBCTL1 ) | ACBCTL1_NMINTE , ACBCTL1 ) ;
val = inb ( ACBCTL1 ) ;
if ( ( val & ACBCTL1_NMINTE ) ! = ACBCTL1_NMINTE ) {
2006-01-19 00:43:10 +03:00
pr_debug ( NAME " : enabled, but NMINTE won't be set, "
" ACBCTL1=0x%02x \n " , val ) ;
2005-04-17 02:20:36 +04:00
return - ENXIO ;
}
return 0 ;
}
2006-06-12 23:44:28 +04:00
static __init struct scx200_acb_iface * scx200_create_iface ( const char * text ,
2007-02-14 00:09:03 +03:00
struct device * dev , int index )
2005-04-17 02:20:36 +04:00
{
struct scx200_acb_iface * iface ;
struct i2c_adapter * adapter ;
2005-10-18 01:09:43 +04:00
iface = kzalloc ( sizeof ( * iface ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! iface ) {
printk ( KERN_ERR NAME " : can't allocate memory \n " ) ;
2006-06-12 23:44:28 +04:00
return NULL ;
2005-04-17 02:20:36 +04:00
}
adapter = & iface - > adapter ;
i2c_set_adapdata ( adapter , iface ) ;
2007-05-02 01:26:28 +04:00
snprintf ( adapter - > name , sizeof ( adapter - > name ) , " %s ACB%d " , text , index ) ;
2005-04-17 02:20:36 +04:00
adapter - > owner = THIS_MODULE ;
adapter - > algo = & scx200_acb_algorithm ;
2008-07-15 00:38:29 +04:00
adapter - > class = I2C_CLASS_HWMON | I2C_CLASS_SPD ;
2007-02-14 00:09:03 +03:00
adapter - > dev . parent = dev ;
2005-04-17 02:20:36 +04:00
2006-01-19 01:17:01 +03:00
mutex_init ( & iface - > mutex ) ;
2005-04-17 02:20:36 +04:00
2006-06-12 23:44:28 +04:00
return iface ;
}
static int __init scx200_acb_create ( struct scx200_acb_iface * iface )
{
struct i2c_adapter * adapter ;
int rc ;
adapter = & iface - > adapter ;
2005-04-17 02:20:36 +04:00
rc = scx200_acb_probe ( iface ) ;
if ( rc ) {
2006-01-19 00:43:10 +03:00
printk ( KERN_WARNING NAME " : probe failed \n " ) ;
2006-06-12 23:44:28 +04:00
return rc ;
2005-04-17 02:20:36 +04:00
}
scx200_acb_reset ( iface ) ;
if ( i2c_add_adapter ( adapter ) < 0 ) {
2006-01-19 00:43:10 +03:00
printk ( KERN_ERR NAME " : failed to register \n " ) ;
2006-06-12 23:44:28 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
2007-07-12 16:12:28 +04:00
mutex_lock ( & scx200_acb_list_mutex ) ;
2005-04-17 02:20:36 +04:00
iface - > next = scx200_acb_list ;
scx200_acb_list = iface ;
2007-07-12 16:12:28 +04:00
mutex_unlock ( & scx200_acb_list_mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
2006-06-12 23:44:28 +04:00
}
2005-04-17 02:20:36 +04:00
2006-06-12 23:44:28 +04:00
static __init int scx200_create_pci ( const char * text , struct pci_dev * pdev ,
int bar )
{
struct scx200_acb_iface * iface ;
int rc ;
2007-02-14 00:09:03 +03:00
iface = scx200_create_iface ( text , & pdev - > dev , 0 ) ;
2006-06-12 23:44:28 +04:00
if ( iface = = NULL )
return - ENOMEM ;
iface - > pdev = pdev ;
iface - > bar = bar ;
2007-12-20 07:28:09 +03:00
rc = pci_enable_device_io ( iface - > pdev ) ;
2006-11-19 09:19:39 +03:00
if ( rc )
goto errout_free ;
2006-06-12 23:44:28 +04:00
rc = pci_request_region ( iface - > pdev , iface - > bar , iface - > adapter . name ) ;
2006-11-19 09:19:39 +03:00
if ( rc ) {
2006-06-12 23:44:28 +04:00
printk ( KERN_ERR NAME " : can't allocate PCI BAR %d \n " ,
iface - > bar ) ;
goto errout_free ;
}
iface - > base = pci_resource_start ( iface - > pdev , iface - > bar ) ;
rc = scx200_acb_create ( iface ) ;
if ( rc = = 0 )
return 0 ;
pci_release_region ( iface - > pdev , iface - > bar ) ;
pci_dev_put ( iface - > pdev ) ;
2006-01-19 00:44:04 +03:00
errout_free :
kfree ( iface ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
2006-06-12 23:44:28 +04:00
static int __init scx200_create_isa ( const char * text , unsigned long base ,
int index )
{
struct scx200_acb_iface * iface ;
int rc ;
2007-02-14 00:09:03 +03:00
iface = scx200_create_iface ( text , NULL , index ) ;
2006-06-12 23:44:28 +04:00
if ( iface = = NULL )
return - ENOMEM ;
2008-04-23 00:16:48 +04:00
if ( ! request_region ( base , 8 , iface - > adapter . name ) ) {
2006-06-12 23:44:28 +04:00
printk ( KERN_ERR NAME " : can't allocate io 0x%lx-0x%lx \n " ,
base , base + 8 - 1 ) ;
rc = - EBUSY ;
goto errout_free ;
}
iface - > base = base ;
rc = scx200_acb_create ( iface ) ;
if ( rc = = 0 )
return 0 ;
2005-04-17 02:20:36 +04:00
2006-06-12 23:44:28 +04:00
release_region ( base , 8 ) ;
errout_free :
kfree ( iface ) ;
return rc ;
}
/* Driver data is an index into the scx200_data array that indicates
* the name and the BAR where the I / O address resource is located . ISA
* devices are flagged with a bar value of - 1 */
static struct pci_device_id scx200_pci [ ] = {
{ PCI_DEVICE ( PCI_VENDOR_ID_NS , PCI_DEVICE_ID_NS_SCx200_BRIDGE ) ,
. driver_data = 0 } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_NS , PCI_DEVICE_ID_NS_SC1100_BRIDGE ) ,
. driver_data = 0 } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_NS , PCI_DEVICE_ID_NS_CS5535_ISA ) ,
. driver_data = 1 } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_AMD , PCI_DEVICE_ID_AMD_CS5536_ISA ) ,
. driver_data = 2 }
2006-01-19 00:48:26 +03:00
} ;
2006-06-12 23:44:28 +04:00
static struct {
const char * name ;
int bar ;
} scx200_data [ ] = {
{ " SCx200 " , - 1 } ,
{ " CS5535 " , 0 } ,
{ " CS5536 " , 0 }
} ;
2006-01-19 00:48:26 +03:00
2006-06-12 23:44:28 +04:00
static __init int scx200_scan_pci ( void )
2006-01-19 00:48:26 +03:00
{
2006-06-12 23:44:28 +04:00
int data , dev ;
int rc = - ENODEV ;
struct pci_dev * pdev ;
for ( dev = 0 ; dev < ARRAY_SIZE ( scx200_pci ) ; dev + + ) {
pdev = pci_get_device ( scx200_pci [ dev ] . vendor ,
scx200_pci [ dev ] . device , NULL ) ;
if ( pdev = = NULL )
continue ;
data = scx200_pci [ dev ] . driver_data ;
/* if .bar is greater or equal to zero, this is a
* PCI device - otherwise , we assume
that the ports are ISA based
*/
if ( scx200_data [ data ] . bar > = 0 )
rc = scx200_create_pci ( scx200_data [ data ] . name , pdev ,
scx200_data [ data ] . bar ) ;
else {
int i ;
2007-05-02 01:26:30 +04:00
pci_dev_put ( pdev ) ;
2006-06-12 23:44:28 +04:00
for ( i = 0 ; i < MAX_DEVICES ; + + i ) {
if ( base [ i ] = = 0 )
continue ;
rc = scx200_create_isa ( scx200_data [ data ] . name ,
base [ i ] ,
i ) ;
}
}
2006-01-19 00:48:26 +03:00
2006-06-12 23:44:28 +04:00
break ;
2006-01-19 00:48:26 +03:00
}
2006-06-12 23:44:28 +04:00
return rc ;
2006-01-19 00:48:26 +03:00
}
2005-04-17 02:20:36 +04:00
static int __init scx200_acb_init ( void )
{
2006-06-12 23:44:28 +04:00
int rc ;
2005-04-17 02:20:36 +04:00
pr_debug ( NAME " : NatSemi SCx200 ACCESS.bus Driver \n " ) ;
2006-06-12 23:44:28 +04:00
rc = scx200_scan_pci ( ) ;
2005-04-17 02:20:36 +04:00
2006-04-27 00:50:32 +04:00
/* If at least one bus was created, init must succeed */
if ( scx200_acb_list )
return 0 ;
2005-04-17 02:20:36 +04:00
return rc ;
}
static void __exit scx200_acb_cleanup ( void )
{
struct scx200_acb_iface * iface ;
2006-01-19 00:41:50 +03:00
2007-07-12 16:12:28 +04:00
mutex_lock ( & scx200_acb_list_mutex ) ;
2005-04-17 02:20:36 +04:00
while ( ( iface = scx200_acb_list ) ! = NULL ) {
scx200_acb_list = iface - > next ;
2007-07-12 16:12:28 +04:00
mutex_unlock ( & scx200_acb_list_mutex ) ;
2005-04-17 02:20:36 +04:00
i2c_del_adapter ( & iface - > adapter ) ;
2006-06-12 23:44:28 +04:00
if ( iface - > pdev ) {
pci_release_region ( iface - > pdev , iface - > bar ) ;
pci_dev_put ( iface - > pdev ) ;
}
else
release_region ( iface - > base , 8 ) ;
2005-04-17 02:20:36 +04:00
kfree ( iface ) ;
2007-07-12 16:12:28 +04:00
mutex_lock ( & scx200_acb_list_mutex ) ;
2005-04-17 02:20:36 +04:00
}
2007-07-12 16:12:28 +04:00
mutex_unlock ( & scx200_acb_list_mutex ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( scx200_acb_init ) ;
module_exit ( scx200_acb_cleanup ) ;