2006-01-15 00:20:40 +03:00
/*
* SGI IOC3 master driver and IRQ demuxer
*
* Copyright ( c ) 2005 Stanislaw Skowronek < skylark @ linux - mips . org >
* Heavily based on similar work by :
* Brent Casavant < bcasavan @ sgi . com > - IOC4 master driver
* Pat Gefre < pfg @ sgi . com > - IOC3 serial port IRQ demuxer
*/
# include <linux/config.h>
# include <linux/errno.h>
# include <linux/module.h>
# include <linux/pci.h>
# include <linux/interrupt.h>
# include <linux/spinlock.h>
# include <linux/delay.h>
# include <linux/ioc3.h>
# include <linux/rwsem.h>
# define IOC3_PCI_SIZE 0x100000
static LIST_HEAD ( ioc3_devices ) ;
static int ioc3_counter ;
static DECLARE_RWSEM ( ioc3_devices_rwsem ) ;
static struct ioc3_submodule * ioc3_submodules [ IOC3_MAX_SUBMODULES ] ;
static struct ioc3_submodule * ioc3_ethernet ;
static rwlock_t ioc3_submodules_lock = RW_LOCK_UNLOCKED ;
/* NIC probing code */
# define GPCR_MLAN_EN 0x00200000 /* enable MCR to pin 8 */
static inline unsigned mcr_pack ( unsigned pulse , unsigned sample )
{
return ( pulse < < 10 ) | ( sample < < 2 ) ;
}
static int nic_wait ( struct ioc3_driver_data * idd )
{
2006-02-01 13:59:06 +03:00
unsigned mcr ;
2006-01-15 00:20:40 +03:00
do {
2006-02-01 13:59:06 +03:00
mcr = readl ( & idd - > vma - > mcr ) ;
2006-01-15 00:20:40 +03:00
} while ( ! ( mcr & 2 ) ) ;
return mcr & 1 ;
}
static int nic_reset ( struct ioc3_driver_data * idd )
{
int presence ;
unsigned long flags ;
local_irq_save ( flags ) ;
2006-02-01 13:59:06 +03:00
writel ( mcr_pack ( 500 , 65 ) , & idd - > vma - > mcr ) ;
2006-01-15 00:20:40 +03:00
presence = nic_wait ( idd ) ;
local_irq_restore ( flags ) ;
udelay ( 500 ) ;
return presence ;
}
2006-03-01 03:59:09 +03:00
static int nic_read_bit ( struct ioc3_driver_data * idd )
2006-01-15 00:20:40 +03:00
{
int result ;
unsigned long flags ;
local_irq_save ( flags ) ;
2006-02-01 13:59:06 +03:00
writel ( mcr_pack ( 6 , 13 ) , & idd - > vma - > mcr ) ;
2006-01-15 00:20:40 +03:00
result = nic_wait ( idd ) ;
local_irq_restore ( flags ) ;
udelay ( 500 ) ;
return result ;
}
2006-03-01 03:59:09 +03:00
static void nic_write_bit ( struct ioc3_driver_data * idd , int bit )
2006-01-15 00:20:40 +03:00
{
if ( bit )
2006-02-01 13:59:06 +03:00
writel ( mcr_pack ( 6 , 110 ) , & idd - > vma - > mcr ) ;
2006-01-15 00:20:40 +03:00
else
2006-02-01 13:59:06 +03:00
writel ( mcr_pack ( 80 , 30 ) , & idd - > vma - > mcr ) ;
2006-01-15 00:20:40 +03:00
nic_wait ( idd ) ;
}
static unsigned nic_read_byte ( struct ioc3_driver_data * idd )
{
unsigned result = 0 ;
int i ;
for ( i = 0 ; i < 8 ; i + + )
result = ( result > > 1 ) | ( nic_read_bit ( idd ) < < 7 ) ;
return result ;
}
static void nic_write_byte ( struct ioc3_driver_data * idd , int byte )
{
int i , bit ;
for ( i = 8 ; i ; i - - ) {
bit = byte & 1 ;
byte > > = 1 ;
nic_write_bit ( idd , bit ) ;
}
}
static unsigned long
nic_find ( struct ioc3_driver_data * idd , int * last , unsigned long addr )
{
int a , b , index , disc ;
nic_reset ( idd ) ;
/* Search ROM. */
nic_write_byte ( idd , 0xF0 ) ;
/* Algorithm from ``Book of iButton Standards''. */
for ( index = 0 , disc = 0 ; index < 64 ; index + + ) {
a = nic_read_bit ( idd ) ;
b = nic_read_bit ( idd ) ;
if ( a & & b ) {
printk ( KERN_WARNING " IOC3 NIC search failed. \n " ) ;
* last = 0 ;
return 0 ;
}
if ( ! a & & ! b ) {
if ( index = = * last ) {
addr | = 1UL < < index ;
} else if ( index > * last ) {
addr & = ~ ( 1UL < < index ) ;
disc = index ;
} else if ( ( addr & ( 1UL < < index ) ) = = 0 )
disc = index ;
nic_write_bit ( idd , ( addr > > index ) & 1 ) ;
continue ;
} else {
if ( a )
addr | = 1UL < < index ;
else
addr & = ~ ( 1UL < < index ) ;
nic_write_bit ( idd , a ) ;
continue ;
}
}
* last = disc ;
return addr ;
}
static void nic_addr ( struct ioc3_driver_data * idd , unsigned long addr )
{
int index ;
nic_reset ( idd ) ;
nic_write_byte ( idd , 0xF0 ) ;
for ( index = 0 ; index < 64 ; index + + ) {
nic_read_bit ( idd ) ;
nic_read_bit ( idd ) ;
nic_write_bit ( idd , ( addr > > index ) & 1 ) ;
}
}
static void crc16_byte ( unsigned int * crc , unsigned char db )
{
int i ;
for ( i = 0 ; i < 8 ; i + + ) {
* crc < < = 1 ;
if ( ( db ^ ( * crc > > 16 ) ) & 1 )
* crc ^ = 0x8005 ;
db > > = 1 ;
}
* crc & = 0xFFFF ;
}
static unsigned int crc16_area ( unsigned char * dbs , int size , unsigned int crc )
{
while ( size - - )
crc16_byte ( & crc , * ( dbs + + ) ) ;
return crc ;
}
static void crc8_byte ( unsigned int * crc , unsigned char db )
{
int i , f ;
for ( i = 0 ; i < 8 ; i + + ) {
f = ( * crc ^ db ) & 1 ;
* crc > > = 1 ;
db > > = 1 ;
if ( f )
* crc ^ = 0x8c ;
}
* crc & = 0xff ;
}
static unsigned int crc8_addr ( unsigned long addr )
{
int i ;
unsigned int crc = 0x00 ;
for ( i = 0 ; i < 8 ; i + + )
crc8_byte ( & crc , addr > > ( i < < 3 ) ) ;
return crc ;
}
static void
read_redir_page ( struct ioc3_driver_data * idd , unsigned long addr , int page ,
unsigned char * redir , unsigned char * data )
{
int loops = 16 , i ;
while ( redir [ page ] ! = 0xFF ) {
page = redir [ page ] ^ 0xFF ;
loops - - ;
if ( loops < 0 ) {
printk ( KERN_ERR " IOC3: NIC circular redirection \n " ) ;
return ;
}
}
loops = 3 ;
while ( loops > 0 ) {
nic_addr ( idd , addr ) ;
nic_write_byte ( idd , 0xF0 ) ;
nic_write_byte ( idd , ( page < < 5 ) & 0xE0 ) ;
nic_write_byte ( idd , ( page > > 3 ) & 0x1F ) ;
for ( i = 0 ; i < 0x20 ; i + + )
data [ i ] = nic_read_byte ( idd ) ;
if ( crc16_area ( data , 0x20 , 0x0000 ) = = 0x800d )
return ;
loops - - ;
}
printk ( KERN_ERR " IOC3: CRC error in data page \n " ) ;
for ( i = 0 ; i < 0x20 ; i + + )
data [ i ] = 0x00 ;
}
static void
read_redir_map ( struct ioc3_driver_data * idd , unsigned long addr ,
unsigned char * redir )
{
int i , j , loops = 3 , crc_ok ;
unsigned int crc ;
while ( loops > 0 ) {
crc_ok = 1 ;
nic_addr ( idd , addr ) ;
nic_write_byte ( idd , 0xAA ) ;
nic_write_byte ( idd , 0x00 ) ;
nic_write_byte ( idd , 0x01 ) ;
for ( i = 0 ; i < 64 ; i + = 8 ) {
for ( j = 0 ; j < 8 ; j + + )
redir [ i + j ] = nic_read_byte ( idd ) ;
crc = crc16_area ( redir + i , 8 , ( i = = 0 ) ? 0x8707 : 0x0000 ) ;
crc16_byte ( & crc , nic_read_byte ( idd ) ) ;
crc16_byte ( & crc , nic_read_byte ( idd ) ) ;
if ( crc ! = 0x800d )
crc_ok = 0 ;
}
if ( crc_ok )
return ;
loops - - ;
}
printk ( KERN_ERR " IOC3: CRC error in redirection page \n " ) ;
for ( i = 0 ; i < 64 ; i + + )
redir [ i ] = 0xFF ;
}
static void read_nic ( struct ioc3_driver_data * idd , unsigned long addr )
{
unsigned char redir [ 64 ] ;
unsigned char data [ 64 ] , part [ 32 ] ;
int i , j ;
/* read redirections */
read_redir_map ( idd , addr , redir ) ;
/* read data pages */
read_redir_page ( idd , addr , 0 , redir , data ) ;
read_redir_page ( idd , addr , 1 , redir , data + 32 ) ;
/* assemble the part # */
j = 0 ;
for ( i = 0 ; i < 19 ; i + + )
if ( data [ i + 11 ] ! = ' ' )
part [ j + + ] = data [ i + 11 ] ;
for ( i = 0 ; i < 6 ; i + + )
if ( data [ i + 32 ] ! = ' ' )
part [ j + + ] = data [ i + 32 ] ;
part [ j ] = 0 ;
/* skip Octane power supplies */
if ( ! strncmp ( part , " 060-0035- " , 9 ) )
return ;
if ( ! strncmp ( part , " 060-0038- " , 9 ) )
return ;
strcpy ( idd - > nic_part , part ) ;
/* assemble the serial # */
j = 0 ;
for ( i = 0 ; i < 10 ; i + + )
if ( data [ i + 1 ] ! = ' ' )
idd - > nic_serial [ j + + ] = data [ i + 1 ] ;
idd - > nic_serial [ j ] = 0 ;
}
static void read_mac ( struct ioc3_driver_data * idd , unsigned long addr )
{
int i , loops = 3 ;
unsigned char data [ 13 ] ;
while ( loops > 0 ) {
nic_addr ( idd , addr ) ;
nic_write_byte ( idd , 0xF0 ) ;
nic_write_byte ( idd , 0x00 ) ;
nic_write_byte ( idd , 0x00 ) ;
nic_read_byte ( idd ) ;
for ( i = 0 ; i < 13 ; i + + )
data [ i ] = nic_read_byte ( idd ) ;
if ( crc16_area ( data , 13 , 0x0000 ) = = 0x800d ) {
for ( i = 10 ; i > 4 ; i - - )
idd - > nic_mac [ 10 - i ] = data [ i ] ;
return ;
}
loops - - ;
}
printk ( KERN_ERR " IOC3: CRC error in MAC address \n " ) ;
for ( i = 0 ; i < 6 ; i + + )
idd - > nic_mac [ i ] = 0x00 ;
}
static void probe_nic ( struct ioc3_driver_data * idd )
{
int save = 0 , loops = 3 ;
unsigned long first , addr ;
2006-02-01 13:59:06 +03:00
writel ( GPCR_MLAN_EN , & idd - > vma - > gpcr_s ) ;
2006-01-15 00:20:40 +03:00
while ( loops > 0 ) {
idd - > nic_part [ 0 ] = 0 ;
idd - > nic_serial [ 0 ] = 0 ;
addr = first = nic_find ( idd , & save , 0 ) ;
if ( ! first )
return ;
while ( 1 ) {
if ( crc8_addr ( addr ) )
break ;
else {
switch ( addr & 0xFF ) {
case 0x0B :
read_nic ( idd , addr ) ;
break ;
case 0x09 :
case 0x89 :
case 0x91 :
read_mac ( idd , addr ) ;
break ;
}
}
addr = nic_find ( idd , & save , addr ) ;
if ( addr = = first )
return ;
}
loops - - ;
}
printk ( KERN_ERR " IOC3: CRC error in NIC address \n " ) ;
}
/* Interrupts */
2006-03-01 03:59:09 +03:00
static void write_ireg ( struct ioc3_driver_data * idd , uint32_t val , int which )
2006-01-15 00:20:40 +03:00
{
unsigned long flags ;
spin_lock_irqsave ( & idd - > ir_lock , flags ) ;
switch ( which ) {
case IOC3_W_IES :
writel ( val , & idd - > vma - > sio_ies ) ;
break ;
case IOC3_W_IEC :
writel ( val , & idd - > vma - > sio_iec ) ;
break ;
}
spin_unlock_irqrestore ( & idd - > ir_lock , flags ) ;
}
static inline uint32_t get_pending_intrs ( struct ioc3_driver_data * idd )
{
unsigned long flag ;
uint32_t intrs = 0 ;
spin_lock_irqsave ( & idd - > ir_lock , flag ) ;
intrs = readl ( & idd - > vma - > sio_ir ) ;
intrs & = readl ( & idd - > vma - > sio_ies ) ;
spin_unlock_irqrestore ( & idd - > ir_lock , flag ) ;
return intrs ;
}
static irqreturn_t ioc3_intr_io ( int irq , void * arg , struct pt_regs * regs )
{
unsigned long flags ;
struct ioc3_driver_data * idd = ( struct ioc3_driver_data * ) arg ;
int handled = 1 , id ;
unsigned int pending ;
read_lock_irqsave ( & ioc3_submodules_lock , flags ) ;
2006-02-01 13:59:06 +03:00
if ( idd - > dual_irq & & readb ( & idd - > vma - > eisr ) ) {
2006-01-15 00:20:40 +03:00
/* send Ethernet IRQ to the driver */
if ( ioc3_ethernet & & idd - > active [ ioc3_ethernet - > id ] & &
ioc3_ethernet - > intr ) {
handled = handled & & ! ioc3_ethernet - > intr ( ioc3_ethernet ,
idd , 0 , regs ) ;
}
}
pending = get_pending_intrs ( idd ) ; /* look at the IO IRQs */
for ( id = 0 ; id < IOC3_MAX_SUBMODULES ; id + + ) {
if ( idd - > active [ id ] & & ioc3_submodules [ id ]
& & ( pending & ioc3_submodules [ id ] - > irq_mask )
& & ioc3_submodules [ id ] - > intr ) {
write_ireg ( idd , ioc3_submodules [ id ] - > irq_mask ,
IOC3_W_IEC ) ;
if ( ! ioc3_submodules [ id ] - > intr ( ioc3_submodules [ id ] ,
idd , pending & ioc3_submodules [ id ] - > irq_mask ,
regs ) )
pending & = ~ ioc3_submodules [ id ] - > irq_mask ;
if ( ioc3_submodules [ id ] - > reset_mask )
write_ireg ( idd , ioc3_submodules [ id ] - > irq_mask ,
IOC3_W_IES ) ;
}
}
read_unlock_irqrestore ( & ioc3_submodules_lock , flags ) ;
if ( pending ) {
printk ( KERN_WARNING
" IOC3: Pending IRQs 0x%08x discarded and disabled \n " , pending ) ;
write_ireg ( idd , pending , IOC3_W_IEC ) ;
handled = 1 ;
}
return handled ? IRQ_HANDLED : IRQ_NONE ;
}
static irqreturn_t ioc3_intr_eth ( int irq , void * arg , struct pt_regs * regs )
{
unsigned long flags ;
struct ioc3_driver_data * idd = ( struct ioc3_driver_data * ) arg ;
int handled = 1 ;
if ( ! idd - > dual_irq )
return IRQ_NONE ;
read_lock_irqsave ( & ioc3_submodules_lock , flags ) ;
if ( ioc3_ethernet & & idd - > active [ ioc3_ethernet - > id ]
& & ioc3_ethernet - > intr )
handled = handled & & ! ioc3_ethernet - > intr ( ioc3_ethernet , idd , 0 ,
regs ) ;
read_unlock_irqrestore ( & ioc3_submodules_lock , flags ) ;
return handled ? IRQ_HANDLED : IRQ_NONE ;
}
void ioc3_enable ( struct ioc3_submodule * is ,
struct ioc3_driver_data * idd , unsigned int irqs )
{
write_ireg ( idd , irqs & is - > irq_mask , IOC3_W_IES ) ;
}
void ioc3_ack ( struct ioc3_submodule * is , struct ioc3_driver_data * idd ,
unsigned int irqs )
{
writel ( irqs & is - > irq_mask , & idd - > vma - > sio_ir ) ;
}
void ioc3_disable ( struct ioc3_submodule * is ,
struct ioc3_driver_data * idd , unsigned int irqs )
{
write_ireg ( idd , irqs & is - > irq_mask , IOC3_W_IEC ) ;
}
void ioc3_gpcr_set ( struct ioc3_driver_data * idd , unsigned int val )
{
unsigned long flags ;
spin_lock_irqsave ( & idd - > gpio_lock , flags ) ;
writel ( val , & idd - > vma - > gpcr_s ) ;
spin_unlock_irqrestore ( & idd - > gpio_lock , flags ) ;
}
/* Keep it simple, stupid! */
static int find_slot ( void * * tab , int max )
{
int i ;
for ( i = 0 ; i < max ; i + + )
if ( ! ( tab [ i ] ) )
return i ;
return - 1 ;
}
/* Register an IOC3 submodule */
int ioc3_register_submodule ( struct ioc3_submodule * is )
{
struct ioc3_driver_data * idd ;
int alloc_id ;
unsigned long flags ;
write_lock_irqsave ( & ioc3_submodules_lock , flags ) ;
alloc_id = find_slot ( ( void * * ) ioc3_submodules , IOC3_MAX_SUBMODULES ) ;
if ( alloc_id ! = - 1 ) {
ioc3_submodules [ alloc_id ] = is ;
if ( is - > ethernet ) {
if ( ioc3_ethernet = = NULL )
ioc3_ethernet = is ;
else
printk ( KERN_WARNING
" IOC3 Ethernet module already registered! \n " ) ;
}
}
write_unlock_irqrestore ( & ioc3_submodules_lock , flags ) ;
if ( alloc_id = = - 1 ) {
printk ( KERN_WARNING " Increase IOC3_MAX_SUBMODULES! \n " ) ;
return - ENOMEM ;
}
is - > id = alloc_id ;
/* Initialize submodule for each IOC3 */
if ( ! is - > probe )
return 0 ;
down_read ( & ioc3_devices_rwsem ) ;
list_for_each_entry ( idd , & ioc3_devices , list ) {
/* set to 1 for IRQs in probe */
idd - > active [ alloc_id ] = 1 ;
idd - > active [ alloc_id ] = ! is - > probe ( is , idd ) ;
}
up_read ( & ioc3_devices_rwsem ) ;
return 0 ;
}
/* Unregister an IOC3 submodule */
void ioc3_unregister_submodule ( struct ioc3_submodule * is )
{
struct ioc3_driver_data * idd ;
unsigned long flags ;
write_lock_irqsave ( & ioc3_submodules_lock , flags ) ;
if ( ioc3_submodules [ is - > id ] = = is )
ioc3_submodules [ is - > id ] = NULL ;
else
printk ( KERN_WARNING
" IOC3 submodule %s has wrong ID. \n " , is - > name ) ;
if ( ioc3_ethernet = = is )
ioc3_ethernet = NULL ;
write_unlock_irqrestore ( & ioc3_submodules_lock , flags ) ;
/* Remove submodule for each IOC3 */
down_read ( & ioc3_devices_rwsem ) ;
list_for_each_entry ( idd , & ioc3_devices , list )
if ( idd - > active [ is - > id ] ) {
if ( is - > remove )
if ( is - > remove ( is , idd ) )
printk ( KERN_WARNING
" %s: IOC3 submodule %s remove failed "
" for pci_dev %s. \n " ,
__FUNCTION__ , module_name ( is - > owner ) ,
pci_name ( idd - > pdev ) ) ;
idd - > active [ is - > id ] = 0 ;
if ( is - > irq_mask )
write_ireg ( idd , is - > irq_mask , IOC3_W_IEC ) ;
}
up_read ( & ioc3_devices_rwsem ) ;
}
/*********************
* Device management *
* * * * * * * * * * * * * * * * * * * * */
static char *
ioc3_class_names [ ] = { " unknown " , " IP27 BaseIO " , " IP30 system " , " MENET 1/2/3 " ,
" MENET 4 " , " CADduo " , " Altix Serial " } ;
static int ioc3_class ( struct ioc3_driver_data * idd )
{
int res = IOC3_CLASS_NONE ;
/* NIC-based logic */
if ( ! strncmp ( idd - > nic_part , " 030-0891- " , 9 ) )
res = IOC3_CLASS_BASE_IP30 ;
if ( ! strncmp ( idd - > nic_part , " 030-1155- " , 9 ) )
res = IOC3_CLASS_CADDUO ;
if ( ! strncmp ( idd - > nic_part , " 030-1657- " , 9 ) )
res = IOC3_CLASS_SERIAL ;
if ( ! strncmp ( idd - > nic_part , " 030-1664- " , 9 ) )
res = IOC3_CLASS_SERIAL ;
/* total random heuristics */
# ifdef CONFIG_SGI_IP27
if ( ! idd - > nic_part [ 0 ] )
res = IOC3_CLASS_BASE_IP27 ;
# endif
/* print educational message */
printk ( KERN_INFO " IOC3 part: [%s], serial: [%s] => class %s \n " ,
idd - > nic_part , idd - > nic_serial , ioc3_class_names [ res ] ) ;
return res ;
}
/* Adds a new instance of an IOC3 card */
static int ioc3_probe ( struct pci_dev * pdev , const struct pci_device_id * pci_id )
{
struct ioc3_driver_data * idd ;
uint32_t pcmd ;
int ret , id ;
/* Enable IOC3 and take ownership of it */
if ( ( ret = pci_enable_device ( pdev ) ) ) {
printk ( KERN_WARNING
" %s: Failed to enable IOC3 device for pci_dev %s. \n " ,
__FUNCTION__ , pci_name ( pdev ) ) ;
goto out ;
}
pci_set_master ( pdev ) ;
# ifdef USE_64BIT_DMA
ret = pci_set_dma_mask ( pdev , 0xffffffffffffffffULL ) ;
if ( ! ret ) {
ret = pci_set_consistent_dma_mask ( pdev , 0xffffffffffffffffULL ) ;
if ( ret < 0 ) {
printk ( KERN_WARNING " %s: Unable to obtain 64 bit DMA "
" for consistent allocations \n " ,
__FUNCTION__ ) ;
}
}
# endif
/* Set up per-IOC3 data */
idd = kmalloc ( sizeof ( struct ioc3_driver_data ) , GFP_KERNEL ) ;
if ( ! idd ) {
printk ( KERN_WARNING
" %s: Failed to allocate IOC3 data for pci_dev %s. \n " ,
__FUNCTION__ , pci_name ( pdev ) ) ;
ret = - ENODEV ;
goto out_idd ;
}
memset ( idd , 0 , sizeof ( struct ioc3_driver_data ) ) ;
spin_lock_init ( & idd - > ir_lock ) ;
spin_lock_init ( & idd - > gpio_lock ) ;
idd - > pdev = pdev ;
/* Map all IOC3 registers. These are shared between subdevices
* so the main IOC3 module manages them .
*/
idd - > pma = pci_resource_start ( pdev , 0 ) ;
if ( ! idd - > pma ) {
printk ( KERN_WARNING
" %s: Unable to find IOC3 resource "
" for pci_dev %s. \n " ,
__FUNCTION__ , pci_name ( pdev ) ) ;
ret = - ENODEV ;
goto out_pci ;
}
if ( ! request_region ( idd - > pma , IOC3_PCI_SIZE , " ioc3 " ) ) {
printk ( KERN_WARNING
" %s: Unable to request IOC3 region "
" for pci_dev %s. \n " ,
__FUNCTION__ , pci_name ( pdev ) ) ;
ret = - ENODEV ;
goto out_pci ;
}
idd - > vma = ioremap ( idd - > pma , IOC3_PCI_SIZE ) ;
if ( ! idd - > vma ) {
printk ( KERN_WARNING
" %s: Unable to remap IOC3 region "
" for pci_dev %s. \n " ,
__FUNCTION__ , pci_name ( pdev ) ) ;
ret = - ENODEV ;
goto out_misc_region ;
}
/* Track PCI-device specific data */
pci_set_drvdata ( pdev , idd ) ;
down_write ( & ioc3_devices_rwsem ) ;
list_add ( & idd - > list , & ioc3_devices ) ;
idd - > id = ioc3_counter + + ;
up_write ( & ioc3_devices_rwsem ) ;
2006-02-01 13:59:06 +03:00
idd - > gpdr_shadow = readl ( & idd - > vma - > gpdr ) ;
2006-01-15 00:20:40 +03:00
/* Read IOC3 NIC contents */
probe_nic ( idd ) ;
/* Detect IOC3 class */
idd - > class = ioc3_class ( idd ) ;
/* Initialize IOC3 */
pci_read_config_dword ( pdev , PCI_COMMAND , & pcmd ) ;
pci_write_config_dword ( pdev , PCI_COMMAND ,
pcmd | PCI_COMMAND_MEMORY |
PCI_COMMAND_PARITY | PCI_COMMAND_SERR |
PCI_SCR_DROP_MODE_EN ) ;
write_ireg ( idd , ~ 0 , IOC3_W_IEC ) ;
writel ( ~ 0 , & idd - > vma - > sio_ir ) ;
/* Set up IRQs */
if ( idd - > class = = IOC3_CLASS_BASE_IP30
| | idd - > class = = IOC3_CLASS_BASE_IP27 ) {
writel ( 0 , & idd - > vma - > eier ) ;
writel ( ~ 0 , & idd - > vma - > eisr ) ;
idd - > dual_irq = 1 ;
if ( ! request_irq ( pdev - > irq , ioc3_intr_eth , SA_SHIRQ ,
" ioc3-eth " , ( void * ) idd ) ) {
idd - > irq_eth = pdev - > irq ;
} else {
printk ( KERN_WARNING
" %s : request_irq fails for IRQ 0x%x \n " ,
__FUNCTION__ , pdev - > irq ) ;
}
if ( ! request_irq ( pdev - > irq + 2 , ioc3_intr_io , SA_SHIRQ ,
" ioc3-io " , ( void * ) idd ) ) {
idd - > irq_io = pdev - > irq + 2 ;
} else {
printk ( KERN_WARNING
" %s : request_irq fails for IRQ 0x%x \n " ,
__FUNCTION__ , pdev - > irq + 2 ) ;
}
} else {
if ( ! request_irq ( pdev - > irq , ioc3_intr_io , SA_SHIRQ ,
" ioc3 " , ( void * ) idd ) ) {
idd - > irq_io = pdev - > irq ;
} else {
printk ( KERN_WARNING
" %s : request_irq fails for IRQ 0x%x \n " ,
__FUNCTION__ , pdev - > irq ) ;
}
}
/* Add this IOC3 to all submodules */
for ( id = 0 ; id < IOC3_MAX_SUBMODULES ; id + + )
if ( ioc3_submodules [ id ] & & ioc3_submodules [ id ] - > probe ) {
idd - > active [ id ] = 1 ;
idd - > active [ id ] = ! ioc3_submodules [ id ] - > probe
( ioc3_submodules [ id ] , idd ) ;
}
printk ( KERN_INFO " IOC3 Master Driver loaded for %s \n " , pci_name ( pdev ) ) ;
return 0 ;
out_misc_region :
release_region ( idd - > pma , IOC3_PCI_SIZE ) ;
out_pci :
kfree ( idd ) ;
out_idd :
pci_disable_device ( pdev ) ;
out :
return ret ;
}
/* Removes a particular instance of an IOC3 card. */
static void ioc3_remove ( struct pci_dev * pdev )
{
int id ;
struct ioc3_driver_data * idd ;
idd = pci_get_drvdata ( pdev ) ;
/* Remove this IOC3 from all submodules */
for ( id = 0 ; id < IOC3_MAX_SUBMODULES ; id + + )
if ( idd - > active [ id ] ) {
if ( ioc3_submodules [ id ] & & ioc3_submodules [ id ] - > remove )
if ( ioc3_submodules [ id ] - > remove ( ioc3_submodules [ id ] ,
idd ) )
printk ( KERN_WARNING
" %s: IOC3 submodule 0x%s remove failed "
" for pci_dev %s. \n " ,
__FUNCTION__ ,
module_name ( ioc3_submodules [ id ] - > owner ) ,
pci_name ( pdev ) ) ;
idd - > active [ id ] = 0 ;
}
/* Clear and disable all IRQs */
write_ireg ( idd , ~ 0 , IOC3_W_IEC ) ;
writel ( ~ 0 , & idd - > vma - > sio_ir ) ;
/* Release resources */
free_irq ( idd - > irq_io , ( void * ) idd ) ;
if ( idd - > dual_irq )
free_irq ( idd - > irq_eth , ( void * ) idd ) ;
iounmap ( idd - > vma ) ;
release_region ( idd - > pma , IOC3_PCI_SIZE ) ;
/* Disable IOC3 and relinquish */
pci_disable_device ( pdev ) ;
/* Remove and free driver data */
down_write ( & ioc3_devices_rwsem ) ;
list_del ( & idd - > list ) ;
up_write ( & ioc3_devices_rwsem ) ;
kfree ( idd ) ;
}
static struct pci_device_id ioc3_id_table [ ] = {
{ PCI_VENDOR_ID_SGI , PCI_DEVICE_ID_SGI_IOC3 , PCI_ANY_ID , PCI_ANY_ID } ,
{ 0 }
} ;
static struct pci_driver ioc3_driver = {
. name = " IOC3 " ,
. id_table = ioc3_id_table ,
. probe = ioc3_probe ,
. remove = ioc3_remove ,
} ;
MODULE_DEVICE_TABLE ( pci , ioc3_id_table ) ;
/*********************
* Module management *
* * * * * * * * * * * * * * * * * * * * */
/* Module load */
static int __devinit ioc3_init ( void )
{
if ( ia64_platform_is ( " sn2 " ) )
return pci_register_driver ( & ioc3_driver ) ;
return 0 ;
}
/* Module unload */
static void __devexit ioc3_exit ( void )
{
pci_unregister_driver ( & ioc3_driver ) ;
}
module_init ( ioc3_init ) ;
module_exit ( ioc3_exit ) ;
MODULE_AUTHOR ( " Stanislaw Skowronek <skylark@linux-mips.org> " ) ;
MODULE_DESCRIPTION ( " PCI driver for SGI IOC3 " ) ;
MODULE_LICENSE ( " GPL " ) ;
2006-02-01 14:06:41 +03:00
EXPORT_SYMBOL_GPL ( ioc3_register_submodule ) ;
EXPORT_SYMBOL_GPL ( ioc3_unregister_submodule ) ;
EXPORT_SYMBOL_GPL ( ioc3_ack ) ;
EXPORT_SYMBOL_GPL ( ioc3_gpcr_set ) ;
EXPORT_SYMBOL_GPL ( ioc3_disable ) ;
EXPORT_SYMBOL_GPL ( ioc3_enable ) ;