2005-04-17 02:20:36 +04:00
/*
* Driver for the Cirrus PD6729 PCI - PCMCIA bridge .
*
* Based on the i82092 . c driver .
*
* This software may be used and distributed according to the terms of
* the GNU General Public License , incorporated herein by reference .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/pci.h>
# include <linux/init.h>
# include <linux/workqueue.h>
# include <linux/interrupt.h>
# include <linux/device.h>
# include <pcmcia/cs_types.h>
# include <pcmcia/ss.h>
# include <pcmcia/cs.h>
# include <asm/system.h>
# include <asm/io.h>
# include "pd6729.h"
# include "i82365.h"
# include "cirrus.h"
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " Driver for the Cirrus PD6729 PCI-PCMCIA bridge " ) ;
MODULE_AUTHOR ( " Jun Komuro <komurojun-mbn@nifty.com> " ) ;
# define MAX_SOCKETS 2
/*
* simple helper functions
* External clock time , in nanoseconds . 120 ns = 8.33 MHz
*/
# define to_cycles(ns) ((ns) / 120)
# ifndef NO_IRQ
# define NO_IRQ ((unsigned int)(0))
# endif
/*
* PARAMETERS
* irq_mode = n
* Specifies the interrupt delivery mode . The default ( 1 ) is to use PCI
* interrupts ; a value of 0 selects ISA interrupts . This must be set for
* correct operation of PCI card readers .
*
* irq_list = i , j , . . .
* This list limits the set of interrupts that can be used by PCMCIA
* cards .
* The default list is 3 , 4 , 5 , 7 , 9 , 10 , 11.
* ( irq_list parameter is not used , if irq_mode = 1 )
*/
static int irq_mode = 1 ; /* 0 = ISA interrupt, 1 = PCI interrupt */
static int irq_list [ 16 ] ;
static int irq_list_count = 0 ;
module_param ( irq_mode , int , 0444 ) ;
module_param_array ( irq_list , int , & irq_list_count , 0444 ) ;
MODULE_PARM_DESC ( irq_mode ,
" interrupt delivery mode. 0 = ISA, 1 = PCI. default is 1 " ) ;
MODULE_PARM_DESC ( irq_list , " interrupts that can be used by PCMCIA cards " ) ;
static DEFINE_SPINLOCK ( port_lock ) ;
/* basic value read/write functions */
static unsigned char indirect_read ( struct pd6729_socket * socket ,
unsigned short reg )
{
unsigned long port ;
unsigned char val ;
unsigned long flags ;
spin_lock_irqsave ( & port_lock , flags ) ;
reg + = socket - > number * 0x40 ;
port = socket - > io_base ;
outb ( reg , port ) ;
val = inb ( port + 1 ) ;
spin_unlock_irqrestore ( & port_lock , flags ) ;
return val ;
}
static unsigned short indirect_read16 ( struct pd6729_socket * socket ,
unsigned short reg )
{
unsigned long port ;
unsigned short tmp ;
unsigned long flags ;
spin_lock_irqsave ( & port_lock , flags ) ;
reg = reg + socket - > number * 0x40 ;
port = socket - > io_base ;
outb ( reg , port ) ;
tmp = inb ( port + 1 ) ;
reg + + ;
outb ( reg , port ) ;
tmp = tmp | ( inb ( port + 1 ) < < 8 ) ;
spin_unlock_irqrestore ( & port_lock , flags ) ;
return tmp ;
}
static void indirect_write ( struct pd6729_socket * socket , unsigned short reg ,
unsigned char value )
{
unsigned long port ;
unsigned long flags ;
spin_lock_irqsave ( & port_lock , flags ) ;
reg = reg + socket - > number * 0x40 ;
port = socket - > io_base ;
outb ( reg , port ) ;
outb ( value , port + 1 ) ;
spin_unlock_irqrestore ( & port_lock , flags ) ;
}
static void indirect_setbit ( struct pd6729_socket * socket , unsigned short reg ,
unsigned char mask )
{
unsigned long port ;
unsigned char val ;
unsigned long flags ;
spin_lock_irqsave ( & port_lock , flags ) ;
reg = reg + socket - > number * 0x40 ;
port = socket - > io_base ;
outb ( reg , port ) ;
val = inb ( port + 1 ) ;
val | = mask ;
outb ( reg , port ) ;
outb ( val , port + 1 ) ;
spin_unlock_irqrestore ( & port_lock , flags ) ;
}
static void indirect_resetbit ( struct pd6729_socket * socket , unsigned short reg ,
unsigned char mask )
{
unsigned long port ;
unsigned char val ;
unsigned long flags ;
spin_lock_irqsave ( & port_lock , flags ) ;
reg = reg + socket - > number * 0x40 ;
port = socket - > io_base ;
outb ( reg , port ) ;
val = inb ( port + 1 ) ;
val & = ~ mask ;
outb ( reg , port ) ;
outb ( val , port + 1 ) ;
spin_unlock_irqrestore ( & port_lock , flags ) ;
}
static void indirect_write16 ( struct pd6729_socket * socket , unsigned short reg ,
unsigned short value )
{
unsigned long port ;
unsigned char val ;
unsigned long flags ;
spin_lock_irqsave ( & port_lock , flags ) ;
reg = reg + socket - > number * 0x40 ;
port = socket - > io_base ;
outb ( reg , port ) ;
val = value & 255 ;
outb ( val , port + 1 ) ;
reg + + ;
outb ( reg , port ) ;
val = value > > 8 ;
outb ( val , port + 1 ) ;
spin_unlock_irqrestore ( & port_lock , flags ) ;
}
/* Interrupt handler functionality */
static irqreturn_t pd6729_interrupt ( int irq , void * dev , struct pt_regs * regs )
{
struct pd6729_socket * socket = ( struct pd6729_socket * ) dev ;
int i ;
int loopcount = 0 ;
int handled = 0 ;
unsigned int events , active = 0 ;
while ( 1 ) {
loopcount + + ;
if ( loopcount > 20 ) {
printk ( KERN_ERR " pd6729: infinite eventloop "
" in interrupt \n " ) ;
break ;
}
active = 0 ;
for ( i = 0 ; i < MAX_SOCKETS ; i + + ) {
unsigned int csc ;
/* card status change register */
csc = indirect_read ( & socket [ i ] , I365_CSC ) ;
if ( csc = = 0 ) /* no events on this socket */
continue ;
handled = 1 ;
events = 0 ;
if ( csc & I365_CSC_DETECT ) {
events | = SS_DETECT ;
dprintk ( " Card detected in socket %i! \n " , i ) ;
}
if ( indirect_read ( & socket [ i ] , I365_INTCTL )
& I365_PC_IOCARD ) {
/* For IO/CARDS, bit 0 means "read the card" */
events | = ( csc & I365_CSC_STSCHG )
? SS_STSCHG : 0 ;
} else {
/* Check for battery/ready events */
events | = ( csc & I365_CSC_BVD1 )
? SS_BATDEAD : 0 ;
events | = ( csc & I365_CSC_BVD2 )
? SS_BATWARN : 0 ;
events | = ( csc & I365_CSC_READY )
? SS_READY : 0 ;
}
if ( events ) {
pcmcia_parse_events ( & socket [ i ] . socket , events ) ;
}
active | = events ;
}
if ( active = = 0 ) /* no more events to handle */
break ;
}
return IRQ_RETVAL ( handled ) ;
}
/* socket functions */
static void pd6729_interrupt_wrapper ( unsigned long data )
{
struct pd6729_socket * socket = ( struct pd6729_socket * ) data ;
pd6729_interrupt ( 0 , ( void * ) socket , NULL ) ;
mod_timer ( & socket - > poll_timer , jiffies + HZ ) ;
}
static int pd6729_get_status ( struct pcmcia_socket * sock , u_int * value )
{
struct pd6729_socket * socket
= container_of ( sock , struct pd6729_socket , socket ) ;
unsigned int status ;
unsigned int data ;
struct pd6729_socket * t ;
/* Interface Status Register */
status = indirect_read ( socket , I365_STATUS ) ;
* value = 0 ;
if ( ( status & I365_CS_DETECT ) = = I365_CS_DETECT ) {
* value | = SS_DETECT ;
}
/*
* IO cards have a different meaning of bits 0 , 1
* Also notice the inverse - logic on the bits
*/
if ( indirect_read ( socket , I365_INTCTL ) & I365_PC_IOCARD ) {
/* IO card */
if ( ! ( status & I365_CS_STSCHG ) )
* value | = SS_STSCHG ;
} else {
/* non I/O card */
if ( ! ( status & I365_CS_BVD1 ) )
* value | = SS_BATDEAD ;
if ( ! ( status & I365_CS_BVD2 ) )
* value | = SS_BATWARN ;
}
if ( status & I365_CS_WRPROT )
* value | = SS_WRPROT ; /* card is write protected */
if ( status & I365_CS_READY )
* value | = SS_READY ; /* card is not busy */
if ( status & I365_CS_POWERON )
* value | = SS_POWERON ; /* power is applied to the card */
t = ( socket - > number ) ? socket : socket + 1 ;
indirect_write ( t , PD67_EXT_INDEX , PD67_EXTERN_DATA ) ;
data = indirect_read16 ( t , PD67_EXT_DATA ) ;
* value | = ( data & PD67_EXD_VS1 ( socket - > number ) ) ? 0 : SS_3VCARD ;
return 0 ;
}
static int pd6729_set_socket ( struct pcmcia_socket * sock , socket_state_t * state )
{
struct pd6729_socket * socket
= container_of ( sock , struct pd6729_socket , socket ) ;
unsigned char reg , data ;
/* First, set the global controller options */
indirect_write ( socket , I365_GBLCTL , 0x00 ) ;
indirect_write ( socket , I365_GENCTL , 0x00 ) ;
/* Values for the IGENC register */
socket - > card_irq = state - > io_irq ;
reg = 0 ;
/* The reset bit has "inverse" logic */
if ( ! ( state - > flags & SS_RESET ) )
reg | = I365_PC_RESET ;
if ( state - > flags & SS_IOCARD )
reg | = I365_PC_IOCARD ;
/* IGENC, Interrupt and General Control Register */
indirect_write ( socket , I365_INTCTL , reg ) ;
/* Power registers */
reg = I365_PWR_NORESET ; /* default: disable resetdrv on resume */
if ( state - > flags & SS_PWR_AUTO ) {
dprintk ( " Auto power \n " ) ;
reg | = I365_PWR_AUTO ; /* automatic power mngmnt */
}
if ( state - > flags & SS_OUTPUT_ENA ) {
dprintk ( " Power Enabled \n " ) ;
reg | = I365_PWR_OUT ; /* enable power */
}
switch ( state - > Vcc ) {
case 0 :
break ;
case 33 :
dprintk ( " setting voltage to Vcc to 3.3V on socket %i \n " ,
socket - > number ) ;
reg | = I365_VCC_5V ;
indirect_setbit ( socket , PD67_MISC_CTL_1 , PD67_MC1_VCC_3V ) ;
break ;
case 50 :
dprintk ( " setting voltage to Vcc to 5V on socket %i \n " ,
socket - > number ) ;
reg | = I365_VCC_5V ;
indirect_resetbit ( socket , PD67_MISC_CTL_1 , PD67_MC1_VCC_3V ) ;
break ;
default :
dprintk ( " pd6729: pd6729_set_socket called with "
" invalid VCC power value: %i \n " ,
state - > Vcc ) ;
return - EINVAL ;
}
switch ( state - > Vpp ) {
case 0 :
dprintk ( " not setting Vpp on socket %i \n " , socket - > number ) ;
break ;
case 33 :
case 50 :
dprintk ( " setting Vpp to Vcc for socket %i \n " , socket - > number ) ;
reg | = I365_VPP1_5V ;
break ;
case 120 :
dprintk ( " setting Vpp to 12.0 \n " ) ;
reg | = I365_VPP1_12V ;
break ;
default :
dprintk ( " pd6729: pd6729_set_socket called with invalid VPP power value: %i \n " ,
state - > Vpp ) ;
return - EINVAL ;
}
/* only write if changed */
if ( reg ! = indirect_read ( socket , I365_POWER ) )
indirect_write ( socket , I365_POWER , reg ) ;
if ( irq_mode = = 1 ) {
/* all interrupts are to be done as PCI interrupts */
data = PD67_EC1_INV_MGMT_IRQ | PD67_EC1_INV_CARD_IRQ ;
} else
data = 0 ;
indirect_write ( socket , PD67_EXT_INDEX , PD67_EXT_CTL_1 ) ;
indirect_write ( socket , PD67_EXT_DATA , data ) ;
/* Enable specific interrupt events */
reg = 0x00 ;
if ( state - > csc_mask & SS_DETECT ) {
reg | = I365_CSC_DETECT ;
}
if ( state - > flags & SS_IOCARD ) {
if ( state - > csc_mask & SS_STSCHG )
reg | = I365_CSC_STSCHG ;
} else {
if ( state - > csc_mask & SS_BATDEAD )
reg | = I365_CSC_BVD1 ;
if ( state - > csc_mask & SS_BATWARN )
reg | = I365_CSC_BVD2 ;
if ( state - > csc_mask & SS_READY )
reg | = I365_CSC_READY ;
}
if ( irq_mode = = 1 )
reg | = 0x30 ; /* management IRQ: PCI INTA# = "irq 3" */
indirect_write ( socket , I365_CSCINT , reg ) ;
reg = indirect_read ( socket , I365_INTCTL ) ;
if ( irq_mode = = 1 )
reg | = 0x03 ; /* card IRQ: PCI INTA# = "irq 3" */
else
reg | = socket - > card_irq ;
indirect_write ( socket , I365_INTCTL , reg ) ;
/* now clear the (probably bogus) pending stuff by doing a dummy read */
( void ) indirect_read ( socket , I365_CSC ) ;
return 0 ;
}
static int pd6729_set_io_map ( struct pcmcia_socket * sock ,
struct pccard_io_map * io )
{
struct pd6729_socket * socket
= container_of ( sock , struct pd6729_socket , socket ) ;
unsigned char map , ioctl ;
map = io - > map ;
/* Check error conditions */
if ( map > 1 ) {
dprintk ( " pd6729_set_io_map with invalid map " ) ;
return - EINVAL ;
}
/* Turn off the window before changing anything */
if ( indirect_read ( socket , I365_ADDRWIN ) & I365_ENA_IO ( map ) )
indirect_resetbit ( socket , I365_ADDRWIN , I365_ENA_IO ( map ) ) ;
/* dprintk("set_io_map: Setting range to %x - %x\n",
io - > start , io - > stop ) ; */
/* write the new values */
indirect_write16 ( socket , I365_IO ( map ) + I365_W_START , io - > start ) ;
indirect_write16 ( socket , I365_IO ( map ) + I365_W_STOP , io - > stop ) ;
ioctl = indirect_read ( socket , I365_IOCTL ) & ~ I365_IOCTL_MASK ( map ) ;
if ( io - > flags & MAP_0WS ) ioctl | = I365_IOCTL_0WS ( map ) ;
if ( io - > flags & MAP_16BIT ) ioctl | = I365_IOCTL_16BIT ( map ) ;
if ( io - > flags & MAP_AUTOSZ ) ioctl | = I365_IOCTL_IOCS16 ( map ) ;
indirect_write ( socket , I365_IOCTL , ioctl ) ;
/* Turn the window back on if needed */
if ( io - > flags & MAP_ACTIVE )
indirect_setbit ( socket , I365_ADDRWIN , I365_ENA_IO ( map ) ) ;
return 0 ;
}
static int pd6729_set_mem_map ( struct pcmcia_socket * sock ,
struct pccard_mem_map * mem )
{
struct pd6729_socket * socket
= container_of ( sock , struct pd6729_socket , socket ) ;
unsigned short base , i ;
unsigned char map ;
map = mem - > map ;
if ( map > 4 ) {
printk ( " pd6729_set_mem_map: invalid map " ) ;
return - EINVAL ;
}
if ( ( mem - > res - > start > mem - > res - > end ) | | ( mem - > speed > 1000 ) ) {
printk ( " pd6729_set_mem_map: invalid address / speed " ) ;
return - EINVAL ;
}
/* Turn off the window before changing anything */
if ( indirect_read ( socket , I365_ADDRWIN ) & I365_ENA_MEM ( map ) )
indirect_resetbit ( socket , I365_ADDRWIN , I365_ENA_MEM ( map ) ) ;
/* write the start address */
base = I365_MEM ( map ) ;
i = ( mem - > res - > start > > 12 ) & 0x0fff ;
if ( mem - > flags & MAP_16BIT )
i | = I365_MEM_16BIT ;
if ( mem - > flags & MAP_0WS )
i | = I365_MEM_0WS ;
indirect_write16 ( socket , base + I365_W_START , i ) ;
/* write the stop address */
i = ( mem - > res - > end > > 12 ) & 0x0fff ;
switch ( to_cycles ( mem - > speed ) ) {
case 0 :
break ;
case 1 :
i | = I365_MEM_WS0 ;
break ;
case 2 :
i | = I365_MEM_WS1 ;
break ;
default :
i | = I365_MEM_WS1 | I365_MEM_WS0 ;
break ;
}
indirect_write16 ( socket , base + I365_W_STOP , i ) ;
/* Take care of high byte */
indirect_write ( socket , PD67_EXT_INDEX , PD67_MEM_PAGE ( map ) ) ;
indirect_write ( socket , PD67_EXT_DATA , mem - > res - > start > > 24 ) ;
/* card start */
i = ( ( mem - > card_start - mem - > res - > start ) > > 12 ) & 0x3fff ;
if ( mem - > flags & MAP_WRPROT )
i | = I365_MEM_WRPROT ;
if ( mem - > flags & MAP_ATTRIB ) {
/* dprintk("requesting attribute memory for socket %i\n",
socket - > number ) ; */
i | = I365_MEM_REG ;
} else {
/* dprintk("requesting normal memory for socket %i\n",
socket - > number ) ; */
}
indirect_write16 ( socket , base + I365_W_OFF , i ) ;
/* Enable the window if necessary */
if ( mem - > flags & MAP_ACTIVE )
indirect_setbit ( socket , I365_ADDRWIN , I365_ENA_MEM ( map ) ) ;
return 0 ;
}
static int pd6729_init ( struct pcmcia_socket * sock )
{
int i ;
struct resource res = { . end = 0x0fff } ;
pccard_io_map io = { 0 , 0 , 0 , 0 , 1 } ;
pccard_mem_map mem = { . res = & res , } ;
pd6729_set_socket ( sock , & dead_socket ) ;
for ( i = 0 ; i < 2 ; i + + ) {
io . map = i ;
pd6729_set_io_map ( sock , & io ) ;
}
for ( i = 0 ; i < 5 ; i + + ) {
mem . map = i ;
pd6729_set_mem_map ( sock , & mem ) ;
}
return 0 ;
}
/* the pccard structure and its functions */
static struct pccard_operations pd6729_operations = {
. init = pd6729_init ,
. get_status = pd6729_get_status ,
. set_socket = pd6729_set_socket ,
. set_io_map = pd6729_set_io_map ,
. set_mem_map = pd6729_set_mem_map ,
} ;
static irqreturn_t pd6729_test ( int irq , void * dev , struct pt_regs * regs )
{
dprintk ( " -> hit on irq %d \n " , irq ) ;
return IRQ_HANDLED ;
}
static int pd6729_check_irq ( int irq , int flags )
{
if ( request_irq ( irq , pd6729_test , flags , " x " , pd6729_test ) ! = 0 )
return - 1 ;
free_irq ( irq , pd6729_test ) ;
return 0 ;
}
2006-05-21 02:00:32 +04:00
static u_int __devinit pd6729_isa_scan ( void )
2005-04-17 02:20:36 +04:00
{
u_int mask0 , mask = 0 ;
int i ;
if ( irq_mode = = 1 ) {
printk ( KERN_INFO " pd6729: PCI card interrupts, "
" PCI status changes \n " ) ;
return 0 ;
}
if ( irq_list_count = = 0 )
mask0 = 0xffff ;
else
for ( i = mask0 = 0 ; i < irq_list_count ; i + + )
mask0 | = ( 1 < < irq_list [ i ] ) ;
mask0 & = PD67_MASK ;
/* just find interrupts that aren't in use */
for ( i = 0 ; i < 16 ; i + + )
if ( ( mask0 & ( 1 < < i ) ) & & ( pd6729_check_irq ( i , 0 ) = = 0 ) )
mask | = ( 1 < < i ) ;
printk ( KERN_INFO " pd6729: ISA irqs = " ) ;
for ( i = 0 ; i < 16 ; i + + )
if ( mask & ( 1 < < i ) )
printk ( " %s%d " , ( ( mask & ( ( 1 < < i ) - 1 ) ) ? " , " : " " ) , i ) ;
if ( mask = = 0 ) printk ( " none! " ) ;
printk ( " polling status changes. \n " ) ;
return mask ;
}
static int __devinit pd6729_pci_probe ( struct pci_dev * dev ,
const struct pci_device_id * id )
{
int i , j , ret ;
u_int mask ;
char configbyte ;
struct pd6729_socket * socket ;
2005-12-11 23:18:26 +03:00
socket = kzalloc ( sizeof ( struct pd6729_socket ) * MAX_SOCKETS ,
2005-04-17 02:20:36 +04:00
GFP_KERNEL ) ;
if ( ! socket )
return - ENOMEM ;
if ( ( ret = pci_enable_device ( dev ) ) )
goto err_out_free_mem ;
printk ( KERN_INFO " pd6729: Cirrus PD6729 PCI to PCMCIA Bridge "
" at 0x%lx on irq %d \n " , pci_resource_start ( dev , 0 ) , dev - > irq ) ;
/*
* Since we have no memory BARs some firmware may not
* have had PCI_COMMAND_MEMORY enabled , yet the device needs it .
*/
pci_read_config_byte ( dev , PCI_COMMAND , & configbyte ) ;
if ( ! ( configbyte & PCI_COMMAND_MEMORY ) ) {
printk ( KERN_DEBUG " pd6729: Enabling PCI_COMMAND_MEMORY. \n " ) ;
configbyte | = PCI_COMMAND_MEMORY ;
pci_write_config_byte ( dev , PCI_COMMAND , configbyte ) ;
}
ret = pci_request_regions ( dev , " pd6729 " ) ;
if ( ret ) {
printk ( KERN_INFO " pd6729: pci request region failed. \n " ) ;
goto err_out_disable ;
}
if ( dev - > irq = = NO_IRQ )
irq_mode = 0 ; /* fall back to ISA interrupt mode */
mask = pd6729_isa_scan ( ) ;
if ( irq_mode = = 0 & & mask = = 0 ) {
printk ( KERN_INFO " pd6729: no ISA interrupt is available. \n " ) ;
goto err_out_free_res ;
}
for ( i = 0 ; i < MAX_SOCKETS ; i + + ) {
socket [ i ] . io_base = pci_resource_start ( dev , 0 ) ;
2005-05-06 03:15:56 +04:00
socket [ i ] . socket . features | = SS_CAP_PAGE_REGS | SS_CAP_PCCARD ;
2005-04-17 02:20:36 +04:00
socket [ i ] . socket . map_size = 0x1000 ;
socket [ i ] . socket . irq_mask = mask ;
socket [ i ] . socket . pci_irq = dev - > irq ;
socket [ i ] . socket . owner = THIS_MODULE ;
socket [ i ] . number = i ;
socket [ i ] . socket . ops = & pd6729_operations ;
socket [ i ] . socket . resource_ops = & pccard_nonstatic_ops ;
socket [ i ] . socket . dev . dev = & dev - > dev ;
socket [ i ] . socket . driver_data = & socket [ i ] ;
}
pci_set_drvdata ( dev , socket ) ;
if ( irq_mode = = 1 ) {
/* Register the interrupt handler */
if ( ( ret = request_irq ( dev - > irq , pd6729_interrupt , SA_SHIRQ ,
" pd6729 " , socket ) ) ) {
printk ( KERN_ERR " pd6729: Failed to register irq %d, "
" aborting \n " , dev - > irq ) ;
goto err_out_free_res ;
}
} else {
/* poll Card status change */
init_timer ( & socket - > poll_timer ) ;
socket - > poll_timer . function = pd6729_interrupt_wrapper ;
socket - > poll_timer . data = ( unsigned long ) socket ;
socket - > poll_timer . expires = jiffies + HZ ;
add_timer ( & socket - > poll_timer ) ;
}
for ( i = 0 ; i < MAX_SOCKETS ; i + + ) {
ret = pcmcia_register_socket ( & socket [ i ] . socket ) ;
if ( ret ) {
printk ( KERN_INFO " pd6729: pcmcia_register_socket "
" failed. \n " ) ;
for ( j = 0 ; j < i ; j + + )
pcmcia_unregister_socket ( & socket [ j ] . socket ) ;
goto err_out_free_res2 ;
}
}
return 0 ;
err_out_free_res2 :
if ( irq_mode = = 1 )
free_irq ( dev - > irq , socket ) ;
else
del_timer_sync ( & socket - > poll_timer ) ;
err_out_free_res :
pci_release_regions ( dev ) ;
err_out_disable :
pci_disable_device ( dev ) ;
err_out_free_mem :
kfree ( socket ) ;
return ret ;
}
static void __devexit pd6729_pci_remove ( struct pci_dev * dev )
{
int i ;
struct pd6729_socket * socket = pci_get_drvdata ( dev ) ;
for ( i = 0 ; i < MAX_SOCKETS ; i + + ) {
/* Turn off all interrupt sources */
indirect_write ( & socket [ i ] , I365_CSCINT , 0 ) ;
indirect_write ( & socket [ i ] , I365_INTCTL , 0 ) ;
pcmcia_unregister_socket ( & socket [ i ] . socket ) ;
}
if ( irq_mode = = 1 )
free_irq ( dev - > irq , socket ) ;
else
del_timer_sync ( & socket - > poll_timer ) ;
pci_release_regions ( dev ) ;
pci_disable_device ( dev ) ;
kfree ( socket ) ;
}
static int pd6729_socket_suspend ( struct pci_dev * dev , pm_message_t state )
{
return pcmcia_socket_dev_suspend ( & dev - > dev , state ) ;
}
static int pd6729_socket_resume ( struct pci_dev * dev )
{
return pcmcia_socket_dev_resume ( & dev - > dev ) ;
}
static struct pci_device_id pd6729_pci_ids [ ] = {
{
. vendor = PCI_VENDOR_ID_CIRRUS ,
. device = PCI_DEVICE_ID_CIRRUS_6729 ,
. subvendor = PCI_ANY_ID ,
. subdevice = PCI_ANY_ID ,
} ,
{ }
} ;
MODULE_DEVICE_TABLE ( pci , pd6729_pci_ids ) ;
static struct pci_driver pd6729_pci_drv = {
. name = " pd6729 " ,
. id_table = pd6729_pci_ids ,
. probe = pd6729_pci_probe ,
. remove = __devexit_p ( pd6729_pci_remove ) ,
. suspend = pd6729_socket_suspend ,
. resume = pd6729_socket_resume ,
} ;
static int pd6729_module_init ( void )
{
return pci_register_driver ( & pd6729_pci_drv ) ;
}
static void pd6729_module_exit ( void )
{
pci_unregister_driver ( & pd6729_pci_drv ) ;
}
module_init ( pd6729_module_init ) ;
module_exit ( pd6729_module_exit ) ;