| /* $Id: diva.c,v 1.33.2.6 2004/02/11 13:21:33 keil Exp $ | 
 |  * | 
 |  * low level stuff for Eicon.Diehl Diva Family 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. | 
 |  * | 
 |  * For changes and modifications please read | 
 |  * Documentation/isdn/HiSax.cert | 
 |  * | 
 |  * Thanks to Eicon Technology for documents and information | 
 |  * | 
 |  */ | 
 |  | 
 | #include <linux/init.h> | 
 | #include "hisax.h" | 
 | #include "isac.h" | 
 | #include "hscx.h" | 
 | #include "ipac.h" | 
 | #include "ipacx.h" | 
 | #include "isdnl1.h" | 
 | #include <linux/pci.h> | 
 | #include <linux/isapnp.h> | 
 |  | 
 | static const char *Diva_revision = "$Revision: 1.33.2.6 $"; | 
 |  | 
 | #define byteout(addr, val) outb(val, addr) | 
 | #define bytein(addr) inb(addr) | 
 |  | 
 | #define DIVA_HSCX_DATA		0 | 
 | #define DIVA_HSCX_ADR		4 | 
 | #define DIVA_ISA_ISAC_DATA	2 | 
 | #define DIVA_ISA_ISAC_ADR	6 | 
 | #define DIVA_ISA_CTRL		7 | 
 | #define DIVA_IPAC_ADR		0 | 
 | #define DIVA_IPAC_DATA		1 | 
 |  | 
 | #define DIVA_PCI_ISAC_DATA	8 | 
 | #define DIVA_PCI_ISAC_ADR	0xc | 
 | #define DIVA_PCI_CTRL		0x10 | 
 |  | 
 | /* SUB Types */ | 
 | #define DIVA_ISA	1 | 
 | #define DIVA_PCI	2 | 
 | #define DIVA_IPAC_ISA	3 | 
 | #define DIVA_IPAC_PCI	4 | 
 | #define DIVA_IPACX_PCI	5 | 
 |  | 
 | /* CTRL (Read) */ | 
 | #define DIVA_IRQ_STAT	0x01 | 
 | #define DIVA_EEPROM_SDA	0x02 | 
 |  | 
 | /* CTRL (Write) */ | 
 | #define DIVA_IRQ_REQ	0x01 | 
 | #define DIVA_RESET	0x08 | 
 | #define DIVA_EEPROM_CLK	0x40 | 
 | #define DIVA_PCI_LED_A	0x10 | 
 | #define DIVA_PCI_LED_B	0x20 | 
 | #define DIVA_ISA_LED_A	0x20 | 
 | #define DIVA_ISA_LED_B	0x40 | 
 | #define DIVA_IRQ_CLR	0x80 | 
 |  | 
 | /* Siemens PITA */ | 
 | #define PITA_MISC_REG		0x1c | 
 | #ifdef __BIG_ENDIAN | 
 | #define PITA_PARA_SOFTRESET	0x00000001 | 
 | #define PITA_SER_SOFTRESET	0x00000002 | 
 | #define PITA_PARA_MPX_MODE	0x00000004 | 
 | #define PITA_INT0_ENABLE	0x00000200 | 
 | #else | 
 | #define PITA_PARA_SOFTRESET	0x01000000 | 
 | #define PITA_SER_SOFTRESET	0x02000000 | 
 | #define PITA_PARA_MPX_MODE	0x04000000 | 
 | #define PITA_INT0_ENABLE	0x00020000 | 
 | #endif | 
 | #define PITA_INT0_STATUS	0x02 | 
 |  | 
 | static inline u_char | 
 | readreg(unsigned int ale, unsigned int adr, u_char off) | 
 | { | 
 | 	register u_char ret; | 
 |  | 
 | 	byteout(ale, off); | 
 | 	ret = bytein(adr); | 
 | 	return (ret); | 
 | } | 
 |  | 
 | static inline void | 
 | readfifo(unsigned int ale, unsigned int adr, u_char off, u_char *data, int size) | 
 | { | 
 | 	byteout(ale, off); | 
 | 	insb(adr, data, size); | 
 | } | 
 |  | 
 |  | 
 | static inline void | 
 | writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) | 
 | { | 
 | 	byteout(ale, off); | 
 | 	byteout(adr, data); | 
 | } | 
 |  | 
 | static inline void | 
 | writefifo(unsigned int ale, unsigned int adr, u_char off, u_char *data, int size) | 
 | { | 
 | 	byteout(ale, off); | 
 | 	outsb(adr, data, size); | 
 | } | 
 |  | 
 | static inline u_char | 
 | memreadreg(unsigned long adr, u_char off) | 
 | { | 
 | 	return (*((unsigned char *) | 
 | 		 (((unsigned int *)adr) + off))); | 
 | } | 
 |  | 
 | static inline void | 
 | memwritereg(unsigned long adr, u_char off, u_char data) | 
 | { | 
 | 	register u_char *p; | 
 |  | 
 | 	p = (unsigned char *)(((unsigned int *)adr) + off); | 
 | 	*p = data; | 
 | } | 
 |  | 
 | /* Interface functions */ | 
 |  | 
 | static u_char | 
 | ReadISAC(struct IsdnCardState *cs, u_char offset) | 
 | { | 
 | 	return (readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset)); | 
 | } | 
 |  | 
 | static void | 
 | WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) | 
 | { | 
 | 	writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset, value); | 
 | } | 
 |  | 
 | static void | 
 | ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size) | 
 | { | 
 | 	readfifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0, data, size); | 
 | } | 
 |  | 
 | static void | 
 | WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size) | 
 | { | 
 | 	writefifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0, data, size); | 
 | } | 
 |  | 
 | static u_char | 
 | ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset) | 
 | { | 
 | 	return (readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset + 0x80)); | 
 | } | 
 |  | 
 | static void | 
 | WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value) | 
 | { | 
 | 	writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset | 0x80, value); | 
 | } | 
 |  | 
 | static void | 
 | ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char *data, int size) | 
 | { | 
 | 	readfifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0x80, data, size); | 
 | } | 
 |  | 
 | static void | 
 | WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char *data, int size) | 
 | { | 
 | 	writefifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0x80, data, size); | 
 | } | 
 |  | 
 | static u_char | 
 | ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) | 
 | { | 
 | 	return (readreg(cs->hw.diva.hscx_adr, | 
 | 		       cs->hw.diva.hscx, offset + (hscx ? 0x40 : 0))); | 
 | } | 
 |  | 
 | static void | 
 | WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) | 
 | { | 
 | 	writereg(cs->hw.diva.hscx_adr, | 
 | 		 cs->hw.diva.hscx, offset + (hscx ? 0x40 : 0), value); | 
 | } | 
 |  | 
 | static u_char | 
 | MemReadISAC_IPAC(struct IsdnCardState *cs, u_char offset) | 
 | { | 
 | 	return (memreadreg(cs->hw.diva.cfg_reg, offset + 0x80)); | 
 | } | 
 |  | 
 | static void | 
 | MemWriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value) | 
 | { | 
 | 	memwritereg(cs->hw.diva.cfg_reg, offset | 0x80, value); | 
 | } | 
 |  | 
 | static void | 
 | MemReadISACfifo_IPAC(struct IsdnCardState *cs, u_char *data, int size) | 
 | { | 
 | 	while (size--) | 
 | 		*data++ = memreadreg(cs->hw.diva.cfg_reg, 0x80); | 
 | } | 
 |  | 
 | static void | 
 | MemWriteISACfifo_IPAC(struct IsdnCardState *cs, u_char *data, int size) | 
 | { | 
 | 	while (size--) | 
 | 		memwritereg(cs->hw.diva.cfg_reg, 0x80, *data++); | 
 | } | 
 |  | 
 | static u_char | 
 | MemReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) | 
 | { | 
 | 	return (memreadreg(cs->hw.diva.cfg_reg, offset + (hscx ? 0x40 : 0))); | 
 | } | 
 |  | 
 | static void | 
 | MemWriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) | 
 | { | 
 | 	memwritereg(cs->hw.diva.cfg_reg, offset + (hscx ? 0x40 : 0), value); | 
 | } | 
 |  | 
 | /* IO-Functions for IPACX type cards */ | 
 | static u_char | 
 | MemReadISAC_IPACX(struct IsdnCardState *cs, u_char offset) | 
 | { | 
 | 	return (memreadreg(cs->hw.diva.cfg_reg, offset)); | 
 | } | 
 |  | 
 | static void | 
 | MemWriteISAC_IPACX(struct IsdnCardState *cs, u_char offset, u_char value) | 
 | { | 
 | 	memwritereg(cs->hw.diva.cfg_reg, offset, value); | 
 | } | 
 |  | 
 | static void | 
 | MemReadISACfifo_IPACX(struct IsdnCardState *cs, u_char *data, int size) | 
 | { | 
 | 	while (size--) | 
 | 		*data++ = memreadreg(cs->hw.diva.cfg_reg, 0); | 
 | } | 
 |  | 
 | static void | 
 | MemWriteISACfifo_IPACX(struct IsdnCardState *cs, u_char *data, int size) | 
 | { | 
 | 	while (size--) | 
 | 		memwritereg(cs->hw.diva.cfg_reg, 0, *data++); | 
 | } | 
 |  | 
 | static u_char | 
 | MemReadHSCX_IPACX(struct IsdnCardState *cs, int hscx, u_char offset) | 
 | { | 
 | 	return (memreadreg(cs->hw.diva.cfg_reg, offset + | 
 | 			  (hscx ? IPACX_OFF_B2 : IPACX_OFF_B1))); | 
 | } | 
 |  | 
 | static void | 
 | MemWriteHSCX_IPACX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) | 
 | { | 
 | 	memwritereg(cs->hw.diva.cfg_reg, offset + | 
 | 		    (hscx ? IPACX_OFF_B2 : IPACX_OFF_B1), value); | 
 | } | 
 |  | 
 | /* | 
 |  * fast interrupt HSCX stuff goes here | 
 |  */ | 
 |  | 
 | #define READHSCX(cs, nr, reg) readreg(cs->hw.diva.hscx_adr,		\ | 
 | 				      cs->hw.diva.hscx, reg + (nr ? 0x40 : 0)) | 
 | #define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.diva.hscx_adr,	\ | 
 | 					      cs->hw.diva.hscx, reg + (nr ? 0x40 : 0), data) | 
 |  | 
 | #define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.diva.hscx_adr,	\ | 
 | 						cs->hw.diva.hscx, (nr ? 0x40 : 0), ptr, cnt) | 
 |  | 
 | #define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.diva.hscx_adr, \ | 
 | 						  cs->hw.diva.hscx, (nr ? 0x40 : 0), ptr, cnt) | 
 |  | 
 | #include "hscx_irq.c" | 
 |  | 
 | static irqreturn_t | 
 | diva_interrupt(int intno, void *dev_id) | 
 | { | 
 | 	struct IsdnCardState *cs = dev_id; | 
 | 	u_char val, sval; | 
 | 	u_long flags; | 
 | 	int cnt = 5; | 
 |  | 
 | 	spin_lock_irqsave(&cs->lock, flags); | 
 | 	while (((sval = bytein(cs->hw.diva.ctrl)) & DIVA_IRQ_REQ) && cnt) { | 
 | 		val = readreg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_ISTA + 0x40); | 
 | 		if (val) | 
 | 			hscx_int_main(cs, val); | 
 | 		val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_ISTA); | 
 | 		if (val) | 
 | 			isac_interrupt(cs, val); | 
 | 		cnt--; | 
 | 	} | 
 | 	if (!cnt) | 
 | 		printk(KERN_WARNING "Diva: IRQ LOOP\n"); | 
 | 	writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK, 0xFF); | 
 | 	writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK + 0x40, 0xFF); | 
 | 	writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_MASK, 0xFF); | 
 | 	writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_MASK, 0x0); | 
 | 	writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK, 0x0); | 
 | 	writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK + 0x40, 0x0); | 
 | 	spin_unlock_irqrestore(&cs->lock, flags); | 
 | 	return IRQ_HANDLED; | 
 | } | 
 |  | 
 | static irqreturn_t | 
 | diva_irq_ipac_isa(int intno, void *dev_id) | 
 | { | 
 | 	struct IsdnCardState *cs = dev_id; | 
 | 	u_char ista, val; | 
 | 	u_long flags; | 
 | 	int icnt = 5; | 
 |  | 
 | 	spin_lock_irqsave(&cs->lock, flags); | 
 | 	ista = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ISTA); | 
 | Start_IPACISA: | 
 | 	if (cs->debug & L1_DEB_IPAC) | 
 | 		debugl1(cs, "IPAC ISTA %02X", ista); | 
 | 	if (ista & 0x0f) { | 
 | 		val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, HSCX_ISTA + 0x40); | 
 | 		if (ista & 0x01) | 
 | 			val |= 0x01; | 
 | 		if (ista & 0x04) | 
 | 			val |= 0x02; | 
 | 		if (ista & 0x08) | 
 | 			val |= 0x04; | 
 | 		if (val) | 
 | 			hscx_int_main(cs, val); | 
 | 	} | 
 | 	if (ista & 0x20) { | 
 | 		val = 0xfe & readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_ISTA + 0x80); | 
 | 		if (val) { | 
 | 			isac_interrupt(cs, val); | 
 | 		} | 
 | 	} | 
 | 	if (ista & 0x10) { | 
 | 		val = 0x01; | 
 | 		isac_interrupt(cs, val); | 
 | 	} | 
 | 	ista  = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ISTA); | 
 | 	if ((ista & 0x3f) && icnt) { | 
 | 		icnt--; | 
 | 		goto Start_IPACISA; | 
 | 	} | 
 | 	if (!icnt) | 
 | 		printk(KERN_WARNING "DIVA IPAC IRQ LOOP\n"); | 
 | 	writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xFF); | 
 | 	writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xC0); | 
 | 	spin_unlock_irqrestore(&cs->lock, flags); | 
 | 	return IRQ_HANDLED; | 
 | } | 
 |  | 
 | static inline void | 
 | MemwaitforCEC(struct IsdnCardState *cs, int hscx) | 
 | { | 
 | 	int to = 50; | 
 |  | 
 | 	while ((MemReadHSCX(cs, hscx, HSCX_STAR) & 0x04) && to) { | 
 | 		udelay(1); | 
 | 		to--; | 
 | 	} | 
 | 	if (!to) | 
 | 		printk(KERN_WARNING "HiSax: waitforCEC timeout\n"); | 
 | } | 
 |  | 
 |  | 
 | static inline void | 
 | MemwaitforXFW(struct IsdnCardState *cs, int hscx) | 
 | { | 
 | 	int to = 50; | 
 |  | 
 | 	while (((MemReadHSCX(cs, hscx, HSCX_STAR) & 0x44) != 0x40) && to) { | 
 | 		udelay(1); | 
 | 		to--; | 
 | 	} | 
 | 	if (!to) | 
 | 		printk(KERN_WARNING "HiSax: waitforXFW timeout\n"); | 
 | } | 
 |  | 
 | static inline void | 
 | MemWriteHSCXCMDR(struct IsdnCardState *cs, int hscx, u_char data) | 
 | { | 
 | 	MemwaitforCEC(cs, hscx); | 
 | 	MemWriteHSCX(cs, hscx, HSCX_CMDR, data); | 
 | } | 
 |  | 
 | static void | 
 | Memhscx_empty_fifo(struct BCState *bcs, int count) | 
 | { | 
 | 	u_char *ptr; | 
 | 	struct IsdnCardState *cs = bcs->cs; | 
 | 	int cnt; | 
 |  | 
 | 	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) | 
 | 		debugl1(cs, "hscx_empty_fifo"); | 
 |  | 
 | 	if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) { | 
 | 		if (cs->debug & L1_DEB_WARN) | 
 | 			debugl1(cs, "hscx_empty_fifo: incoming packet too large"); | 
 | 		MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80); | 
 | 		bcs->hw.hscx.rcvidx = 0; | 
 | 		return; | 
 | 	} | 
 | 	ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx; | 
 | 	cnt = count; | 
 | 	while (cnt--) | 
 | 		*ptr++ = memreadreg(cs->hw.diva.cfg_reg, bcs->hw.hscx.hscx ? 0x40 : 0); | 
 | 	MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80); | 
 | 	ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx; | 
 | 	bcs->hw.hscx.rcvidx += count; | 
 | 	if (cs->debug & L1_DEB_HSCX_FIFO) { | 
 | 		char *t = bcs->blog; | 
 |  | 
 | 		t += sprintf(t, "hscx_empty_fifo %c cnt %d", | 
 | 			     bcs->hw.hscx.hscx ? 'B' : 'A', count); | 
 | 		QuickHex(t, ptr, count); | 
 | 		debugl1(cs, "%s", bcs->blog); | 
 | 	} | 
 | } | 
 |  | 
 | static void | 
 | Memhscx_fill_fifo(struct BCState *bcs) | 
 | { | 
 | 	struct IsdnCardState *cs = bcs->cs; | 
 | 	int more, count, cnt; | 
 | 	int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags) ? 64 : 32; | 
 | 	u_char *ptr, *p; | 
 |  | 
 | 	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) | 
 | 		debugl1(cs, "hscx_fill_fifo"); | 
 |  | 
 | 	if (!bcs->tx_skb) | 
 | 		return; | 
 | 	if (bcs->tx_skb->len <= 0) | 
 | 		return; | 
 |  | 
 | 	more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0; | 
 | 	if (bcs->tx_skb->len > fifo_size) { | 
 | 		more = !0; | 
 | 		count = fifo_size; | 
 | 	} else | 
 | 		count = bcs->tx_skb->len; | 
 | 	cnt = count; | 
 | 	MemwaitforXFW(cs, bcs->hw.hscx.hscx); | 
 | 	p = ptr = bcs->tx_skb->data; | 
 | 	skb_pull(bcs->tx_skb, count); | 
 | 	bcs->tx_cnt -= count; | 
 | 	bcs->hw.hscx.count += count; | 
 | 	while (cnt--) | 
 | 		memwritereg(cs->hw.diva.cfg_reg, bcs->hw.hscx.hscx ? 0x40 : 0, | 
 | 			    *p++); | 
 | 	MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, more ? 0x8 : 0xa); | 
 | 	if (cs->debug & L1_DEB_HSCX_FIFO) { | 
 | 		char *t = bcs->blog; | 
 |  | 
 | 		t += sprintf(t, "hscx_fill_fifo %c cnt %d", | 
 | 			     bcs->hw.hscx.hscx ? 'B' : 'A', count); | 
 | 		QuickHex(t, ptr, count); | 
 | 		debugl1(cs, "%s", bcs->blog); | 
 | 	} | 
 | } | 
 |  | 
 | static void | 
 | Memhscx_interrupt(struct IsdnCardState *cs, u_char val, u_char hscx) | 
 | { | 
 | 	u_char r; | 
 | 	struct BCState *bcs = cs->bcs + hscx; | 
 | 	struct sk_buff *skb; | 
 | 	int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags) ? 64 : 32; | 
 | 	int count; | 
 |  | 
 | 	if (!test_bit(BC_FLG_INIT, &bcs->Flag)) | 
 | 		return; | 
 |  | 
 | 	if (val & 0x80) {	/* RME */ | 
 | 		r = MemReadHSCX(cs, hscx, HSCX_RSTA); | 
 | 		if ((r & 0xf0) != 0xa0) { | 
 | 			if (!(r & 0x80)) | 
 | 				if (cs->debug & L1_DEB_WARN) | 
 | 					debugl1(cs, "HSCX invalid frame"); | 
 | 			if ((r & 0x40) && bcs->mode) | 
 | 				if (cs->debug & L1_DEB_WARN) | 
 | 					debugl1(cs, "HSCX RDO mode=%d", | 
 | 						bcs->mode); | 
 | 			if (!(r & 0x20)) | 
 | 				if (cs->debug & L1_DEB_WARN) | 
 | 					debugl1(cs, "HSCX CRC error"); | 
 | 			MemWriteHSCXCMDR(cs, hscx, 0x80); | 
 | 		} else { | 
 | 			count = MemReadHSCX(cs, hscx, HSCX_RBCL) & ( | 
 | 				test_bit(HW_IPAC, &cs->HW_Flags) ? 0x3f : 0x1f); | 
 | 			if (count == 0) | 
 | 				count = fifo_size; | 
 | 			Memhscx_empty_fifo(bcs, count); | 
 | 			if ((count = bcs->hw.hscx.rcvidx - 1) > 0) { | 
 | 				if (cs->debug & L1_DEB_HSCX_FIFO) | 
 | 					debugl1(cs, "HX Frame %d", count); | 
 | 				if (!(skb = dev_alloc_skb(count))) | 
 | 					printk(KERN_WARNING "HSCX: receive out of memory\n"); | 
 | 				else { | 
 | 					skb_put_data(skb, bcs->hw.hscx.rcvbuf, | 
 | 						     count); | 
 | 					skb_queue_tail(&bcs->rqueue, skb); | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 		bcs->hw.hscx.rcvidx = 0; | 
 | 		schedule_event(bcs, B_RCVBUFREADY); | 
 | 	} | 
 | 	if (val & 0x40) {	/* RPF */ | 
 | 		Memhscx_empty_fifo(bcs, fifo_size); | 
 | 		if (bcs->mode == L1_MODE_TRANS) { | 
 | 			/* receive audio data */ | 
 | 			if (!(skb = dev_alloc_skb(fifo_size))) | 
 | 				printk(KERN_WARNING "HiSax: receive out of memory\n"); | 
 | 			else { | 
 | 				skb_put_data(skb, bcs->hw.hscx.rcvbuf, | 
 | 					     fifo_size); | 
 | 				skb_queue_tail(&bcs->rqueue, skb); | 
 | 			} | 
 | 			bcs->hw.hscx.rcvidx = 0; | 
 | 			schedule_event(bcs, B_RCVBUFREADY); | 
 | 		} | 
 | 	} | 
 | 	if (val & 0x10) {	/* XPR */ | 
 | 		if (bcs->tx_skb) { | 
 | 			if (bcs->tx_skb->len) { | 
 | 				Memhscx_fill_fifo(bcs); | 
 | 				return; | 
 | 			} else { | 
 | 				if (test_bit(FLG_LLI_L1WAKEUP, &bcs->st->lli.flag) && | 
 | 				    (PACKET_NOACK != bcs->tx_skb->pkt_type)) { | 
 | 					u_long	flags; | 
 | 					spin_lock_irqsave(&bcs->aclock, flags); | 
 | 					bcs->ackcnt += bcs->hw.hscx.count; | 
 | 					spin_unlock_irqrestore(&bcs->aclock, flags); | 
 | 					schedule_event(bcs, B_ACKPENDING); | 
 | 				} | 
 | 				dev_kfree_skb_irq(bcs->tx_skb); | 
 | 				bcs->hw.hscx.count = 0; | 
 | 				bcs->tx_skb = NULL; | 
 | 			} | 
 | 		} | 
 | 		if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) { | 
 | 			bcs->hw.hscx.count = 0; | 
 | 			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); | 
 | 			Memhscx_fill_fifo(bcs); | 
 | 		} else { | 
 | 			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); | 
 | 			schedule_event(bcs, B_XMTBUFREADY); | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | static inline void | 
 | Memhscx_int_main(struct IsdnCardState *cs, u_char val) | 
 | { | 
 |  | 
 | 	u_char exval; | 
 | 	struct BCState *bcs; | 
 |  | 
 | 	if (val & 0x01) { // EXB | 
 | 		bcs = cs->bcs + 1; | 
 | 		exval = MemReadHSCX(cs, 1, HSCX_EXIR); | 
 | 		if (exval & 0x40) { | 
 | 			if (bcs->mode == 1) | 
 | 				Memhscx_fill_fifo(bcs); | 
 | 			else { | 
 | 				/* Here we lost an TX interrupt, so | 
 | 				 * restart transmitting the whole frame. | 
 | 				 */ | 
 | 				if (bcs->tx_skb) { | 
 | 					skb_push(bcs->tx_skb, bcs->hw.hscx.count); | 
 | 					bcs->tx_cnt += bcs->hw.hscx.count; | 
 | 					bcs->hw.hscx.count = 0; | 
 | 				} | 
 | 				MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01); | 
 | 				if (cs->debug & L1_DEB_WARN) | 
 | 					debugl1(cs, "HSCX B EXIR %x Lost TX", exval); | 
 | 			} | 
 | 		} else if (cs->debug & L1_DEB_HSCX) | 
 | 			debugl1(cs, "HSCX B EXIR %x", exval); | 
 | 	} | 
 | 	if (val & 0xf8) { | 
 | 		if (cs->debug & L1_DEB_HSCX) | 
 | 			debugl1(cs, "HSCX B interrupt %x", val); | 
 | 		Memhscx_interrupt(cs, val, 1); | 
 | 	} | 
 | 	if (val & 0x02) {	// EXA | 
 | 		bcs = cs->bcs; | 
 | 		exval = MemReadHSCX(cs, 0, HSCX_EXIR); | 
 | 		if (exval & 0x40) { | 
 | 			if (bcs->mode == L1_MODE_TRANS) | 
 | 				Memhscx_fill_fifo(bcs); | 
 | 			else { | 
 | 				/* Here we lost an TX interrupt, so | 
 | 				 * restart transmitting the whole frame. | 
 | 				 */ | 
 | 				if (bcs->tx_skb) { | 
 | 					skb_push(bcs->tx_skb, bcs->hw.hscx.count); | 
 | 					bcs->tx_cnt += bcs->hw.hscx.count; | 
 | 					bcs->hw.hscx.count = 0; | 
 | 				} | 
 | 				MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01); | 
 | 				if (cs->debug & L1_DEB_WARN) | 
 | 					debugl1(cs, "HSCX A EXIR %x Lost TX", exval); | 
 | 			} | 
 | 		} else if (cs->debug & L1_DEB_HSCX) | 
 | 			debugl1(cs, "HSCX A EXIR %x", exval); | 
 | 	} | 
 | 	if (val & 0x04) {	// ICA | 
 | 		exval = MemReadHSCX(cs, 0, HSCX_ISTA); | 
 | 		if (cs->debug & L1_DEB_HSCX) | 
 | 			debugl1(cs, "HSCX A interrupt %x", exval); | 
 | 		Memhscx_interrupt(cs, exval, 0); | 
 | 	} | 
 | } | 
 |  | 
 | static irqreturn_t | 
 | diva_irq_ipac_pci(int intno, void *dev_id) | 
 | { | 
 | 	struct IsdnCardState *cs = dev_id; | 
 | 	u_char ista, val; | 
 | 	int icnt = 5; | 
 | 	u_char *cfg; | 
 | 	u_long flags; | 
 |  | 
 | 	spin_lock_irqsave(&cs->lock, flags); | 
 | 	cfg = (u_char *) cs->hw.diva.pci_cfg; | 
 | 	val = *cfg; | 
 | 	if (!(val & PITA_INT0_STATUS)) { | 
 | 		spin_unlock_irqrestore(&cs->lock, flags); | 
 | 		return IRQ_NONE; /* other shared IRQ */ | 
 | 	} | 
 | 	*cfg = PITA_INT0_STATUS; /* Reset pending INT0 */ | 
 | 	ista = memreadreg(cs->hw.diva.cfg_reg, IPAC_ISTA); | 
 | Start_IPACPCI: | 
 | 	if (cs->debug & L1_DEB_IPAC) | 
 | 		debugl1(cs, "IPAC ISTA %02X", ista); | 
 | 	if (ista & 0x0f) { | 
 | 		val = memreadreg(cs->hw.diva.cfg_reg, HSCX_ISTA + 0x40); | 
 | 		if (ista & 0x01) | 
 | 			val |= 0x01; | 
 | 		if (ista & 0x04) | 
 | 			val |= 0x02; | 
 | 		if (ista & 0x08) | 
 | 			val |= 0x04; | 
 | 		if (val) | 
 | 			Memhscx_int_main(cs, val); | 
 | 	} | 
 | 	if (ista & 0x20) { | 
 | 		val = 0xfe & memreadreg(cs->hw.diva.cfg_reg, ISAC_ISTA + 0x80); | 
 | 		if (val) { | 
 | 			isac_interrupt(cs, val); | 
 | 		} | 
 | 	} | 
 | 	if (ista & 0x10) { | 
 | 		val = 0x01; | 
 | 		isac_interrupt(cs, val); | 
 | 	} | 
 | 	ista  = memreadreg(cs->hw.diva.cfg_reg, IPAC_ISTA); | 
 | 	if ((ista & 0x3f) && icnt) { | 
 | 		icnt--; | 
 | 		goto Start_IPACPCI; | 
 | 	} | 
 | 	if (!icnt) | 
 | 		printk(KERN_WARNING "DIVA IPAC PCI IRQ LOOP\n"); | 
 | 	memwritereg(cs->hw.diva.cfg_reg, IPAC_MASK, 0xFF); | 
 | 	memwritereg(cs->hw.diva.cfg_reg, IPAC_MASK, 0xC0); | 
 | 	spin_unlock_irqrestore(&cs->lock, flags); | 
 | 	return IRQ_HANDLED; | 
 | } | 
 |  | 
 | static irqreturn_t | 
 | diva_irq_ipacx_pci(int intno, void *dev_id) | 
 | { | 
 | 	struct IsdnCardState *cs = dev_id; | 
 | 	u_char val; | 
 | 	u_char *cfg; | 
 | 	u_long flags; | 
 |  | 
 | 	spin_lock_irqsave(&cs->lock, flags); | 
 | 	cfg = (u_char *) cs->hw.diva.pci_cfg; | 
 | 	val = *cfg; | 
 | 	if (!(val & PITA_INT0_STATUS)) { | 
 | 		spin_unlock_irqrestore(&cs->lock, flags); | 
 | 		return IRQ_NONE; // other shared IRQ | 
 | 	} | 
 | 	interrupt_ipacx(cs);      // handler for chip | 
 | 	*cfg = PITA_INT0_STATUS;  // Reset PLX interrupt | 
 | 	spin_unlock_irqrestore(&cs->lock, flags); | 
 | 	return IRQ_HANDLED; | 
 | } | 
 |  | 
 | static void | 
 | release_io_diva(struct IsdnCardState *cs) | 
 | { | 
 | 	int bytecnt; | 
 |  | 
 | 	if ((cs->subtyp == DIVA_IPAC_PCI) || | 
 | 	    (cs->subtyp == DIVA_IPACX_PCI)) { | 
 | 		u_int *cfg = (unsigned int *)cs->hw.diva.pci_cfg; | 
 |  | 
 | 		*cfg = 0; /* disable INT0/1 */ | 
 | 		*cfg = 2; /* reset pending INT0 */ | 
 | 		if (cs->hw.diva.cfg_reg) | 
 | 			iounmap((void *)cs->hw.diva.cfg_reg); | 
 | 		if (cs->hw.diva.pci_cfg) | 
 | 			iounmap((void *)cs->hw.diva.pci_cfg); | 
 | 		return; | 
 | 	} else if (cs->subtyp != DIVA_IPAC_ISA) { | 
 | 		del_timer(&cs->hw.diva.tl); | 
 | 		if (cs->hw.diva.cfg_reg) | 
 | 			byteout(cs->hw.diva.ctrl, 0); /* LED off, Reset */ | 
 | 	} | 
 | 	if ((cs->subtyp == DIVA_ISA) || (cs->subtyp == DIVA_IPAC_ISA)) | 
 | 		bytecnt = 8; | 
 | 	else | 
 | 		bytecnt = 32; | 
 | 	if (cs->hw.diva.cfg_reg) { | 
 | 		release_region(cs->hw.diva.cfg_reg, bytecnt); | 
 | 	} | 
 | } | 
 |  | 
 | static void | 
 | iounmap_diva(struct IsdnCardState *cs) | 
 | { | 
 | 	if ((cs->subtyp == DIVA_IPAC_PCI) || (cs->subtyp == DIVA_IPACX_PCI)) { | 
 | 		if (cs->hw.diva.cfg_reg) { | 
 | 			iounmap((void *)cs->hw.diva.cfg_reg); | 
 | 			cs->hw.diva.cfg_reg = 0; | 
 | 		} | 
 | 		if (cs->hw.diva.pci_cfg) { | 
 | 			iounmap((void *)cs->hw.diva.pci_cfg); | 
 | 			cs->hw.diva.pci_cfg = 0; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return; | 
 | } | 
 |  | 
 | static void | 
 | reset_diva(struct IsdnCardState *cs) | 
 | { | 
 | 	if (cs->subtyp == DIVA_IPAC_ISA) { | 
 | 		writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x20); | 
 | 		mdelay(10); | 
 | 		writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x00); | 
 | 		mdelay(10); | 
 | 		writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xc0); | 
 | 	} else if (cs->subtyp == DIVA_IPAC_PCI) { | 
 | 		unsigned int *ireg = (unsigned int *)(cs->hw.diva.pci_cfg + | 
 | 						      PITA_MISC_REG); | 
 | 		*ireg = PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE; | 
 | 		mdelay(10); | 
 | 		*ireg = PITA_PARA_MPX_MODE; | 
 | 		mdelay(10); | 
 | 		memwritereg(cs->hw.diva.cfg_reg, IPAC_MASK, 0xc0); | 
 | 	} else if (cs->subtyp == DIVA_IPACX_PCI) { | 
 | 		unsigned int *ireg = (unsigned int *)(cs->hw.diva.pci_cfg + | 
 | 						      PITA_MISC_REG); | 
 | 		*ireg = PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE; | 
 | 		mdelay(10); | 
 | 		*ireg = PITA_PARA_MPX_MODE | PITA_SER_SOFTRESET; | 
 | 		mdelay(10); | 
 | 		MemWriteISAC_IPACX(cs, IPACX_MASK, 0xff); // Interrupts off | 
 | 	} else { /* DIVA 2.0 */ | 
 | 		cs->hw.diva.ctrl_reg = 0;        /* Reset On */ | 
 | 		byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); | 
 | 		mdelay(10); | 
 | 		cs->hw.diva.ctrl_reg |= DIVA_RESET;  /* Reset Off */ | 
 | 		byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); | 
 | 		mdelay(10); | 
 | 		if (cs->subtyp == DIVA_ISA) | 
 | 			cs->hw.diva.ctrl_reg |= DIVA_ISA_LED_A; | 
 | 		else { | 
 | 			/* Workaround PCI9060 */ | 
 | 			byteout(cs->hw.diva.pci_cfg + 0x69, 9); | 
 | 			cs->hw.diva.ctrl_reg |= DIVA_PCI_LED_A; | 
 | 		} | 
 | 		byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); | 
 | 	} | 
 | } | 
 |  | 
 | #define DIVA_ASSIGN 1 | 
 |  | 
 | static void | 
 | diva_led_handler(struct timer_list *t) | 
 | { | 
 | 	struct IsdnCardState *cs = from_timer(cs, t, hw.diva.tl); | 
 | 	int blink = 0; | 
 |  | 
 | 	if ((cs->subtyp == DIVA_IPAC_ISA) || | 
 | 	    (cs->subtyp == DIVA_IPAC_PCI) || | 
 | 	    (cs->subtyp == DIVA_IPACX_PCI)) | 
 | 		return; | 
 | 	del_timer(&cs->hw.diva.tl); | 
 | 	if (cs->hw.diva.status & DIVA_ASSIGN) | 
 | 		cs->hw.diva.ctrl_reg |= (DIVA_ISA == cs->subtyp) ? | 
 | 			DIVA_ISA_LED_A : DIVA_PCI_LED_A; | 
 | 	else { | 
 | 		cs->hw.diva.ctrl_reg ^= (DIVA_ISA == cs->subtyp) ? | 
 | 			DIVA_ISA_LED_A : DIVA_PCI_LED_A; | 
 | 		blink = 250; | 
 | 	} | 
 | 	if (cs->hw.diva.status & 0xf000) | 
 | 		cs->hw.diva.ctrl_reg |= (DIVA_ISA == cs->subtyp) ? | 
 | 			DIVA_ISA_LED_B : DIVA_PCI_LED_B; | 
 | 	else if (cs->hw.diva.status & 0x0f00) { | 
 | 		cs->hw.diva.ctrl_reg ^= (DIVA_ISA == cs->subtyp) ? | 
 | 			DIVA_ISA_LED_B : DIVA_PCI_LED_B; | 
 | 		blink = 500; | 
 | 	} else | 
 | 		cs->hw.diva.ctrl_reg &= ~((DIVA_ISA == cs->subtyp) ? | 
 | 					  DIVA_ISA_LED_B : DIVA_PCI_LED_B); | 
 |  | 
 | 	byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); | 
 | 	if (blink) { | 
 | 		cs->hw.diva.tl.expires = jiffies + ((blink * HZ) / 1000); | 
 | 		add_timer(&cs->hw.diva.tl); | 
 | 	} | 
 | } | 
 |  | 
 | static int | 
 | Diva_card_msg(struct IsdnCardState *cs, int mt, void *arg) | 
 | { | 
 | 	u_int *ireg; | 
 | 	u_long flags; | 
 |  | 
 | 	switch (mt) { | 
 | 	case CARD_RESET: | 
 | 		spin_lock_irqsave(&cs->lock, flags); | 
 | 		reset_diva(cs); | 
 | 		spin_unlock_irqrestore(&cs->lock, flags); | 
 | 		return (0); | 
 | 	case CARD_RELEASE: | 
 | 		release_io_diva(cs); | 
 | 		return (0); | 
 | 	case CARD_INIT: | 
 | 		spin_lock_irqsave(&cs->lock, flags); | 
 | 		reset_diva(cs); | 
 | 		if (cs->subtyp == DIVA_IPACX_PCI) { | 
 | 			ireg = (unsigned int *)cs->hw.diva.pci_cfg; | 
 | 			*ireg = PITA_INT0_ENABLE; | 
 | 			init_ipacx(cs, 3); // init chip and enable interrupts | 
 | 			spin_unlock_irqrestore(&cs->lock, flags); | 
 | 			return (0); | 
 | 		} | 
 | 		if (cs->subtyp == DIVA_IPAC_PCI) { | 
 | 			ireg = (unsigned int *)cs->hw.diva.pci_cfg; | 
 | 			*ireg = PITA_INT0_ENABLE; | 
 | 		} | 
 | 		inithscxisac(cs, 3); | 
 | 		spin_unlock_irqrestore(&cs->lock, flags); | 
 | 		return (0); | 
 | 	case CARD_TEST: | 
 | 		return (0); | 
 | 	case (MDL_REMOVE | REQUEST): | 
 | 		cs->hw.diva.status = 0; | 
 | 		break; | 
 | 	case (MDL_ASSIGN | REQUEST): | 
 | 		cs->hw.diva.status |= DIVA_ASSIGN; | 
 | 		break; | 
 | 	case MDL_INFO_SETUP: | 
 | 		if ((long)arg) | 
 | 			cs->hw.diva.status |=  0x0200; | 
 | 		else | 
 | 			cs->hw.diva.status |=  0x0100; | 
 | 		break; | 
 | 	case MDL_INFO_CONN: | 
 | 		if ((long)arg) | 
 | 			cs->hw.diva.status |=  0x2000; | 
 | 		else | 
 | 			cs->hw.diva.status |=  0x1000; | 
 | 		break; | 
 | 	case MDL_INFO_REL: | 
 | 		if ((long)arg) { | 
 | 			cs->hw.diva.status &=  ~0x2000; | 
 | 			cs->hw.diva.status &=  ~0x0200; | 
 | 		} else { | 
 | 			cs->hw.diva.status &=  ~0x1000; | 
 | 			cs->hw.diva.status &=  ~0x0100; | 
 | 		} | 
 | 		break; | 
 | 	} | 
 | 	if ((cs->subtyp != DIVA_IPAC_ISA) && | 
 | 	    (cs->subtyp != DIVA_IPAC_PCI) && | 
 | 	    (cs->subtyp != DIVA_IPACX_PCI)) { | 
 | 		spin_lock_irqsave(&cs->lock, flags); | 
 | 		diva_led_handler(&cs->hw.diva.tl); | 
 | 		spin_unlock_irqrestore(&cs->lock, flags); | 
 | 	} | 
 | 	return (0); | 
 | } | 
 |  | 
 | static int setup_diva_common(struct IsdnCardState *cs) | 
 | { | 
 | 	int bytecnt; | 
 | 	u_char val; | 
 |  | 
 | 	if ((cs->subtyp == DIVA_ISA) || (cs->subtyp == DIVA_IPAC_ISA)) | 
 | 		bytecnt = 8; | 
 | 	else | 
 | 		bytecnt = 32; | 
 |  | 
 | 	printk(KERN_INFO | 
 | 	       "Diva: %s card configured at %#lx IRQ %d\n", | 
 | 	       (cs->subtyp == DIVA_PCI) ? "PCI" : | 
 | 	       (cs->subtyp == DIVA_ISA) ? "ISA" : | 
 | 	       (cs->subtyp == DIVA_IPAC_ISA) ? "IPAC ISA" : | 
 | 	       (cs->subtyp == DIVA_IPAC_PCI) ? "IPAC PCI" : "IPACX PCI", | 
 | 	       cs->hw.diva.cfg_reg, cs->irq); | 
 | 	if ((cs->subtyp == DIVA_IPAC_PCI)  || | 
 | 	    (cs->subtyp == DIVA_IPACX_PCI) || | 
 | 	    (cs->subtyp == DIVA_PCI)) | 
 | 		printk(KERN_INFO "Diva: %s space at %#lx\n", | 
 | 		       (cs->subtyp == DIVA_PCI) ? "PCI" : | 
 | 		       (cs->subtyp == DIVA_IPAC_PCI) ? "IPAC PCI" : "IPACX PCI", | 
 | 		       cs->hw.diva.pci_cfg); | 
 | 	if ((cs->subtyp != DIVA_IPAC_PCI) && | 
 | 	    (cs->subtyp != DIVA_IPACX_PCI)) { | 
 | 		if (!request_region(cs->hw.diva.cfg_reg, bytecnt, "diva isdn")) { | 
 | 			printk(KERN_WARNING | 
 | 			       "HiSax: %s config port %lx-%lx already in use\n", | 
 | 			       "diva", | 
 | 			       cs->hw.diva.cfg_reg, | 
 | 			       cs->hw.diva.cfg_reg + bytecnt); | 
 | 			iounmap_diva(cs); | 
 | 			return (0); | 
 | 		} | 
 | 	} | 
 | 	cs->BC_Read_Reg  = &ReadHSCX; | 
 | 	cs->BC_Write_Reg = &WriteHSCX; | 
 | 	cs->BC_Send_Data = &hscx_fill_fifo; | 
 | 	cs->cardmsg = &Diva_card_msg; | 
 | 	setup_isac(cs); | 
 | 	if (cs->subtyp == DIVA_IPAC_ISA) { | 
 | 		cs->readisac  = &ReadISAC_IPAC; | 
 | 		cs->writeisac = &WriteISAC_IPAC; | 
 | 		cs->readisacfifo  = &ReadISACfifo_IPAC; | 
 | 		cs->writeisacfifo = &WriteISACfifo_IPAC; | 
 | 		cs->irq_func = &diva_irq_ipac_isa; | 
 | 		val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ID); | 
 | 		printk(KERN_INFO "Diva: IPAC version %x\n", val); | 
 | 	} else if (cs->subtyp == DIVA_IPAC_PCI) { | 
 | 		cs->readisac  = &MemReadISAC_IPAC; | 
 | 		cs->writeisac = &MemWriteISAC_IPAC; | 
 | 		cs->readisacfifo  = &MemReadISACfifo_IPAC; | 
 | 		cs->writeisacfifo = &MemWriteISACfifo_IPAC; | 
 | 		cs->BC_Read_Reg  = &MemReadHSCX; | 
 | 		cs->BC_Write_Reg = &MemWriteHSCX; | 
 | 		cs->BC_Send_Data = &Memhscx_fill_fifo; | 
 | 		cs->irq_func = &diva_irq_ipac_pci; | 
 | 		val = memreadreg(cs->hw.diva.cfg_reg, IPAC_ID); | 
 | 		printk(KERN_INFO "Diva: IPAC version %x\n", val); | 
 | 	} else if (cs->subtyp == DIVA_IPACX_PCI) { | 
 | 		cs->readisac  = &MemReadISAC_IPACX; | 
 | 		cs->writeisac = &MemWriteISAC_IPACX; | 
 | 		cs->readisacfifo  = &MemReadISACfifo_IPACX; | 
 | 		cs->writeisacfifo = &MemWriteISACfifo_IPACX; | 
 | 		cs->BC_Read_Reg  = &MemReadHSCX_IPACX; | 
 | 		cs->BC_Write_Reg = &MemWriteHSCX_IPACX; | 
 | 		cs->BC_Send_Data = NULL; // function located in ipacx module | 
 | 		cs->irq_func = &diva_irq_ipacx_pci; | 
 | 		printk(KERN_INFO "Diva: IPACX Design Id: %x\n", | 
 | 		       MemReadISAC_IPACX(cs, IPACX_ID) & 0x3F); | 
 | 	} else { /* DIVA 2.0 */ | 
 | 		timer_setup(&cs->hw.diva.tl, diva_led_handler, 0); | 
 | 		cs->readisac  = &ReadISAC; | 
 | 		cs->writeisac = &WriteISAC; | 
 | 		cs->readisacfifo  = &ReadISACfifo; | 
 | 		cs->writeisacfifo = &WriteISACfifo; | 
 | 		cs->irq_func = &diva_interrupt; | 
 | 		ISACVersion(cs, "Diva:"); | 
 | 		if (HscxVersion(cs, "Diva:")) { | 
 | 			printk(KERN_WARNING | 
 | 			       "Diva: wrong HSCX versions check IO address\n"); | 
 | 			release_io_diva(cs); | 
 | 			return (0); | 
 | 		} | 
 | 	} | 
 | 	return (1); | 
 | } | 
 |  | 
 | #ifdef CONFIG_ISA | 
 |  | 
 | static int setup_diva_isa(struct IsdnCard *card) | 
 | { | 
 | 	struct IsdnCardState *cs = card->cs; | 
 | 	u_char val; | 
 |  | 
 | 	if (!card->para[1]) | 
 | 		return (-1);	/* card not found; continue search */ | 
 |  | 
 | 	cs->hw.diva.ctrl_reg = 0; | 
 | 	cs->hw.diva.cfg_reg = card->para[1]; | 
 | 	val = readreg(cs->hw.diva.cfg_reg + DIVA_IPAC_ADR, | 
 | 		      cs->hw.diva.cfg_reg + DIVA_IPAC_DATA, IPAC_ID); | 
 | 	printk(KERN_INFO "Diva: IPAC version %x\n", val); | 
 | 	if ((val == 1) || (val == 2)) { | 
 | 		cs->subtyp = DIVA_IPAC_ISA; | 
 | 		cs->hw.diva.ctrl = 0; | 
 | 		cs->hw.diva.isac = card->para[1] + DIVA_IPAC_DATA; | 
 | 		cs->hw.diva.hscx = card->para[1] + DIVA_IPAC_DATA; | 
 | 		cs->hw.diva.isac_adr = card->para[1] + DIVA_IPAC_ADR; | 
 | 		cs->hw.diva.hscx_adr = card->para[1] + DIVA_IPAC_ADR; | 
 | 		test_and_set_bit(HW_IPAC, &cs->HW_Flags); | 
 | 	} else { | 
 | 		cs->subtyp = DIVA_ISA; | 
 | 		cs->hw.diva.ctrl = card->para[1] + DIVA_ISA_CTRL; | 
 | 		cs->hw.diva.isac = card->para[1] + DIVA_ISA_ISAC_DATA; | 
 | 		cs->hw.diva.hscx = card->para[1] + DIVA_HSCX_DATA; | 
 | 		cs->hw.diva.isac_adr = card->para[1] + DIVA_ISA_ISAC_ADR; | 
 | 		cs->hw.diva.hscx_adr = card->para[1] + DIVA_HSCX_ADR; | 
 | 	} | 
 | 	cs->irq = card->para[0]; | 
 |  | 
 | 	return (1);		/* card found */ | 
 | } | 
 |  | 
 | #else	/* if !CONFIG_ISA */ | 
 |  | 
 | static int setup_diva_isa(struct IsdnCard *card) | 
 | { | 
 | 	return (-1);	/* card not found; continue search */ | 
 | } | 
 |  | 
 | #endif	/* CONFIG_ISA */ | 
 |  | 
 | #ifdef __ISAPNP__ | 
 | static struct isapnp_device_id diva_ids[] = { | 
 | 	{ ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x51), | 
 | 	  ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x51), | 
 | 	  (unsigned long) "Diva picola" }, | 
 | 	{ ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x51), | 
 | 	  ISAPNP_VENDOR('E', 'I', 'C'), ISAPNP_FUNCTION(0x51), | 
 | 	  (unsigned long) "Diva picola" }, | 
 | 	{ ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x71), | 
 | 	  ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x71), | 
 | 	  (unsigned long) "Diva 2.0" }, | 
 | 	{ ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x71), | 
 | 	  ISAPNP_VENDOR('E', 'I', 'C'), ISAPNP_FUNCTION(0x71), | 
 | 	  (unsigned long) "Diva 2.0" }, | 
 | 	{ ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0xA1), | 
 | 	  ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0xA1), | 
 | 	  (unsigned long) "Diva 2.01" }, | 
 | 	{ ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0xA1), | 
 | 	  ISAPNP_VENDOR('E', 'I', 'C'), ISAPNP_FUNCTION(0xA1), | 
 | 	  (unsigned long) "Diva 2.01" }, | 
 | 	{ 0, } | 
 | }; | 
 |  | 
 | static struct isapnp_device_id *ipid = &diva_ids[0]; | 
 | static struct pnp_card *pnp_c = NULL; | 
 |  | 
 | static int setup_diva_isapnp(struct IsdnCard *card) | 
 | { | 
 | 	struct IsdnCardState *cs = card->cs; | 
 | 	struct pnp_dev *pnp_d; | 
 |  | 
 | 	if (!isapnp_present()) | 
 | 		return (-1);	/* card not found; continue search */ | 
 |  | 
 | 	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", | 
 | 					       __func__, err); | 
 | 					return (0); | 
 | 				} | 
 | 				card->para[1] = pnp_port_start(pnp_d, 0); | 
 | 				card->para[0] = pnp_irq(pnp_d, 0); | 
 | 				if (card->para[0] == -1 || !card->para[1]) { | 
 | 					printk(KERN_ERR "Diva PnP:some resources are missing %ld/%lx\n", | 
 | 					       card->para[0], card->para[1]); | 
 | 					pnp_disable_dev(pnp_d); | 
 | 					return (0); | 
 | 				} | 
 | 				cs->hw.diva.cfg_reg  = card->para[1]; | 
 | 				cs->irq = card->para[0]; | 
 | 				if (ipid->function == ISAPNP_FUNCTION(0xA1)) { | 
 | 					cs->subtyp = DIVA_IPAC_ISA; | 
 | 					cs->hw.diva.ctrl = 0; | 
 | 					cs->hw.diva.isac = | 
 | 						card->para[1] + DIVA_IPAC_DATA; | 
 | 					cs->hw.diva.hscx = | 
 | 						card->para[1] + DIVA_IPAC_DATA; | 
 | 					cs->hw.diva.isac_adr = | 
 | 						card->para[1] + DIVA_IPAC_ADR; | 
 | 					cs->hw.diva.hscx_adr = | 
 | 						card->para[1] + DIVA_IPAC_ADR; | 
 | 					test_and_set_bit(HW_IPAC, &cs->HW_Flags); | 
 | 				} else { | 
 | 					cs->subtyp = DIVA_ISA; | 
 | 					cs->hw.diva.ctrl = | 
 | 						card->para[1] + DIVA_ISA_CTRL; | 
 | 					cs->hw.diva.isac = | 
 | 						card->para[1] + DIVA_ISA_ISAC_DATA; | 
 | 					cs->hw.diva.hscx = | 
 | 						card->para[1] + DIVA_HSCX_DATA; | 
 | 					cs->hw.diva.isac_adr = | 
 | 						card->para[1] + DIVA_ISA_ISAC_ADR; | 
 | 					cs->hw.diva.hscx_adr = | 
 | 						card->para[1] + DIVA_HSCX_ADR; | 
 | 				} | 
 | 				return (1);		/* card found */ | 
 | 			} else { | 
 | 				printk(KERN_ERR "Diva PnP: PnP error card found, no device\n"); | 
 | 				return (0); | 
 | 			} | 
 | 		} | 
 | 		ipid++; | 
 | 		pnp_c = NULL; | 
 | 	} | 
 |  | 
 | 	return (-1);	/* card not found; continue search */ | 
 | } | 
 |  | 
 | #else	/* if !ISAPNP */ | 
 |  | 
 | static int setup_diva_isapnp(struct IsdnCard *card) | 
 | { | 
 | 	return (-1);	/* card not found; continue search */ | 
 | } | 
 |  | 
 | #endif	/* ISAPNP */ | 
 |  | 
 | #ifdef CONFIG_PCI | 
 | static struct pci_dev *dev_diva = NULL; | 
 | static struct pci_dev *dev_diva_u = NULL; | 
 | static struct pci_dev *dev_diva201 = NULL; | 
 | static struct pci_dev *dev_diva202 = NULL; | 
 |  | 
 | static int setup_diva_pci(struct IsdnCard *card) | 
 | { | 
 | 	struct IsdnCardState *cs = card->cs; | 
 |  | 
 | 	cs->subtyp = 0; | 
 | 	if ((dev_diva = hisax_find_pci_device(PCI_VENDOR_ID_EICON, | 
 | 					      PCI_DEVICE_ID_EICON_DIVA20, dev_diva))) { | 
 | 		if (pci_enable_device(dev_diva)) | 
 | 			return (0); | 
 | 		cs->subtyp = DIVA_PCI; | 
 | 		cs->irq = dev_diva->irq; | 
 | 		cs->hw.diva.cfg_reg = pci_resource_start(dev_diva, 2); | 
 | 	} else if ((dev_diva_u = hisax_find_pci_device(PCI_VENDOR_ID_EICON, | 
 | 						       PCI_DEVICE_ID_EICON_DIVA20_U, dev_diva_u))) { | 
 | 		if (pci_enable_device(dev_diva_u)) | 
 | 			return (0); | 
 | 		cs->subtyp = DIVA_PCI; | 
 | 		cs->irq = dev_diva_u->irq; | 
 | 		cs->hw.diva.cfg_reg = pci_resource_start(dev_diva_u, 2); | 
 | 	} else if ((dev_diva201 = hisax_find_pci_device(PCI_VENDOR_ID_EICON, | 
 | 							PCI_DEVICE_ID_EICON_DIVA201, dev_diva201))) { | 
 | 		if (pci_enable_device(dev_diva201)) | 
 | 			return (0); | 
 | 		cs->subtyp = DIVA_IPAC_PCI; | 
 | 		cs->irq = dev_diva201->irq; | 
 | 		cs->hw.diva.pci_cfg = | 
 | 			(ulong) ioremap(pci_resource_start(dev_diva201, 0), 4096); | 
 | 		cs->hw.diva.cfg_reg = | 
 | 			(ulong) ioremap(pci_resource_start(dev_diva201, 1), 4096); | 
 | 	} else if ((dev_diva202 = hisax_find_pci_device(PCI_VENDOR_ID_EICON, | 
 | 							PCI_DEVICE_ID_EICON_DIVA202, dev_diva202))) { | 
 | 		if (pci_enable_device(dev_diva202)) | 
 | 			return (0); | 
 | 		cs->subtyp = DIVA_IPACX_PCI; | 
 | 		cs->irq = dev_diva202->irq; | 
 | 		cs->hw.diva.pci_cfg = | 
 | 			(ulong) ioremap(pci_resource_start(dev_diva202, 0), 4096); | 
 | 		cs->hw.diva.cfg_reg = | 
 | 			(ulong) ioremap(pci_resource_start(dev_diva202, 1), 4096); | 
 | 	} else { | 
 | 		return (-1);	/* card not found; continue search */ | 
 | 	} | 
 |  | 
 | 	if (!cs->irq) { | 
 | 		printk(KERN_WARNING "Diva: No IRQ for PCI card found\n"); | 
 | 		iounmap_diva(cs); | 
 | 		return (0); | 
 | 	} | 
 |  | 
 | 	if (!cs->hw.diva.cfg_reg) { | 
 | 		printk(KERN_WARNING "Diva: No IO-Adr for PCI card found\n"); | 
 | 		iounmap_diva(cs); | 
 | 		return (0); | 
 | 	} | 
 | 	cs->irq_flags |= IRQF_SHARED; | 
 |  | 
 | 	if ((cs->subtyp == DIVA_IPAC_PCI) || | 
 | 	    (cs->subtyp == DIVA_IPACX_PCI)) { | 
 | 		cs->hw.diva.ctrl = 0; | 
 | 		cs->hw.diva.isac = 0; | 
 | 		cs->hw.diva.hscx = 0; | 
 | 		cs->hw.diva.isac_adr = 0; | 
 | 		cs->hw.diva.hscx_adr = 0; | 
 | 		test_and_set_bit(HW_IPAC, &cs->HW_Flags); | 
 | 	} else { | 
 | 		cs->hw.diva.ctrl = cs->hw.diva.cfg_reg + DIVA_PCI_CTRL; | 
 | 		cs->hw.diva.isac = cs->hw.diva.cfg_reg + DIVA_PCI_ISAC_DATA; | 
 | 		cs->hw.diva.hscx = cs->hw.diva.cfg_reg + DIVA_HSCX_DATA; | 
 | 		cs->hw.diva.isac_adr = cs->hw.diva.cfg_reg + DIVA_PCI_ISAC_ADR; | 
 | 		cs->hw.diva.hscx_adr = cs->hw.diva.cfg_reg + DIVA_HSCX_ADR; | 
 | 	} | 
 |  | 
 | 	return (1);		/* card found */ | 
 | } | 
 |  | 
 | #else	/* if !CONFIG_PCI */ | 
 |  | 
 | static int setup_diva_pci(struct IsdnCard *card) | 
 | { | 
 | 	return (-1);	/* card not found; continue search */ | 
 | } | 
 |  | 
 | #endif	/* CONFIG_PCI */ | 
 |  | 
 | int setup_diva(struct IsdnCard *card) | 
 | { | 
 | 	int rc, have_card = 0; | 
 | 	struct IsdnCardState *cs = card->cs; | 
 | 	char tmp[64]; | 
 |  | 
 | 	strcpy(tmp, Diva_revision); | 
 | 	printk(KERN_INFO "HiSax: Eicon.Diehl Diva driver Rev. %s\n", HiSax_getrev(tmp)); | 
 | 	if (cs->typ != ISDN_CTYPE_DIEHLDIVA) | 
 | 		return (0); | 
 | 	cs->hw.diva.status = 0; | 
 |  | 
 | 	rc = setup_diva_isa(card); | 
 | 	if (!rc) | 
 | 		return rc; | 
 | 	if (rc > 0) { | 
 | 		have_card = 1; | 
 | 		goto ready; | 
 | 	} | 
 |  | 
 | 	rc = setup_diva_isapnp(card); | 
 | 	if (!rc) | 
 | 		return rc; | 
 | 	if (rc > 0) { | 
 | 		have_card = 1; | 
 | 		goto ready; | 
 | 	} | 
 |  | 
 | 	rc = setup_diva_pci(card); | 
 | 	if (!rc) | 
 | 		return rc; | 
 | 	if (rc > 0) | 
 | 		have_card = 1; | 
 |  | 
 | ready: | 
 | 	if (!have_card) { | 
 | 		printk(KERN_WARNING "Diva: No ISA, ISAPNP or PCI card found\n"); | 
 | 		return (0); | 
 | 	} | 
 |  | 
 | 	return setup_diva_common(card->cs); | 
 | } |