2005-04-16 15:20:36 -07:00
/* $Id: teles3.c,v 2.19.2.4 2004/01/13 23:48:39 keil Exp $
*
* low level stuff for Teles 16.3 & PNP isdn cards
*
* Author Karsten Keil
* Copyright by Karsten Keil < keil @ isdn4linux . de >
*
* This software may be used and distributed according to the terms
* of the GNU General Public License , incorporated herein by reference .
*
* Thanks to Jan den Ouden
* Fritz Elfert
* Beat Doebeli
*
*/
# include <linux/init.h>
# include <linux/isapnp.h>
# include "hisax.h"
# include "isac.h"
# include "hscx.h"
# include "isdnl1.h"
extern const char * CardType [ ] ;
2005-06-25 14:59:18 -07:00
static const char * teles3_revision = " $Revision: 2.19.2.4 $ " ;
2005-04-16 15:20:36 -07:00
# define byteout(addr,val) outb(val,addr)
# define bytein(addr) inb(addr)
static inline u_char
readreg ( unsigned int adr , u_char off )
{
return ( bytein ( adr + off ) ) ;
}
static inline void
writereg ( unsigned int adr , u_char off , u_char data )
{
byteout ( adr + off , data ) ;
}
static inline void
read_fifo ( unsigned int adr , u_char * data , int size )
{
insb ( adr , data , size ) ;
}
static void
write_fifo ( unsigned int adr , u_char * data , int size )
{
outsb ( adr , data , size ) ;
}
/* Interface functions */
static u_char
ReadISAC ( struct IsdnCardState * cs , u_char offset )
{
return ( readreg ( cs - > hw . teles3 . isac , offset ) ) ;
}
static void
WriteISAC ( struct IsdnCardState * cs , u_char offset , u_char value )
{
writereg ( cs - > hw . teles3 . isac , offset , value ) ;
}
static void
ReadISACfifo ( struct IsdnCardState * cs , u_char * data , int size )
{
read_fifo ( cs - > hw . teles3 . isacfifo , data , size ) ;
}
static void
WriteISACfifo ( struct IsdnCardState * cs , u_char * data , int size )
{
write_fifo ( cs - > hw . teles3 . isacfifo , data , size ) ;
}
static u_char
ReadHSCX ( struct IsdnCardState * cs , int hscx , u_char offset )
{
return ( readreg ( cs - > hw . teles3 . hscx [ hscx ] , offset ) ) ;
}
static void
WriteHSCX ( struct IsdnCardState * cs , int hscx , u_char offset , u_char value )
{
writereg ( cs - > hw . teles3 . hscx [ hscx ] , offset , value ) ;
}
/*
* fast interrupt HSCX stuff goes here
*/
# define READHSCX(cs, nr, reg) readreg(cs->hw.teles3.hscx[nr], reg)
# define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.teles3.hscx[nr], reg, data)
# define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.teles3.hscxfifo[nr], ptr, cnt)
# define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.teles3.hscxfifo[nr], ptr, cnt)
# include "hscx_irq.c"
static irqreturn_t
teles3_interrupt ( int intno , void * dev_id , struct pt_regs * regs )
{
# define MAXCOUNT 5
struct IsdnCardState * cs = dev_id ;
u_char val ;
u_long flags ;
int count = 0 ;
spin_lock_irqsave ( & cs - > lock , flags ) ;
val = readreg ( cs - > hw . teles3 . hscx [ 1 ] , HSCX_ISTA ) ;
Start_HSCX :
if ( val )
hscx_int_main ( cs , val ) ;
val = readreg ( cs - > hw . teles3 . isac , ISAC_ISTA ) ;
Start_ISAC :
if ( val )
isac_interrupt ( cs , val ) ;
count + + ;
val = readreg ( cs - > hw . teles3 . hscx [ 1 ] , HSCX_ISTA ) ;
if ( val & & count < MAXCOUNT ) {
if ( cs - > debug & L1_DEB_HSCX )
debugl1 ( cs , " HSCX IntStat after IntRoutine " ) ;
goto Start_HSCX ;
}
val = readreg ( cs - > hw . teles3 . isac , ISAC_ISTA ) ;
if ( val & & count < MAXCOUNT ) {
if ( cs - > debug & L1_DEB_ISAC )
debugl1 ( cs , " ISAC IntStat after IntRoutine " ) ;
goto Start_ISAC ;
}
if ( count > = MAXCOUNT )
printk ( KERN_WARNING " Teles3: more than %d loops in teles3_interrupt \n " , count ) ;
writereg ( cs - > hw . teles3 . hscx [ 0 ] , HSCX_MASK , 0xFF ) ;
writereg ( cs - > hw . teles3 . hscx [ 1 ] , HSCX_MASK , 0xFF ) ;
writereg ( cs - > hw . teles3 . isac , ISAC_MASK , 0xFF ) ;
writereg ( cs - > hw . teles3 . isac , ISAC_MASK , 0x0 ) ;
writereg ( cs - > hw . teles3 . hscx [ 0 ] , HSCX_MASK , 0x0 ) ;
writereg ( cs - > hw . teles3 . hscx [ 1 ] , HSCX_MASK , 0x0 ) ;
spin_unlock_irqrestore ( & cs - > lock , flags ) ;
return IRQ_HANDLED ;
}
2005-07-27 11:46:09 -07:00
static inline void
2005-04-16 15:20:36 -07:00
release_ioregs ( struct IsdnCardState * cs , int mask )
{
if ( mask & 1 )
release_region ( cs - > hw . teles3 . isac + 32 , 32 ) ;
if ( mask & 2 )
release_region ( cs - > hw . teles3 . hscx [ 0 ] + 32 , 32 ) ;
if ( mask & 4 )
release_region ( cs - > hw . teles3 . hscx [ 1 ] + 32 , 32 ) ;
}
2005-06-25 14:59:18 -07:00
static void
2005-04-16 15:20:36 -07:00
release_io_teles3 ( struct IsdnCardState * cs )
{
if ( cs - > typ = = ISDN_CTYPE_TELESPCMCIA ) {
release_region ( cs - > hw . teles3 . hscx [ 1 ] , 96 ) ;
} else {
if ( cs - > hw . teles3 . cfg_reg ) {
if ( cs - > typ = = ISDN_CTYPE_COMPAQ_ISA ) {
release_region ( cs - > hw . teles3 . cfg_reg , 1 ) ;
} else {
release_region ( cs - > hw . teles3 . cfg_reg , 8 ) ;
}
}
release_ioregs ( cs , 0x7 ) ;
}
}
static int
reset_teles3 ( struct IsdnCardState * cs )
{
u_char irqcfg ;
if ( cs - > typ ! = ISDN_CTYPE_TELESPCMCIA ) {
if ( ( cs - > hw . teles3 . cfg_reg ) & & ( cs - > typ ! = ISDN_CTYPE_COMPAQ_ISA ) ) {
switch ( cs - > irq ) {
case 2 :
case 9 :
irqcfg = 0x00 ;
break ;
case 3 :
irqcfg = 0x02 ;
break ;
case 4 :
irqcfg = 0x04 ;
break ;
case 5 :
irqcfg = 0x06 ;
break ;
case 10 :
irqcfg = 0x08 ;
break ;
case 11 :
irqcfg = 0x0A ;
break ;
case 12 :
irqcfg = 0x0C ;
break ;
case 15 :
irqcfg = 0x0E ;
break ;
default :
return ( 1 ) ;
}
byteout ( cs - > hw . teles3 . cfg_reg + 4 , irqcfg ) ;
HZDELAY ( HZ / 10 + 1 ) ;
byteout ( cs - > hw . teles3 . cfg_reg + 4 , irqcfg | 1 ) ;
HZDELAY ( HZ / 10 + 1 ) ;
} else if ( cs - > typ = = ISDN_CTYPE_COMPAQ_ISA ) {
byteout ( cs - > hw . teles3 . cfg_reg , 0xff ) ;
HZDELAY ( 2 ) ;
byteout ( cs - > hw . teles3 . cfg_reg , 0x00 ) ;
HZDELAY ( 2 ) ;
} else {
/* Reset off for 16.3 PnP , thanks to Georg Acher */
byteout ( cs - > hw . teles3 . isac + 0x3c , 0 ) ;
HZDELAY ( 2 ) ;
byteout ( cs - > hw . teles3 . isac + 0x3c , 1 ) ;
HZDELAY ( 2 ) ;
}
}
return ( 0 ) ;
}
static int
Teles_card_msg ( struct IsdnCardState * cs , int mt , void * arg )
{
u_long flags ;
switch ( mt ) {
case CARD_RESET :
spin_lock_irqsave ( & cs - > lock , flags ) ;
reset_teles3 ( cs ) ;
spin_unlock_irqrestore ( & cs - > lock , flags ) ;
return ( 0 ) ;
case CARD_RELEASE :
release_io_teles3 ( cs ) ;
return ( 0 ) ;
case CARD_INIT :
spin_lock_irqsave ( & cs - > lock , flags ) ;
inithscxisac ( cs , 3 ) ;
spin_unlock_irqrestore ( & cs - > lock , flags ) ;
return ( 0 ) ;
case CARD_TEST :
return ( 0 ) ;
}
return ( 0 ) ;
}
# ifdef __ISAPNP__
static struct isapnp_device_id teles_ids [ ] __devinitdata = {
{ ISAPNP_VENDOR ( ' T ' , ' A ' , ' G ' ) , ISAPNP_FUNCTION ( 0x2110 ) ,
ISAPNP_VENDOR ( ' T ' , ' A ' , ' G ' ) , ISAPNP_FUNCTION ( 0x2110 ) ,
( unsigned long ) " Teles 16.3 PnP " } ,
{ ISAPNP_VENDOR ( ' C ' , ' T ' , ' X ' ) , ISAPNP_FUNCTION ( 0x0 ) ,
ISAPNP_VENDOR ( ' C ' , ' T ' , ' X ' ) , ISAPNP_FUNCTION ( 0x0 ) ,
( unsigned long ) " Creatix 16.3 PnP " } ,
{ ISAPNP_VENDOR ( ' C ' , ' P ' , ' Q ' ) , ISAPNP_FUNCTION ( 0x1002 ) ,
ISAPNP_VENDOR ( ' C ' , ' P ' , ' Q ' ) , ISAPNP_FUNCTION ( 0x1002 ) ,
( unsigned long ) " Compaq ISDN S0 " } ,
{ 0 , }
} ;
static struct isapnp_device_id * ipid __devinitdata = & teles_ids [ 0 ] ;
static struct pnp_card * pnp_c __devinitdata = NULL ;
# endif
int __devinit
setup_teles3 ( struct IsdnCard * card )
{
u_char val ;
struct IsdnCardState * cs = card - > cs ;
char tmp [ 64 ] ;
strcpy ( tmp , teles3_revision ) ;
printk ( KERN_INFO " HiSax: Teles IO driver Rev. %s \n " , HiSax_getrev ( tmp ) ) ;
if ( ( cs - > typ ! = ISDN_CTYPE_16_3 ) & & ( cs - > typ ! = ISDN_CTYPE_PNP )
& & ( cs - > typ ! = ISDN_CTYPE_TELESPCMCIA ) & & ( cs - > typ ! = ISDN_CTYPE_COMPAQ_ISA ) )
return ( 0 ) ;
# ifdef __ISAPNP__
if ( ! card - > para [ 1 ] & & isapnp_present ( ) ) {
struct pnp_dev * pnp_d ;
while ( ipid - > card_vendor ) {
if ( ( pnp_c = pnp_find_card ( ipid - > card_vendor ,
ipid - > card_device , pnp_c ) ) ) {
pnp_d = NULL ;
if ( ( pnp_d = pnp_find_dev ( pnp_c ,
ipid - > vendor , ipid - > function , pnp_d ) ) ) {
int err ;
printk ( KERN_INFO " HiSax: %s detected \n " ,
( char * ) ipid - > driver_data ) ;
pnp_disable_dev ( pnp_d ) ;
err = pnp_activate_dev ( pnp_d ) ;
if ( err < 0 ) {
printk ( KERN_WARNING " %s: pnp_activate_dev ret(%d) \n " ,
__FUNCTION__ , err ) ;
return ( 0 ) ;
}
card - > para [ 3 ] = pnp_port_start ( pnp_d , 2 ) ;
card - > para [ 2 ] = pnp_port_start ( pnp_d , 1 ) ;
card - > para [ 1 ] = pnp_port_start ( pnp_d , 0 ) ;
card - > para [ 0 ] = pnp_irq ( pnp_d , 0 ) ;
if ( ! card - > para [ 0 ] | | ! card - > para [ 1 ] | | ! card - > para [ 2 ] ) {
printk ( KERN_ERR " Teles PnP:some resources are missing %ld/%lx/%lx \n " ,
card - > para [ 0 ] , card - > para [ 1 ] , card - > para [ 2 ] ) ;
pnp_disable_dev ( pnp_d ) ;
return ( 0 ) ;
}
break ;
} else {
printk ( KERN_ERR " Teles PnP: PnP error card found, no device \n " ) ;
}
}
ipid + + ;
pnp_c = NULL ;
}
if ( ! ipid - > card_vendor ) {
printk ( KERN_INFO " Teles PnP: no ISAPnP card found \n " ) ;
return ( 0 ) ;
}
}
# endif
if ( cs - > typ = = ISDN_CTYPE_16_3 ) {
cs - > hw . teles3 . cfg_reg = card - > para [ 1 ] ;
switch ( cs - > hw . teles3 . cfg_reg ) {
case 0x180 :
case 0x280 :
case 0x380 :
cs - > hw . teles3 . cfg_reg | = 0xc00 ;
break ;
}
cs - > hw . teles3 . isac = cs - > hw . teles3 . cfg_reg - 0x420 ;
cs - > hw . teles3 . hscx [ 0 ] = cs - > hw . teles3 . cfg_reg - 0xc20 ;
cs - > hw . teles3 . hscx [ 1 ] = cs - > hw . teles3 . cfg_reg - 0x820 ;
} else if ( cs - > typ = = ISDN_CTYPE_TELESPCMCIA ) {
cs - > hw . teles3 . cfg_reg = 0 ;
cs - > hw . teles3 . hscx [ 0 ] = card - > para [ 1 ] - 0x20 ;
cs - > hw . teles3 . hscx [ 1 ] = card - > para [ 1 ] ;
cs - > hw . teles3 . isac = card - > para [ 1 ] + 0x20 ;
} else if ( cs - > typ = = ISDN_CTYPE_COMPAQ_ISA ) {
cs - > hw . teles3 . cfg_reg = card - > para [ 3 ] ;
cs - > hw . teles3 . isac = card - > para [ 2 ] - 32 ;
cs - > hw . teles3 . hscx [ 0 ] = card - > para [ 1 ] - 32 ;
cs - > hw . teles3 . hscx [ 1 ] = card - > para [ 1 ] ;
} else { /* PNP */
cs - > hw . teles3 . cfg_reg = 0 ;
cs - > hw . teles3 . isac = card - > para [ 1 ] - 32 ;
cs - > hw . teles3 . hscx [ 0 ] = card - > para [ 2 ] - 32 ;
cs - > hw . teles3 . hscx [ 1 ] = card - > para [ 2 ] ;
}
cs - > irq = card - > para [ 0 ] ;
cs - > hw . teles3 . isacfifo = cs - > hw . teles3 . isac + 0x3e ;
cs - > hw . teles3 . hscxfifo [ 0 ] = cs - > hw . teles3 . hscx [ 0 ] + 0x3e ;
cs - > hw . teles3 . hscxfifo [ 1 ] = cs - > hw . teles3 . hscx [ 1 ] + 0x3e ;
if ( cs - > typ = = ISDN_CTYPE_TELESPCMCIA ) {
if ( ! request_region ( cs - > hw . teles3 . hscx [ 1 ] , 96 , " HiSax Teles PCMCIA " ) ) {
printk ( KERN_WARNING
" HiSax: %s ports %x-%x already in use \n " ,
CardType [ cs - > typ ] ,
cs - > hw . teles3 . hscx [ 1 ] ,
cs - > hw . teles3 . hscx [ 1 ] + 96 ) ;
return ( 0 ) ;
}
2006-07-01 19:29:36 -07:00
cs - > irq_flags | = IRQF_SHARED ; /* cardbus can share */
2005-04-16 15:20:36 -07:00
} else {
if ( cs - > hw . teles3 . cfg_reg ) {
if ( cs - > typ = = ISDN_CTYPE_COMPAQ_ISA ) {
if ( ! request_region ( cs - > hw . teles3 . cfg_reg , 1 , " teles3 cfg " ) ) {
printk ( KERN_WARNING
" HiSax: %s config port %x already in use \n " ,
CardType [ card - > typ ] ,
cs - > hw . teles3 . cfg_reg ) ;
return ( 0 ) ;
}
} else {
if ( ! request_region ( cs - > hw . teles3 . cfg_reg , 8 , " teles3 cfg " ) ) {
printk ( KERN_WARNING
" HiSax: %s config port %x-%x already in use \n " ,
CardType [ card - > typ ] ,
cs - > hw . teles3 . cfg_reg ,
cs - > hw . teles3 . cfg_reg + 8 ) ;
return ( 0 ) ;
}
}
}
if ( ! request_region ( cs - > hw . teles3 . isac + 32 , 32 , " HiSax isac " ) ) {
printk ( KERN_WARNING
" HiSax: %s isac ports %x-%x already in use \n " ,
CardType [ cs - > typ ] ,
cs - > hw . teles3 . isac + 32 ,
cs - > hw . teles3 . isac + 64 ) ;
if ( cs - > hw . teles3 . cfg_reg ) {
if ( cs - > typ = = ISDN_CTYPE_COMPAQ_ISA ) {
release_region ( cs - > hw . teles3 . cfg_reg , 1 ) ;
} else {
release_region ( cs - > hw . teles3 . cfg_reg , 8 ) ;
}
}
return ( 0 ) ;
}
if ( ! request_region ( cs - > hw . teles3 . hscx [ 0 ] + 32 , 32 , " HiSax hscx A " ) ) {
printk ( KERN_WARNING
" HiSax: %s hscx A ports %x-%x already in use \n " ,
CardType [ cs - > typ ] ,
cs - > hw . teles3 . hscx [ 0 ] + 32 ,
cs - > hw . teles3 . hscx [ 0 ] + 64 ) ;
if ( cs - > hw . teles3 . cfg_reg ) {
if ( cs - > typ = = ISDN_CTYPE_COMPAQ_ISA ) {
release_region ( cs - > hw . teles3 . cfg_reg , 1 ) ;
} else {
release_region ( cs - > hw . teles3 . cfg_reg , 8 ) ;
}
}
release_ioregs ( cs , 1 ) ;
return ( 0 ) ;
}
if ( ! request_region ( cs - > hw . teles3 . hscx [ 1 ] + 32 , 32 , " HiSax hscx B " ) ) {
printk ( KERN_WARNING
" HiSax: %s hscx B ports %x-%x already in use \n " ,
CardType [ cs - > typ ] ,
cs - > hw . teles3 . hscx [ 1 ] + 32 ,
cs - > hw . teles3 . hscx [ 1 ] + 64 ) ;
if ( cs - > hw . teles3 . cfg_reg ) {
if ( cs - > typ = = ISDN_CTYPE_COMPAQ_ISA ) {
release_region ( cs - > hw . teles3 . cfg_reg , 1 ) ;
} else {
release_region ( cs - > hw . teles3 . cfg_reg , 8 ) ;
}
}
release_ioregs ( cs , 3 ) ;
return ( 0 ) ;
}
}
if ( ( cs - > hw . teles3 . cfg_reg ) & & ( cs - > typ ! = ISDN_CTYPE_COMPAQ_ISA ) ) {
if ( ( val = bytein ( cs - > hw . teles3 . cfg_reg + 0 ) ) ! = 0x51 ) {
printk ( KERN_WARNING " Teles: 16.3 Byte at %x is %x \n " ,
cs - > hw . teles3 . cfg_reg + 0 , val ) ;
release_io_teles3 ( cs ) ;
return ( 0 ) ;
}
if ( ( val = bytein ( cs - > hw . teles3 . cfg_reg + 1 ) ) ! = 0x93 ) {
printk ( KERN_WARNING " Teles: 16.3 Byte at %x is %x \n " ,
cs - > hw . teles3 . cfg_reg + 1 , val ) ;
release_io_teles3 ( cs ) ;
return ( 0 ) ;
}
val = bytein ( cs - > hw . teles3 . cfg_reg + 2 ) ; /* 0x1e=without AB
* 0x1f = with AB
* 0x1c 16.3 ? ? ?
* 0x39 16.3 1.1
* 0x38 16.3 1.3
* 0x46 16.3 with AB + Video ( Teles - Vision )
*/
if ( val ! = 0x46 & & val ! = 0x39 & & val ! = 0x38 & & val ! = 0x1c & & val ! = 0x1e & & val ! = 0x1f ) {
printk ( KERN_WARNING " Teles: 16.3 Byte at %x is %x \n " ,
cs - > hw . teles3 . cfg_reg + 2 , val ) ;
release_io_teles3 ( cs ) ;
return ( 0 ) ;
}
}
printk ( KERN_INFO
" HiSax: %s config irq:%d isac:0x%X cfg:0x%X \n " ,
CardType [ cs - > typ ] , cs - > irq ,
cs - > hw . teles3 . isac + 32 , cs - > hw . teles3 . cfg_reg ) ;
printk ( KERN_INFO
" HiSax: hscx A:0x%X hscx B:0x%X \n " ,
cs - > hw . teles3 . hscx [ 0 ] + 32 , cs - > hw . teles3 . hscx [ 1 ] + 32 ) ;
setup_isac ( cs ) ;
if ( reset_teles3 ( cs ) ) {
printk ( KERN_WARNING " Teles3: wrong IRQ \n " ) ;
release_io_teles3 ( cs ) ;
return ( 0 ) ;
}
cs - > readisac = & ReadISAC ;
cs - > writeisac = & WriteISAC ;
cs - > readisacfifo = & ReadISACfifo ;
cs - > writeisacfifo = & WriteISACfifo ;
cs - > BC_Read_Reg = & ReadHSCX ;
cs - > BC_Write_Reg = & WriteHSCX ;
cs - > BC_Send_Data = & hscx_fill_fifo ;
cs - > cardmsg = & Teles_card_msg ;
cs - > irq_func = & teles3_interrupt ;
ISACVersion ( cs , " Teles3: " ) ;
if ( HscxVersion ( cs , " Teles3: " ) ) {
printk ( KERN_WARNING
" Teles3: wrong HSCX versions check IO address \n " ) ;
release_io_teles3 ( cs ) ;
return ( 0 ) ;
}
return ( 1 ) ;
}