2005-04-16 15:20:36 -07:00
/*****************************************************************************
* sdladrv . c SDLA Support Module . Main module .
*
* This module is a library of common hardware - specific functions
* used by all Sangoma drivers .
*
* Author : Gideon Hack
*
* Copyright : ( c ) 1995 - 2000 Sangoma Technologies Inc .
*
* 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 .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* Mar 20 , 2001 Nenad Corbic Added the auto_pci_cfg filed , to support
* the PCISLOT # 0.
* Apr 04 , 2000 Nenad Corbic Fixed the auto memory detection code .
* The memory test at address 0xC8000 .
* Mar 09 , 2000 Nenad Corbic Added Gideon ' s Bug Fix : clear pci
* interrupt flags on initial load .
* Jun 02 , 1999 Gideon Hack Added support for the S514 adapter .
* Updates for Linux 2.2 . X kernels .
* Sep 17 , 1998 Jaspreet Singh Updates for linux 2.2 . X kernels
* Dec 20 , 1996 Gene Kozin Version 3.0 .0 . Complete overhaul .
* Jul 12 , 1996 Gene Kozin Changes for Linux 2.0 compatibility .
* Jun 12 , 1996 Gene Kozin Added support for S503 card .
* Apr 30 , 1996 Gene Kozin SDLA hardware interrupt is acknowledged before
* calling protocolspecific ISR .
* Register I / O ports with Linux kernel .
* Miscellaneous bug fixes .
* Dec 20 , 1995 Gene Kozin Fixed a bug in interrupt routine .
* Oct 14 , 1995 Gene Kozin Initial version .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*****************************************************************************
* Notes :
* - - - - - -
* 1. This code is ment to be system - independent ( as much as possible ) . To
* achive this , various macros are used to hide system - specific interfaces .
* To compile this code , one of the following constants must be defined :
*
* Platform Define
* - - - - - - - - - - - - - -
* Linux _LINUX_
* SCO Unix _SCO_UNIX_
*
* 2. Supported adapter types :
*
* S502A
* ES502A ( S502E )
* S503
* S507
* S508 ( S509 )
*
* 3. S502A Notes :
*
* There is no separate DPM window enable / disable control in S502A . It
* opens immediately after a window number it written to the HMCR
* register . To close the window , HMCR has to be written a value
* ? ? ? ? 1111 b ( e . g . 0x0F or 0xFF ) .
*
* S502A DPM window cannot be located at offset E000 ( e . g . 0xAE000 ) .
*
* There should be a delay of ? ? ? before reading back S502A status
* register .
*
* 4. S502E Notes :
*
* S502E has a h / w bug : although default IRQ line state is HIGH , enabling
* interrupts by setting bit 1 of the control register ( BASE ) to ' 1 '
* causes it to go LOW ! Therefore , disabling interrupts by setting that
* bit to ' 0 ' causes low - to - high transition on IRQ line ( ghosty
* interrupt ) . The same occurs when disabling CPU by resetting bit 0 of
* CPU control register ( BASE + 3 ) - see the next note .
*
* S502E CPU and DPM control is limited :
*
* o CPU cannot be stopped independently . Resetting bit 0 of the CPUi
* control register ( BASE + 3 ) shuts the board down entirely , including
* DPM ;
*
* o DPM access cannot be controlled dynamically . Ones CPU is started ,
* bit 1 of the control register ( BASE ) is used to enable / disable IRQ ,
* so that access to shared memory cannot be disabled while CPU is
* running .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# define _LINUX_
# if defined(_LINUX_) /****** Linux *******************************/
# include <linux/config.h>
# include <linux/kernel.h> /* printk(), and other useful stuff */
# include <linux/stddef.h> /* offsetof(), etc. */
# include <linux/errno.h> /* return codes */
# include <linux/string.h> /* inline memset(), etc. */
# include <linux/module.h> /* support for loadable modules */
# include <linux/jiffies.h> /* for jiffies, HZ, etc. */
# include <linux/sdladrv.h> /* API definitions */
# include <linux/sdlasfm.h> /* SDLA firmware module definitions */
# include <linux/sdlapci.h> /* SDLA PCI hardware definitions */
# include <linux/pci.h> /* PCI defines and function prototypes */
# include <asm/io.h> /* for inb(), outb(), etc. */
# define _INB(port) (inb(port))
# define _OUTB(port, byte) (outb((byte),(port)))
# define SYSTEM_TICK jiffies
# include <linux/init.h>
# elif defined(_SCO_UNIX_) /****** SCO Unix ****************************/
# if !defined(INKERNEL)
# error This code MUST be compiled in kernel mode!
# endif
# include <sys/sdladrv.h> /* API definitions */
# include <sys/sdlasfm.h> /* SDLA firmware module definitions */
# include <sys/inline.h> /* for inb(), outb(), etc. */
# define _INB(port) (inb(port))
# define _OUTB(port, byte) (outb((port),(byte)))
# define SYSTEM_TICK lbolt
# else
# error Unknown system type!
# endif
# define MOD_VERSION 3
# define MOD_RELEASE 0
# define SDLA_IODELAY 100 /* I/O Rd/Wr delay, 10 works for 486DX2-66 */
# define EXEC_DELAY 20 /* shared memory access delay, mks */
# define EXEC_TIMEOUT (HZ*2) /* command timeout, in ticks */
/* I/O port address range */
# define S502A_IORANGE 3
# define S502E_IORANGE 4
# define S503_IORANGE 3
# define S507_IORANGE 4
# define S508_IORANGE 4
/* Maximum amount of memory */
# define S502_MAXMEM 0x10000L
# define S503_MAXMEM 0x10000L
# define S507_MAXMEM 0x40000L
# define S508_MAXMEM 0x40000L
/* Minimum amount of memory */
# define S502_MINMEM 0x8000L
# define S503_MINMEM 0x8000L
# define S507_MINMEM 0x20000L
# define S508_MINMEM 0x20000L
# define NO_PORT -1
/****** Function Prototypes *************************************************/
/* Hardware-specific functions */
static int sdla_detect ( sdlahw_t * hw ) ;
static int sdla_autodpm ( sdlahw_t * hw ) ;
static int sdla_setdpm ( sdlahw_t * hw ) ;
static int sdla_load ( sdlahw_t * hw , sfm_t * sfm , unsigned len ) ;
static int sdla_init ( sdlahw_t * hw ) ;
static unsigned long sdla_memtest ( sdlahw_t * hw ) ;
static int sdla_bootcfg ( sdlahw_t * hw , sfm_info_t * sfminfo ) ;
static unsigned char make_config_byte ( sdlahw_t * hw ) ;
static int sdla_start ( sdlahw_t * hw , unsigned addr ) ;
static int init_s502a ( sdlahw_t * hw ) ;
static int init_s502e ( sdlahw_t * hw ) ;
static int init_s503 ( sdlahw_t * hw ) ;
static int init_s507 ( sdlahw_t * hw ) ;
static int init_s508 ( sdlahw_t * hw ) ;
static int detect_s502a ( int port ) ;
static int detect_s502e ( int port ) ;
static int detect_s503 ( int port ) ;
static int detect_s507 ( int port ) ;
static int detect_s508 ( int port ) ;
static int detect_s514 ( sdlahw_t * hw ) ;
static int find_s514_adapter ( sdlahw_t * hw , char find_first_S514_card ) ;
/* Miscellaneous functions */
static void peek_by_4 ( unsigned long src , void * buf , unsigned len ) ;
static void poke_by_4 ( unsigned long dest , void * buf , unsigned len ) ;
static int calibrate_delay ( int mks ) ;
static int get_option_index ( unsigned * optlist , unsigned optval ) ;
static unsigned check_memregion ( void * ptr , unsigned len ) ;
static unsigned test_memregion ( void * ptr , unsigned len ) ;
static unsigned short checksum ( unsigned char * buf , unsigned len ) ;
static int init_pci_slot ( sdlahw_t * ) ;
static int pci_probe ( sdlahw_t * hw ) ;
/****** Global Data **********************************************************
* Note : All data must be explicitly initialized ! ! !
*/
static struct pci_device_id sdladrv_pci_tbl [ ] = {
{ V3_VENDOR_ID , V3_DEVICE_ID , PCI_ANY_ID , PCI_ANY_ID , } ,
{ } /* Terminating entry */
} ;
MODULE_DEVICE_TABLE ( pci , sdladrv_pci_tbl ) ;
MODULE_LICENSE ( " GPL " ) ;
/* private data */
static char modname [ ] = " sdladrv " ;
static char fullname [ ] = " SDLA Support Module " ;
static char copyright [ ] = " (c) 1995-1999 Sangoma Technologies Inc. " ;
static unsigned exec_idle ;
/* Hardware configuration options.
* These are arrays of configuration options used by verification routines .
* The first element of each array is its size ( i . e . number of options ) .
*/
static unsigned s502_port_options [ ] =
{ 4 , 0x250 , 0x300 , 0x350 , 0x360 }
;
static unsigned s503_port_options [ ] =
{ 8 , 0x250 , 0x254 , 0x300 , 0x304 , 0x350 , 0x354 , 0x360 , 0x364 }
;
static unsigned s508_port_options [ ] =
{ 8 , 0x250 , 0x270 , 0x280 , 0x300 , 0x350 , 0x360 , 0x380 , 0x390 }
;
static unsigned s502a_irq_options [ ] = { 0 } ;
static unsigned s502e_irq_options [ ] = { 4 , 2 , 3 , 5 , 7 } ;
static unsigned s503_irq_options [ ] = { 5 , 2 , 3 , 4 , 5 , 7 } ;
static unsigned s508_irq_options [ ] = { 8 , 3 , 4 , 5 , 7 , 10 , 11 , 12 , 15 } ;
static unsigned s502a_dpmbase_options [ ] =
{
28 ,
0xA0000 , 0xA2000 , 0xA4000 , 0xA6000 , 0xA8000 , 0xAA000 , 0xAC000 ,
0xC0000 , 0xC2000 , 0xC4000 , 0xC6000 , 0xC8000 , 0xCA000 , 0xCC000 ,
0xD0000 , 0xD2000 , 0xD4000 , 0xD6000 , 0xD8000 , 0xDA000 , 0xDC000 ,
0xE0000 , 0xE2000 , 0xE4000 , 0xE6000 , 0xE8000 , 0xEA000 , 0xEC000 ,
} ;
static unsigned s507_dpmbase_options [ ] =
{
32 ,
0xA0000 , 0xA2000 , 0xA4000 , 0xA6000 , 0xA8000 , 0xAA000 , 0xAC000 , 0xAE000 ,
0xB0000 , 0xB2000 , 0xB4000 , 0xB6000 , 0xB8000 , 0xBA000 , 0xBC000 , 0xBE000 ,
0xC0000 , 0xC2000 , 0xC4000 , 0xC6000 , 0xC8000 , 0xCA000 , 0xCC000 , 0xCE000 ,
0xE0000 , 0xE2000 , 0xE4000 , 0xE6000 , 0xE8000 , 0xEA000 , 0xEC000 , 0xEE000 ,
} ;
static unsigned s508_dpmbase_options [ ] = /* incl. S502E and S503 */
{
32 ,
0xA0000 , 0xA2000 , 0xA4000 , 0xA6000 , 0xA8000 , 0xAA000 , 0xAC000 , 0xAE000 ,
0xC0000 , 0xC2000 , 0xC4000 , 0xC6000 , 0xC8000 , 0xCA000 , 0xCC000 , 0xCE000 ,
0xD0000 , 0xD2000 , 0xD4000 , 0xD6000 , 0xD8000 , 0xDA000 , 0xDC000 , 0xDE000 ,
0xE0000 , 0xE2000 , 0xE4000 , 0xE6000 , 0xE8000 , 0xEA000 , 0xEC000 , 0xEE000 ,
} ;
/*
static unsigned s502_dpmsize_options [ ] = { 2 , 0x2000 , 0x10000 } ;
static unsigned s507_dpmsize_options [ ] = { 2 , 0x2000 , 0x4000 } ;
static unsigned s508_dpmsize_options [ ] = { 1 , 0x2000 } ;
*/
static unsigned s502a_pclk_options [ ] = { 2 , 3600 , 7200 } ;
static unsigned s502e_pclk_options [ ] = { 5 , 3600 , 5000 , 7200 , 8000 , 10000 } ;
static unsigned s503_pclk_options [ ] = { 3 , 7200 , 8000 , 10000 } ;
static unsigned s507_pclk_options [ ] = { 1 , 12288 } ;
static unsigned s508_pclk_options [ ] = { 1 , 16000 } ;
/* Host memory control register masks */
static unsigned char s502a_hmcr [ ] =
{
0x10 , 0x12 , 0x14 , 0x16 , 0x18 , 0x1A , 0x1C , /* A0000 - AC000 */
0x20 , 0x22 , 0x24 , 0x26 , 0x28 , 0x2A , 0x2C , /* C0000 - CC000 */
0x00 , 0x02 , 0x04 , 0x06 , 0x08 , 0x0A , 0x0C , /* D0000 - DC000 */
0x30 , 0x32 , 0x34 , 0x36 , 0x38 , 0x3A , 0x3C , /* E0000 - EC000 */
} ;
static unsigned char s502e_hmcr [ ] =
{
0x10 , 0x12 , 0x14 , 0x16 , 0x18 , 0x1A , 0x1C , 0x1E , /* A0000 - AE000 */
0x20 , 0x22 , 0x24 , 0x26 , 0x28 , 0x2A , 0x2C , 0x2E , /* C0000 - CE000 */
0x00 , 0x02 , 0x04 , 0x06 , 0x08 , 0x0A , 0x0C , 0x0E , /* D0000 - DE000 */
0x30 , 0x32 , 0x34 , 0x36 , 0x38 , 0x3A , 0x3C , 0x3E , /* E0000 - EE000 */
} ;
static unsigned char s507_hmcr [ ] =
{
0x00 , 0x02 , 0x04 , 0x06 , 0x08 , 0x0A , 0x0C , 0x0E , /* A0000 - AE000 */
0x40 , 0x42 , 0x44 , 0x46 , 0x48 , 0x4A , 0x4C , 0x4E , /* B0000 - BE000 */
0x80 , 0x82 , 0x84 , 0x86 , 0x88 , 0x8A , 0x8C , 0x8E , /* C0000 - CE000 */
0xC0 , 0xC2 , 0xC4 , 0xC6 , 0xC8 , 0xCA , 0xCC , 0xCE , /* E0000 - EE000 */
} ;
static unsigned char s508_hmcr [ ] =
{
0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x05 , 0x06 , 0x07 , /* A0000 - AE000 */
0x10 , 0x11 , 0x12 , 0x13 , 0x14 , 0x15 , 0x16 , 0x17 , /* C0000 - CE000 */
0x08 , 0x09 , 0x0A , 0x0B , 0x0C , 0x0D , 0x0E , 0x0F , /* D0000 - DE000 */
0x18 , 0x19 , 0x1A , 0x1B , 0x1C , 0x1D , 0x1E , 0x1F , /* E0000 - EE000 */
} ;
static unsigned char s507_irqmask [ ] =
{
0x00 , 0x20 , 0x40 , 0x60 , 0x80 , 0xA0 , 0xC0 , 0xE0
} ;
static int pci_slot_ar [ MAX_S514_CARDS ] ;
/******* Kernel Loadable Module Entry Points ********************************/
/*============================================================================
* Module ' insert ' entry point .
* o print announcement
* o initialize static data
* o calibrate SDLA shared memory access delay .
*
* Return : 0 Ok
* < 0 error .
* Context : process
*/
static int __init sdladrv_init ( void )
{
int i = 0 ;
printk ( KERN_INFO " %s v%u.%u %s \n " ,
fullname , MOD_VERSION , MOD_RELEASE , copyright ) ;
exec_idle = calibrate_delay ( EXEC_DELAY ) ;
# ifdef WANDEBUG
printk ( KERN_DEBUG " %s: exec_idle = %d \n " , modname , exec_idle ) ;
# endif
/* Initialize the PCI Card array, which
* will store flags , used to mark
* card initialization state */
for ( i = 0 ; i < MAX_S514_CARDS ; i + + )
pci_slot_ar [ i ] = 0xFF ;
return 0 ;
}
/*============================================================================
* Module ' remove ' entry point .
* o release all remaining system resources
*/
static void __exit sdladrv_cleanup ( void )
{
}
module_init ( sdladrv_init ) ;
module_exit ( sdladrv_cleanup ) ;
/******* Kernel APIs ********************************************************/
/*============================================================================
* Set up adapter .
* o detect adapter type
* o verify hardware configuration options
* o check for hardware conflicts
* o set up adapter shared memory
* o test adapter memory
* o load firmware
* Return : 0 ok .
* < 0 error
*/
EXPORT_SYMBOL ( sdla_setup ) ;
int sdla_setup ( sdlahw_t * hw , void * sfm , unsigned len )
{
unsigned * irq_opt = NULL ; /* IRQ options */
unsigned * dpmbase_opt = NULL ; /* DPM window base options */
unsigned * pclk_opt = NULL ; /* CPU clock rate options */
int err = 0 ;
if ( sdla_detect ( hw ) ) {
if ( hw - > type ! = SDLA_S514 )
printk ( KERN_INFO " %s: no SDLA card found at port 0x%X \n " ,
modname , hw - > port ) ;
return - EINVAL ;
}
if ( hw - > type ! = SDLA_S514 ) {
printk ( KERN_INFO " %s: found S%04u card at port 0x%X. \n " ,
modname , hw - > type , hw - > port ) ;
hw - > dpmsize = SDLA_WINDOWSIZE ;
switch ( hw - > type ) {
case SDLA_S502A :
hw - > io_range = S502A_IORANGE ;
irq_opt = s502a_irq_options ;
dpmbase_opt = s502a_dpmbase_options ;
pclk_opt = s502a_pclk_options ;
break ;
case SDLA_S502E :
hw - > io_range = S502E_IORANGE ;
irq_opt = s502e_irq_options ;
dpmbase_opt = s508_dpmbase_options ;
pclk_opt = s502e_pclk_options ;
break ;
case SDLA_S503 :
hw - > io_range = S503_IORANGE ;
irq_opt = s503_irq_options ;
dpmbase_opt = s508_dpmbase_options ;
pclk_opt = s503_pclk_options ;
break ;
case SDLA_S507 :
hw - > io_range = S507_IORANGE ;
irq_opt = s508_irq_options ;
dpmbase_opt = s507_dpmbase_options ;
pclk_opt = s507_pclk_options ;
break ;
case SDLA_S508 :
hw - > io_range = S508_IORANGE ;
irq_opt = s508_irq_options ;
dpmbase_opt = s508_dpmbase_options ;
pclk_opt = s508_pclk_options ;
break ;
}
/* Verify IRQ configuration options */
if ( ! get_option_index ( irq_opt , hw - > irq ) ) {
printk ( KERN_INFO " %s: IRQ %d is invalid! \n " ,
modname , hw - > irq ) ;
return - EINVAL ;
}
/* Verify CPU clock rate configuration options */
if ( hw - > pclk = = 0 )
hw - > pclk = pclk_opt [ 1 ] ; /* use default */
else if ( ! get_option_index ( pclk_opt , hw - > pclk ) ) {
printk ( KERN_INFO " %s: CPU clock %u is invalid! \n " ,
modname , hw - > pclk ) ;
return - EINVAL ;
}
printk ( KERN_INFO " %s: assuming CPU clock rate of %u kHz. \n " ,
modname , hw - > pclk ) ;
/* Setup adapter dual-port memory window and test memory */
if ( hw - > dpmbase = = 0 ) {
err = sdla_autodpm ( hw ) ;
if ( err ) {
printk ( KERN_INFO
" %s: can't find available memory region! \n " ,
modname ) ;
return err ;
}
}
else if ( ! get_option_index ( dpmbase_opt ,
virt_to_phys ( hw - > dpmbase ) ) ) {
printk ( KERN_INFO
" %s: memory address 0x%lX is invalid! \n " ,
modname , virt_to_phys ( hw - > dpmbase ) ) ;
return - EINVAL ;
}
else if ( sdla_setdpm ( hw ) ) {
printk ( KERN_INFO
" %s: 8K memory region at 0x%lX is not available! \n " ,
modname , virt_to_phys ( hw - > dpmbase ) ) ;
return - EINVAL ;
}
printk ( KERN_INFO
" %s: dual-port memory window is set at 0x%lX. \n " ,
modname , virt_to_phys ( hw - > dpmbase ) ) ;
/* If we find memory in 0xE**** Memory region,
* warn the user to disable the SHADOW RAM .
* Since memory corruption can occur if SHADOW is
* enabled . This can causes random crashes ! */
if ( virt_to_phys ( hw - > dpmbase ) > = 0xE0000 ) {
printk ( KERN_WARNING " \n %s: !!!!!!!! WARNING !!!!!!!! \n " , modname ) ;
printk ( KERN_WARNING " %s: WANPIPE is using 0x%lX memory region !!! \n " ,
modname , virt_to_phys ( hw - > dpmbase ) ) ;
printk ( KERN_WARNING " Please disable the SHADOW RAM, otherwise \n " ) ;
printk ( KERN_WARNING " your system might crash randomly from time to time ! \n " ) ;
printk ( KERN_WARNING " %s: !!!!!!!! WARNING !!!!!!!! \n \n " , modname ) ;
}
}
else {
hw - > memory = test_memregion ( ( void * ) hw - > dpmbase ,
MAX_SIZEOF_S514_MEMORY ) ;
if ( hw - > memory < ( 256 * 1024 ) ) {
printk ( KERN_INFO
" %s: error in testing S514 memory (0x%lX) \n " ,
modname , hw - > memory ) ;
sdla_down ( hw ) ;
return - EINVAL ;
}
}
printk ( KERN_INFO " %s: found %luK bytes of on-board memory \n " ,
modname , hw - > memory / 1024 ) ;
/* Load firmware. If loader fails then shut down adapter */
err = sdla_load ( hw , sfm , len ) ;
if ( err ) sdla_down ( hw ) ; /* shutdown adapter */
return err ;
}
/*============================================================================
* Shut down SDLA : disable shared memory access and interrupts , stop CPU , etc .
*/
EXPORT_SYMBOL ( sdla_down ) ;
int sdla_down ( sdlahw_t * hw )
{
unsigned port = hw - > port ;
int i ;
unsigned char CPU_no ;
u32 int_config , int_status ;
if ( ! port & & ( hw - > type ! = SDLA_S514 ) )
return - EFAULT ;
switch ( hw - > type ) {
case SDLA_S502A :
_OUTB ( port , 0x08 ) ; /* halt CPU */
_OUTB ( port , 0x08 ) ;
_OUTB ( port , 0x08 ) ;
hw - > regs [ 0 ] = 0x08 ;
_OUTB ( port + 1 , 0xFF ) ; /* close memory window */
hw - > regs [ 1 ] = 0xFF ;
break ;
case SDLA_S502E :
_OUTB ( port + 3 , 0 ) ; /* stop CPU */
_OUTB ( port , 0 ) ; /* reset board */
for ( i = 0 ; i < S502E_IORANGE ; + + i )
hw - > regs [ i ] = 0
;
break ;
case SDLA_S503 :
case SDLA_S507 :
case SDLA_S508 :
_OUTB ( port , 0 ) ; /* reset board logic */
hw - > regs [ 0 ] = 0 ;
break ;
case SDLA_S514 :
/* halt the adapter */
* ( char * ) hw - > vector = S514_CPU_HALT ;
CPU_no = hw - > S514_cpu_no [ 0 ] ;
/* disable the PCI IRQ and disable memory access */
pci_read_config_dword ( hw - > pci_dev , PCI_INT_CONFIG , & int_config ) ;
int_config & = ( CPU_no = = S514_CPU_A ) ? ~ PCI_DISABLE_IRQ_CPU_A : ~ PCI_DISABLE_IRQ_CPU_B ;
pci_write_config_dword ( hw - > pci_dev , PCI_INT_CONFIG , int_config ) ;
read_S514_int_stat ( hw , & int_status ) ;
S514_intack ( hw , int_status ) ;
if ( CPU_no = = S514_CPU_A )
pci_write_config_dword ( hw - > pci_dev , PCI_MAP0_DWORD ,
PCI_CPU_A_MEM_DISABLE ) ;
else
pci_write_config_dword ( hw - > pci_dev , PCI_MAP1_DWORD ,
PCI_CPU_B_MEM_DISABLE ) ;
/* free up the allocated virtual memory */
iounmap ( ( void * ) hw - > dpmbase ) ;
iounmap ( ( void * ) hw - > vector ) ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
/*============================================================================
* Map shared memory window into SDLA address space .
*/
EXPORT_SYMBOL ( sdla_mapmem ) ;
int sdla_mapmem ( sdlahw_t * hw , unsigned long addr )
{
unsigned port = hw - > port ;
register int tmp ;
switch ( hw - > type ) {
case SDLA_S502A :
case SDLA_S502E :
if ( addr < S502_MAXMEM ) { /* verify parameter */
tmp = addr > > 13 ; /* convert to register mask */
_OUTB ( port + 2 , tmp ) ;
hw - > regs [ 2 ] = tmp ;
}
else return - EINVAL ;
break ;
case SDLA_S503 :
if ( addr < S503_MAXMEM ) { /* verify parameter */
tmp = ( hw - > regs [ 0 ] & 0x8F ) | ( ( addr > > 9 ) & 0x70 ) ;
_OUTB ( port , tmp ) ;
hw - > regs [ 0 ] = tmp ;
}
else return - EINVAL ;
break ;
case SDLA_S507 :
if ( addr < S507_MAXMEM ) {
if ( ! ( _INB ( port ) & 0x02 ) )
return - EIO ;
tmp = addr > > 13 ; /* convert to register mask */
_OUTB ( port + 2 , tmp ) ;
hw - > regs [ 2 ] = tmp ;
}
else return - EINVAL ;
break ;
case SDLA_S508 :
if ( addr < S508_MAXMEM ) {
tmp = addr > > 13 ; /* convert to register mask */
_OUTB ( port + 2 , tmp ) ;
hw - > regs [ 2 ] = tmp ;
}
else return - EINVAL ;
break ;
case SDLA_S514 :
return 0 ;
default :
return - EINVAL ;
}
hw - > vector = addr & 0xFFFFE000L ;
return 0 ;
}
/*============================================================================
* Enable interrupt generation .
*/
2005-09-09 23:17:28 -07:00
static int sdla_inten ( sdlahw_t * hw )
2005-04-16 15:20:36 -07:00
{
unsigned port = hw - > port ;
int tmp , i ;
switch ( hw - > type ) {
case SDLA_S502E :
/* Note thar interrupt control operations on S502E are allowed
* only if CPU is enabled ( bit 0 of status register is set ) .
*/
if ( _INB ( port ) & 0x01 ) {
_OUTB ( port , 0x02 ) ; /* bit1 = 1, bit2 = 0 */
_OUTB ( port , 0x06 ) ; /* bit1 = 1, bit2 = 1 */
hw - > regs [ 0 ] = 0x06 ;
}
else return - EIO ;
break ;
case SDLA_S503 :
tmp = hw - > regs [ 0 ] | 0x04 ;
_OUTB ( port , tmp ) ;
hw - > regs [ 0 ] = tmp ; /* update mirror */
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ; /* delay */
if ( ! ( _INB ( port ) & 0x02 ) ) /* verify */
return - EIO ;
break ;
case SDLA_S508 :
tmp = hw - > regs [ 0 ] | 0x10 ;
_OUTB ( port , tmp ) ;
hw - > regs [ 0 ] = tmp ; /* update mirror */
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ; /* delay */
if ( ! ( _INB ( port + 1 ) & 0x10 ) ) /* verify */
return - EIO ;
break ;
case SDLA_S502A :
case SDLA_S507 :
break ;
case SDLA_S514 :
break ;
default :
return - EINVAL ;
}
return 0 ;
}
/*============================================================================
* Disable interrupt generation .
*/
2005-09-09 23:17:28 -07:00
#if 0
2005-04-16 15:20:36 -07:00
int sdla_intde ( sdlahw_t * hw )
{
unsigned port = hw - > port ;
int tmp , i ;
switch ( hw - > type ) {
case SDLA_S502E :
/* Notes:
* 1 ) interrupt control operations are allowed only if CPU is
* enabled ( bit 0 of status register is set ) .
* 2 ) disabling interrupts using bit 1 of control register
* causes IRQ line go high , therefore we are going to use
* 0x04 instead : lower it to inhibit interrupts to PC .
*/
if ( _INB ( port ) & 0x01 ) {
_OUTB ( port , hw - > regs [ 0 ] & ~ 0x04 ) ;
hw - > regs [ 0 ] & = ~ 0x04 ;
}
else return - EIO ;
break ;
case SDLA_S503 :
tmp = hw - > regs [ 0 ] & ~ 0x04 ;
_OUTB ( port , tmp ) ;
hw - > regs [ 0 ] = tmp ; /* update mirror */
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ; /* delay */
if ( _INB ( port ) & 0x02 ) /* verify */
return - EIO ;
break ;
case SDLA_S508 :
tmp = hw - > regs [ 0 ] & ~ 0x10 ;
_OUTB ( port , tmp ) ;
hw - > regs [ 0 ] = tmp ; /* update mirror */
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ; /* delay */
if ( _INB ( port ) & 0x10 ) /* verify */
return - EIO ;
break ;
case SDLA_S502A :
case SDLA_S507 :
break ;
default :
return - EINVAL ;
}
return 0 ;
}
2005-09-09 23:17:28 -07:00
# endif /* 0 */
2005-04-16 15:20:36 -07:00
/*============================================================================
* Acknowledge SDLA hardware interrupt .
*/
2005-09-09 23:17:28 -07:00
static int sdla_intack ( sdlahw_t * hw )
2005-04-16 15:20:36 -07:00
{
unsigned port = hw - > port ;
int tmp ;
switch ( hw - > type ) {
case SDLA_S502E :
/* To acknoledge hardware interrupt we have to toggle bit 3 of
* control register : \ _ /
* Note that interrupt control operations on S502E are allowed
* only if CPU is enabled ( bit 1 of status register is set ) .
*/
if ( _INB ( port ) & 0x01 ) {
tmp = hw - > regs [ 0 ] & ~ 0x04 ;
_OUTB ( port , tmp ) ;
tmp | = 0x04 ;
_OUTB ( port , tmp ) ;
hw - > regs [ 0 ] = tmp ;
}
else return - EIO ;
break ;
case SDLA_S503 :
if ( _INB ( port ) & 0x04 ) {
tmp = hw - > regs [ 0 ] & ~ 0x08 ;
_OUTB ( port , tmp ) ;
tmp | = 0x08 ;
_OUTB ( port , tmp ) ;
hw - > regs [ 0 ] = tmp ;
}
break ;
case SDLA_S502A :
case SDLA_S507 :
case SDLA_S508 :
break ;
default :
return - EINVAL ;
}
return 0 ;
}
/*============================================================================
* Acknowledge S514 hardware interrupt .
*/
EXPORT_SYMBOL ( S514_intack ) ;
void S514_intack ( sdlahw_t * hw , u32 int_status )
{
pci_write_config_dword ( hw - > pci_dev , PCI_INT_STATUS , int_status ) ;
}
/*============================================================================
* Read the S514 hardware interrupt status .
*/
EXPORT_SYMBOL ( read_S514_int_stat ) ;
void read_S514_int_stat ( sdlahw_t * hw , u32 * int_status )
{
pci_read_config_dword ( hw - > pci_dev , PCI_INT_STATUS , int_status ) ;
}
/*============================================================================
* Generate an interrupt to adapter ' s CPU .
*/
2005-09-09 23:17:28 -07:00
#if 0
2005-04-16 15:20:36 -07:00
int sdla_intr ( sdlahw_t * hw )
{
unsigned port = hw - > port ;
switch ( hw - > type ) {
case SDLA_S502A :
if ( ! ( _INB ( port ) & 0x40 ) ) {
_OUTB ( port , 0x10 ) ; /* issue NMI to CPU */
hw - > regs [ 0 ] = 0x10 ;
}
else return - EIO ;
break ;
case SDLA_S507 :
if ( ( _INB ( port ) & 0x06 ) = = 0x06 ) {
_OUTB ( port + 3 , 0 ) ;
}
else return - EIO ;
break ;
case SDLA_S508 :
if ( _INB ( port + 1 ) & 0x02 ) {
_OUTB ( port , 0x08 ) ;
}
else return - EIO ;
break ;
case SDLA_S502E :
case SDLA_S503 :
default :
return - EINVAL ;
}
return 0 ;
}
2005-09-09 23:17:28 -07:00
# endif /* 0 */
2005-04-16 15:20:36 -07:00
/*============================================================================
* Execute Adapter Command .
* o Set exec flag .
* o Busy - wait until flag is reset .
* o Return number of loops made , or 0 if command timed out .
*/
EXPORT_SYMBOL ( sdla_exec ) ;
int sdla_exec ( void * opflag )
{
volatile unsigned char * flag = opflag ;
unsigned long tstop ;
int nloops ;
if ( readb ( flag ) ! = 0x00 ) {
printk ( KERN_INFO
" WANPIPE: opp flag set on entry to sdla_exec \n " ) ;
return 0 ;
}
writeb ( 0x01 , flag ) ;
tstop = SYSTEM_TICK + EXEC_TIMEOUT ;
for ( nloops = 1 ; ( readb ( flag ) = = 0x01 ) ; + + nloops ) {
unsigned delay = exec_idle ;
while ( - - delay ) ; /* delay */
if ( SYSTEM_TICK > tstop ) return 0 ; /* time is up! */
}
return nloops ;
}
/*============================================================================
* Read absolute adapter memory .
* Transfer data from adapter ' s memory to data buffer .
*
* Note :
* Care should be taken when crossing dual - port memory window boundary .
* This function is not atomic , so caller must disable interrupt if
* interrupt routines are accessing adapter shared memory .
*/
EXPORT_SYMBOL ( sdla_peek ) ;
int sdla_peek ( sdlahw_t * hw , unsigned long addr , void * buf , unsigned len )
{
if ( addr + len > hw - > memory ) /* verify arguments */
return - EINVAL ;
if ( hw - > type = = SDLA_S514 ) { /* copy data for the S514 adapter */
peek_by_4 ( ( unsigned long ) hw - > dpmbase + addr , buf , len ) ;
return 0 ;
}
else { /* copy data for the S508 adapter */
unsigned long oldvec = hw - > vector ;
unsigned winsize = hw - > dpmsize ;
unsigned curpos , curlen ; /* current offset and block size */
unsigned long curvec ; /* current DPM window vector */
int err = 0 ;
while ( len & & ! err ) {
curpos = addr % winsize ; /* current window offset */
curvec = addr - curpos ; /* current window vector */
curlen = ( len > ( winsize - curpos ) ) ?
( winsize - curpos ) : len ;
/* Relocate window and copy block of data */
err = sdla_mapmem ( hw , curvec ) ;
peek_by_4 ( ( unsigned long ) hw - > dpmbase + curpos , buf ,
curlen ) ;
addr + = curlen ;
buf = ( char * ) buf + curlen ;
len - = curlen ;
}
/* Restore DPM window position */
sdla_mapmem ( hw , oldvec ) ;
return err ;
}
}
/*============================================================================
* Read data from adapter ' s memory to a data buffer in 4 - byte chunks .
* Note that we ensure that the SDLA memory address is on a 4 - byte boundary
* before we begin moving the data in 4 - byte chunks .
*/
static void peek_by_4 ( unsigned long src , void * buf , unsigned len )
{
/* byte copy data until we get to a 4-byte boundary */
while ( len & & ( src & 0x03 ) ) {
* ( char * ) buf + + = readb ( src + + ) ;
len - - ;
}
/* copy data in 4-byte chunks */
while ( len > = 4 ) {
* ( unsigned long * ) buf = readl ( src ) ;
buf + = 4 ;
src + = 4 ;
len - = 4 ;
}
/* byte copy any remaining data */
while ( len ) {
* ( char * ) buf + + = readb ( src + + ) ;
len - - ;
}
}
/*============================================================================
* Write Absolute Adapter Memory .
* Transfer data from data buffer to adapter ' s memory .
*
* Note :
* Care should be taken when crossing dual - port memory window boundary .
* This function is not atomic , so caller must disable interrupt if
* interrupt routines are accessing adapter shared memory .
*/
EXPORT_SYMBOL ( sdla_poke ) ;
int sdla_poke ( sdlahw_t * hw , unsigned long addr , void * buf , unsigned len )
{
if ( addr + len > hw - > memory ) /* verify arguments */
return - EINVAL ;
if ( hw - > type = = SDLA_S514 ) { /* copy data for the S514 adapter */
poke_by_4 ( ( unsigned long ) hw - > dpmbase + addr , buf , len ) ;
return 0 ;
}
else { /* copy data for the S508 adapter */
unsigned long oldvec = hw - > vector ;
unsigned winsize = hw - > dpmsize ;
unsigned curpos , curlen ; /* current offset and block size */
unsigned long curvec ; /* current DPM window vector */
int err = 0 ;
while ( len & & ! err ) {
curpos = addr % winsize ; /* current window offset */
curvec = addr - curpos ; /* current window vector */
curlen = ( len > ( winsize - curpos ) ) ?
( winsize - curpos ) : len ;
/* Relocate window and copy block of data */
sdla_mapmem ( hw , curvec ) ;
poke_by_4 ( ( unsigned long ) hw - > dpmbase + curpos , buf ,
curlen ) ;
addr + = curlen ;
buf = ( char * ) buf + curlen ;
len - = curlen ;
}
/* Restore DPM window position */
sdla_mapmem ( hw , oldvec ) ;
return err ;
}
}
/*============================================================================
* Write from a data buffer to adapter ' s memory in 4 - byte chunks .
* Note that we ensure that the SDLA memory address is on a 4 - byte boundary
* before we begin moving the data in 4 - byte chunks .
*/
static void poke_by_4 ( unsigned long dest , void * buf , unsigned len )
{
/* byte copy data until we get to a 4-byte boundary */
while ( len & & ( dest & 0x03 ) ) {
writeb ( * ( char * ) buf + + , dest + + ) ;
len - - ;
}
/* copy data in 4-byte chunks */
while ( len > = 4 ) {
writel ( * ( unsigned long * ) buf , dest ) ;
dest + = 4 ;
buf + = 4 ;
len - = 4 ;
}
/* byte copy any remaining data */
while ( len ) {
writeb ( * ( char * ) buf + + , dest + + ) ;
len - - ;
}
}
# ifdef DONT_COMPIPLE_THIS
# endif /* DONT_COMPIPLE_THIS */
/****** Hardware-Specific Functions *****************************************/
/*============================================================================
* Detect adapter type .
* o if adapter type is specified then call detection routine for that adapter
* type . Otherwise call detection routines for every adapter types until
* adapter is detected .
*
* Notes :
* 1 ) Detection tests are destructive ! Adapter will be left in shutdown state
* after the test .
*/
static int sdla_detect ( sdlahw_t * hw )
{
unsigned port = hw - > port ;
int err = 0 ;
if ( ! port & & ( hw - > type ! = SDLA_S514 ) )
return - EFAULT ;
switch ( hw - > type ) {
case SDLA_S502A :
if ( ! detect_s502a ( port ) ) err = - ENODEV ;
break ;
case SDLA_S502E :
if ( ! detect_s502e ( port ) ) err = - ENODEV ;
break ;
case SDLA_S503 :
if ( ! detect_s503 ( port ) ) err = - ENODEV ;
break ;
case SDLA_S507 :
if ( ! detect_s507 ( port ) ) err = - ENODEV ;
break ;
case SDLA_S508 :
if ( ! detect_s508 ( port ) ) err = - ENODEV ;
break ;
case SDLA_S514 :
if ( ! detect_s514 ( hw ) ) err = - ENODEV ;
break ;
default :
if ( detect_s502a ( port ) )
hw - > type = SDLA_S502A ;
else if ( detect_s502e ( port ) )
hw - > type = SDLA_S502E ;
else if ( detect_s503 ( port ) )
hw - > type = SDLA_S503 ;
else if ( detect_s507 ( port ) )
hw - > type = SDLA_S507 ;
else if ( detect_s508 ( port ) )
hw - > type = SDLA_S508 ;
else err = - ENODEV ;
}
return err ;
}
/*============================================================================
* Autoselect memory region .
* o try all available DMP address options from the top down until success .
*/
static int sdla_autodpm ( sdlahw_t * hw )
{
int i , err = - EINVAL ;
unsigned * opt ;
switch ( hw - > type ) {
case SDLA_S502A :
opt = s502a_dpmbase_options ;
break ;
case SDLA_S502E :
case SDLA_S503 :
case SDLA_S508 :
opt = s508_dpmbase_options ;
break ;
case SDLA_S507 :
opt = s507_dpmbase_options ;
break ;
default :
return - EINVAL ;
}
/* Start testing from 8th position, address
* 0xC8000 from the 508 address table .
* We don ' t want to test A * * * * addresses , since
* they are usually used for Video */
for ( i = 8 ; i < = opt [ 0 ] & & err ; i + + ) {
hw - > dpmbase = phys_to_virt ( opt [ i ] ) ;
err = sdla_setdpm ( hw ) ;
}
return err ;
}
/*============================================================================
* Set up adapter dual - port memory window .
* o shut down adapter
* o make sure that no physical memory exists in this region , i . e entire
* region reads 0xFF and is not writable when adapter is shut down .
* o initialize adapter hardware
* o make sure that region is usable with SDLA card , i . e . we can write to it
* when adapter is configured .
*/
static int sdla_setdpm ( sdlahw_t * hw )
{
int err ;
/* Shut down card and verify memory region */
sdla_down ( hw ) ;
if ( check_memregion ( hw - > dpmbase , hw - > dpmsize ) )
return - EINVAL ;
/* Initialize adapter and test on-board memory segment by segment.
* If memory size appears to be less than shared memory window size ,
* assume that memory region is unusable .
*/
err = sdla_init ( hw ) ;
if ( err ) return err ;
if ( sdla_memtest ( hw ) < hw - > dpmsize ) { /* less than window size */
sdla_down ( hw ) ;
return - EIO ;
}
sdla_mapmem ( hw , 0L ) ; /* set window vector at bottom */
return 0 ;
}
/*============================================================================
* Load adapter from the memory image of the SDLA firmware module .
* o verify firmware integrity and compatibility
* o start adapter up
*/
static int sdla_load ( sdlahw_t * hw , sfm_t * sfm , unsigned len )
{
int i ;
/* Verify firmware signature */
if ( strcmp ( sfm - > signature , SFM_SIGNATURE ) ) {
printk ( KERN_INFO " %s: not SDLA firmware! \n " ,
modname ) ;
return - EINVAL ;
}
/* Verify firmware module format version */
if ( sfm - > version ! = SFM_VERSION ) {
printk ( KERN_INFO
" %s: firmware format %u rejected! Expecting %u. \n " ,
modname , sfm - > version , SFM_VERSION ) ;
return - EINVAL ;
}
/* Verify firmware module length and checksum */
if ( ( len - offsetof ( sfm_t , image ) ! = sfm - > info . codesize ) | |
( checksum ( ( void * ) & sfm - > info ,
sizeof ( sfm_info_t ) + sfm - > info . codesize ) ! = sfm - > checksum ) ) {
printk ( KERN_INFO " %s: firmware corrupted! \n " , modname ) ;
return - EINVAL ;
}
/* Announce */
printk ( KERN_INFO " %s: loading %s (ID=%u)... \n " , modname ,
( sfm - > descr [ 0 ] ! = ' \0 ' ) ? sfm - > descr : " unknown firmware " ,
sfm - > info . codeid ) ;
if ( hw - > type = = SDLA_S514 )
printk ( KERN_INFO " %s: loading S514 adapter, CPU %c \n " ,
modname , hw - > S514_cpu_no [ 0 ] ) ;
/* Scan through the list of compatible adapters and make sure our
* adapter type is listed .
*/
for ( i = 0 ;
( i < SFM_MAX_SDLA ) & & ( sfm - > info . adapter [ i ] ! = hw - > type ) ;
+ + i ) ;
if ( i = = SFM_MAX_SDLA ) {
printk ( KERN_INFO " %s: firmware is not compatible with S%u! \n " ,
modname , hw - > type ) ;
return - EINVAL ;
}
/* Make sure there is enough on-board memory */
if ( hw - > memory < sfm - > info . memsize ) {
printk ( KERN_INFO
" %s: firmware needs %lu bytes of on-board memory! \n " ,
modname , sfm - > info . memsize ) ;
return - EINVAL ;
}
/* Move code onto adapter */
if ( sdla_poke ( hw , sfm - > info . codeoffs , sfm - > image , sfm - > info . codesize ) ) {
printk ( KERN_INFO " %s: failed to load code segment! \n " ,
modname ) ;
return - EIO ;
}
/* Prepare boot-time configuration data and kick-off CPU */
sdla_bootcfg ( hw , & sfm - > info ) ;
if ( sdla_start ( hw , sfm - > info . startoffs ) ) {
printk ( KERN_INFO " %s: Damn... Adapter won't start! \n " ,
modname ) ;
return - EIO ;
}
/* position DPM window over the mailbox and enable interrupts */
if ( sdla_mapmem ( hw , sfm - > info . winoffs ) | | sdla_inten ( hw ) ) {
printk ( KERN_INFO " %s: adapter hardware failure! \n " ,
modname ) ;
return - EIO ;
}
hw - > fwid = sfm - > info . codeid ; /* set firmware ID */
return 0 ;
}
/*============================================================================
* Initialize SDLA hardware : setup memory window , IRQ , etc .
*/
static int sdla_init ( sdlahw_t * hw )
{
int i ;
for ( i = 0 ; i < SDLA_MAXIORANGE ; + + i )
hw - > regs [ i ] = 0 ;
switch ( hw - > type ) {
case SDLA_S502A : return init_s502a ( hw ) ;
case SDLA_S502E : return init_s502e ( hw ) ;
case SDLA_S503 : return init_s503 ( hw ) ;
case SDLA_S507 : return init_s507 ( hw ) ;
case SDLA_S508 : return init_s508 ( hw ) ;
}
return - EINVAL ;
}
/*============================================================================
* Test adapter on - board memory .
* o slide DPM window from the bottom up and test adapter memory segment by
* segment .
* Return adapter memory size .
*/
static unsigned long sdla_memtest ( sdlahw_t * hw )
{
unsigned long memsize ;
unsigned winsize ;
for ( memsize = 0 , winsize = hw - > dpmsize ;
! sdla_mapmem ( hw , memsize ) & &
( test_memregion ( hw - > dpmbase , winsize ) = = winsize )
;
memsize + = winsize )
;
hw - > memory = memsize ;
return memsize ;
}
/*============================================================================
* Prepare boot - time firmware configuration data .
* o position DPM window
* o initialize configuration data area
*/
static int sdla_bootcfg ( sdlahw_t * hw , sfm_info_t * sfminfo )
{
unsigned char * data ;
if ( ! sfminfo - > datasize ) return 0 ; /* nothing to do */
if ( sdla_mapmem ( hw , sfminfo - > dataoffs ) ! = 0 )
return - EIO ;
if ( hw - > type = = SDLA_S514 )
data = ( void * ) ( hw - > dpmbase + sfminfo - > dataoffs ) ;
else
data = ( void * ) ( ( u8 * ) hw - > dpmbase +
( sfminfo - > dataoffs - hw - > vector ) ) ;
memset_io ( data , 0 , sfminfo - > datasize ) ;
writeb ( make_config_byte ( hw ) , & data [ 0x00 ] ) ;
switch ( sfminfo - > codeid ) {
case SFID_X25_502 :
case SFID_X25_508 :
writeb ( 3 , & data [ 0x01 ] ) ; /* T1 timer */
writeb ( 10 , & data [ 0x03 ] ) ; /* N2 */
writeb ( 7 , & data [ 0x06 ] ) ; /* HDLC window size */
writeb ( 1 , & data [ 0x0B ] ) ; /* DTE */
writeb ( 2 , & data [ 0x0C ] ) ; /* X.25 packet window size */
writew ( 128 , & data [ 0x0D ] ) ; /* default X.25 data size */
writew ( 128 , & data [ 0x0F ] ) ; /* maximum X.25 data size */
break ;
}
return 0 ;
}
/*============================================================================
* Prepare configuration byte identifying adapter type and CPU clock rate .
*/
static unsigned char make_config_byte ( sdlahw_t * hw )
{
unsigned char byte = 0 ;
switch ( hw - > pclk ) {
case 5000 : byte = 0x01 ; break ;
case 7200 : byte = 0x02 ; break ;
case 8000 : byte = 0x03 ; break ;
case 10000 : byte = 0x04 ; break ;
case 16000 : byte = 0x05 ; break ;
}
switch ( hw - > type ) {
case SDLA_S502E : byte | = 0x80 ; break ;
case SDLA_S503 : byte | = 0x40 ; break ;
}
return byte ;
}
/*============================================================================
* Start adapter ' s CPU .
* o calculate a pointer to adapter ' s cold boot entry point
* o position DPM window
* o place boot instruction ( jp addr ) at cold boot entry point
* o start CPU
*/
static int sdla_start ( sdlahw_t * hw , unsigned addr )
{
unsigned port = hw - > port ;
unsigned char * bootp ;
int err , tmp , i ;
if ( ! port & & ( hw - > type ! = SDLA_S514 ) ) return - EFAULT ;
switch ( hw - > type ) {
case SDLA_S502A :
bootp = hw - > dpmbase ;
bootp + = 0x66 ;
break ;
case SDLA_S502E :
case SDLA_S503 :
case SDLA_S507 :
case SDLA_S508 :
case SDLA_S514 :
bootp = hw - > dpmbase ;
break ;
default :
return - EINVAL ;
}
err = sdla_mapmem ( hw , 0 ) ;
if ( err ) return err ;
writeb ( 0xC3 , bootp ) ; /* Z80: 'jp' opcode */
bootp + + ;
writew ( addr , bootp ) ;
switch ( hw - > type ) {
case SDLA_S502A :
_OUTB ( port , 0x10 ) ; /* issue NMI to CPU */
hw - > regs [ 0 ] = 0x10 ;
break ;
case SDLA_S502E :
_OUTB ( port + 3 , 0x01 ) ; /* start CPU */
hw - > regs [ 3 ] = 0x01 ;
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ;
if ( _INB ( port ) & 0x01 ) { /* verify */
/*
* Enabling CPU changes functionality of the
* control register , so we have to reset its
* mirror .
*/
_OUTB ( port , 0 ) ; /* disable interrupts */
hw - > regs [ 0 ] = 0 ;
}
else return - EIO ;
break ;
case SDLA_S503 :
tmp = hw - > regs [ 0 ] | 0x09 ; /* set bits 0 and 3 */
_OUTB ( port , tmp ) ;
hw - > regs [ 0 ] = tmp ; /* update mirror */
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ;
if ( ! ( _INB ( port ) & 0x01 ) ) /* verify */
return - EIO ;
break ;
case SDLA_S507 :
tmp = hw - > regs [ 0 ] | 0x02 ;
_OUTB ( port , tmp ) ;
hw - > regs [ 0 ] = tmp ; /* update mirror */
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ;
if ( ! ( _INB ( port ) & 0x04 ) ) /* verify */
return - EIO ;
break ;
case SDLA_S508 :
tmp = hw - > regs [ 0 ] | 0x02 ;
_OUTB ( port , tmp ) ;
hw - > regs [ 0 ] = tmp ; /* update mirror */
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ;
if ( ! ( _INB ( port + 1 ) & 0x02 ) ) /* verify */
return - EIO ;
break ;
case SDLA_S514 :
writeb ( S514_CPU_START , hw - > vector ) ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
/*============================================================================
* Initialize S502A adapter .
*/
static int init_s502a ( sdlahw_t * hw )
{
unsigned port = hw - > port ;
int tmp , i ;
if ( ! detect_s502a ( port ) )
return - ENODEV ;
hw - > regs [ 0 ] = 0x08 ;
hw - > regs [ 1 ] = 0xFF ;
/* Verify configuration options */
i = get_option_index ( s502a_dpmbase_options , virt_to_phys ( hw - > dpmbase ) ) ;
if ( i = = 0 )
return - EINVAL ;
tmp = s502a_hmcr [ i - 1 ] ;
switch ( hw - > dpmsize ) {
case 0x2000 :
tmp | = 0x01 ;
break ;
case 0x10000L :
break ;
default :
return - EINVAL ;
}
/* Setup dual-port memory window (this also enables memory access) */
_OUTB ( port + 1 , tmp ) ;
hw - > regs [ 1 ] = tmp ;
hw - > regs [ 0 ] = 0x08 ;
return 0 ;
}
/*============================================================================
* Initialize S502E adapter .
*/
static int init_s502e ( sdlahw_t * hw )
{
unsigned port = hw - > port ;
int tmp , i ;
if ( ! detect_s502e ( port ) )
return - ENODEV ;
/* Verify configuration options */
i = get_option_index ( s508_dpmbase_options , virt_to_phys ( hw - > dpmbase ) ) ;
if ( i = = 0 )
return - EINVAL ;
tmp = s502e_hmcr [ i - 1 ] ;
switch ( hw - > dpmsize ) {
case 0x2000 :
tmp | = 0x01 ;
break ;
case 0x10000L :
break ;
default :
return - EINVAL ;
}
/* Setup dual-port memory window */
_OUTB ( port + 1 , tmp ) ;
hw - > regs [ 1 ] = tmp ;
/* Enable memory access */
_OUTB ( port , 0x02 ) ;
hw - > regs [ 0 ] = 0x02 ;
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ; /* delay */
return ( _INB ( port ) & 0x02 ) ? 0 : - EIO ;
}
/*============================================================================
* Initialize S503 adapter .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
static int init_s503 ( sdlahw_t * hw )
{
unsigned port = hw - > port ;
int tmp , i ;
if ( ! detect_s503 ( port ) )
return - ENODEV ;
/* Verify configuration options */
i = get_option_index ( s508_dpmbase_options , virt_to_phys ( hw - > dpmbase ) ) ;
if ( i = = 0 )
return - EINVAL ;
tmp = s502e_hmcr [ i - 1 ] ;
switch ( hw - > dpmsize ) {
case 0x2000 :
tmp | = 0x01 ;
break ;
case 0x10000L :
break ;
default :
return - EINVAL ;
}
/* Setup dual-port memory window */
_OUTB ( port + 1 , tmp ) ;
hw - > regs [ 1 ] = tmp ;
/* Enable memory access */
_OUTB ( port , 0x02 ) ;
hw - > regs [ 0 ] = 0x02 ; /* update mirror */
return 0 ;
}
/*============================================================================
* Initialize S507 adapter .
*/
static int init_s507 ( sdlahw_t * hw )
{
unsigned port = hw - > port ;
int tmp , i ;
if ( ! detect_s507 ( port ) )
return - ENODEV ;
/* Verify configuration options */
i = get_option_index ( s507_dpmbase_options , virt_to_phys ( hw - > dpmbase ) ) ;
if ( i = = 0 )
return - EINVAL ;
tmp = s507_hmcr [ i - 1 ] ;
switch ( hw - > dpmsize ) {
case 0x2000 :
tmp | = 0x01 ;
break ;
case 0x10000L :
break ;
default :
return - EINVAL ;
}
/* Enable adapter's logic */
_OUTB ( port , 0x01 ) ;
hw - > regs [ 0 ] = 0x01 ;
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ; /* delay */
if ( ! ( _INB ( port ) & 0x20 ) )
return - EIO ;
/* Setup dual-port memory window */
_OUTB ( port + 1 , tmp ) ;
hw - > regs [ 1 ] = tmp ;
/* Enable memory access */
tmp = hw - > regs [ 0 ] | 0x04 ;
if ( hw - > irq ) {
i = get_option_index ( s508_irq_options , hw - > irq ) ;
if ( i ) tmp | = s507_irqmask [ i - 1 ] ;
}
_OUTB ( port , tmp ) ;
hw - > regs [ 0 ] = tmp ; /* update mirror */
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ; /* delay */
return ( _INB ( port ) & 0x08 ) ? 0 : - EIO ;
}
/*============================================================================
* Initialize S508 adapter .
*/
static int init_s508 ( sdlahw_t * hw )
{
unsigned port = hw - > port ;
int tmp , i ;
if ( ! detect_s508 ( port ) )
return - ENODEV ;
/* Verify configuration options */
i = get_option_index ( s508_dpmbase_options , virt_to_phys ( hw - > dpmbase ) ) ;
if ( i = = 0 )
return - EINVAL ;
/* Setup memory configuration */
tmp = s508_hmcr [ i - 1 ] ;
_OUTB ( port + 1 , tmp ) ;
hw - > regs [ 1 ] = tmp ;
/* Enable memory access */
_OUTB ( port , 0x04 ) ;
hw - > regs [ 0 ] = 0x04 ; /* update mirror */
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ; /* delay */
return ( _INB ( port + 1 ) & 0x04 ) ? 0 : - EIO ;
}
/*============================================================================
* Detect S502A adapter .
* Following tests are used to detect S502A adapter :
* 1. All registers other than status ( BASE ) should read 0xFF
* 2. After writing 00001000 b to control register , status register should
* read 01000000 b .
* 3. After writing 0 to control register , status register should still
* read 01000000 b .
* 4. After writing 00000100 b to control register , status register should
* read 01000100 b .
* Return 1 if detected o . k . or 0 if failed .
* Note : This test is destructive ! Adapter will be left in shutdown
* state after the test .
*/
static int detect_s502a ( int port )
{
int i , j ;
if ( ! get_option_index ( s502_port_options , port ) )
return 0 ;
for ( j = 1 ; j < SDLA_MAXIORANGE ; + + j ) {
if ( _INB ( port + j ) ! = 0xFF )
return 0 ;
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ; /* delay */
}
_OUTB ( port , 0x08 ) ; /* halt CPU */
_OUTB ( port , 0x08 ) ;
_OUTB ( port , 0x08 ) ;
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ; /* delay */
if ( _INB ( port ) ! = 0x40 )
return 0 ;
_OUTB ( port , 0x00 ) ;
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ; /* delay */
if ( _INB ( port ) ! = 0x40 )
return 0 ;
_OUTB ( port , 0x04 ) ;
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ; /* delay */
if ( _INB ( port ) ! = 0x44 )
return 0 ;
/* Reset adapter */
_OUTB ( port , 0x08 ) ;
_OUTB ( port , 0x08 ) ;
_OUTB ( port , 0x08 ) ;
_OUTB ( port + 1 , 0xFF ) ;
return 1 ;
}
/*============================================================================
* Detect S502E adapter .
* Following tests are used to verify adapter presence :
* 1. All registers other than status ( BASE ) should read 0xFF .
* 2. After writing 0 to CPU control register ( BASE + 3 ) , status register
* ( BASE ) should read 11111000 b .
* 3. After writing 00000100 b to port BASE ( set bit 2 ) , status register
* ( BASE ) should read 11111100 b .
* Return 1 if detected o . k . or 0 if failed .
* Note : This test is destructive ! Adapter will be left in shutdown
* state after the test .
*/
static int detect_s502e ( int port )
{
int i , j ;
if ( ! get_option_index ( s502_port_options , port ) )
return 0 ;
for ( j = 1 ; j < SDLA_MAXIORANGE ; + + j ) {
if ( _INB ( port + j ) ! = 0xFF )
return 0 ;
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ; /* delay */
}
_OUTB ( port + 3 , 0 ) ; /* CPU control reg. */
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ; /* delay */
if ( _INB ( port ) ! = 0xF8 ) /* read status */
return 0 ;
_OUTB ( port , 0x04 ) ; /* set bit 2 */
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ; /* delay */
if ( _INB ( port ) ! = 0xFC ) /* verify */
return 0 ;
/* Reset adapter */
_OUTB ( port , 0 ) ;
return 1 ;
}
/*============================================================================
* Detect s503 adapter .
* Following tests are used to verify adapter presence :
* 1. All registers other than status ( BASE ) should read 0xFF .
* 2. After writing 0 to control register ( BASE ) , status register ( BASE )
* should read 11110000 b .
* 3. After writing 00000100 b ( set bit 2 ) to control register ( BASE ) ,
* status register should read 11110010 b .
* Return 1 if detected o . k . or 0 if failed .
* Note : This test is destructive ! Adapter will be left in shutdown
* state after the test .
*/
static int detect_s503 ( int port )
{
int i , j ;
if ( ! get_option_index ( s503_port_options , port ) )
return 0 ;
for ( j = 1 ; j < SDLA_MAXIORANGE ; + + j ) {
if ( _INB ( port + j ) ! = 0xFF )
return 0 ;
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ; /* delay */
}
_OUTB ( port , 0 ) ; /* reset control reg.*/
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ; /* delay */
if ( _INB ( port ) ! = 0xF0 ) /* read status */
return 0 ;
_OUTB ( port , 0x04 ) ; /* set bit 2 */
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ; /* delay */
if ( _INB ( port ) ! = 0xF2 ) /* verify */
return 0 ;
/* Reset adapter */
_OUTB ( port , 0 ) ;
return 1 ;
}
/*============================================================================
* Detect s507 adapter .
* Following tests are used to detect s507 adapter :
* 1. All ports should read the same value .
* 2. After writing 0x00 to control register , status register should read
* ? 011000 ? b .
* 3. After writing 0x01 to control register , status register should read
* ? 011001 ? b .
* Return 1 if detected o . k . or 0 if failed .
* Note : This test is destructive ! Adapter will be left in shutdown
* state after the test .
*/
static int detect_s507 ( int port )
{
int tmp , i , j ;
if ( ! get_option_index ( s508_port_options , port ) )
return 0 ;
tmp = _INB ( port ) ;
for ( j = 1 ; j < S507_IORANGE ; + + j ) {
if ( _INB ( port + j ) ! = tmp )
return 0 ;
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ; /* delay */
}
_OUTB ( port , 0x00 ) ;
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ; /* delay */
if ( ( _INB ( port ) & 0x7E ) ! = 0x30 )
return 0 ;
_OUTB ( port , 0x01 ) ;
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ; /* delay */
if ( ( _INB ( port ) & 0x7E ) ! = 0x32 )
return 0 ;
/* Reset adapter */
_OUTB ( port , 0x00 ) ;
return 1 ;
}
/*============================================================================
* Detect s508 adapter .
* Following tests are used to detect s508 adapter :
* 1. After writing 0x00 to control register , status register should read
* ? ? 000000 b .
* 2. After writing 0x10 to control register , status register should read
* ? ? 010000 b
* Return 1 if detected o . k . or 0 if failed .
* Note : This test is destructive ! Adapter will be left in shutdown
* state after the test .
*/
static int detect_s508 ( int port )
{
int i ;
if ( ! get_option_index ( s508_port_options , port ) )
return 0 ;
_OUTB ( port , 0x00 ) ;
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ; /* delay */
if ( ( _INB ( port + 1 ) & 0x3F ) ! = 0x00 )
return 0 ;
_OUTB ( port , 0x10 ) ;
for ( i = 0 ; i < SDLA_IODELAY ; + + i ) ; /* delay */
if ( ( _INB ( port + 1 ) & 0x3F ) ! = 0x10 )
return 0 ;
/* Reset adapter */
_OUTB ( port , 0x00 ) ;
return 1 ;
}
/*============================================================================
* Detect s514 PCI adapter .
* Return 1 if detected o . k . or 0 if failed .
* Note : This test is destructive ! Adapter will be left in shutdown
* state after the test .
*/
static int detect_s514 ( sdlahw_t * hw )
{
unsigned char CPU_no , slot_no , auto_slot_cfg ;
int number_S514_cards = 0 ;
u32 S514_mem_base_addr = 0 ;
u32 ut_u32 ;
struct pci_dev * pci_dev ;
# ifndef CONFIG_PCI
printk ( KERN_INFO " %s: Linux not compiled for PCI usage! \n " , modname ) ;
return 0 ;
# endif
/*
The ' setup ( ) ' procedure in ' sdlamain . c ' passes the CPU number and the
slot number defined in ' router . conf ' via the ' port ' definition .
*/
CPU_no = hw - > S514_cpu_no [ 0 ] ;
slot_no = hw - > S514_slot_no ;
auto_slot_cfg = hw - > auto_pci_cfg ;
if ( auto_slot_cfg ) {
printk ( KERN_INFO " %s: srch... S514 card, CPU %c, Slot=Auto \n " ,
modname , CPU_no ) ;
} else {
printk ( KERN_INFO " %s: srch... S514 card, CPU %c, Slot #%d \n " ,
modname , CPU_no , slot_no ) ;
}
/* check to see that CPU A or B has been selected in 'router.conf' */
switch ( CPU_no ) {
case S514_CPU_A :
case S514_CPU_B :
break ;
default :
printk ( KERN_INFO " %s: S514 CPU definition invalid. \n " ,
modname ) ;
printk ( KERN_INFO " Must be 'A' or 'B' \n " ) ;
return 0 ;
}
number_S514_cards = find_s514_adapter ( hw , 0 ) ;
if ( ! number_S514_cards )
return 0 ;
/* we are using a single S514 adapter with a slot of 0 so re-read the */
/* location of this adapter */
if ( ( number_S514_cards = = 1 ) & & auto_slot_cfg ) {
number_S514_cards = find_s514_adapter ( hw , 1 ) ;
if ( ! number_S514_cards ) {
printk ( KERN_INFO " %s: Error finding PCI card \n " ,
modname ) ;
return 0 ;
}
}
pci_dev = hw - > pci_dev ;
/* read the physical memory base address */
S514_mem_base_addr = ( CPU_no = = S514_CPU_A ) ?
( pci_dev - > resource [ 1 ] . start ) :
( pci_dev - > resource [ 2 ] . start ) ;
printk ( KERN_INFO " %s: S514 PCI memory at 0x%X \n " ,
modname , S514_mem_base_addr ) ;
if ( ! S514_mem_base_addr ) {
if ( CPU_no = = S514_CPU_B )
printk ( KERN_INFO " %s: CPU #B not present on the card \n " , modname ) ;
else
printk ( KERN_INFO " %s: No PCI memory allocated to card \n " , modname ) ;
return 0 ;
}
/* enable the PCI memory */
pci_read_config_dword ( pci_dev ,
( CPU_no = = S514_CPU_A ) ? PCI_MAP0_DWORD : PCI_MAP1_DWORD ,
& ut_u32 ) ;
pci_write_config_dword ( pci_dev ,
( CPU_no = = S514_CPU_A ) ? PCI_MAP0_DWORD : PCI_MAP1_DWORD ,
( ut_u32 | PCI_MEMORY_ENABLE ) ) ;
/* check the IRQ allocated and enable IRQ usage */
if ( ! ( hw - > irq = pci_dev - > irq ) ) {
printk ( KERN_INFO " %s: IRQ not allocated to S514 adapter \n " ,
modname ) ;
return 0 ;
}
/* BUG FIX : Mar 6 2000
* On a initial loading of the card , we must check
* and clear PCI interrupt bits , due to a reset
* problem on some other boards . i . e . An interrupt
* might be pending , even after system bootup ,
* in which case , when starting wanrouter the machine
* would crash .
*/
if ( init_pci_slot ( hw ) )
return 0 ;
pci_read_config_dword ( pci_dev , PCI_INT_CONFIG , & ut_u32 ) ;
ut_u32 | = ( CPU_no = = S514_CPU_A ) ?
PCI_ENABLE_IRQ_CPU_A : PCI_ENABLE_IRQ_CPU_B ;
pci_write_config_dword ( pci_dev , PCI_INT_CONFIG , ut_u32 ) ;
printk ( KERN_INFO " %s: IRQ %d allocated to the S514 card \n " ,
modname , hw - > irq ) ;
/* map the physical PCI memory to virtual memory */
2005-11-15 00:09:14 -08:00
hw - > dpmbase = ioremap ( ( unsigned long ) S514_mem_base_addr ,
2005-04-16 15:20:36 -07:00
( unsigned long ) MAX_SIZEOF_S514_MEMORY ) ;
/* map the physical control register memory to virtual memory */
hw - > vector = ( unsigned long ) ioremap (
( unsigned long ) ( S514_mem_base_addr + S514_CTRL_REG_BYTE ) ,
( unsigned long ) 16 ) ;
if ( ! hw - > dpmbase | | ! hw - > vector ) {
printk ( KERN_INFO " %s: PCI virtual memory allocation failed \n " ,
modname ) ;
return 0 ;
}
/* halt the adapter */
writeb ( S514_CPU_HALT , hw - > vector ) ;
return 1 ;
}
/*============================================================================
* Find the S514 PCI adapter in the PCI bus .
* Return the number of S514 adapters found ( 0 if no adapter found ) .
*/
static int find_s514_adapter ( sdlahw_t * hw , char find_first_S514_card )
{
unsigned char slot_no ;
int number_S514_cards = 0 ;
char S514_found_in_slot = 0 ;
u16 PCI_subsys_vendor ;
struct pci_dev * pci_dev = NULL ;
slot_no = hw - > S514_slot_no ;
while ( ( pci_dev = pci_find_device ( V3_VENDOR_ID , V3_DEVICE_ID , pci_dev ) )
! = NULL ) {
pci_read_config_word ( pci_dev , PCI_SUBSYS_VENDOR_WORD ,
& PCI_subsys_vendor ) ;
if ( PCI_subsys_vendor ! = SANGOMA_SUBSYS_VENDOR )
continue ;
hw - > pci_dev = pci_dev ;
if ( find_first_S514_card )
return ( 1 ) ;
number_S514_cards + + ;
printk ( KERN_INFO
" %s: S514 card found, slot #%d (devfn 0x%X) \n " ,
modname , ( ( pci_dev - > devfn > > 3 ) & PCI_DEV_SLOT_MASK ) ,
pci_dev - > devfn ) ;
if ( hw - > auto_pci_cfg ) {
hw - > S514_slot_no = ( ( pci_dev - > devfn > > 3 ) & PCI_DEV_SLOT_MASK ) ;
slot_no = hw - > S514_slot_no ;
} else if ( ( ( pci_dev - > devfn > > 3 ) & PCI_DEV_SLOT_MASK ) = = slot_no ) {
S514_found_in_slot = 1 ;
break ;
}
}
/* if no S514 adapter has been found, then exit */
if ( ! number_S514_cards ) {
printk ( KERN_INFO " %s: Error, no S514 adapters found \n " , modname ) ;
return 0 ;
}
/* if more than one S514 card has been found, then the user must have */ /* defined a slot number so that the correct adapter is used */
else if ( ( number_S514_cards > 1 ) & & hw - > auto_pci_cfg ) {
printk ( KERN_INFO " %s: Error, PCI Slot autodetect Failed! \n "
" %s: More than one S514 adapter found. \n "
" %s: Disable the Autodetect feature and supply \n "
" %s: the PCISLOT numbers for each card. \n " ,
modname , modname , modname , modname ) ;
return 0 ;
}
/* if the user has specified a slot number and the S514 adapter has */
/* not been found in that slot, then exit */
else if ( ! hw - > auto_pci_cfg & & ! S514_found_in_slot ) {
printk ( KERN_INFO
" %s: Error, S514 card not found in specified slot #%d \n " ,
modname , slot_no ) ;
return 0 ;
}
return ( number_S514_cards ) ;
}
/******* Miscellaneous ******************************************************/
/*============================================================================
* Calibrate SDLA memory access delay .
* Count number of idle loops made within 1 second and then calculate the
* number of loops that should be made to achive desired delay .
*/
static int calibrate_delay ( int mks )
{
unsigned int delay ;
unsigned long stop ;
for ( delay = 0 , stop = SYSTEM_TICK + HZ ; SYSTEM_TICK < stop ; + + delay ) ;
return ( delay / ( 1000000L / mks ) + 1 ) ;
}
/*============================================================================
* Get option ' s index into the options list .
* Return option ' s index ( 1 . . N ) or zero if option is invalid .
*/
static int get_option_index ( unsigned * optlist , unsigned optval )
{
int i ;
for ( i = 1 ; i < = optlist [ 0 ] ; + + i )
if ( optlist [ i ] = = optval )
return i ;
return 0 ;
}
/*============================================================================
* Check memory region to see if it ' s available .
* Return : 0 ok .
*/
static unsigned check_memregion ( void * ptr , unsigned len )
{
volatile unsigned char * p = ptr ;
for ( ; len & & ( readb ( p ) = = 0xFF ) ; - - len , + + p ) {
writeb ( 0 , p ) ; /* attempt to write 0 */
if ( readb ( p ) ! = 0xFF ) { /* still has to read 0xFF */
writeb ( 0xFF , p ) ; /* restore original value */
break ; /* not good */
}
}
return len ;
}
/*============================================================================
* Test memory region .
* Return : size of the region that passed the test .
* Note : Region size must be multiple of 2 !
*/
static unsigned test_memregion ( void * ptr , unsigned len )
{
volatile unsigned short * w_ptr ;
unsigned len_w = len > > 1 ; /* region len in words */
unsigned i ;
for ( i = 0 , w_ptr = ptr ; i < len_w ; + + i , + + w_ptr )
writew ( 0xAA55 , w_ptr ) ;
for ( i = 0 , w_ptr = ptr ; i < len_w ; + + i , + + w_ptr )
if ( readw ( w_ptr ) ! = 0xAA55 ) {
len_w = i ;
break ;
}
for ( i = 0 , w_ptr = ptr ; i < len_w ; + + i , + + w_ptr )
writew ( 0x55AA , w_ptr ) ;
for ( i = 0 , w_ptr = ptr ; i < len_w ; + + i , + + w_ptr )
if ( readw ( w_ptr ) ! = 0x55AA ) {
len_w = i ;
break ;
}
for ( i = 0 , w_ptr = ptr ; i < len_w ; + + i , + + w_ptr )
writew ( 0 , w_ptr ) ;
return len_w < < 1 ;
}
/*============================================================================
* Calculate 16 - bit CRC using CCITT polynomial .
*/
static unsigned short checksum ( unsigned char * buf , unsigned len )
{
unsigned short crc = 0 ;
unsigned mask , flag ;
for ( ; len ; - - len , + + buf ) {
for ( mask = 0x80 ; mask ; mask > > = 1 ) {
flag = ( crc & 0x8000 ) ;
crc < < = 1 ;
crc | = ( ( * buf & mask ) ? 1 : 0 ) ;
if ( flag ) crc ^ = 0x1021 ;
}
}
return crc ;
}
static int init_pci_slot ( sdlahw_t * hw )
{
u32 int_status ;
int volatile found = 0 ;
int i = 0 ;
/* Check if this is a very first load for a specific
* pci card . If it is , clear the interrput bits , and
* set the flag indicating that this card was initialized .
*/
for ( i = 0 ; ( i < MAX_S514_CARDS ) & & ! found ; i + + ) {
if ( pci_slot_ar [ i ] = = hw - > S514_slot_no ) {
found = 1 ;
break ;
}
if ( pci_slot_ar [ i ] = = 0xFF ) {
break ;
}
}
if ( ! found ) {
read_S514_int_stat ( hw , & int_status ) ;
S514_intack ( hw , int_status ) ;
if ( i = = MAX_S514_CARDS ) {
printk ( KERN_INFO " %s: Critical Error !!! \n " , modname ) ;
printk ( KERN_INFO
" %s: Number of Sangoma PCI cards exceeded maximum limit. \n " ,
modname ) ;
printk ( KERN_INFO " Please contact Sangoma Technologies \n " ) ;
return 1 ;
}
pci_slot_ar [ i ] = hw - > S514_slot_no ;
}
return 0 ;
}
static int pci_probe ( sdlahw_t * hw )
{
unsigned char slot_no ;
int number_S514_cards = 0 ;
u16 PCI_subsys_vendor ;
u16 PCI_card_type ;
struct pci_dev * pci_dev = NULL ;
struct pci_bus * bus = NULL ;
slot_no = 0 ;
while ( ( pci_dev = pci_find_device ( V3_VENDOR_ID , V3_DEVICE_ID , pci_dev ) )
! = NULL ) {
pci_read_config_word ( pci_dev , PCI_SUBSYS_VENDOR_WORD ,
& PCI_subsys_vendor ) ;
if ( PCI_subsys_vendor ! = SANGOMA_SUBSYS_VENDOR )
continue ;
pci_read_config_word ( pci_dev , PCI_CARD_TYPE ,
& PCI_card_type ) ;
bus = pci_dev - > bus ;
/* A dual cpu card can support up to 4 physical connections,
* where a single cpu card can support up to 2 physical
* connections . The FT1 card can only support a single
* connection , however we cannot distinguish between a Single
* CPU card and an FT1 card . */
if ( PCI_card_type = = S514_DUAL_CPU ) {
number_S514_cards + = 4 ;
printk ( KERN_INFO
" wanpipe: S514-PCI card found, cpu(s) 2, bus #%d, slot #%d, irq #%d \n " ,
bus - > number , ( ( pci_dev - > devfn > > 3 ) & PCI_DEV_SLOT_MASK ) ,
pci_dev - > irq ) ;
} else {
number_S514_cards + = 2 ;
printk ( KERN_INFO
" wanpipe: S514-PCI card found, cpu(s) 1, bus #%d, slot #%d, irq #%d \n " ,
bus - > number , ( ( pci_dev - > devfn > > 3 ) & PCI_DEV_SLOT_MASK ) ,
pci_dev - > irq ) ;
}
}
return number_S514_cards ;
}
EXPORT_SYMBOL ( wanpipe_hw_probe ) ;
unsigned wanpipe_hw_probe ( void )
{
sdlahw_t hw ;
unsigned * opt = s508_port_options ;
unsigned cardno = 0 ;
int i ;
memset ( & hw , 0 , sizeof ( hw ) ) ;
for ( i = 1 ; i < = opt [ 0 ] ; i + + ) {
if ( detect_s508 ( opt [ i ] ) ) {
/* S508 card can support up to two physical links */
cardno + = 2 ;
printk ( KERN_INFO " wanpipe: S508-ISA card found, port 0x%x \n " , opt [ i ] ) ;
}
}
# ifdef CONFIG_PCI
hw . S514_slot_no = 0 ;
cardno + = pci_probe ( & hw ) ;
# else
printk ( KERN_INFO " wanpipe: Warning, Kernel not compiled for PCI support! \n " ) ;
printk ( KERN_INFO " wanpipe: PCI Hardware Probe Failed! \n " ) ;
# endif
return cardno ;
}
/****** End *****************************************************************/