xref: /openbmc/linux/drivers/isdn/hardware/mISDN/w6692.c (revision 707b2ce6c1f4f1261788f2ff09ad82c35e0e6240)
1*707b2ce6SKarsten Keil /*
2*707b2ce6SKarsten Keil  * w6692.c     mISDN driver for Winbond w6692 based cards
3*707b2ce6SKarsten Keil  *
4*707b2ce6SKarsten Keil  * Author      Karsten Keil <kkeil@suse.de>
5*707b2ce6SKarsten Keil  *             based on the w6692 I4L driver from Petr Novak <petr.novak@i.cz>
6*707b2ce6SKarsten Keil  *
7*707b2ce6SKarsten Keil  * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
8*707b2ce6SKarsten Keil  *
9*707b2ce6SKarsten Keil  * This program is free software; you can redistribute it and/or modify
10*707b2ce6SKarsten Keil  * it under the terms of the GNU General Public License version 2 as
11*707b2ce6SKarsten Keil  * published by the Free Software Foundation.
12*707b2ce6SKarsten Keil  *
13*707b2ce6SKarsten Keil  * This program is distributed in the hope that it will be useful,
14*707b2ce6SKarsten Keil  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15*707b2ce6SKarsten Keil  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16*707b2ce6SKarsten Keil  * GNU General Public License for more details.
17*707b2ce6SKarsten Keil  *
18*707b2ce6SKarsten Keil  * You should have received a copy of the GNU General Public License
19*707b2ce6SKarsten Keil  * along with this program; if not, write to the Free Software
20*707b2ce6SKarsten Keil  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21*707b2ce6SKarsten Keil  *
22*707b2ce6SKarsten Keil  */
23*707b2ce6SKarsten Keil 
24*707b2ce6SKarsten Keil #include <linux/module.h>
25*707b2ce6SKarsten Keil #include <linux/pci.h>
26*707b2ce6SKarsten Keil #include <linux/delay.h>
27*707b2ce6SKarsten Keil #include <linux/mISDNhw.h>
28*707b2ce6SKarsten Keil #include "w6692.h"
29*707b2ce6SKarsten Keil 
30*707b2ce6SKarsten Keil #define W6692_REV	"2.0"
31*707b2ce6SKarsten Keil 
32*707b2ce6SKarsten Keil #define DBUSY_TIMER_VALUE	80
33*707b2ce6SKarsten Keil 
34*707b2ce6SKarsten Keil enum {
35*707b2ce6SKarsten Keil 	W6692_ASUS,
36*707b2ce6SKarsten Keil 	W6692_WINBOND,
37*707b2ce6SKarsten Keil 	W6692_USR
38*707b2ce6SKarsten Keil };
39*707b2ce6SKarsten Keil 
40*707b2ce6SKarsten Keil /* private data in the PCI devices list */
41*707b2ce6SKarsten Keil struct w6692map {
42*707b2ce6SKarsten Keil 	u_int	subtype;
43*707b2ce6SKarsten Keil 	char	*name;
44*707b2ce6SKarsten Keil };
45*707b2ce6SKarsten Keil 
46*707b2ce6SKarsten Keil static const struct w6692map  w6692_map[] =
47*707b2ce6SKarsten Keil {
48*707b2ce6SKarsten Keil 	{W6692_ASUS, "Dynalink/AsusCom IS64PH"},
49*707b2ce6SKarsten Keil 	{W6692_WINBOND, "Winbond W6692"},
50*707b2ce6SKarsten Keil 	{W6692_USR, "USR W6692"}
51*707b2ce6SKarsten Keil };
52*707b2ce6SKarsten Keil 
53*707b2ce6SKarsten Keil #ifndef PCI_VENDOR_ID_USR
54*707b2ce6SKarsten Keil #define PCI_VENDOR_ID_USR	0x16ec
55*707b2ce6SKarsten Keil #define PCI_DEVICE_ID_USR_6692	0x3409
56*707b2ce6SKarsten Keil #endif
57*707b2ce6SKarsten Keil 
58*707b2ce6SKarsten Keil struct w6692_ch {
59*707b2ce6SKarsten Keil 	struct bchannel		bch;
60*707b2ce6SKarsten Keil 	u32			addr;
61*707b2ce6SKarsten Keil 	struct timer_list	timer;
62*707b2ce6SKarsten Keil 	u8			b_mode;
63*707b2ce6SKarsten Keil };
64*707b2ce6SKarsten Keil 
65*707b2ce6SKarsten Keil struct w6692_hw {
66*707b2ce6SKarsten Keil 	struct list_head	list;
67*707b2ce6SKarsten Keil 	struct pci_dev		*pdev;
68*707b2ce6SKarsten Keil 	char			name[MISDN_MAX_IDLEN];
69*707b2ce6SKarsten Keil 	u32			irq;
70*707b2ce6SKarsten Keil 	u32			irqcnt;
71*707b2ce6SKarsten Keil 	u32			addr;
72*707b2ce6SKarsten Keil 	u32			fmask;	/* feature mask - bit set per card nr */
73*707b2ce6SKarsten Keil 	int			subtype;
74*707b2ce6SKarsten Keil 	spinlock_t		lock;	/* hw lock */
75*707b2ce6SKarsten Keil 	u8			imask;
76*707b2ce6SKarsten Keil 	u8			pctl;
77*707b2ce6SKarsten Keil 	u8			xaddr;
78*707b2ce6SKarsten Keil 	u8			xdata;
79*707b2ce6SKarsten Keil 	u8			state;
80*707b2ce6SKarsten Keil 	struct w6692_ch		bc[2];
81*707b2ce6SKarsten Keil 	struct dchannel		dch;
82*707b2ce6SKarsten Keil 	char			log[64];
83*707b2ce6SKarsten Keil };
84*707b2ce6SKarsten Keil 
85*707b2ce6SKarsten Keil static LIST_HEAD(Cards);
86*707b2ce6SKarsten Keil static DEFINE_RWLOCK(card_lock); /* protect Cards */
87*707b2ce6SKarsten Keil 
88*707b2ce6SKarsten Keil static int w6692_cnt;
89*707b2ce6SKarsten Keil static int debug;
90*707b2ce6SKarsten Keil static u32 led;
91*707b2ce6SKarsten Keil static u32 pots;
92*707b2ce6SKarsten Keil 
93*707b2ce6SKarsten Keil static void
94*707b2ce6SKarsten Keil _set_debug(struct w6692_hw *card)
95*707b2ce6SKarsten Keil {
96*707b2ce6SKarsten Keil 	card->dch.debug = debug;
97*707b2ce6SKarsten Keil 	card->bc[0].bch.debug = debug;
98*707b2ce6SKarsten Keil 	card->bc[1].bch.debug = debug;
99*707b2ce6SKarsten Keil }
100*707b2ce6SKarsten Keil 
101*707b2ce6SKarsten Keil static int
102*707b2ce6SKarsten Keil set_debug(const char *val, struct kernel_param *kp)
103*707b2ce6SKarsten Keil {
104*707b2ce6SKarsten Keil 	int ret;
105*707b2ce6SKarsten Keil 	struct w6692_hw *card;
106*707b2ce6SKarsten Keil 
107*707b2ce6SKarsten Keil 	ret = param_set_uint(val, kp);
108*707b2ce6SKarsten Keil 	if (!ret) {
109*707b2ce6SKarsten Keil 		read_lock(&card_lock);
110*707b2ce6SKarsten Keil 		list_for_each_entry(card, &Cards, list)
111*707b2ce6SKarsten Keil 			_set_debug(card);
112*707b2ce6SKarsten Keil 		read_unlock(&card_lock);
113*707b2ce6SKarsten Keil 	}
114*707b2ce6SKarsten Keil 	return ret;
115*707b2ce6SKarsten Keil }
116*707b2ce6SKarsten Keil 
117*707b2ce6SKarsten Keil MODULE_AUTHOR("Karsten Keil");
118*707b2ce6SKarsten Keil MODULE_LICENSE("GPL v2");
119*707b2ce6SKarsten Keil MODULE_VERSION(W6692_REV);
120*707b2ce6SKarsten Keil module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
121*707b2ce6SKarsten Keil MODULE_PARM_DESC(debug, "W6692 debug mask");
122*707b2ce6SKarsten Keil module_param(led, uint, S_IRUGO | S_IWUSR);
123*707b2ce6SKarsten Keil MODULE_PARM_DESC(led, "W6692 LED support bitmask (one bit per card)");
124*707b2ce6SKarsten Keil module_param(pots, uint, S_IRUGO | S_IWUSR);
125*707b2ce6SKarsten Keil MODULE_PARM_DESC(pots, "W6692 POTS support bitmask (one bit per card)");
126*707b2ce6SKarsten Keil 
127*707b2ce6SKarsten Keil static inline u8
128*707b2ce6SKarsten Keil ReadW6692(struct w6692_hw *card, u8 offset)
129*707b2ce6SKarsten Keil {
130*707b2ce6SKarsten Keil 	return inb(card->addr + offset);
131*707b2ce6SKarsten Keil }
132*707b2ce6SKarsten Keil 
133*707b2ce6SKarsten Keil static inline void
134*707b2ce6SKarsten Keil WriteW6692(struct w6692_hw *card, u8 offset, u8 value)
135*707b2ce6SKarsten Keil {
136*707b2ce6SKarsten Keil 	outb(value, card->addr + offset);
137*707b2ce6SKarsten Keil }
138*707b2ce6SKarsten Keil 
139*707b2ce6SKarsten Keil static inline u8
140*707b2ce6SKarsten Keil ReadW6692B(struct w6692_ch *bc, u8 offset)
141*707b2ce6SKarsten Keil {
142*707b2ce6SKarsten Keil 	return inb(bc->addr + offset);
143*707b2ce6SKarsten Keil }
144*707b2ce6SKarsten Keil 
145*707b2ce6SKarsten Keil static inline void
146*707b2ce6SKarsten Keil WriteW6692B(struct w6692_ch *bc, u8 offset, u8 value)
147*707b2ce6SKarsten Keil {
148*707b2ce6SKarsten Keil 	outb(value, bc->addr + offset);
149*707b2ce6SKarsten Keil }
150*707b2ce6SKarsten Keil 
151*707b2ce6SKarsten Keil static void
152*707b2ce6SKarsten Keil enable_hwirq(struct w6692_hw *card)
153*707b2ce6SKarsten Keil {
154*707b2ce6SKarsten Keil 	WriteW6692(card, W_IMASK, card->imask);
155*707b2ce6SKarsten Keil }
156*707b2ce6SKarsten Keil 
157*707b2ce6SKarsten Keil static void
158*707b2ce6SKarsten Keil disable_hwirq(struct w6692_hw *card)
159*707b2ce6SKarsten Keil {
160*707b2ce6SKarsten Keil 	WriteW6692(card, W_IMASK, 0xff);
161*707b2ce6SKarsten Keil }
162*707b2ce6SKarsten Keil 
163*707b2ce6SKarsten Keil static const char *W6692Ver[] = {"V00", "V01", "V10", "V11"};
164*707b2ce6SKarsten Keil 
165*707b2ce6SKarsten Keil static void
166*707b2ce6SKarsten Keil W6692Version(struct w6692_hw *card)
167*707b2ce6SKarsten Keil {
168*707b2ce6SKarsten Keil 	int val;
169*707b2ce6SKarsten Keil 
170*707b2ce6SKarsten Keil 	val = ReadW6692(card, W_D_RBCH);
171*707b2ce6SKarsten Keil 	pr_notice("%s: Winbond W6692 version: %s\n", card->name,
172*707b2ce6SKarsten Keil 		W6692Ver[(val >> 6) & 3]);
173*707b2ce6SKarsten Keil }
174*707b2ce6SKarsten Keil 
175*707b2ce6SKarsten Keil static void
176*707b2ce6SKarsten Keil w6692_led_handler(struct w6692_hw *card, int on)
177*707b2ce6SKarsten Keil {
178*707b2ce6SKarsten Keil 	if ((!(card->fmask & led)) || card->subtype == W6692_USR)
179*707b2ce6SKarsten Keil 		return;
180*707b2ce6SKarsten Keil 	if (on) {
181*707b2ce6SKarsten Keil 		card->xdata &= 0xfb;	/*  LED ON */
182*707b2ce6SKarsten Keil 		WriteW6692(card, W_XDATA, card->xdata);
183*707b2ce6SKarsten Keil 	} else {
184*707b2ce6SKarsten Keil 		card->xdata |= 0x04;	/*  LED OFF */
185*707b2ce6SKarsten Keil 		WriteW6692(card, W_XDATA, card->xdata);
186*707b2ce6SKarsten Keil 	}
187*707b2ce6SKarsten Keil }
188*707b2ce6SKarsten Keil 
189*707b2ce6SKarsten Keil static void
190*707b2ce6SKarsten Keil ph_command(struct w6692_hw *card, u8 cmd)
191*707b2ce6SKarsten Keil {
192*707b2ce6SKarsten Keil 	pr_debug("%s: ph_command %x\n", card->name, cmd);
193*707b2ce6SKarsten Keil 	WriteW6692(card, W_CIX, cmd);
194*707b2ce6SKarsten Keil }
195*707b2ce6SKarsten Keil 
196*707b2ce6SKarsten Keil static void
197*707b2ce6SKarsten Keil W6692_new_ph(struct w6692_hw *card)
198*707b2ce6SKarsten Keil {
199*707b2ce6SKarsten Keil 	if (card->state == W_L1CMD_RST)
200*707b2ce6SKarsten Keil 		ph_command(card, W_L1CMD_DRC);
201*707b2ce6SKarsten Keil 	schedule_event(&card->dch, FLG_PHCHANGE);
202*707b2ce6SKarsten Keil }
203*707b2ce6SKarsten Keil 
204*707b2ce6SKarsten Keil static void
205*707b2ce6SKarsten Keil W6692_ph_bh(struct dchannel *dch)
206*707b2ce6SKarsten Keil {
207*707b2ce6SKarsten Keil 	struct w6692_hw *card = dch->hw;
208*707b2ce6SKarsten Keil 
209*707b2ce6SKarsten Keil 	switch (card->state) {
210*707b2ce6SKarsten Keil 	case W_L1CMD_RST:
211*707b2ce6SKarsten Keil 		dch->state = 0;
212*707b2ce6SKarsten Keil 		l1_event(dch->l1, HW_RESET_IND);
213*707b2ce6SKarsten Keil 		break;
214*707b2ce6SKarsten Keil 	case W_L1IND_CD:
215*707b2ce6SKarsten Keil 		dch->state = 3;
216*707b2ce6SKarsten Keil 		l1_event(dch->l1, HW_DEACT_CNF);
217*707b2ce6SKarsten Keil 		break;
218*707b2ce6SKarsten Keil 	case W_L1IND_DRD:
219*707b2ce6SKarsten Keil 		dch->state = 3;
220*707b2ce6SKarsten Keil 		l1_event(dch->l1, HW_DEACT_IND);
221*707b2ce6SKarsten Keil 		break;
222*707b2ce6SKarsten Keil 	case W_L1IND_CE:
223*707b2ce6SKarsten Keil 		dch->state = 4;
224*707b2ce6SKarsten Keil 		l1_event(dch->l1, HW_POWERUP_IND);
225*707b2ce6SKarsten Keil 		break;
226*707b2ce6SKarsten Keil 	case W_L1IND_LD:
227*707b2ce6SKarsten Keil 		if (dch->state <= 5) {
228*707b2ce6SKarsten Keil 			dch->state = 5;
229*707b2ce6SKarsten Keil 			l1_event(dch->l1, ANYSIGNAL);
230*707b2ce6SKarsten Keil 		} else {
231*707b2ce6SKarsten Keil 			dch->state = 8;
232*707b2ce6SKarsten Keil 			l1_event(dch->l1, LOSTFRAMING);
233*707b2ce6SKarsten Keil 		}
234*707b2ce6SKarsten Keil 		break;
235*707b2ce6SKarsten Keil 	case W_L1IND_ARD:
236*707b2ce6SKarsten Keil 		dch->state = 6;
237*707b2ce6SKarsten Keil 		l1_event(dch->l1, INFO2);
238*707b2ce6SKarsten Keil 		break;
239*707b2ce6SKarsten Keil 	case W_L1IND_AI8:
240*707b2ce6SKarsten Keil 		dch->state = 7;
241*707b2ce6SKarsten Keil 		l1_event(dch->l1, INFO4_P8);
242*707b2ce6SKarsten Keil 		break;
243*707b2ce6SKarsten Keil 	case W_L1IND_AI10:
244*707b2ce6SKarsten Keil 		dch->state = 7;
245*707b2ce6SKarsten Keil 		l1_event(dch->l1, INFO4_P10);
246*707b2ce6SKarsten Keil 		break;
247*707b2ce6SKarsten Keil 	default:
248*707b2ce6SKarsten Keil 		pr_debug("%s: TE unknown state %02x dch state %02x\n",
249*707b2ce6SKarsten Keil 			card->name, card->state, dch->state);
250*707b2ce6SKarsten Keil 		break;
251*707b2ce6SKarsten Keil 	}
252*707b2ce6SKarsten Keil 	pr_debug("%s: TE newstate %02x\n", card->name, dch->state);
253*707b2ce6SKarsten Keil }
254*707b2ce6SKarsten Keil 
255*707b2ce6SKarsten Keil static void
256*707b2ce6SKarsten Keil W6692_empty_Dfifo(struct w6692_hw *card, int count)
257*707b2ce6SKarsten Keil {
258*707b2ce6SKarsten Keil 	struct dchannel *dch = &card->dch;
259*707b2ce6SKarsten Keil 	u8 *ptr;
260*707b2ce6SKarsten Keil 
261*707b2ce6SKarsten Keil 	pr_debug("%s: empty_Dfifo %d\n", card->name, count);
262*707b2ce6SKarsten Keil 	if (!dch->rx_skb) {
263*707b2ce6SKarsten Keil 		dch->rx_skb = mI_alloc_skb(card->dch.maxlen, GFP_ATOMIC);
264*707b2ce6SKarsten Keil 		if (!dch->rx_skb) {
265*707b2ce6SKarsten Keil 			pr_info("%s: D receive out of memory\n", card->name);
266*707b2ce6SKarsten Keil 			WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK);
267*707b2ce6SKarsten Keil 			return;
268*707b2ce6SKarsten Keil 		}
269*707b2ce6SKarsten Keil 	}
270*707b2ce6SKarsten Keil 	if ((dch->rx_skb->len + count) >= dch->maxlen) {
271*707b2ce6SKarsten Keil 		pr_debug("%s: empty_Dfifo overrun %d\n", card->name,
272*707b2ce6SKarsten Keil 			dch->rx_skb->len + count);
273*707b2ce6SKarsten Keil 		WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK);
274*707b2ce6SKarsten Keil 		return;
275*707b2ce6SKarsten Keil 	}
276*707b2ce6SKarsten Keil 	ptr = skb_put(dch->rx_skb, count);
277*707b2ce6SKarsten Keil 	insb(card->addr + W_D_RFIFO, ptr, count);
278*707b2ce6SKarsten Keil 	WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK);
279*707b2ce6SKarsten Keil 	if (debug & DEBUG_HW_DFIFO) {
280*707b2ce6SKarsten Keil 		snprintf(card->log, 63, "D-recv %s %d ",
281*707b2ce6SKarsten Keil 			card->name, count);
282*707b2ce6SKarsten Keil 		print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count);
283*707b2ce6SKarsten Keil 	}
284*707b2ce6SKarsten Keil }
285*707b2ce6SKarsten Keil 
286*707b2ce6SKarsten Keil static void
287*707b2ce6SKarsten Keil W6692_fill_Dfifo(struct w6692_hw *card)
288*707b2ce6SKarsten Keil {
289*707b2ce6SKarsten Keil 	struct dchannel *dch = &card->dch;
290*707b2ce6SKarsten Keil 	int count;
291*707b2ce6SKarsten Keil 	u8 *ptr;
292*707b2ce6SKarsten Keil 	u8 cmd = W_D_CMDR_XMS;
293*707b2ce6SKarsten Keil 
294*707b2ce6SKarsten Keil 	pr_debug("%s: fill_Dfifo\n", card->name);
295*707b2ce6SKarsten Keil 	if (!dch->tx_skb)
296*707b2ce6SKarsten Keil 		return;
297*707b2ce6SKarsten Keil 	count = dch->tx_skb->len - dch->tx_idx;
298*707b2ce6SKarsten Keil 	if (count <= 0)
299*707b2ce6SKarsten Keil 		return;
300*707b2ce6SKarsten Keil 	if (count > W_D_FIFO_THRESH)
301*707b2ce6SKarsten Keil 		count = W_D_FIFO_THRESH;
302*707b2ce6SKarsten Keil 	else
303*707b2ce6SKarsten Keil 		cmd |= W_D_CMDR_XME;
304*707b2ce6SKarsten Keil 	ptr = dch->tx_skb->data + dch->tx_idx;
305*707b2ce6SKarsten Keil 	dch->tx_idx += count;
306*707b2ce6SKarsten Keil 	outsb(card->addr + W_D_XFIFO, ptr, count);
307*707b2ce6SKarsten Keil 	WriteW6692(card, W_D_CMDR, cmd);
308*707b2ce6SKarsten Keil 	if (test_and_set_bit(FLG_BUSY_TIMER, &dch->Flags)) {
309*707b2ce6SKarsten Keil 		pr_debug("%s: fill_Dfifo dbusytimer running\n", card->name);
310*707b2ce6SKarsten Keil 		del_timer(&dch->timer);
311*707b2ce6SKarsten Keil 	}
312*707b2ce6SKarsten Keil 	init_timer(&dch->timer);
313*707b2ce6SKarsten Keil 	dch->timer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
314*707b2ce6SKarsten Keil 	add_timer(&dch->timer);
315*707b2ce6SKarsten Keil 	if (debug & DEBUG_HW_DFIFO) {
316*707b2ce6SKarsten Keil 		snprintf(card->log, 63, "D-send %s %d ",
317*707b2ce6SKarsten Keil 			card->name, count);
318*707b2ce6SKarsten Keil 		print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count);
319*707b2ce6SKarsten Keil 	}
320*707b2ce6SKarsten Keil }
321*707b2ce6SKarsten Keil 
322*707b2ce6SKarsten Keil static void
323*707b2ce6SKarsten Keil d_retransmit(struct w6692_hw *card)
324*707b2ce6SKarsten Keil {
325*707b2ce6SKarsten Keil 	struct dchannel *dch = &card->dch;
326*707b2ce6SKarsten Keil 
327*707b2ce6SKarsten Keil 	if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
328*707b2ce6SKarsten Keil 		del_timer(&dch->timer);
329*707b2ce6SKarsten Keil #ifdef FIXME
330*707b2ce6SKarsten Keil 	if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags))
331*707b2ce6SKarsten Keil 		dchannel_sched_event(dch, D_CLEARBUSY);
332*707b2ce6SKarsten Keil #endif
333*707b2ce6SKarsten Keil 	if (test_bit(FLG_TX_BUSY, &dch->Flags)) {
334*707b2ce6SKarsten Keil 		/* Restart frame */
335*707b2ce6SKarsten Keil 		dch->tx_idx = 0;
336*707b2ce6SKarsten Keil 		W6692_fill_Dfifo(card);
337*707b2ce6SKarsten Keil 	} else if (dch->tx_skb) { /* should not happen */
338*707b2ce6SKarsten Keil 		pr_info("%s: %s without TX_BUSY\n", card->name, __func__);
339*707b2ce6SKarsten Keil 		test_and_set_bit(FLG_TX_BUSY, &dch->Flags);
340*707b2ce6SKarsten Keil 		dch->tx_idx = 0;
341*707b2ce6SKarsten Keil 		W6692_fill_Dfifo(card);
342*707b2ce6SKarsten Keil 	} else {
343*707b2ce6SKarsten Keil 		pr_info("%s: XDU no TX_BUSY\n", card->name);
344*707b2ce6SKarsten Keil 		if (get_next_dframe(dch))
345*707b2ce6SKarsten Keil 			W6692_fill_Dfifo(card);
346*707b2ce6SKarsten Keil 	}
347*707b2ce6SKarsten Keil }
348*707b2ce6SKarsten Keil 
349*707b2ce6SKarsten Keil static void
350*707b2ce6SKarsten Keil handle_rxD(struct w6692_hw *card) {
351*707b2ce6SKarsten Keil 	u8	stat;
352*707b2ce6SKarsten Keil 	int	count;
353*707b2ce6SKarsten Keil 
354*707b2ce6SKarsten Keil 	stat = ReadW6692(card, W_D_RSTA);
355*707b2ce6SKarsten Keil 	if (stat & (W_D_RSTA_RDOV | W_D_RSTA_CRCE | W_D_RSTA_RMB)) {
356*707b2ce6SKarsten Keil 		if (stat & W_D_RSTA_RDOV) {
357*707b2ce6SKarsten Keil 			pr_debug("%s: D-channel RDOV\n", card->name);
358*707b2ce6SKarsten Keil #ifdef ERROR_STATISTIC
359*707b2ce6SKarsten Keil 			card->dch.err_rx++;
360*707b2ce6SKarsten Keil #endif
361*707b2ce6SKarsten Keil 		}
362*707b2ce6SKarsten Keil 		if (stat & W_D_RSTA_CRCE) {
363*707b2ce6SKarsten Keil 			pr_debug("%s: D-channel CRC error\n", card->name);
364*707b2ce6SKarsten Keil #ifdef ERROR_STATISTIC
365*707b2ce6SKarsten Keil 			card->dch.err_crc++;
366*707b2ce6SKarsten Keil #endif
367*707b2ce6SKarsten Keil 		}
368*707b2ce6SKarsten Keil 		if (stat & W_D_RSTA_RMB) {
369*707b2ce6SKarsten Keil 			pr_debug("%s: D-channel ABORT\n", card->name);
370*707b2ce6SKarsten Keil #ifdef ERROR_STATISTIC
371*707b2ce6SKarsten Keil 			card->dch.err_rx++;
372*707b2ce6SKarsten Keil #endif
373*707b2ce6SKarsten Keil 		}
374*707b2ce6SKarsten Keil 		if (card->dch.rx_skb)
375*707b2ce6SKarsten Keil 			dev_kfree_skb(card->dch.rx_skb);
376*707b2ce6SKarsten Keil 		card->dch.rx_skb = NULL;
377*707b2ce6SKarsten Keil 		WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK | W_D_CMDR_RRST);
378*707b2ce6SKarsten Keil 	} else {
379*707b2ce6SKarsten Keil 		count = ReadW6692(card, W_D_RBCL) & (W_D_FIFO_THRESH - 1);
380*707b2ce6SKarsten Keil 		if (count == 0)
381*707b2ce6SKarsten Keil 			count = W_D_FIFO_THRESH;
382*707b2ce6SKarsten Keil 		W6692_empty_Dfifo(card, count);
383*707b2ce6SKarsten Keil 		recv_Dchannel(&card->dch);
384*707b2ce6SKarsten Keil 	}
385*707b2ce6SKarsten Keil }
386*707b2ce6SKarsten Keil 
387*707b2ce6SKarsten Keil static void
388*707b2ce6SKarsten Keil handle_txD(struct w6692_hw *card) {
389*707b2ce6SKarsten Keil 	if (test_and_clear_bit(FLG_BUSY_TIMER, &card->dch.Flags))
390*707b2ce6SKarsten Keil 		del_timer(&card->dch.timer);
391*707b2ce6SKarsten Keil 	if (card->dch.tx_skb && card->dch.tx_idx < card->dch.tx_skb->len) {
392*707b2ce6SKarsten Keil 		W6692_fill_Dfifo(card);
393*707b2ce6SKarsten Keil 	} else {
394*707b2ce6SKarsten Keil 		if (card->dch.tx_skb)
395*707b2ce6SKarsten Keil 			dev_kfree_skb(card->dch.tx_skb);
396*707b2ce6SKarsten Keil 		if (get_next_dframe(&card->dch))
397*707b2ce6SKarsten Keil 			W6692_fill_Dfifo(card);
398*707b2ce6SKarsten Keil 	}
399*707b2ce6SKarsten Keil }
400*707b2ce6SKarsten Keil 
401*707b2ce6SKarsten Keil static void
402*707b2ce6SKarsten Keil handle_statusD(struct w6692_hw *card)
403*707b2ce6SKarsten Keil {
404*707b2ce6SKarsten Keil 	struct dchannel *dch = &card->dch;
405*707b2ce6SKarsten Keil 	u8 exval, v1, cir;
406*707b2ce6SKarsten Keil 
407*707b2ce6SKarsten Keil 	exval = ReadW6692(card, W_D_EXIR);
408*707b2ce6SKarsten Keil 
409*707b2ce6SKarsten Keil 	pr_debug("%s: D_EXIR %02x\n", card->name, exval);
410*707b2ce6SKarsten Keil 	if (exval & (W_D_EXI_XDUN | W_D_EXI_XCOL)) {
411*707b2ce6SKarsten Keil 		/* Transmit underrun/collision */
412*707b2ce6SKarsten Keil 		pr_debug("%s: D-channel underrun/collision\n", card->name);
413*707b2ce6SKarsten Keil #ifdef ERROR_STATISTIC
414*707b2ce6SKarsten Keil 		dch->err_tx++;
415*707b2ce6SKarsten Keil #endif
416*707b2ce6SKarsten Keil 		d_retransmit(card);
417*707b2ce6SKarsten Keil 	}
418*707b2ce6SKarsten Keil 	if (exval & W_D_EXI_RDOV) {	/* RDOV */
419*707b2ce6SKarsten Keil 		pr_debug("%s: D-channel RDOV\n", card->name);
420*707b2ce6SKarsten Keil 		WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST);
421*707b2ce6SKarsten Keil 	}
422*707b2ce6SKarsten Keil 	if (exval & W_D_EXI_TIN2)	/* TIN2 - never */
423*707b2ce6SKarsten Keil 		pr_debug("%s: spurious TIN2 interrupt\n", card->name);
424*707b2ce6SKarsten Keil 	if (exval & W_D_EXI_MOC) {	/* MOC - not supported */
425*707b2ce6SKarsten Keil 		v1 = ReadW6692(card, W_MOSR);
426*707b2ce6SKarsten Keil 		pr_debug("%s: spurious MOC interrupt MOSR %02x\n",
427*707b2ce6SKarsten Keil 			card->name, v1);
428*707b2ce6SKarsten Keil 	}
429*707b2ce6SKarsten Keil 	if (exval & W_D_EXI_ISC) {	/* ISC - Level1 change */
430*707b2ce6SKarsten Keil 		cir = ReadW6692(card, W_CIR);
431*707b2ce6SKarsten Keil 		pr_debug("%s: ISC CIR %02X\n", card->name, cir);
432*707b2ce6SKarsten Keil 		if (cir & W_CIR_ICC) {
433*707b2ce6SKarsten Keil 			v1 = cir & W_CIR_COD_MASK;
434*707b2ce6SKarsten Keil 			pr_debug("%s: ph_state_change %x -> %x\n", card->name,
435*707b2ce6SKarsten Keil 				dch->state, v1);
436*707b2ce6SKarsten Keil 			card->state = v1;
437*707b2ce6SKarsten Keil 			if (card->fmask & led) {
438*707b2ce6SKarsten Keil 				switch (v1) {
439*707b2ce6SKarsten Keil 				case W_L1IND_AI8:
440*707b2ce6SKarsten Keil 				case W_L1IND_AI10:
441*707b2ce6SKarsten Keil 					w6692_led_handler(card, 1);
442*707b2ce6SKarsten Keil 					break;
443*707b2ce6SKarsten Keil 				default:
444*707b2ce6SKarsten Keil 					w6692_led_handler(card, 0);
445*707b2ce6SKarsten Keil 					break;
446*707b2ce6SKarsten Keil 				}
447*707b2ce6SKarsten Keil 			}
448*707b2ce6SKarsten Keil 			W6692_new_ph(card);
449*707b2ce6SKarsten Keil 		}
450*707b2ce6SKarsten Keil 		if (cir & W_CIR_SCC) {
451*707b2ce6SKarsten Keil 			v1 = ReadW6692(card, W_SQR);
452*707b2ce6SKarsten Keil 			pr_debug("%s: SCC SQR %02X\n", card->name, v1);
453*707b2ce6SKarsten Keil 		}
454*707b2ce6SKarsten Keil 	}
455*707b2ce6SKarsten Keil 	if (exval & W_D_EXI_WEXP)
456*707b2ce6SKarsten Keil 		pr_debug("%s: spurious WEXP interrupt!\n", card->name);
457*707b2ce6SKarsten Keil 	if (exval & W_D_EXI_TEXP)
458*707b2ce6SKarsten Keil 		pr_debug("%s: spurious TEXP interrupt!\n", card->name);
459*707b2ce6SKarsten Keil }
460*707b2ce6SKarsten Keil 
461*707b2ce6SKarsten Keil static void
462*707b2ce6SKarsten Keil W6692_empty_Bfifo(struct w6692_ch *wch, int count)
463*707b2ce6SKarsten Keil {
464*707b2ce6SKarsten Keil 	struct w6692_hw *card = wch->bch.hw;
465*707b2ce6SKarsten Keil 	u8 *ptr;
466*707b2ce6SKarsten Keil 
467*707b2ce6SKarsten Keil 	pr_debug("%s: empty_Bfifo %d\n", card->name, count);
468*707b2ce6SKarsten Keil 	if (unlikely(wch->bch.state == ISDN_P_NONE)) {
469*707b2ce6SKarsten Keil 		pr_debug("%s: empty_Bfifo ISDN_P_NONE\n", card->name);
470*707b2ce6SKarsten Keil 		WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
471*707b2ce6SKarsten Keil 		if (wch->bch.rx_skb)
472*707b2ce6SKarsten Keil 			skb_trim(wch->bch.rx_skb, 0);
473*707b2ce6SKarsten Keil 		return;
474*707b2ce6SKarsten Keil 	}
475*707b2ce6SKarsten Keil 	if (!wch->bch.rx_skb) {
476*707b2ce6SKarsten Keil 		wch->bch.rx_skb = mI_alloc_skb(wch->bch.maxlen, GFP_ATOMIC);
477*707b2ce6SKarsten Keil 		if (unlikely(!wch->bch.rx_skb)) {
478*707b2ce6SKarsten Keil 			pr_info("%s: B receive out of memory\n", card->name);
479*707b2ce6SKarsten Keil 			WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK |
480*707b2ce6SKarsten Keil 				W_B_CMDR_RACT);
481*707b2ce6SKarsten Keil 			return;
482*707b2ce6SKarsten Keil 		}
483*707b2ce6SKarsten Keil 	}
484*707b2ce6SKarsten Keil 	if (wch->bch.rx_skb->len + count > wch->bch.maxlen) {
485*707b2ce6SKarsten Keil 		pr_debug("%s: empty_Bfifo incoming packet too large\n",
486*707b2ce6SKarsten Keil 			card->name);
487*707b2ce6SKarsten Keil 		WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
488*707b2ce6SKarsten Keil 		skb_trim(wch->bch.rx_skb, 0);
489*707b2ce6SKarsten Keil 		return;
490*707b2ce6SKarsten Keil 	}
491*707b2ce6SKarsten Keil 	ptr = skb_put(wch->bch.rx_skb, count);
492*707b2ce6SKarsten Keil 	insb(wch->addr + W_B_RFIFO, ptr, count);
493*707b2ce6SKarsten Keil 	WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
494*707b2ce6SKarsten Keil 	if (debug & DEBUG_HW_DFIFO) {
495*707b2ce6SKarsten Keil 		snprintf(card->log, 63, "B%1d-recv %s %d ",
496*707b2ce6SKarsten Keil 			wch->bch.nr, card->name, count);
497*707b2ce6SKarsten Keil 		print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count);
498*707b2ce6SKarsten Keil 	}
499*707b2ce6SKarsten Keil }
500*707b2ce6SKarsten Keil 
501*707b2ce6SKarsten Keil static void
502*707b2ce6SKarsten Keil W6692_fill_Bfifo(struct w6692_ch *wch)
503*707b2ce6SKarsten Keil {
504*707b2ce6SKarsten Keil 	struct w6692_hw *card = wch->bch.hw;
505*707b2ce6SKarsten Keil 	int count;
506*707b2ce6SKarsten Keil 	u8 *ptr, cmd = W_B_CMDR_RACT | W_B_CMDR_XMS;
507*707b2ce6SKarsten Keil 
508*707b2ce6SKarsten Keil 	pr_debug("%s: fill Bfifo\n", card->name);
509*707b2ce6SKarsten Keil 	if (!wch->bch.tx_skb)
510*707b2ce6SKarsten Keil 		return;
511*707b2ce6SKarsten Keil 	count = wch->bch.tx_skb->len - wch->bch.tx_idx;
512*707b2ce6SKarsten Keil 	if (count <= 0)
513*707b2ce6SKarsten Keil 		return;
514*707b2ce6SKarsten Keil 	ptr = wch->bch.tx_skb->data + wch->bch.tx_idx;
515*707b2ce6SKarsten Keil 	if (count > W_B_FIFO_THRESH)
516*707b2ce6SKarsten Keil 		count = W_B_FIFO_THRESH;
517*707b2ce6SKarsten Keil 	else if (test_bit(FLG_HDLC, &wch->bch.Flags))
518*707b2ce6SKarsten Keil 		cmd |= W_B_CMDR_XME;
519*707b2ce6SKarsten Keil 
520*707b2ce6SKarsten Keil 	pr_debug("%s: fill Bfifo%d/%d\n", card->name,
521*707b2ce6SKarsten Keil 			count, wch->bch.tx_idx);
522*707b2ce6SKarsten Keil 	wch->bch.tx_idx += count;
523*707b2ce6SKarsten Keil 	outsb(wch->addr + W_B_XFIFO, ptr, count);
524*707b2ce6SKarsten Keil 	WriteW6692B(wch, W_B_CMDR, cmd);
525*707b2ce6SKarsten Keil 	if (debug & DEBUG_HW_DFIFO) {
526*707b2ce6SKarsten Keil 		snprintf(card->log, 63, "B%1d-send %s %d ",
527*707b2ce6SKarsten Keil 			wch->bch.nr, card->name, count);
528*707b2ce6SKarsten Keil 		print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count);
529*707b2ce6SKarsten Keil 	}
530*707b2ce6SKarsten Keil }
531*707b2ce6SKarsten Keil 
532*707b2ce6SKarsten Keil static int
533*707b2ce6SKarsten Keil setvolume(struct w6692_ch *wch, int mic, struct sk_buff *skb)
534*707b2ce6SKarsten Keil {
535*707b2ce6SKarsten Keil 	struct w6692_hw *card = wch->bch.hw;
536*707b2ce6SKarsten Keil 	u16 *vol = (u16 *)skb->data;
537*707b2ce6SKarsten Keil 	u8 val;
538*707b2ce6SKarsten Keil 
539*707b2ce6SKarsten Keil 	if ((!(card->fmask & pots)) ||
540*707b2ce6SKarsten Keil 	    !test_bit(FLG_TRANSPARENT, &wch->bch.Flags))
541*707b2ce6SKarsten Keil 		return -ENODEV;
542*707b2ce6SKarsten Keil 	if (skb->len < 2)
543*707b2ce6SKarsten Keil 		return -EINVAL;
544*707b2ce6SKarsten Keil 	if (*vol > 7)
545*707b2ce6SKarsten Keil 		return -EINVAL;
546*707b2ce6SKarsten Keil 	val = *vol & 7;
547*707b2ce6SKarsten Keil 	val = 7 - val;
548*707b2ce6SKarsten Keil 	if (mic) {
549*707b2ce6SKarsten Keil 		val <<= 3;
550*707b2ce6SKarsten Keil 		card->xaddr &= 0xc7;
551*707b2ce6SKarsten Keil 	} else {
552*707b2ce6SKarsten Keil 		card->xaddr &= 0xf8;
553*707b2ce6SKarsten Keil 	}
554*707b2ce6SKarsten Keil 	card->xaddr |= val;
555*707b2ce6SKarsten Keil 	WriteW6692(card, W_XADDR, card->xaddr);
556*707b2ce6SKarsten Keil 	return 0;
557*707b2ce6SKarsten Keil }
558*707b2ce6SKarsten Keil 
559*707b2ce6SKarsten Keil static int
560*707b2ce6SKarsten Keil enable_pots(struct w6692_ch *wch)
561*707b2ce6SKarsten Keil {
562*707b2ce6SKarsten Keil 	struct w6692_hw *card = wch->bch.hw;
563*707b2ce6SKarsten Keil 
564*707b2ce6SKarsten Keil 	if ((!(card->fmask & pots)) ||
565*707b2ce6SKarsten Keil 	    !test_bit(FLG_TRANSPARENT, &wch->bch.Flags))
566*707b2ce6SKarsten Keil 		return -ENODEV;
567*707b2ce6SKarsten Keil 	wch->b_mode |= W_B_MODE_EPCM | W_B_MODE_BSW0;
568*707b2ce6SKarsten Keil 	WriteW6692B(wch, W_B_MODE, wch->b_mode);
569*707b2ce6SKarsten Keil 	WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
570*707b2ce6SKarsten Keil 	card->pctl |= ((wch->bch.nr & 2) ? W_PCTL_PCX : 0);
571*707b2ce6SKarsten Keil 	WriteW6692(card, W_PCTL, card->pctl);
572*707b2ce6SKarsten Keil 	return 0;
573*707b2ce6SKarsten Keil }
574*707b2ce6SKarsten Keil 
575*707b2ce6SKarsten Keil static int
576*707b2ce6SKarsten Keil disable_pots(struct w6692_ch *wch)
577*707b2ce6SKarsten Keil {
578*707b2ce6SKarsten Keil 	struct w6692_hw *card = wch->bch.hw;
579*707b2ce6SKarsten Keil 
580*707b2ce6SKarsten Keil 	if (!(card->fmask & pots))
581*707b2ce6SKarsten Keil 		return -ENODEV;
582*707b2ce6SKarsten Keil 	wch->b_mode &= ~(W_B_MODE_EPCM | W_B_MODE_BSW0);
583*707b2ce6SKarsten Keil 	WriteW6692B(wch, W_B_MODE, wch->b_mode);
584*707b2ce6SKarsten Keil 	WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT |
585*707b2ce6SKarsten Keil 		W_B_CMDR_XRST);
586*707b2ce6SKarsten Keil 	return 0;
587*707b2ce6SKarsten Keil }
588*707b2ce6SKarsten Keil 
589*707b2ce6SKarsten Keil static int
590*707b2ce6SKarsten Keil w6692_mode(struct w6692_ch *wch, u32 pr)
591*707b2ce6SKarsten Keil {
592*707b2ce6SKarsten Keil 	struct w6692_hw	*card;
593*707b2ce6SKarsten Keil 
594*707b2ce6SKarsten Keil 	card = wch->bch.hw;
595*707b2ce6SKarsten Keil 	pr_debug("%s: B%d protocol %x-->%x\n", card->name,
596*707b2ce6SKarsten Keil 		wch->bch.nr, wch->bch.state, pr);
597*707b2ce6SKarsten Keil 	switch (pr) {
598*707b2ce6SKarsten Keil 	case ISDN_P_NONE:
599*707b2ce6SKarsten Keil 		if ((card->fmask & pots) && (wch->b_mode & W_B_MODE_EPCM))
600*707b2ce6SKarsten Keil 			disable_pots(wch);
601*707b2ce6SKarsten Keil 		wch->b_mode = 0;
602*707b2ce6SKarsten Keil 		mISDN_clear_bchannel(&wch->bch);
603*707b2ce6SKarsten Keil 		WriteW6692B(wch, W_B_MODE, wch->b_mode);
604*707b2ce6SKarsten Keil 		WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
605*707b2ce6SKarsten Keil 		test_and_clear_bit(FLG_HDLC, &wch->bch.Flags);
606*707b2ce6SKarsten Keil 		test_and_clear_bit(FLG_TRANSPARENT, &wch->bch.Flags);
607*707b2ce6SKarsten Keil 		break;
608*707b2ce6SKarsten Keil 	case ISDN_P_B_RAW:
609*707b2ce6SKarsten Keil 		wch->b_mode = W_B_MODE_MMS;
610*707b2ce6SKarsten Keil 		WriteW6692B(wch, W_B_MODE, wch->b_mode);
611*707b2ce6SKarsten Keil 		WriteW6692B(wch, W_B_EXIM, 0);
612*707b2ce6SKarsten Keil 		WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT |
613*707b2ce6SKarsten Keil 			W_B_CMDR_XRST);
614*707b2ce6SKarsten Keil 		test_and_set_bit(FLG_TRANSPARENT, &wch->bch.Flags);
615*707b2ce6SKarsten Keil 		break;
616*707b2ce6SKarsten Keil 	case ISDN_P_B_HDLC:
617*707b2ce6SKarsten Keil 		wch->b_mode = W_B_MODE_ITF;
618*707b2ce6SKarsten Keil 		WriteW6692B(wch, W_B_MODE, wch->b_mode);
619*707b2ce6SKarsten Keil 		WriteW6692B(wch, W_B_ADM1, 0xff);
620*707b2ce6SKarsten Keil 		WriteW6692B(wch, W_B_ADM2, 0xff);
621*707b2ce6SKarsten Keil 		WriteW6692B(wch, W_B_EXIM, 0);
622*707b2ce6SKarsten Keil 		WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT |
623*707b2ce6SKarsten Keil 			W_B_CMDR_XRST);
624*707b2ce6SKarsten Keil 		test_and_set_bit(FLG_HDLC, &wch->bch.Flags);
625*707b2ce6SKarsten Keil 		break;
626*707b2ce6SKarsten Keil 	default:
627*707b2ce6SKarsten Keil 		pr_info("%s: protocol %x not known\n", card->name, pr);
628*707b2ce6SKarsten Keil 		return -ENOPROTOOPT;
629*707b2ce6SKarsten Keil 	}
630*707b2ce6SKarsten Keil 	wch->bch.state = pr;
631*707b2ce6SKarsten Keil 	return 0;
632*707b2ce6SKarsten Keil }
633*707b2ce6SKarsten Keil 
634*707b2ce6SKarsten Keil static void
635*707b2ce6SKarsten Keil send_next(struct w6692_ch *wch)
636*707b2ce6SKarsten Keil {
637*707b2ce6SKarsten Keil 	if (wch->bch.tx_skb && wch->bch.tx_idx < wch->bch.tx_skb->len)
638*707b2ce6SKarsten Keil 		W6692_fill_Bfifo(wch);
639*707b2ce6SKarsten Keil 	else {
640*707b2ce6SKarsten Keil 		if (wch->bch.tx_skb) {
641*707b2ce6SKarsten Keil 			/* send confirm, on trans, free on hdlc. */
642*707b2ce6SKarsten Keil 			if (test_bit(FLG_TRANSPARENT, &wch->bch.Flags))
643*707b2ce6SKarsten Keil 				confirm_Bsend(&wch->bch);
644*707b2ce6SKarsten Keil 			dev_kfree_skb(wch->bch.tx_skb);
645*707b2ce6SKarsten Keil 		}
646*707b2ce6SKarsten Keil 		if (get_next_bframe(&wch->bch))
647*707b2ce6SKarsten Keil 			W6692_fill_Bfifo(wch);
648*707b2ce6SKarsten Keil 	}
649*707b2ce6SKarsten Keil }
650*707b2ce6SKarsten Keil 
651*707b2ce6SKarsten Keil static void
652*707b2ce6SKarsten Keil W6692B_interrupt(struct w6692_hw *card, int ch)
653*707b2ce6SKarsten Keil {
654*707b2ce6SKarsten Keil 	struct w6692_ch	*wch = &card->bc[ch];
655*707b2ce6SKarsten Keil 	int		count;
656*707b2ce6SKarsten Keil 	u8		stat, star = 0;
657*707b2ce6SKarsten Keil 
658*707b2ce6SKarsten Keil 	stat = ReadW6692B(wch, W_B_EXIR);
659*707b2ce6SKarsten Keil 	pr_debug("%s: B%d EXIR %02x\n", card->name, wch->bch.nr, stat);
660*707b2ce6SKarsten Keil 	if (stat & W_B_EXI_RME) {
661*707b2ce6SKarsten Keil 		star = ReadW6692B(wch, W_B_STAR);
662*707b2ce6SKarsten Keil 		if (star & (W_B_STAR_RDOV | W_B_STAR_CRCE | W_B_STAR_RMB)) {
663*707b2ce6SKarsten Keil 			if ((star & W_B_STAR_RDOV) &&
664*707b2ce6SKarsten Keil 			    test_bit(FLG_ACTIVE, &wch->bch.Flags)) {
665*707b2ce6SKarsten Keil 				pr_debug("%s: B%d RDOV proto=%x\n", card->name,
666*707b2ce6SKarsten Keil 					wch->bch.nr, wch->bch.state);
667*707b2ce6SKarsten Keil #ifdef ERROR_STATISTIC
668*707b2ce6SKarsten Keil 				wch->bch.err_rdo++;
669*707b2ce6SKarsten Keil #endif
670*707b2ce6SKarsten Keil 			}
671*707b2ce6SKarsten Keil 			if (test_bit(FLG_HDLC, &wch->bch.Flags)) {
672*707b2ce6SKarsten Keil 				if (star & W_B_STAR_CRCE) {
673*707b2ce6SKarsten Keil 					pr_debug("%s: B%d CRC error\n",
674*707b2ce6SKarsten Keil 						card->name, wch->bch.nr);
675*707b2ce6SKarsten Keil #ifdef ERROR_STATISTIC
676*707b2ce6SKarsten Keil 					wch->bch.err_crc++;
677*707b2ce6SKarsten Keil #endif
678*707b2ce6SKarsten Keil 				}
679*707b2ce6SKarsten Keil 				if (star & W_B_STAR_RMB) {
680*707b2ce6SKarsten Keil 					pr_debug("%s: B%d message abort\n",
681*707b2ce6SKarsten Keil 						card->name, wch->bch.nr);
682*707b2ce6SKarsten Keil #ifdef ERROR_STATISTIC
683*707b2ce6SKarsten Keil 					wch->bch.err_inv++;
684*707b2ce6SKarsten Keil #endif
685*707b2ce6SKarsten Keil 				}
686*707b2ce6SKarsten Keil 			}
687*707b2ce6SKarsten Keil 			WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK |
688*707b2ce6SKarsten Keil 				W_B_CMDR_RRST | W_B_CMDR_RACT);
689*707b2ce6SKarsten Keil 			if (wch->bch.rx_skb)
690*707b2ce6SKarsten Keil 				skb_trim(wch->bch.rx_skb, 0);
691*707b2ce6SKarsten Keil 		} else {
692*707b2ce6SKarsten Keil 			count = ReadW6692B(wch, W_B_RBCL) &
693*707b2ce6SKarsten Keil 				(W_B_FIFO_THRESH - 1);
694*707b2ce6SKarsten Keil 			if (count == 0)
695*707b2ce6SKarsten Keil 				count = W_B_FIFO_THRESH;
696*707b2ce6SKarsten Keil 			W6692_empty_Bfifo(wch, count);
697*707b2ce6SKarsten Keil 			recv_Bchannel(&wch->bch, 0);
698*707b2ce6SKarsten Keil 		}
699*707b2ce6SKarsten Keil 	}
700*707b2ce6SKarsten Keil 	if (stat & W_B_EXI_RMR) {
701*707b2ce6SKarsten Keil 		if (!(stat & W_B_EXI_RME))
702*707b2ce6SKarsten Keil 			star = ReadW6692B(wch, W_B_STAR);
703*707b2ce6SKarsten Keil 		if (star & W_B_STAR_RDOV) {
704*707b2ce6SKarsten Keil 			pr_debug("%s: B%d RDOV proto=%x\n", card->name,
705*707b2ce6SKarsten Keil 				wch->bch.nr, wch->bch.state);
706*707b2ce6SKarsten Keil #ifdef ERROR_STATISTIC
707*707b2ce6SKarsten Keil 			wch->bch.err_rdo++;
708*707b2ce6SKarsten Keil #endif
709*707b2ce6SKarsten Keil 			WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK |
710*707b2ce6SKarsten Keil 				W_B_CMDR_RRST | W_B_CMDR_RACT);
711*707b2ce6SKarsten Keil 		} else {
712*707b2ce6SKarsten Keil 			W6692_empty_Bfifo(wch, W_B_FIFO_THRESH);
713*707b2ce6SKarsten Keil 			if (test_bit(FLG_TRANSPARENT, &wch->bch.Flags) &&
714*707b2ce6SKarsten Keil 			    wch->bch.rx_skb && (wch->bch.rx_skb->len > 0))
715*707b2ce6SKarsten Keil 				recv_Bchannel(&wch->bch, 0);
716*707b2ce6SKarsten Keil 		}
717*707b2ce6SKarsten Keil 	}
718*707b2ce6SKarsten Keil 	if (stat & W_B_EXI_RDOV) {
719*707b2ce6SKarsten Keil 		/* only if it is not handled yet */
720*707b2ce6SKarsten Keil 		if (!(star & W_B_STAR_RDOV)) {
721*707b2ce6SKarsten Keil 			pr_debug("%s: B%d RDOV IRQ proto=%x\n", card->name,
722*707b2ce6SKarsten Keil 				wch->bch.nr, wch->bch.state);
723*707b2ce6SKarsten Keil #ifdef ERROR_STATISTIC
724*707b2ce6SKarsten Keil 			wch->bch.err_rdo++;
725*707b2ce6SKarsten Keil #endif
726*707b2ce6SKarsten Keil 			WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK |
727*707b2ce6SKarsten Keil 				W_B_CMDR_RRST | W_B_CMDR_RACT);
728*707b2ce6SKarsten Keil 		}
729*707b2ce6SKarsten Keil 	}
730*707b2ce6SKarsten Keil 	if (stat & W_B_EXI_XFR) {
731*707b2ce6SKarsten Keil 		if (!(stat & (W_B_EXI_RME | W_B_EXI_RMR))) {
732*707b2ce6SKarsten Keil 			star = ReadW6692B(wch, W_B_STAR);
733*707b2ce6SKarsten Keil 			pr_debug("%s: B%d star %02x\n", card->name,
734*707b2ce6SKarsten Keil 				wch->bch.nr, star);
735*707b2ce6SKarsten Keil 		}
736*707b2ce6SKarsten Keil 		if (star & W_B_STAR_XDOW) {
737*707b2ce6SKarsten Keil 			pr_debug("%s: B%d XDOW proto=%x\n", card->name,
738*707b2ce6SKarsten Keil 				wch->bch.nr, wch->bch.state);
739*707b2ce6SKarsten Keil #ifdef ERROR_STATISTIC
740*707b2ce6SKarsten Keil 			wch->bch.err_xdu++;
741*707b2ce6SKarsten Keil #endif
742*707b2ce6SKarsten Keil 			WriteW6692B(wch, W_B_CMDR, W_B_CMDR_XRST |
743*707b2ce6SKarsten Keil 				W_B_CMDR_RACT);
744*707b2ce6SKarsten Keil 			/* resend */
745*707b2ce6SKarsten Keil 			if (wch->bch.tx_skb) {
746*707b2ce6SKarsten Keil 				if (!test_bit(FLG_TRANSPARENT, &wch->bch.Flags))
747*707b2ce6SKarsten Keil 					wch->bch.tx_idx = 0;
748*707b2ce6SKarsten Keil 			}
749*707b2ce6SKarsten Keil 		}
750*707b2ce6SKarsten Keil 		send_next(wch);
751*707b2ce6SKarsten Keil 		if (stat & W_B_EXI_XDUN)
752*707b2ce6SKarsten Keil 			return; /* handle XDOW only once */
753*707b2ce6SKarsten Keil 	}
754*707b2ce6SKarsten Keil 	if (stat & W_B_EXI_XDUN) {
755*707b2ce6SKarsten Keil 		pr_debug("%s: B%d XDUN proto=%x\n", card->name,
756*707b2ce6SKarsten Keil 			wch->bch.nr, wch->bch.state);
757*707b2ce6SKarsten Keil #ifdef ERROR_STATISTIC
758*707b2ce6SKarsten Keil 		wch->bch.err_xdu++;
759*707b2ce6SKarsten Keil #endif
760*707b2ce6SKarsten Keil 		WriteW6692B(wch, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT);
761*707b2ce6SKarsten Keil 		/* resend */
762*707b2ce6SKarsten Keil 		if (wch->bch.tx_skb) {
763*707b2ce6SKarsten Keil 			if (!test_bit(FLG_TRANSPARENT, &wch->bch.Flags))
764*707b2ce6SKarsten Keil 				wch->bch.tx_idx = 0;
765*707b2ce6SKarsten Keil 		}
766*707b2ce6SKarsten Keil 		send_next(wch);
767*707b2ce6SKarsten Keil 	}
768*707b2ce6SKarsten Keil }
769*707b2ce6SKarsten Keil 
770*707b2ce6SKarsten Keil static irqreturn_t
771*707b2ce6SKarsten Keil w6692_irq(int intno, void *dev_id)
772*707b2ce6SKarsten Keil {
773*707b2ce6SKarsten Keil 	struct w6692_hw	*card = dev_id;
774*707b2ce6SKarsten Keil 	u8		ista;
775*707b2ce6SKarsten Keil 
776*707b2ce6SKarsten Keil 	spin_lock(&card->lock);
777*707b2ce6SKarsten Keil 	ista = ReadW6692(card, W_ISTA);
778*707b2ce6SKarsten Keil 	if ((ista | card->imask) == card->imask) {
779*707b2ce6SKarsten Keil 		/* possible a shared  IRQ reqest */
780*707b2ce6SKarsten Keil 		spin_unlock(&card->lock);
781*707b2ce6SKarsten Keil 		return IRQ_NONE;
782*707b2ce6SKarsten Keil 	}
783*707b2ce6SKarsten Keil 	card->irqcnt++;
784*707b2ce6SKarsten Keil 	pr_debug("%s: ista %02x\n", card->name, ista);
785*707b2ce6SKarsten Keil 	ista &= ~card->imask;
786*707b2ce6SKarsten Keil 	if (ista & W_INT_B1_EXI)
787*707b2ce6SKarsten Keil 		W6692B_interrupt(card, 0);
788*707b2ce6SKarsten Keil 	if (ista & W_INT_B2_EXI)
789*707b2ce6SKarsten Keil 		W6692B_interrupt(card, 1);
790*707b2ce6SKarsten Keil 	if (ista & W_INT_D_RME)
791*707b2ce6SKarsten Keil 		handle_rxD(card);
792*707b2ce6SKarsten Keil 	if (ista & W_INT_D_RMR)
793*707b2ce6SKarsten Keil 		W6692_empty_Dfifo(card, W_D_FIFO_THRESH);
794*707b2ce6SKarsten Keil 	if (ista & W_INT_D_XFR)
795*707b2ce6SKarsten Keil 		handle_txD(card);
796*707b2ce6SKarsten Keil 	if (ista & W_INT_D_EXI)
797*707b2ce6SKarsten Keil 		handle_statusD(card);
798*707b2ce6SKarsten Keil 	if (ista & (W_INT_XINT0 | W_INT_XINT1)) /* XINT0/1 - never */
799*707b2ce6SKarsten Keil 		pr_debug("%s: W6692 spurious XINT!\n", card->name);
800*707b2ce6SKarsten Keil /* End IRQ Handler */
801*707b2ce6SKarsten Keil 	spin_unlock(&card->lock);
802*707b2ce6SKarsten Keil 	return IRQ_HANDLED;
803*707b2ce6SKarsten Keil }
804*707b2ce6SKarsten Keil 
805*707b2ce6SKarsten Keil static void
806*707b2ce6SKarsten Keil dbusy_timer_handler(struct dchannel *dch)
807*707b2ce6SKarsten Keil {
808*707b2ce6SKarsten Keil 	struct w6692_hw	*card = dch->hw;
809*707b2ce6SKarsten Keil 	int		rbch, star;
810*707b2ce6SKarsten Keil 	u_long		flags;
811*707b2ce6SKarsten Keil 
812*707b2ce6SKarsten Keil 	if (test_bit(FLG_BUSY_TIMER, &dch->Flags)) {
813*707b2ce6SKarsten Keil 		spin_lock_irqsave(&card->lock, flags);
814*707b2ce6SKarsten Keil 		rbch = ReadW6692(card, W_D_RBCH);
815*707b2ce6SKarsten Keil 		star = ReadW6692(card, W_D_STAR);
816*707b2ce6SKarsten Keil 		pr_debug("%s: D-Channel Busy RBCH %02x STAR %02x\n",
817*707b2ce6SKarsten Keil 			card->name, rbch, star);
818*707b2ce6SKarsten Keil 		if (star & W_D_STAR_XBZ)	/* D-Channel Busy */
819*707b2ce6SKarsten Keil 			test_and_set_bit(FLG_L1_BUSY, &dch->Flags);
820*707b2ce6SKarsten Keil 		else {
821*707b2ce6SKarsten Keil 			/* discard frame; reset transceiver */
822*707b2ce6SKarsten Keil 			test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags);
823*707b2ce6SKarsten Keil 			if (dch->tx_idx)
824*707b2ce6SKarsten Keil 				dch->tx_idx = 0;
825*707b2ce6SKarsten Keil 			else
826*707b2ce6SKarsten Keil 				pr_info("%s: W6692 D-Channel Busy no tx_idx\n",
827*707b2ce6SKarsten Keil 					card->name);
828*707b2ce6SKarsten Keil 			/* Transmitter reset */
829*707b2ce6SKarsten Keil 			WriteW6692(card, W_D_CMDR, W_D_CMDR_XRST);
830*707b2ce6SKarsten Keil 		}
831*707b2ce6SKarsten Keil 		spin_unlock_irqrestore(&card->lock, flags);
832*707b2ce6SKarsten Keil 	}
833*707b2ce6SKarsten Keil }
834*707b2ce6SKarsten Keil 
835*707b2ce6SKarsten Keil void initW6692(struct w6692_hw *card)
836*707b2ce6SKarsten Keil {
837*707b2ce6SKarsten Keil 	u8	val;
838*707b2ce6SKarsten Keil 
839*707b2ce6SKarsten Keil 	card->dch.timer.function = (void *)dbusy_timer_handler;
840*707b2ce6SKarsten Keil 	card->dch.timer.data = (u_long)&card->dch;
841*707b2ce6SKarsten Keil 	init_timer(&card->dch.timer);
842*707b2ce6SKarsten Keil 	w6692_mode(&card->bc[0], ISDN_P_NONE);
843*707b2ce6SKarsten Keil 	w6692_mode(&card->bc[1], ISDN_P_NONE);
844*707b2ce6SKarsten Keil 	WriteW6692(card, W_D_CTL, 0x00);
845*707b2ce6SKarsten Keil 	disable_hwirq(card);
846*707b2ce6SKarsten Keil 	WriteW6692(card, W_D_SAM, 0xff);
847*707b2ce6SKarsten Keil 	WriteW6692(card, W_D_TAM, 0xff);
848*707b2ce6SKarsten Keil 	WriteW6692(card, W_D_MODE, W_D_MODE_RACT);
849*707b2ce6SKarsten Keil 	card->state = W_L1CMD_RST;
850*707b2ce6SKarsten Keil 	ph_command(card, W_L1CMD_RST);
851*707b2ce6SKarsten Keil 	ph_command(card, W_L1CMD_ECK);
852*707b2ce6SKarsten Keil 	/* enable all IRQ but extern */
853*707b2ce6SKarsten Keil 	card->imask = 0x18;
854*707b2ce6SKarsten Keil 	WriteW6692(card, W_D_EXIM, 0x00);
855*707b2ce6SKarsten Keil 	WriteW6692B(&card->bc[0], W_B_EXIM, 0);
856*707b2ce6SKarsten Keil 	WriteW6692B(&card->bc[1], W_B_EXIM, 0);
857*707b2ce6SKarsten Keil 	/* Reset D-chan receiver and transmitter */
858*707b2ce6SKarsten Keil 	WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST | W_D_CMDR_XRST);
859*707b2ce6SKarsten Keil 	/* Reset B-chan receiver and transmitter */
860*707b2ce6SKarsten Keil 	WriteW6692B(&card->bc[0], W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
861*707b2ce6SKarsten Keil 	WriteW6692B(&card->bc[1], W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
862*707b2ce6SKarsten Keil 	/* enable peripheral */
863*707b2ce6SKarsten Keil 	if (card->subtype == W6692_USR) {
864*707b2ce6SKarsten Keil 		/* seems that USR implemented some power control features
865*707b2ce6SKarsten Keil 		 * Pin 79 is connected to the oscilator circuit so we
866*707b2ce6SKarsten Keil 		 * have to handle it here
867*707b2ce6SKarsten Keil 		 */
868*707b2ce6SKarsten Keil 		card->pctl = 0x80;
869*707b2ce6SKarsten Keil 		card->xdata = 0;
870*707b2ce6SKarsten Keil 		WriteW6692(card, W_PCTL, card->pctl);
871*707b2ce6SKarsten Keil 		WriteW6692(card, W_XDATA, card->xdata);
872*707b2ce6SKarsten Keil 	} else {
873*707b2ce6SKarsten Keil 		card->pctl = W_PCTL_OE5 | W_PCTL_OE4 | W_PCTL_OE2 |
874*707b2ce6SKarsten Keil 			W_PCTL_OE1 | W_PCTL_OE0;
875*707b2ce6SKarsten Keil 		card->xaddr = 0x00;/* all sw off */
876*707b2ce6SKarsten Keil 		if (card->fmask & pots)
877*707b2ce6SKarsten Keil 			card->xdata |= 0x06;	/*  POWER UP/ LED OFF / ALAW */
878*707b2ce6SKarsten Keil 		if (card->fmask & led)
879*707b2ce6SKarsten Keil 			card->xdata |= 0x04;	/* LED OFF */
880*707b2ce6SKarsten Keil 		if ((card->fmask & pots) || (card->fmask & led)) {
881*707b2ce6SKarsten Keil 			WriteW6692(card, W_PCTL, card->pctl);
882*707b2ce6SKarsten Keil 			WriteW6692(card, W_XADDR, card->xaddr);
883*707b2ce6SKarsten Keil 			WriteW6692(card, W_XDATA, card->xdata);
884*707b2ce6SKarsten Keil 			val = ReadW6692(card, W_XADDR);
885*707b2ce6SKarsten Keil 			if (debug & DEBUG_HW)
886*707b2ce6SKarsten Keil 				pr_notice("%s: W_XADDR=%02x\n",
887*707b2ce6SKarsten Keil 					card->name, val);
888*707b2ce6SKarsten Keil 		}
889*707b2ce6SKarsten Keil 	}
890*707b2ce6SKarsten Keil }
891*707b2ce6SKarsten Keil 
892*707b2ce6SKarsten Keil static void
893*707b2ce6SKarsten Keil reset_w6692(struct w6692_hw *card)
894*707b2ce6SKarsten Keil {
895*707b2ce6SKarsten Keil 	WriteW6692(card, W_D_CTL, W_D_CTL_SRST);
896*707b2ce6SKarsten Keil 	mdelay(10);
897*707b2ce6SKarsten Keil 	WriteW6692(card, W_D_CTL, 0);
898*707b2ce6SKarsten Keil }
899*707b2ce6SKarsten Keil 
900*707b2ce6SKarsten Keil static int
901*707b2ce6SKarsten Keil init_card(struct w6692_hw *card)
902*707b2ce6SKarsten Keil {
903*707b2ce6SKarsten Keil 	int	cnt = 3;
904*707b2ce6SKarsten Keil 	u_long	flags;
905*707b2ce6SKarsten Keil 
906*707b2ce6SKarsten Keil 	spin_lock_irqsave(&card->lock, flags);
907*707b2ce6SKarsten Keil 	disable_hwirq(card);
908*707b2ce6SKarsten Keil 	spin_unlock_irqrestore(&card->lock, flags);
909*707b2ce6SKarsten Keil 	if (request_irq(card->irq, w6692_irq, IRQF_SHARED, card->name, card)) {
910*707b2ce6SKarsten Keil 		pr_info("%s: couldn't get interrupt %d\n", card->name,
911*707b2ce6SKarsten Keil 			card->irq);
912*707b2ce6SKarsten Keil 		return -EIO;
913*707b2ce6SKarsten Keil 	}
914*707b2ce6SKarsten Keil 	while (cnt--) {
915*707b2ce6SKarsten Keil 		spin_lock_irqsave(&card->lock, flags);
916*707b2ce6SKarsten Keil 		initW6692(card);
917*707b2ce6SKarsten Keil 		enable_hwirq(card);
918*707b2ce6SKarsten Keil 		spin_unlock_irqrestore(&card->lock, flags);
919*707b2ce6SKarsten Keil 		/* Timeout 10ms */
920*707b2ce6SKarsten Keil 		msleep_interruptible(10);
921*707b2ce6SKarsten Keil 		if (debug & DEBUG_HW)
922*707b2ce6SKarsten Keil 			pr_notice("%s: IRQ %d count %d\n", card->name,
923*707b2ce6SKarsten Keil 				card->irq, card->irqcnt);
924*707b2ce6SKarsten Keil 		if (!card->irqcnt) {
925*707b2ce6SKarsten Keil 			pr_info("%s: IRQ(%d) getting no IRQs during init %d\n",
926*707b2ce6SKarsten Keil 				card->name, card->irq, 3 - cnt);
927*707b2ce6SKarsten Keil 			reset_w6692(card);
928*707b2ce6SKarsten Keil 		} else
929*707b2ce6SKarsten Keil 			return 0;
930*707b2ce6SKarsten Keil 	}
931*707b2ce6SKarsten Keil 	free_irq(card->irq, card);
932*707b2ce6SKarsten Keil 	return -EIO;
933*707b2ce6SKarsten Keil }
934*707b2ce6SKarsten Keil 
935*707b2ce6SKarsten Keil static int
936*707b2ce6SKarsten Keil w6692_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb)
937*707b2ce6SKarsten Keil {
938*707b2ce6SKarsten Keil 	struct bchannel *bch = container_of(ch, struct bchannel, ch);
939*707b2ce6SKarsten Keil 	struct w6692_ch	*bc = container_of(bch, struct w6692_ch, bch);
940*707b2ce6SKarsten Keil 	struct w6692_hw *card = bch->hw;
941*707b2ce6SKarsten Keil 	int ret = -EINVAL;
942*707b2ce6SKarsten Keil 	struct mISDNhead *hh = mISDN_HEAD_P(skb);
943*707b2ce6SKarsten Keil 	u32 id;
944*707b2ce6SKarsten Keil 	u_long flags;
945*707b2ce6SKarsten Keil 
946*707b2ce6SKarsten Keil 	switch (hh->prim) {
947*707b2ce6SKarsten Keil 	case PH_DATA_REQ:
948*707b2ce6SKarsten Keil 		spin_lock_irqsave(&card->lock, flags);
949*707b2ce6SKarsten Keil 		ret = bchannel_senddata(bch, skb);
950*707b2ce6SKarsten Keil 		if (ret > 0) { /* direct TX */
951*707b2ce6SKarsten Keil 			id = hh->id; /* skb can be freed */
952*707b2ce6SKarsten Keil 			ret = 0;
953*707b2ce6SKarsten Keil 			W6692_fill_Bfifo(bc);
954*707b2ce6SKarsten Keil 			spin_unlock_irqrestore(&card->lock, flags);
955*707b2ce6SKarsten Keil 			if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
956*707b2ce6SKarsten Keil 				queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
957*707b2ce6SKarsten Keil 		} else
958*707b2ce6SKarsten Keil 			spin_unlock_irqrestore(&card->lock, flags);
959*707b2ce6SKarsten Keil 		return ret;
960*707b2ce6SKarsten Keil 	case PH_ACTIVATE_REQ:
961*707b2ce6SKarsten Keil 		spin_lock_irqsave(&card->lock, flags);
962*707b2ce6SKarsten Keil 		if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
963*707b2ce6SKarsten Keil 			ret = w6692_mode(bc, ch->protocol);
964*707b2ce6SKarsten Keil 		else
965*707b2ce6SKarsten Keil 			ret = 0;
966*707b2ce6SKarsten Keil 		spin_unlock_irqrestore(&card->lock, flags);
967*707b2ce6SKarsten Keil 		if (!ret)
968*707b2ce6SKarsten Keil 			_queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
969*707b2ce6SKarsten Keil 				NULL, GFP_KERNEL);
970*707b2ce6SKarsten Keil 		break;
971*707b2ce6SKarsten Keil 	case PH_DEACTIVATE_REQ:
972*707b2ce6SKarsten Keil 		spin_lock_irqsave(&card->lock, flags);
973*707b2ce6SKarsten Keil 		mISDN_clear_bchannel(bch);
974*707b2ce6SKarsten Keil 		w6692_mode(bc, ISDN_P_NONE);
975*707b2ce6SKarsten Keil 		spin_unlock_irqrestore(&card->lock, flags);
976*707b2ce6SKarsten Keil 		_queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
977*707b2ce6SKarsten Keil 			NULL, GFP_KERNEL);
978*707b2ce6SKarsten Keil 		ret = 0;
979*707b2ce6SKarsten Keil 		break;
980*707b2ce6SKarsten Keil 	default:
981*707b2ce6SKarsten Keil 		pr_info("%s: %s unknown prim(%x,%x)\n",
982*707b2ce6SKarsten Keil 			card->name, __func__, hh->prim, hh->id);
983*707b2ce6SKarsten Keil 		ret = -EINVAL;
984*707b2ce6SKarsten Keil 	}
985*707b2ce6SKarsten Keil 	if (!ret)
986*707b2ce6SKarsten Keil 		dev_kfree_skb(skb);
987*707b2ce6SKarsten Keil 	return ret;
988*707b2ce6SKarsten Keil }
989*707b2ce6SKarsten Keil 
990*707b2ce6SKarsten Keil static int
991*707b2ce6SKarsten Keil channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
992*707b2ce6SKarsten Keil {
993*707b2ce6SKarsten Keil 	int	ret = 0;
994*707b2ce6SKarsten Keil 
995*707b2ce6SKarsten Keil 	switch (cq->op) {
996*707b2ce6SKarsten Keil 	case MISDN_CTRL_GETOP:
997*707b2ce6SKarsten Keil 		cq->op = 0;
998*707b2ce6SKarsten Keil 		break;
999*707b2ce6SKarsten Keil 	/* Nothing implemented yet */
1000*707b2ce6SKarsten Keil 	case MISDN_CTRL_FILL_EMPTY:
1001*707b2ce6SKarsten Keil 	default:
1002*707b2ce6SKarsten Keil 		pr_info("%s: unknown Op %x\n", __func__, cq->op);
1003*707b2ce6SKarsten Keil 		ret = -EINVAL;
1004*707b2ce6SKarsten Keil 		break;
1005*707b2ce6SKarsten Keil 	}
1006*707b2ce6SKarsten Keil 	return ret;
1007*707b2ce6SKarsten Keil }
1008*707b2ce6SKarsten Keil 
1009*707b2ce6SKarsten Keil static int
1010*707b2ce6SKarsten Keil open_bchannel(struct w6692_hw *card, struct channel_req *rq)
1011*707b2ce6SKarsten Keil {
1012*707b2ce6SKarsten Keil 	struct bchannel *bch;
1013*707b2ce6SKarsten Keil 
1014*707b2ce6SKarsten Keil 	if (rq->adr.channel > 2)
1015*707b2ce6SKarsten Keil 		return -EINVAL;
1016*707b2ce6SKarsten Keil 	if (rq->protocol == ISDN_P_NONE)
1017*707b2ce6SKarsten Keil 		return -EINVAL;
1018*707b2ce6SKarsten Keil 	bch = &card->bc[rq->adr.channel - 1].bch;
1019*707b2ce6SKarsten Keil 	if (test_and_set_bit(FLG_OPEN, &bch->Flags))
1020*707b2ce6SKarsten Keil 		return -EBUSY; /* b-channel can be only open once */
1021*707b2ce6SKarsten Keil 	test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);
1022*707b2ce6SKarsten Keil 	bch->ch.protocol = rq->protocol;
1023*707b2ce6SKarsten Keil 	rq->ch = &bch->ch;
1024*707b2ce6SKarsten Keil 	return 0;
1025*707b2ce6SKarsten Keil }
1026*707b2ce6SKarsten Keil 
1027*707b2ce6SKarsten Keil static int
1028*707b2ce6SKarsten Keil channel_ctrl(struct w6692_hw *card, struct mISDN_ctrl_req *cq)
1029*707b2ce6SKarsten Keil {
1030*707b2ce6SKarsten Keil 	int	ret = 0;
1031*707b2ce6SKarsten Keil 
1032*707b2ce6SKarsten Keil 	switch (cq->op) {
1033*707b2ce6SKarsten Keil 	case MISDN_CTRL_GETOP:
1034*707b2ce6SKarsten Keil 		cq->op = 0;
1035*707b2ce6SKarsten Keil 		break;
1036*707b2ce6SKarsten Keil 	default:
1037*707b2ce6SKarsten Keil 		pr_info("%s: unknown CTRL OP %x\n", card->name, cq->op);
1038*707b2ce6SKarsten Keil 		ret = -EINVAL;
1039*707b2ce6SKarsten Keil 		break;
1040*707b2ce6SKarsten Keil 	}
1041*707b2ce6SKarsten Keil 	return ret;
1042*707b2ce6SKarsten Keil }
1043*707b2ce6SKarsten Keil 
1044*707b2ce6SKarsten Keil static int
1045*707b2ce6SKarsten Keil w6692_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
1046*707b2ce6SKarsten Keil {
1047*707b2ce6SKarsten Keil 	struct bchannel *bch = container_of(ch, struct bchannel, ch);
1048*707b2ce6SKarsten Keil 	struct w6692_ch *bc = container_of(bch, struct w6692_ch, bch);
1049*707b2ce6SKarsten Keil 	struct w6692_hw *card = bch->hw;
1050*707b2ce6SKarsten Keil 	int ret = -EINVAL;
1051*707b2ce6SKarsten Keil 	u_long flags;
1052*707b2ce6SKarsten Keil 
1053*707b2ce6SKarsten Keil 	pr_debug("%s: %s cmd:%x %p\n", card->name, __func__, cmd, arg);
1054*707b2ce6SKarsten Keil 	switch (cmd) {
1055*707b2ce6SKarsten Keil 	case CLOSE_CHANNEL:
1056*707b2ce6SKarsten Keil 		test_and_clear_bit(FLG_OPEN, &bch->Flags);
1057*707b2ce6SKarsten Keil 		if (test_bit(FLG_ACTIVE, &bch->Flags)) {
1058*707b2ce6SKarsten Keil 			spin_lock_irqsave(&card->lock, flags);
1059*707b2ce6SKarsten Keil 			mISDN_freebchannel(bch);
1060*707b2ce6SKarsten Keil 			w6692_mode(bc, ISDN_P_NONE);
1061*707b2ce6SKarsten Keil 			spin_unlock_irqrestore(&card->lock, flags);
1062*707b2ce6SKarsten Keil 		} else {
1063*707b2ce6SKarsten Keil 			skb_queue_purge(&bch->rqueue);
1064*707b2ce6SKarsten Keil 			bch->rcount = 0;
1065*707b2ce6SKarsten Keil 		}
1066*707b2ce6SKarsten Keil 		ch->protocol = ISDN_P_NONE;
1067*707b2ce6SKarsten Keil 		ch->peer = NULL;
1068*707b2ce6SKarsten Keil 		module_put(THIS_MODULE);
1069*707b2ce6SKarsten Keil 		ret = 0;
1070*707b2ce6SKarsten Keil 		break;
1071*707b2ce6SKarsten Keil 	case CONTROL_CHANNEL:
1072*707b2ce6SKarsten Keil 		ret = channel_bctrl(bch, arg);
1073*707b2ce6SKarsten Keil 		break;
1074*707b2ce6SKarsten Keil 	default:
1075*707b2ce6SKarsten Keil 		pr_info("%s: %s unknown prim(%x)\n",
1076*707b2ce6SKarsten Keil 			card->name, __func__, cmd);
1077*707b2ce6SKarsten Keil 	}
1078*707b2ce6SKarsten Keil 	return ret;
1079*707b2ce6SKarsten Keil }
1080*707b2ce6SKarsten Keil 
1081*707b2ce6SKarsten Keil static int
1082*707b2ce6SKarsten Keil w6692_l2l1D(struct mISDNchannel *ch, struct sk_buff *skb)
1083*707b2ce6SKarsten Keil {
1084*707b2ce6SKarsten Keil 	struct mISDNdevice	*dev = container_of(ch, struct mISDNdevice, D);
1085*707b2ce6SKarsten Keil 	struct dchannel		*dch = container_of(dev, struct dchannel, dev);
1086*707b2ce6SKarsten Keil 	struct w6692_hw		*card = container_of(dch, struct w6692_hw, dch);
1087*707b2ce6SKarsten Keil 	int			ret = -EINVAL;
1088*707b2ce6SKarsten Keil 	struct mISDNhead	*hh = mISDN_HEAD_P(skb);
1089*707b2ce6SKarsten Keil 	u32			id;
1090*707b2ce6SKarsten Keil 	u_long			flags;
1091*707b2ce6SKarsten Keil 
1092*707b2ce6SKarsten Keil 	switch (hh->prim) {
1093*707b2ce6SKarsten Keil 	case PH_DATA_REQ:
1094*707b2ce6SKarsten Keil 		spin_lock_irqsave(&card->lock, flags);
1095*707b2ce6SKarsten Keil 		ret = dchannel_senddata(dch, skb);
1096*707b2ce6SKarsten Keil 		if (ret > 0) { /* direct TX */
1097*707b2ce6SKarsten Keil 			id = hh->id; /* skb can be freed */
1098*707b2ce6SKarsten Keil 			W6692_fill_Dfifo(card);
1099*707b2ce6SKarsten Keil 			ret = 0;
1100*707b2ce6SKarsten Keil 			spin_unlock_irqrestore(&card->lock, flags);
1101*707b2ce6SKarsten Keil 			queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
1102*707b2ce6SKarsten Keil 		} else
1103*707b2ce6SKarsten Keil 			spin_unlock_irqrestore(&card->lock, flags);
1104*707b2ce6SKarsten Keil 		return ret;
1105*707b2ce6SKarsten Keil 	case PH_ACTIVATE_REQ:
1106*707b2ce6SKarsten Keil 		ret = l1_event(dch->l1, hh->prim);
1107*707b2ce6SKarsten Keil 		break;
1108*707b2ce6SKarsten Keil 	case PH_DEACTIVATE_REQ:
1109*707b2ce6SKarsten Keil 		test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags);
1110*707b2ce6SKarsten Keil 		ret = l1_event(dch->l1, hh->prim);
1111*707b2ce6SKarsten Keil 		break;
1112*707b2ce6SKarsten Keil 	}
1113*707b2ce6SKarsten Keil 
1114*707b2ce6SKarsten Keil 	if (!ret)
1115*707b2ce6SKarsten Keil 		dev_kfree_skb(skb);
1116*707b2ce6SKarsten Keil 	return ret;
1117*707b2ce6SKarsten Keil }
1118*707b2ce6SKarsten Keil 
1119*707b2ce6SKarsten Keil static int
1120*707b2ce6SKarsten Keil w6692_l1callback(struct dchannel *dch, u32 cmd)
1121*707b2ce6SKarsten Keil {
1122*707b2ce6SKarsten Keil 	struct w6692_hw *card = container_of(dch, struct w6692_hw, dch);
1123*707b2ce6SKarsten Keil 	u_long flags;
1124*707b2ce6SKarsten Keil 
1125*707b2ce6SKarsten Keil 	pr_debug("%s: cmd(%x) state(%02x)\n", card->name, cmd, card->state);
1126*707b2ce6SKarsten Keil 	switch (cmd) {
1127*707b2ce6SKarsten Keil 	case INFO3_P8:
1128*707b2ce6SKarsten Keil 		spin_lock_irqsave(&card->lock, flags);
1129*707b2ce6SKarsten Keil 		ph_command(card, W_L1CMD_AR8);
1130*707b2ce6SKarsten Keil 		spin_unlock_irqrestore(&card->lock, flags);
1131*707b2ce6SKarsten Keil 		break;
1132*707b2ce6SKarsten Keil 	case INFO3_P10:
1133*707b2ce6SKarsten Keil 		spin_lock_irqsave(&card->lock, flags);
1134*707b2ce6SKarsten Keil 		ph_command(card, W_L1CMD_AR10);
1135*707b2ce6SKarsten Keil 		spin_unlock_irqrestore(&card->lock, flags);
1136*707b2ce6SKarsten Keil 		break;
1137*707b2ce6SKarsten Keil 	case HW_RESET_REQ:
1138*707b2ce6SKarsten Keil 		spin_lock_irqsave(&card->lock, flags);
1139*707b2ce6SKarsten Keil 		if (card->state != W_L1IND_DRD)
1140*707b2ce6SKarsten Keil 			ph_command(card, W_L1CMD_RST);
1141*707b2ce6SKarsten Keil 		ph_command(card, W_L1CMD_ECK);
1142*707b2ce6SKarsten Keil 		spin_unlock_irqrestore(&card->lock, flags);
1143*707b2ce6SKarsten Keil 		break;
1144*707b2ce6SKarsten Keil 	case HW_DEACT_REQ:
1145*707b2ce6SKarsten Keil 		skb_queue_purge(&dch->squeue);
1146*707b2ce6SKarsten Keil 		if (dch->tx_skb) {
1147*707b2ce6SKarsten Keil 			dev_kfree_skb(dch->tx_skb);
1148*707b2ce6SKarsten Keil 			dch->tx_skb = NULL;
1149*707b2ce6SKarsten Keil 		}
1150*707b2ce6SKarsten Keil 		dch->tx_idx = 0;
1151*707b2ce6SKarsten Keil 		if (dch->rx_skb) {
1152*707b2ce6SKarsten Keil 			dev_kfree_skb(dch->rx_skb);
1153*707b2ce6SKarsten Keil 			dch->rx_skb = NULL;
1154*707b2ce6SKarsten Keil 		}
1155*707b2ce6SKarsten Keil 		test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
1156*707b2ce6SKarsten Keil 		if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
1157*707b2ce6SKarsten Keil 			del_timer(&dch->timer);
1158*707b2ce6SKarsten Keil 		break;
1159*707b2ce6SKarsten Keil 	case HW_POWERUP_REQ:
1160*707b2ce6SKarsten Keil 		spin_lock_irqsave(&card->lock, flags);
1161*707b2ce6SKarsten Keil 		ph_command(card, W_L1CMD_ECK);
1162*707b2ce6SKarsten Keil 		spin_unlock_irqrestore(&card->lock, flags);
1163*707b2ce6SKarsten Keil 		break;
1164*707b2ce6SKarsten Keil 	case PH_ACTIVATE_IND:
1165*707b2ce6SKarsten Keil 		test_and_set_bit(FLG_ACTIVE, &dch->Flags);
1166*707b2ce6SKarsten Keil 		_queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
1167*707b2ce6SKarsten Keil 			GFP_ATOMIC);
1168*707b2ce6SKarsten Keil 		break;
1169*707b2ce6SKarsten Keil 	case PH_DEACTIVATE_IND:
1170*707b2ce6SKarsten Keil 		test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
1171*707b2ce6SKarsten Keil 		_queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
1172*707b2ce6SKarsten Keil 			GFP_ATOMIC);
1173*707b2ce6SKarsten Keil 		break;
1174*707b2ce6SKarsten Keil 	default:
1175*707b2ce6SKarsten Keil 		pr_debug("%s: %s unknown command %x\n", card->name,
1176*707b2ce6SKarsten Keil 			__func__, cmd);
1177*707b2ce6SKarsten Keil 		return -1;
1178*707b2ce6SKarsten Keil 	}
1179*707b2ce6SKarsten Keil 	return 0;
1180*707b2ce6SKarsten Keil }
1181*707b2ce6SKarsten Keil 
1182*707b2ce6SKarsten Keil static int
1183*707b2ce6SKarsten Keil open_dchannel(struct w6692_hw *card, struct channel_req *rq)
1184*707b2ce6SKarsten Keil {
1185*707b2ce6SKarsten Keil 	pr_debug("%s: %s dev(%d) open from %p\n", card->name, __func__,
1186*707b2ce6SKarsten Keil 		card->dch.dev.id, __builtin_return_address(1));
1187*707b2ce6SKarsten Keil 	if (rq->protocol != ISDN_P_TE_S0)
1188*707b2ce6SKarsten Keil 		return -EINVAL;
1189*707b2ce6SKarsten Keil 	if (rq->adr.channel == 1)
1190*707b2ce6SKarsten Keil 		/* E-Channel not supported */
1191*707b2ce6SKarsten Keil 		return -EINVAL;
1192*707b2ce6SKarsten Keil 	rq->ch = &card->dch.dev.D;
1193*707b2ce6SKarsten Keil 	rq->ch->protocol = rq->protocol;
1194*707b2ce6SKarsten Keil 	if (card->dch.state == 7)
1195*707b2ce6SKarsten Keil 		_queue_data(rq->ch, PH_ACTIVATE_IND, MISDN_ID_ANY,
1196*707b2ce6SKarsten Keil 		    0, NULL, GFP_KERNEL);
1197*707b2ce6SKarsten Keil 	return 0;
1198*707b2ce6SKarsten Keil }
1199*707b2ce6SKarsten Keil 
1200*707b2ce6SKarsten Keil static int
1201*707b2ce6SKarsten Keil w6692_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
1202*707b2ce6SKarsten Keil {
1203*707b2ce6SKarsten Keil 	struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D);
1204*707b2ce6SKarsten Keil 	struct dchannel *dch = container_of(dev, struct dchannel, dev);
1205*707b2ce6SKarsten Keil 	struct w6692_hw *card = container_of(dch, struct w6692_hw, dch);
1206*707b2ce6SKarsten Keil 	struct channel_req *rq;
1207*707b2ce6SKarsten Keil 	int err = 0;
1208*707b2ce6SKarsten Keil 
1209*707b2ce6SKarsten Keil 	pr_debug("%s: DCTRL: %x %p\n", card->name, cmd, arg);
1210*707b2ce6SKarsten Keil 	switch (cmd) {
1211*707b2ce6SKarsten Keil 	case OPEN_CHANNEL:
1212*707b2ce6SKarsten Keil 		rq = arg;
1213*707b2ce6SKarsten Keil 		if (rq->protocol == ISDN_P_TE_S0)
1214*707b2ce6SKarsten Keil 			err = open_dchannel(card, rq);
1215*707b2ce6SKarsten Keil 		else
1216*707b2ce6SKarsten Keil 			err = open_bchannel(card, rq);
1217*707b2ce6SKarsten Keil 		if (err)
1218*707b2ce6SKarsten Keil 			break;
1219*707b2ce6SKarsten Keil 		if (!try_module_get(THIS_MODULE))
1220*707b2ce6SKarsten Keil 			pr_info("%s: cannot get module\n", card->name);
1221*707b2ce6SKarsten Keil 		break;
1222*707b2ce6SKarsten Keil 	case CLOSE_CHANNEL:
1223*707b2ce6SKarsten Keil 		pr_debug("%s: dev(%d) close from %p\n", card->name,
1224*707b2ce6SKarsten Keil 			dch->dev.id, __builtin_return_address(0));
1225*707b2ce6SKarsten Keil 		module_put(THIS_MODULE);
1226*707b2ce6SKarsten Keil 		break;
1227*707b2ce6SKarsten Keil 	case CONTROL_CHANNEL:
1228*707b2ce6SKarsten Keil 		err = channel_ctrl(card, arg);
1229*707b2ce6SKarsten Keil 		break;
1230*707b2ce6SKarsten Keil 	default:
1231*707b2ce6SKarsten Keil 		pr_debug("%s: unknown DCTRL command %x\n", card->name, cmd);
1232*707b2ce6SKarsten Keil 		return -EINVAL;
1233*707b2ce6SKarsten Keil 	}
1234*707b2ce6SKarsten Keil 	return err;
1235*707b2ce6SKarsten Keil }
1236*707b2ce6SKarsten Keil 
1237*707b2ce6SKarsten Keil int
1238*707b2ce6SKarsten Keil setup_w6692(struct w6692_hw *card)
1239*707b2ce6SKarsten Keil {
1240*707b2ce6SKarsten Keil 	u32	val;
1241*707b2ce6SKarsten Keil 
1242*707b2ce6SKarsten Keil 	if (!request_region(card->addr, 256, card->name)) {
1243*707b2ce6SKarsten Keil 		pr_info("%s: config port %x-%x already in use\n", card->name,
1244*707b2ce6SKarsten Keil 		       card->addr, card->addr + 255);
1245*707b2ce6SKarsten Keil 		return -EIO;
1246*707b2ce6SKarsten Keil 	}
1247*707b2ce6SKarsten Keil 	W6692Version(card);
1248*707b2ce6SKarsten Keil 	card->bc[0].addr = card->addr;
1249*707b2ce6SKarsten Keil 	card->bc[1].addr = card->addr + 0x40;
1250*707b2ce6SKarsten Keil 	val = ReadW6692(card, W_ISTA);
1251*707b2ce6SKarsten Keil 	if (debug & DEBUG_HW)
1252*707b2ce6SKarsten Keil 		pr_notice("%s ISTA=%02x\n", card->name, val);
1253*707b2ce6SKarsten Keil 	val = ReadW6692(card, W_IMASK);
1254*707b2ce6SKarsten Keil 	if (debug & DEBUG_HW)
1255*707b2ce6SKarsten Keil 		pr_notice("%s IMASK=%02x\n", card->name, val);
1256*707b2ce6SKarsten Keil 	val = ReadW6692(card, W_D_EXIR);
1257*707b2ce6SKarsten Keil 	if (debug & DEBUG_HW)
1258*707b2ce6SKarsten Keil 		pr_notice("%s D_EXIR=%02x\n", card->name, val);
1259*707b2ce6SKarsten Keil 	val = ReadW6692(card, W_D_EXIM);
1260*707b2ce6SKarsten Keil 	if (debug & DEBUG_HW)
1261*707b2ce6SKarsten Keil 		pr_notice("%s D_EXIM=%02x\n", card->name, val);
1262*707b2ce6SKarsten Keil 	val = ReadW6692(card, W_D_RSTA);
1263*707b2ce6SKarsten Keil 	if (debug & DEBUG_HW)
1264*707b2ce6SKarsten Keil 		pr_notice("%s D_RSTA=%02x\n", card->name, val);
1265*707b2ce6SKarsten Keil 	return 0;
1266*707b2ce6SKarsten Keil }
1267*707b2ce6SKarsten Keil 
1268*707b2ce6SKarsten Keil static void
1269*707b2ce6SKarsten Keil release_card(struct w6692_hw *card)
1270*707b2ce6SKarsten Keil {
1271*707b2ce6SKarsten Keil 	u_long	flags;
1272*707b2ce6SKarsten Keil 
1273*707b2ce6SKarsten Keil 	spin_lock_irqsave(&card->lock, flags);
1274*707b2ce6SKarsten Keil 	disable_hwirq(card);
1275*707b2ce6SKarsten Keil 	w6692_mode(&card->bc[0], ISDN_P_NONE);
1276*707b2ce6SKarsten Keil 	w6692_mode(&card->bc[1], ISDN_P_NONE);
1277*707b2ce6SKarsten Keil 	if ((card->fmask & led) || card->subtype == W6692_USR) {
1278*707b2ce6SKarsten Keil 		card->xdata |= 0x04;	/*  LED OFF */
1279*707b2ce6SKarsten Keil 		WriteW6692(card, W_XDATA, card->xdata);
1280*707b2ce6SKarsten Keil 	}
1281*707b2ce6SKarsten Keil 	spin_unlock_irqrestore(&card->lock, flags);
1282*707b2ce6SKarsten Keil 	free_irq(card->irq, card);
1283*707b2ce6SKarsten Keil 	l1_event(card->dch.l1, CLOSE_CHANNEL);
1284*707b2ce6SKarsten Keil 	mISDN_unregister_device(&card->dch.dev);
1285*707b2ce6SKarsten Keil 	release_region(card->addr, 256);
1286*707b2ce6SKarsten Keil 	mISDN_freebchannel(&card->bc[1].bch);
1287*707b2ce6SKarsten Keil 	mISDN_freebchannel(&card->bc[0].bch);
1288*707b2ce6SKarsten Keil 	mISDN_freedchannel(&card->dch);
1289*707b2ce6SKarsten Keil 	write_lock_irqsave(&card_lock, flags);
1290*707b2ce6SKarsten Keil 	list_del(&card->list);
1291*707b2ce6SKarsten Keil 	write_unlock_irqrestore(&card_lock, flags);
1292*707b2ce6SKarsten Keil 	pci_disable_device(card->pdev);
1293*707b2ce6SKarsten Keil 	pci_set_drvdata(card->pdev, NULL);
1294*707b2ce6SKarsten Keil 	kfree(card);
1295*707b2ce6SKarsten Keil }
1296*707b2ce6SKarsten Keil 
1297*707b2ce6SKarsten Keil static int
1298*707b2ce6SKarsten Keil setup_instance(struct w6692_hw *card)
1299*707b2ce6SKarsten Keil {
1300*707b2ce6SKarsten Keil 	int		i, err;
1301*707b2ce6SKarsten Keil 	u_long		flags;
1302*707b2ce6SKarsten Keil 
1303*707b2ce6SKarsten Keil 	snprintf(card->name, MISDN_MAX_IDLEN - 1, "w6692.%d", w6692_cnt + 1);
1304*707b2ce6SKarsten Keil 	write_lock_irqsave(&card_lock, flags);
1305*707b2ce6SKarsten Keil 	list_add_tail(&card->list, &Cards);
1306*707b2ce6SKarsten Keil 	write_unlock_irqrestore(&card_lock, flags);
1307*707b2ce6SKarsten Keil 	card->fmask = (1 << w6692_cnt);
1308*707b2ce6SKarsten Keil 	_set_debug(card);
1309*707b2ce6SKarsten Keil 	spin_lock_init(&card->lock);
1310*707b2ce6SKarsten Keil 	mISDN_initdchannel(&card->dch, MAX_DFRAME_LEN_L1, W6692_ph_bh);
1311*707b2ce6SKarsten Keil 	card->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0);
1312*707b2ce6SKarsten Keil 	card->dch.dev.D.send = w6692_l2l1D;
1313*707b2ce6SKarsten Keil 	card->dch.dev.D.ctrl = w6692_dctrl;
1314*707b2ce6SKarsten Keil 	card->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
1315*707b2ce6SKarsten Keil 		(1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
1316*707b2ce6SKarsten Keil 	card->dch.hw = card;
1317*707b2ce6SKarsten Keil 	card->dch.dev.nrbchan = 2;
1318*707b2ce6SKarsten Keil 	for (i = 0; i < 2; i++) {
1319*707b2ce6SKarsten Keil 		mISDN_initbchannel(&card->bc[i].bch, MAX_DATA_MEM);
1320*707b2ce6SKarsten Keil 		card->bc[i].bch.hw = card;
1321*707b2ce6SKarsten Keil 		card->bc[i].bch.nr = i + 1;
1322*707b2ce6SKarsten Keil 		card->bc[i].bch.ch.nr = i + 1;
1323*707b2ce6SKarsten Keil 		card->bc[i].bch.ch.send = w6692_l2l1B;
1324*707b2ce6SKarsten Keil 		card->bc[i].bch.ch.ctrl = w6692_bctrl;
1325*707b2ce6SKarsten Keil 		set_channelmap(i + 1, card->dch.dev.channelmap);
1326*707b2ce6SKarsten Keil 		list_add(&card->bc[i].bch.ch.list, &card->dch.dev.bchannels);
1327*707b2ce6SKarsten Keil 	}
1328*707b2ce6SKarsten Keil 	err = setup_w6692(card);
1329*707b2ce6SKarsten Keil 	if (err)
1330*707b2ce6SKarsten Keil 		goto error_setup;
1331*707b2ce6SKarsten Keil 	err = mISDN_register_device(&card->dch.dev, &card->pdev->dev,
1332*707b2ce6SKarsten Keil 		card->name);
1333*707b2ce6SKarsten Keil 	if (err)
1334*707b2ce6SKarsten Keil 		goto error_reg;
1335*707b2ce6SKarsten Keil 	err = init_card(card);
1336*707b2ce6SKarsten Keil 	if (err)
1337*707b2ce6SKarsten Keil 		goto error_init;
1338*707b2ce6SKarsten Keil 	err = create_l1(&card->dch, w6692_l1callback);
1339*707b2ce6SKarsten Keil 	if (!err) {
1340*707b2ce6SKarsten Keil 		w6692_cnt++;
1341*707b2ce6SKarsten Keil 		pr_notice("W6692 %d cards installed\n", w6692_cnt);
1342*707b2ce6SKarsten Keil 		return 0;
1343*707b2ce6SKarsten Keil 	}
1344*707b2ce6SKarsten Keil 
1345*707b2ce6SKarsten Keil 	free_irq(card->irq, card);
1346*707b2ce6SKarsten Keil error_init:
1347*707b2ce6SKarsten Keil 	mISDN_unregister_device(&card->dch.dev);
1348*707b2ce6SKarsten Keil error_reg:
1349*707b2ce6SKarsten Keil 	release_region(card->addr, 256);
1350*707b2ce6SKarsten Keil error_setup:
1351*707b2ce6SKarsten Keil 	mISDN_freebchannel(&card->bc[1].bch);
1352*707b2ce6SKarsten Keil 	mISDN_freebchannel(&card->bc[0].bch);
1353*707b2ce6SKarsten Keil 	mISDN_freedchannel(&card->dch);
1354*707b2ce6SKarsten Keil 	write_lock_irqsave(&card_lock, flags);
1355*707b2ce6SKarsten Keil 	list_del(&card->list);
1356*707b2ce6SKarsten Keil 	write_unlock_irqrestore(&card_lock, flags);
1357*707b2ce6SKarsten Keil 	kfree(card);
1358*707b2ce6SKarsten Keil 	return err;
1359*707b2ce6SKarsten Keil }
1360*707b2ce6SKarsten Keil 
1361*707b2ce6SKarsten Keil static int __devinit
1362*707b2ce6SKarsten Keil w6692_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
1363*707b2ce6SKarsten Keil {
1364*707b2ce6SKarsten Keil 	int		err = -ENOMEM;
1365*707b2ce6SKarsten Keil 	struct w6692_hw	*card;
1366*707b2ce6SKarsten Keil 	struct w6692map	*m = (struct w6692map *)ent->driver_data;
1367*707b2ce6SKarsten Keil 
1368*707b2ce6SKarsten Keil 	card = kzalloc(sizeof(struct w6692_hw), GFP_KERNEL);
1369*707b2ce6SKarsten Keil 	if (!card) {
1370*707b2ce6SKarsten Keil 		pr_info("No kmem for w6692 card\n");
1371*707b2ce6SKarsten Keil 		return err;
1372*707b2ce6SKarsten Keil 	}
1373*707b2ce6SKarsten Keil 	card->pdev = pdev;
1374*707b2ce6SKarsten Keil 	card->subtype = m->subtype;
1375*707b2ce6SKarsten Keil 	err = pci_enable_device(pdev);
1376*707b2ce6SKarsten Keil 	if (err) {
1377*707b2ce6SKarsten Keil 		kfree(card);
1378*707b2ce6SKarsten Keil 		return err;
1379*707b2ce6SKarsten Keil 	}
1380*707b2ce6SKarsten Keil 
1381*707b2ce6SKarsten Keil 	printk(KERN_INFO "mISDN_w6692: found adapter %s at %s\n",
1382*707b2ce6SKarsten Keil 	       m->name, pci_name(pdev));
1383*707b2ce6SKarsten Keil 
1384*707b2ce6SKarsten Keil 	card->addr = pci_resource_start(pdev, 1);
1385*707b2ce6SKarsten Keil 	card->irq = pdev->irq;
1386*707b2ce6SKarsten Keil 	pci_set_drvdata(pdev, card);
1387*707b2ce6SKarsten Keil 	err = setup_instance(card);
1388*707b2ce6SKarsten Keil 	if (err)
1389*707b2ce6SKarsten Keil 		pci_set_drvdata(pdev, NULL);
1390*707b2ce6SKarsten Keil 	return err;
1391*707b2ce6SKarsten Keil }
1392*707b2ce6SKarsten Keil 
1393*707b2ce6SKarsten Keil static void __devexit
1394*707b2ce6SKarsten Keil w6692_remove_pci(struct pci_dev *pdev)
1395*707b2ce6SKarsten Keil {
1396*707b2ce6SKarsten Keil 	struct w6692_hw	*card = pci_get_drvdata(pdev);
1397*707b2ce6SKarsten Keil 
1398*707b2ce6SKarsten Keil 	if (card)
1399*707b2ce6SKarsten Keil 		release_card(card);
1400*707b2ce6SKarsten Keil 	else
1401*707b2ce6SKarsten Keil 		if (debug)
1402*707b2ce6SKarsten Keil 			pr_notice("%s: drvdata allready removed\n", __func__);
1403*707b2ce6SKarsten Keil }
1404*707b2ce6SKarsten Keil 
1405*707b2ce6SKarsten Keil static struct pci_device_id w6692_ids[] = {
1406*707b2ce6SKarsten Keil 	{ PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH,
1407*707b2ce6SKarsten Keil 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, (ulong)&w6692_map[0]},
1408*707b2ce6SKarsten Keil 	{ PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692,
1409*707b2ce6SKarsten Keil 	  PCI_VENDOR_ID_USR, PCI_DEVICE_ID_USR_6692, 0, 0,
1410*707b2ce6SKarsten Keil 	  (ulong)&w6692_map[2]},
1411*707b2ce6SKarsten Keil 	{ PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692,
1412*707b2ce6SKarsten Keil 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, (ulong)&w6692_map[1]},
1413*707b2ce6SKarsten Keil 	{ }
1414*707b2ce6SKarsten Keil };
1415*707b2ce6SKarsten Keil MODULE_DEVICE_TABLE(pci, w6692_ids);
1416*707b2ce6SKarsten Keil 
1417*707b2ce6SKarsten Keil static struct pci_driver w6692_driver = {
1418*707b2ce6SKarsten Keil 	.name =  "w6692",
1419*707b2ce6SKarsten Keil 	.probe = w6692_probe,
1420*707b2ce6SKarsten Keil 	.remove = __devexit_p(w6692_remove_pci),
1421*707b2ce6SKarsten Keil 	.id_table = w6692_ids,
1422*707b2ce6SKarsten Keil };
1423*707b2ce6SKarsten Keil 
1424*707b2ce6SKarsten Keil static int __init w6692_init(void)
1425*707b2ce6SKarsten Keil {
1426*707b2ce6SKarsten Keil 	int err;
1427*707b2ce6SKarsten Keil 
1428*707b2ce6SKarsten Keil 	pr_notice("Winbond W6692 PCI driver Rev. %s\n", W6692_REV);
1429*707b2ce6SKarsten Keil 
1430*707b2ce6SKarsten Keil 	err = pci_register_driver(&w6692_driver);
1431*707b2ce6SKarsten Keil 	return err;
1432*707b2ce6SKarsten Keil }
1433*707b2ce6SKarsten Keil 
1434*707b2ce6SKarsten Keil static void __exit w6692_cleanup(void)
1435*707b2ce6SKarsten Keil {
1436*707b2ce6SKarsten Keil 	pci_unregister_driver(&w6692_driver);
1437*707b2ce6SKarsten Keil }
1438*707b2ce6SKarsten Keil 
1439*707b2ce6SKarsten Keil module_init(w6692_init);
1440*707b2ce6SKarsten Keil module_exit(w6692_cleanup);
1441