162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * w6692.c     mISDN driver for Winbond w6692 based cards
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author      Karsten Keil <kkeil@suse.de>
662306a36Sopenharmony_ci *             based on the w6692 I4L driver from Petr Novak <petr.novak@i.cz>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/interrupt.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/pci.h>
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci#include <linux/mISDNhw.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include "w6692.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define W6692_REV	"2.0"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define DBUSY_TIMER_VALUE	80
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cienum {
2462306a36Sopenharmony_ci	W6692_ASUS,
2562306a36Sopenharmony_ci	W6692_WINBOND,
2662306a36Sopenharmony_ci	W6692_USR
2762306a36Sopenharmony_ci};
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/* private data in the PCI devices list */
3062306a36Sopenharmony_cistruct w6692map {
3162306a36Sopenharmony_ci	u_int	subtype;
3262306a36Sopenharmony_ci	char	*name;
3362306a36Sopenharmony_ci};
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic const struct w6692map  w6692_map[] =
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	{W6692_ASUS, "Dynalink/AsusCom IS64PH"},
3862306a36Sopenharmony_ci	{W6692_WINBOND, "Winbond W6692"},
3962306a36Sopenharmony_ci	{W6692_USR, "USR W6692"}
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define PCI_DEVICE_ID_USR_6692	0x3409
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistruct w6692_ch {
4562306a36Sopenharmony_ci	struct bchannel		bch;
4662306a36Sopenharmony_ci	u32			addr;
4762306a36Sopenharmony_ci	struct timer_list	timer;
4862306a36Sopenharmony_ci	u8			b_mode;
4962306a36Sopenharmony_ci};
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistruct w6692_hw {
5262306a36Sopenharmony_ci	struct list_head	list;
5362306a36Sopenharmony_ci	struct pci_dev		*pdev;
5462306a36Sopenharmony_ci	char			name[MISDN_MAX_IDLEN];
5562306a36Sopenharmony_ci	u32			irq;
5662306a36Sopenharmony_ci	u32			irqcnt;
5762306a36Sopenharmony_ci	u32			addr;
5862306a36Sopenharmony_ci	u32			fmask;	/* feature mask - bit set per card nr */
5962306a36Sopenharmony_ci	int			subtype;
6062306a36Sopenharmony_ci	spinlock_t		lock;	/* hw lock */
6162306a36Sopenharmony_ci	u8			imask;
6262306a36Sopenharmony_ci	u8			pctl;
6362306a36Sopenharmony_ci	u8			xaddr;
6462306a36Sopenharmony_ci	u8			xdata;
6562306a36Sopenharmony_ci	u8			state;
6662306a36Sopenharmony_ci	struct w6692_ch		bc[2];
6762306a36Sopenharmony_ci	struct dchannel		dch;
6862306a36Sopenharmony_ci	char			log[64];
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic LIST_HEAD(Cards);
7262306a36Sopenharmony_cistatic DEFINE_RWLOCK(card_lock); /* protect Cards */
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic int w6692_cnt;
7562306a36Sopenharmony_cistatic int debug;
7662306a36Sopenharmony_cistatic u32 led;
7762306a36Sopenharmony_cistatic u32 pots;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic void
8062306a36Sopenharmony_ci_set_debug(struct w6692_hw *card)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	card->dch.debug = debug;
8362306a36Sopenharmony_ci	card->bc[0].bch.debug = debug;
8462306a36Sopenharmony_ci	card->bc[1].bch.debug = debug;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic int
8862306a36Sopenharmony_ciset_debug(const char *val, const struct kernel_param *kp)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	int ret;
9162306a36Sopenharmony_ci	struct w6692_hw *card;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	ret = param_set_uint(val, kp);
9462306a36Sopenharmony_ci	if (!ret) {
9562306a36Sopenharmony_ci		read_lock(&card_lock);
9662306a36Sopenharmony_ci		list_for_each_entry(card, &Cards, list)
9762306a36Sopenharmony_ci			_set_debug(card);
9862306a36Sopenharmony_ci		read_unlock(&card_lock);
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci	return ret;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ciMODULE_AUTHOR("Karsten Keil");
10462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
10562306a36Sopenharmony_ciMODULE_VERSION(W6692_REV);
10662306a36Sopenharmony_cimodule_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
10762306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "W6692 debug mask");
10862306a36Sopenharmony_cimodule_param(led, uint, S_IRUGO | S_IWUSR);
10962306a36Sopenharmony_ciMODULE_PARM_DESC(led, "W6692 LED support bitmask (one bit per card)");
11062306a36Sopenharmony_cimodule_param(pots, uint, S_IRUGO | S_IWUSR);
11162306a36Sopenharmony_ciMODULE_PARM_DESC(pots, "W6692 POTS support bitmask (one bit per card)");
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic inline u8
11462306a36Sopenharmony_ciReadW6692(struct w6692_hw *card, u8 offset)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	return inb(card->addr + offset);
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic inline void
12062306a36Sopenharmony_ciWriteW6692(struct w6692_hw *card, u8 offset, u8 value)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	outb(value, card->addr + offset);
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic inline u8
12662306a36Sopenharmony_ciReadW6692B(struct w6692_ch *bc, u8 offset)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	return inb(bc->addr + offset);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic inline void
13262306a36Sopenharmony_ciWriteW6692B(struct w6692_ch *bc, u8 offset, u8 value)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	outb(value, bc->addr + offset);
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic void
13862306a36Sopenharmony_cienable_hwirq(struct w6692_hw *card)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	WriteW6692(card, W_IMASK, card->imask);
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic void
14462306a36Sopenharmony_cidisable_hwirq(struct w6692_hw *card)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	WriteW6692(card, W_IMASK, 0xff);
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic const char *W6692Ver[] = {"V00", "V01", "V10", "V11"};
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic void
15262306a36Sopenharmony_ciW6692Version(struct w6692_hw *card)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	int val;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	val = ReadW6692(card, W_D_RBCH);
15762306a36Sopenharmony_ci	pr_notice("%s: Winbond W6692 version: %s\n", card->name,
15862306a36Sopenharmony_ci		  W6692Ver[(val >> 6) & 3]);
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic void
16262306a36Sopenharmony_ciw6692_led_handler(struct w6692_hw *card, int on)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	if ((!(card->fmask & led)) || card->subtype == W6692_USR)
16562306a36Sopenharmony_ci		return;
16662306a36Sopenharmony_ci	if (on) {
16762306a36Sopenharmony_ci		card->xdata &= 0xfb;	/*  LED ON */
16862306a36Sopenharmony_ci		WriteW6692(card, W_XDATA, card->xdata);
16962306a36Sopenharmony_ci	} else {
17062306a36Sopenharmony_ci		card->xdata |= 0x04;	/*  LED OFF */
17162306a36Sopenharmony_ci		WriteW6692(card, W_XDATA, card->xdata);
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic void
17662306a36Sopenharmony_ciph_command(struct w6692_hw *card, u8 cmd)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	pr_debug("%s: ph_command %x\n", card->name, cmd);
17962306a36Sopenharmony_ci	WriteW6692(card, W_CIX, cmd);
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic void
18362306a36Sopenharmony_ciW6692_new_ph(struct w6692_hw *card)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	if (card->state == W_L1CMD_RST)
18662306a36Sopenharmony_ci		ph_command(card, W_L1CMD_DRC);
18762306a36Sopenharmony_ci	schedule_event(&card->dch, FLG_PHCHANGE);
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic void
19162306a36Sopenharmony_ciW6692_ph_bh(struct dchannel *dch)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	struct w6692_hw *card = dch->hw;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	switch (card->state) {
19662306a36Sopenharmony_ci	case W_L1CMD_RST:
19762306a36Sopenharmony_ci		dch->state = 0;
19862306a36Sopenharmony_ci		l1_event(dch->l1, HW_RESET_IND);
19962306a36Sopenharmony_ci		break;
20062306a36Sopenharmony_ci	case W_L1IND_CD:
20162306a36Sopenharmony_ci		dch->state = 3;
20262306a36Sopenharmony_ci		l1_event(dch->l1, HW_DEACT_CNF);
20362306a36Sopenharmony_ci		break;
20462306a36Sopenharmony_ci	case W_L1IND_DRD:
20562306a36Sopenharmony_ci		dch->state = 3;
20662306a36Sopenharmony_ci		l1_event(dch->l1, HW_DEACT_IND);
20762306a36Sopenharmony_ci		break;
20862306a36Sopenharmony_ci	case W_L1IND_CE:
20962306a36Sopenharmony_ci		dch->state = 4;
21062306a36Sopenharmony_ci		l1_event(dch->l1, HW_POWERUP_IND);
21162306a36Sopenharmony_ci		break;
21262306a36Sopenharmony_ci	case W_L1IND_LD:
21362306a36Sopenharmony_ci		if (dch->state <= 5) {
21462306a36Sopenharmony_ci			dch->state = 5;
21562306a36Sopenharmony_ci			l1_event(dch->l1, ANYSIGNAL);
21662306a36Sopenharmony_ci		} else {
21762306a36Sopenharmony_ci			dch->state = 8;
21862306a36Sopenharmony_ci			l1_event(dch->l1, LOSTFRAMING);
21962306a36Sopenharmony_ci		}
22062306a36Sopenharmony_ci		break;
22162306a36Sopenharmony_ci	case W_L1IND_ARD:
22262306a36Sopenharmony_ci		dch->state = 6;
22362306a36Sopenharmony_ci		l1_event(dch->l1, INFO2);
22462306a36Sopenharmony_ci		break;
22562306a36Sopenharmony_ci	case W_L1IND_AI8:
22662306a36Sopenharmony_ci		dch->state = 7;
22762306a36Sopenharmony_ci		l1_event(dch->l1, INFO4_P8);
22862306a36Sopenharmony_ci		break;
22962306a36Sopenharmony_ci	case W_L1IND_AI10:
23062306a36Sopenharmony_ci		dch->state = 7;
23162306a36Sopenharmony_ci		l1_event(dch->l1, INFO4_P10);
23262306a36Sopenharmony_ci		break;
23362306a36Sopenharmony_ci	default:
23462306a36Sopenharmony_ci		pr_debug("%s: TE unknown state %02x dch state %02x\n",
23562306a36Sopenharmony_ci			 card->name, card->state, dch->state);
23662306a36Sopenharmony_ci		break;
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci	pr_debug("%s: TE newstate %02x\n", card->name, dch->state);
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic void
24262306a36Sopenharmony_ciW6692_empty_Dfifo(struct w6692_hw *card, int count)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	struct dchannel *dch = &card->dch;
24562306a36Sopenharmony_ci	u8 *ptr;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	pr_debug("%s: empty_Dfifo %d\n", card->name, count);
24862306a36Sopenharmony_ci	if (!dch->rx_skb) {
24962306a36Sopenharmony_ci		dch->rx_skb = mI_alloc_skb(card->dch.maxlen, GFP_ATOMIC);
25062306a36Sopenharmony_ci		if (!dch->rx_skb) {
25162306a36Sopenharmony_ci			pr_info("%s: D receive out of memory\n", card->name);
25262306a36Sopenharmony_ci			WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK);
25362306a36Sopenharmony_ci			return;
25462306a36Sopenharmony_ci		}
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci	if ((dch->rx_skb->len + count) >= dch->maxlen) {
25762306a36Sopenharmony_ci		pr_debug("%s: empty_Dfifo overrun %d\n", card->name,
25862306a36Sopenharmony_ci			 dch->rx_skb->len + count);
25962306a36Sopenharmony_ci		WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK);
26062306a36Sopenharmony_ci		return;
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci	ptr = skb_put(dch->rx_skb, count);
26362306a36Sopenharmony_ci	insb(card->addr + W_D_RFIFO, ptr, count);
26462306a36Sopenharmony_ci	WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK);
26562306a36Sopenharmony_ci	if (debug & DEBUG_HW_DFIFO) {
26662306a36Sopenharmony_ci		snprintf(card->log, 63, "D-recv %s %d ",
26762306a36Sopenharmony_ci			 card->name, count);
26862306a36Sopenharmony_ci		print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count);
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic void
27362306a36Sopenharmony_ciW6692_fill_Dfifo(struct w6692_hw *card)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct dchannel *dch = &card->dch;
27662306a36Sopenharmony_ci	int count;
27762306a36Sopenharmony_ci	u8 *ptr;
27862306a36Sopenharmony_ci	u8 cmd = W_D_CMDR_XMS;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	pr_debug("%s: fill_Dfifo\n", card->name);
28162306a36Sopenharmony_ci	if (!dch->tx_skb)
28262306a36Sopenharmony_ci		return;
28362306a36Sopenharmony_ci	count = dch->tx_skb->len - dch->tx_idx;
28462306a36Sopenharmony_ci	if (count <= 0)
28562306a36Sopenharmony_ci		return;
28662306a36Sopenharmony_ci	if (count > W_D_FIFO_THRESH)
28762306a36Sopenharmony_ci		count = W_D_FIFO_THRESH;
28862306a36Sopenharmony_ci	else
28962306a36Sopenharmony_ci		cmd |= W_D_CMDR_XME;
29062306a36Sopenharmony_ci	ptr = dch->tx_skb->data + dch->tx_idx;
29162306a36Sopenharmony_ci	dch->tx_idx += count;
29262306a36Sopenharmony_ci	outsb(card->addr + W_D_XFIFO, ptr, count);
29362306a36Sopenharmony_ci	WriteW6692(card, W_D_CMDR, cmd);
29462306a36Sopenharmony_ci	if (test_and_set_bit(FLG_BUSY_TIMER, &dch->Flags)) {
29562306a36Sopenharmony_ci		pr_debug("%s: fill_Dfifo dbusytimer running\n", card->name);
29662306a36Sopenharmony_ci		del_timer(&dch->timer);
29762306a36Sopenharmony_ci	}
29862306a36Sopenharmony_ci	dch->timer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ) / 1000);
29962306a36Sopenharmony_ci	add_timer(&dch->timer);
30062306a36Sopenharmony_ci	if (debug & DEBUG_HW_DFIFO) {
30162306a36Sopenharmony_ci		snprintf(card->log, 63, "D-send %s %d ",
30262306a36Sopenharmony_ci			 card->name, count);
30362306a36Sopenharmony_ci		print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count);
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic void
30862306a36Sopenharmony_cid_retransmit(struct w6692_hw *card)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	struct dchannel *dch = &card->dch;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
31362306a36Sopenharmony_ci		del_timer(&dch->timer);
31462306a36Sopenharmony_ci#ifdef FIXME
31562306a36Sopenharmony_ci	if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags))
31662306a36Sopenharmony_ci		dchannel_sched_event(dch, D_CLEARBUSY);
31762306a36Sopenharmony_ci#endif
31862306a36Sopenharmony_ci	if (test_bit(FLG_TX_BUSY, &dch->Flags)) {
31962306a36Sopenharmony_ci		/* Restart frame */
32062306a36Sopenharmony_ci		dch->tx_idx = 0;
32162306a36Sopenharmony_ci		W6692_fill_Dfifo(card);
32262306a36Sopenharmony_ci	} else if (dch->tx_skb) { /* should not happen */
32362306a36Sopenharmony_ci		pr_info("%s: %s without TX_BUSY\n", card->name, __func__);
32462306a36Sopenharmony_ci		test_and_set_bit(FLG_TX_BUSY, &dch->Flags);
32562306a36Sopenharmony_ci		dch->tx_idx = 0;
32662306a36Sopenharmony_ci		W6692_fill_Dfifo(card);
32762306a36Sopenharmony_ci	} else {
32862306a36Sopenharmony_ci		pr_info("%s: XDU no TX_BUSY\n", card->name);
32962306a36Sopenharmony_ci		if (get_next_dframe(dch))
33062306a36Sopenharmony_ci			W6692_fill_Dfifo(card);
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic void
33562306a36Sopenharmony_cihandle_rxD(struct w6692_hw *card) {
33662306a36Sopenharmony_ci	u8	stat;
33762306a36Sopenharmony_ci	int	count;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	stat = ReadW6692(card, W_D_RSTA);
34062306a36Sopenharmony_ci	if (stat & (W_D_RSTA_RDOV | W_D_RSTA_CRCE | W_D_RSTA_RMB)) {
34162306a36Sopenharmony_ci		if (stat & W_D_RSTA_RDOV) {
34262306a36Sopenharmony_ci			pr_debug("%s: D-channel RDOV\n", card->name);
34362306a36Sopenharmony_ci#ifdef ERROR_STATISTIC
34462306a36Sopenharmony_ci			card->dch.err_rx++;
34562306a36Sopenharmony_ci#endif
34662306a36Sopenharmony_ci		}
34762306a36Sopenharmony_ci		if (stat & W_D_RSTA_CRCE) {
34862306a36Sopenharmony_ci			pr_debug("%s: D-channel CRC error\n", card->name);
34962306a36Sopenharmony_ci#ifdef ERROR_STATISTIC
35062306a36Sopenharmony_ci			card->dch.err_crc++;
35162306a36Sopenharmony_ci#endif
35262306a36Sopenharmony_ci		}
35362306a36Sopenharmony_ci		if (stat & W_D_RSTA_RMB) {
35462306a36Sopenharmony_ci			pr_debug("%s: D-channel ABORT\n", card->name);
35562306a36Sopenharmony_ci#ifdef ERROR_STATISTIC
35662306a36Sopenharmony_ci			card->dch.err_rx++;
35762306a36Sopenharmony_ci#endif
35862306a36Sopenharmony_ci		}
35962306a36Sopenharmony_ci		dev_kfree_skb(card->dch.rx_skb);
36062306a36Sopenharmony_ci		card->dch.rx_skb = NULL;
36162306a36Sopenharmony_ci		WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK | W_D_CMDR_RRST);
36262306a36Sopenharmony_ci	} else {
36362306a36Sopenharmony_ci		count = ReadW6692(card, W_D_RBCL) & (W_D_FIFO_THRESH - 1);
36462306a36Sopenharmony_ci		if (count == 0)
36562306a36Sopenharmony_ci			count = W_D_FIFO_THRESH;
36662306a36Sopenharmony_ci		W6692_empty_Dfifo(card, count);
36762306a36Sopenharmony_ci		recv_Dchannel(&card->dch);
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_cistatic void
37262306a36Sopenharmony_cihandle_txD(struct w6692_hw *card) {
37362306a36Sopenharmony_ci	if (test_and_clear_bit(FLG_BUSY_TIMER, &card->dch.Flags))
37462306a36Sopenharmony_ci		del_timer(&card->dch.timer);
37562306a36Sopenharmony_ci	if (card->dch.tx_skb && card->dch.tx_idx < card->dch.tx_skb->len) {
37662306a36Sopenharmony_ci		W6692_fill_Dfifo(card);
37762306a36Sopenharmony_ci	} else {
37862306a36Sopenharmony_ci		dev_kfree_skb(card->dch.tx_skb);
37962306a36Sopenharmony_ci		if (get_next_dframe(&card->dch))
38062306a36Sopenharmony_ci			W6692_fill_Dfifo(card);
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistatic void
38562306a36Sopenharmony_cihandle_statusD(struct w6692_hw *card)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	struct dchannel *dch = &card->dch;
38862306a36Sopenharmony_ci	u8 exval, v1, cir;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	exval = ReadW6692(card, W_D_EXIR);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	pr_debug("%s: D_EXIR %02x\n", card->name, exval);
39362306a36Sopenharmony_ci	if (exval & (W_D_EXI_XDUN | W_D_EXI_XCOL)) {
39462306a36Sopenharmony_ci		/* Transmit underrun/collision */
39562306a36Sopenharmony_ci		pr_debug("%s: D-channel underrun/collision\n", card->name);
39662306a36Sopenharmony_ci#ifdef ERROR_STATISTIC
39762306a36Sopenharmony_ci		dch->err_tx++;
39862306a36Sopenharmony_ci#endif
39962306a36Sopenharmony_ci		d_retransmit(card);
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci	if (exval & W_D_EXI_RDOV) {	/* RDOV */
40262306a36Sopenharmony_ci		pr_debug("%s: D-channel RDOV\n", card->name);
40362306a36Sopenharmony_ci		WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST);
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci	if (exval & W_D_EXI_TIN2)	/* TIN2 - never */
40662306a36Sopenharmony_ci		pr_debug("%s: spurious TIN2 interrupt\n", card->name);
40762306a36Sopenharmony_ci	if (exval & W_D_EXI_MOC) {	/* MOC - not supported */
40862306a36Sopenharmony_ci		v1 = ReadW6692(card, W_MOSR);
40962306a36Sopenharmony_ci		pr_debug("%s: spurious MOC interrupt MOSR %02x\n",
41062306a36Sopenharmony_ci			 card->name, v1);
41162306a36Sopenharmony_ci	}
41262306a36Sopenharmony_ci	if (exval & W_D_EXI_ISC) {	/* ISC - Level1 change */
41362306a36Sopenharmony_ci		cir = ReadW6692(card, W_CIR);
41462306a36Sopenharmony_ci		pr_debug("%s: ISC CIR %02X\n", card->name, cir);
41562306a36Sopenharmony_ci		if (cir & W_CIR_ICC) {
41662306a36Sopenharmony_ci			v1 = cir & W_CIR_COD_MASK;
41762306a36Sopenharmony_ci			pr_debug("%s: ph_state_change %x -> %x\n", card->name,
41862306a36Sopenharmony_ci				 dch->state, v1);
41962306a36Sopenharmony_ci			card->state = v1;
42062306a36Sopenharmony_ci			if (card->fmask & led) {
42162306a36Sopenharmony_ci				switch (v1) {
42262306a36Sopenharmony_ci				case W_L1IND_AI8:
42362306a36Sopenharmony_ci				case W_L1IND_AI10:
42462306a36Sopenharmony_ci					w6692_led_handler(card, 1);
42562306a36Sopenharmony_ci					break;
42662306a36Sopenharmony_ci				default:
42762306a36Sopenharmony_ci					w6692_led_handler(card, 0);
42862306a36Sopenharmony_ci					break;
42962306a36Sopenharmony_ci				}
43062306a36Sopenharmony_ci			}
43162306a36Sopenharmony_ci			W6692_new_ph(card);
43262306a36Sopenharmony_ci		}
43362306a36Sopenharmony_ci		if (cir & W_CIR_SCC) {
43462306a36Sopenharmony_ci			v1 = ReadW6692(card, W_SQR);
43562306a36Sopenharmony_ci			pr_debug("%s: SCC SQR %02X\n", card->name, v1);
43662306a36Sopenharmony_ci		}
43762306a36Sopenharmony_ci	}
43862306a36Sopenharmony_ci	if (exval & W_D_EXI_WEXP)
43962306a36Sopenharmony_ci		pr_debug("%s: spurious WEXP interrupt!\n", card->name);
44062306a36Sopenharmony_ci	if (exval & W_D_EXI_TEXP)
44162306a36Sopenharmony_ci		pr_debug("%s: spurious TEXP interrupt!\n", card->name);
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic void
44562306a36Sopenharmony_ciW6692_empty_Bfifo(struct w6692_ch *wch, int count)
44662306a36Sopenharmony_ci{
44762306a36Sopenharmony_ci	struct w6692_hw *card = wch->bch.hw;
44862306a36Sopenharmony_ci	u8 *ptr;
44962306a36Sopenharmony_ci	int maxlen;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	pr_debug("%s: empty_Bfifo %d\n", card->name, count);
45262306a36Sopenharmony_ci	if (unlikely(wch->bch.state == ISDN_P_NONE)) {
45362306a36Sopenharmony_ci		pr_debug("%s: empty_Bfifo ISDN_P_NONE\n", card->name);
45462306a36Sopenharmony_ci		WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
45562306a36Sopenharmony_ci		if (wch->bch.rx_skb)
45662306a36Sopenharmony_ci			skb_trim(wch->bch.rx_skb, 0);
45762306a36Sopenharmony_ci		return;
45862306a36Sopenharmony_ci	}
45962306a36Sopenharmony_ci	if (test_bit(FLG_RX_OFF, &wch->bch.Flags)) {
46062306a36Sopenharmony_ci		wch->bch.dropcnt += count;
46162306a36Sopenharmony_ci		WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
46262306a36Sopenharmony_ci		return;
46362306a36Sopenharmony_ci	}
46462306a36Sopenharmony_ci	maxlen = bchannel_get_rxbuf(&wch->bch, count);
46562306a36Sopenharmony_ci	if (maxlen < 0) {
46662306a36Sopenharmony_ci		WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
46762306a36Sopenharmony_ci		if (wch->bch.rx_skb)
46862306a36Sopenharmony_ci			skb_trim(wch->bch.rx_skb, 0);
46962306a36Sopenharmony_ci		pr_warn("%s.B%d: No bufferspace for %d bytes\n",
47062306a36Sopenharmony_ci			card->name, wch->bch.nr, count);
47162306a36Sopenharmony_ci		return;
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci	ptr = skb_put(wch->bch.rx_skb, count);
47462306a36Sopenharmony_ci	insb(wch->addr + W_B_RFIFO, ptr, count);
47562306a36Sopenharmony_ci	WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
47662306a36Sopenharmony_ci	if (debug & DEBUG_HW_DFIFO) {
47762306a36Sopenharmony_ci		snprintf(card->log, 63, "B%1d-recv %s %d ",
47862306a36Sopenharmony_ci			 wch->bch.nr, card->name, count);
47962306a36Sopenharmony_ci		print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count);
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic void
48462306a36Sopenharmony_ciW6692_fill_Bfifo(struct w6692_ch *wch)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	struct w6692_hw *card = wch->bch.hw;
48762306a36Sopenharmony_ci	int count, fillempty = 0;
48862306a36Sopenharmony_ci	u8 *ptr, cmd = W_B_CMDR_RACT | W_B_CMDR_XMS;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	pr_debug("%s: fill Bfifo\n", card->name);
49162306a36Sopenharmony_ci	if (!wch->bch.tx_skb) {
49262306a36Sopenharmony_ci		if (!test_bit(FLG_TX_EMPTY, &wch->bch.Flags))
49362306a36Sopenharmony_ci			return;
49462306a36Sopenharmony_ci		ptr = wch->bch.fill;
49562306a36Sopenharmony_ci		count = W_B_FIFO_THRESH;
49662306a36Sopenharmony_ci		fillempty = 1;
49762306a36Sopenharmony_ci	} else {
49862306a36Sopenharmony_ci		count = wch->bch.tx_skb->len - wch->bch.tx_idx;
49962306a36Sopenharmony_ci		if (count <= 0)
50062306a36Sopenharmony_ci			return;
50162306a36Sopenharmony_ci		ptr = wch->bch.tx_skb->data + wch->bch.tx_idx;
50262306a36Sopenharmony_ci	}
50362306a36Sopenharmony_ci	if (count > W_B_FIFO_THRESH)
50462306a36Sopenharmony_ci		count = W_B_FIFO_THRESH;
50562306a36Sopenharmony_ci	else if (test_bit(FLG_HDLC, &wch->bch.Flags))
50662306a36Sopenharmony_ci		cmd |= W_B_CMDR_XME;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	pr_debug("%s: fill Bfifo%d/%d\n", card->name,
50962306a36Sopenharmony_ci		 count, wch->bch.tx_idx);
51062306a36Sopenharmony_ci	wch->bch.tx_idx += count;
51162306a36Sopenharmony_ci	if (fillempty) {
51262306a36Sopenharmony_ci		while (count > 0) {
51362306a36Sopenharmony_ci			outsb(wch->addr + W_B_XFIFO, ptr, MISDN_BCH_FILL_SIZE);
51462306a36Sopenharmony_ci			count -= MISDN_BCH_FILL_SIZE;
51562306a36Sopenharmony_ci		}
51662306a36Sopenharmony_ci	} else {
51762306a36Sopenharmony_ci		outsb(wch->addr + W_B_XFIFO, ptr, count);
51862306a36Sopenharmony_ci	}
51962306a36Sopenharmony_ci	WriteW6692B(wch, W_B_CMDR, cmd);
52062306a36Sopenharmony_ci	if ((debug & DEBUG_HW_BFIFO) && !fillempty) {
52162306a36Sopenharmony_ci		snprintf(card->log, 63, "B%1d-send %s %d ",
52262306a36Sopenharmony_ci			 wch->bch.nr, card->name, count);
52362306a36Sopenharmony_ci		print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count);
52462306a36Sopenharmony_ci	}
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci#if 0
52862306a36Sopenharmony_cistatic int
52962306a36Sopenharmony_cisetvolume(struct w6692_ch *wch, int mic, struct sk_buff *skb)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci	struct w6692_hw *card = wch->bch.hw;
53262306a36Sopenharmony_ci	u16 *vol = (u16 *)skb->data;
53362306a36Sopenharmony_ci	u8 val;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	if ((!(card->fmask & pots)) ||
53662306a36Sopenharmony_ci	    !test_bit(FLG_TRANSPARENT, &wch->bch.Flags))
53762306a36Sopenharmony_ci		return -ENODEV;
53862306a36Sopenharmony_ci	if (skb->len < 2)
53962306a36Sopenharmony_ci		return -EINVAL;
54062306a36Sopenharmony_ci	if (*vol > 7)
54162306a36Sopenharmony_ci		return -EINVAL;
54262306a36Sopenharmony_ci	val = *vol & 7;
54362306a36Sopenharmony_ci	val = 7 - val;
54462306a36Sopenharmony_ci	if (mic) {
54562306a36Sopenharmony_ci		val <<= 3;
54662306a36Sopenharmony_ci		card->xaddr &= 0xc7;
54762306a36Sopenharmony_ci	} else {
54862306a36Sopenharmony_ci		card->xaddr &= 0xf8;
54962306a36Sopenharmony_ci	}
55062306a36Sopenharmony_ci	card->xaddr |= val;
55162306a36Sopenharmony_ci	WriteW6692(card, W_XADDR, card->xaddr);
55262306a36Sopenharmony_ci	return 0;
55362306a36Sopenharmony_ci}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_cistatic int
55662306a36Sopenharmony_cienable_pots(struct w6692_ch *wch)
55762306a36Sopenharmony_ci{
55862306a36Sopenharmony_ci	struct w6692_hw *card = wch->bch.hw;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	if ((!(card->fmask & pots)) ||
56162306a36Sopenharmony_ci	    !test_bit(FLG_TRANSPARENT, &wch->bch.Flags))
56262306a36Sopenharmony_ci		return -ENODEV;
56362306a36Sopenharmony_ci	wch->b_mode |= W_B_MODE_EPCM | W_B_MODE_BSW0;
56462306a36Sopenharmony_ci	WriteW6692B(wch, W_B_MODE, wch->b_mode);
56562306a36Sopenharmony_ci	WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
56662306a36Sopenharmony_ci	card->pctl |= ((wch->bch.nr & 2) ? W_PCTL_PCX : 0);
56762306a36Sopenharmony_ci	WriteW6692(card, W_PCTL, card->pctl);
56862306a36Sopenharmony_ci	return 0;
56962306a36Sopenharmony_ci}
57062306a36Sopenharmony_ci#endif
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistatic int
57362306a36Sopenharmony_cidisable_pots(struct w6692_ch *wch)
57462306a36Sopenharmony_ci{
57562306a36Sopenharmony_ci	struct w6692_hw *card = wch->bch.hw;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	if (!(card->fmask & pots))
57862306a36Sopenharmony_ci		return -ENODEV;
57962306a36Sopenharmony_ci	wch->b_mode &= ~(W_B_MODE_EPCM | W_B_MODE_BSW0);
58062306a36Sopenharmony_ci	WriteW6692B(wch, W_B_MODE, wch->b_mode);
58162306a36Sopenharmony_ci	WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT |
58262306a36Sopenharmony_ci		    W_B_CMDR_XRST);
58362306a36Sopenharmony_ci	return 0;
58462306a36Sopenharmony_ci}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_cistatic int
58762306a36Sopenharmony_ciw6692_mode(struct w6692_ch *wch, u32 pr)
58862306a36Sopenharmony_ci{
58962306a36Sopenharmony_ci	struct w6692_hw	*card;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	card = wch->bch.hw;
59262306a36Sopenharmony_ci	pr_debug("%s: B%d protocol %x-->%x\n", card->name,
59362306a36Sopenharmony_ci		 wch->bch.nr, wch->bch.state, pr);
59462306a36Sopenharmony_ci	switch (pr) {
59562306a36Sopenharmony_ci	case ISDN_P_NONE:
59662306a36Sopenharmony_ci		if ((card->fmask & pots) && (wch->b_mode & W_B_MODE_EPCM))
59762306a36Sopenharmony_ci			disable_pots(wch);
59862306a36Sopenharmony_ci		wch->b_mode = 0;
59962306a36Sopenharmony_ci		mISDN_clear_bchannel(&wch->bch);
60062306a36Sopenharmony_ci		WriteW6692B(wch, W_B_MODE, wch->b_mode);
60162306a36Sopenharmony_ci		WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
60262306a36Sopenharmony_ci		test_and_clear_bit(FLG_HDLC, &wch->bch.Flags);
60362306a36Sopenharmony_ci		test_and_clear_bit(FLG_TRANSPARENT, &wch->bch.Flags);
60462306a36Sopenharmony_ci		break;
60562306a36Sopenharmony_ci	case ISDN_P_B_RAW:
60662306a36Sopenharmony_ci		wch->b_mode = W_B_MODE_MMS;
60762306a36Sopenharmony_ci		WriteW6692B(wch, W_B_MODE, wch->b_mode);
60862306a36Sopenharmony_ci		WriteW6692B(wch, W_B_EXIM, 0);
60962306a36Sopenharmony_ci		WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT |
61062306a36Sopenharmony_ci			    W_B_CMDR_XRST);
61162306a36Sopenharmony_ci		test_and_set_bit(FLG_TRANSPARENT, &wch->bch.Flags);
61262306a36Sopenharmony_ci		break;
61362306a36Sopenharmony_ci	case ISDN_P_B_HDLC:
61462306a36Sopenharmony_ci		wch->b_mode = W_B_MODE_ITF;
61562306a36Sopenharmony_ci		WriteW6692B(wch, W_B_MODE, wch->b_mode);
61662306a36Sopenharmony_ci		WriteW6692B(wch, W_B_ADM1, 0xff);
61762306a36Sopenharmony_ci		WriteW6692B(wch, W_B_ADM2, 0xff);
61862306a36Sopenharmony_ci		WriteW6692B(wch, W_B_EXIM, 0);
61962306a36Sopenharmony_ci		WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT |
62062306a36Sopenharmony_ci			    W_B_CMDR_XRST);
62162306a36Sopenharmony_ci		test_and_set_bit(FLG_HDLC, &wch->bch.Flags);
62262306a36Sopenharmony_ci		break;
62362306a36Sopenharmony_ci	default:
62462306a36Sopenharmony_ci		pr_info("%s: protocol %x not known\n", card->name, pr);
62562306a36Sopenharmony_ci		return -ENOPROTOOPT;
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci	wch->bch.state = pr;
62862306a36Sopenharmony_ci	return 0;
62962306a36Sopenharmony_ci}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_cistatic void
63262306a36Sopenharmony_cisend_next(struct w6692_ch *wch)
63362306a36Sopenharmony_ci{
63462306a36Sopenharmony_ci	if (wch->bch.tx_skb && wch->bch.tx_idx < wch->bch.tx_skb->len) {
63562306a36Sopenharmony_ci		W6692_fill_Bfifo(wch);
63662306a36Sopenharmony_ci	} else {
63762306a36Sopenharmony_ci		dev_kfree_skb(wch->bch.tx_skb);
63862306a36Sopenharmony_ci		if (get_next_bframe(&wch->bch)) {
63962306a36Sopenharmony_ci			W6692_fill_Bfifo(wch);
64062306a36Sopenharmony_ci			test_and_clear_bit(FLG_TX_EMPTY, &wch->bch.Flags);
64162306a36Sopenharmony_ci		} else if (test_bit(FLG_TX_EMPTY, &wch->bch.Flags)) {
64262306a36Sopenharmony_ci			W6692_fill_Bfifo(wch);
64362306a36Sopenharmony_ci		}
64462306a36Sopenharmony_ci	}
64562306a36Sopenharmony_ci}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cistatic void
64862306a36Sopenharmony_ciW6692B_interrupt(struct w6692_hw *card, int ch)
64962306a36Sopenharmony_ci{
65062306a36Sopenharmony_ci	struct w6692_ch	*wch = &card->bc[ch];
65162306a36Sopenharmony_ci	int		count;
65262306a36Sopenharmony_ci	u8		stat, star = 0;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	stat = ReadW6692B(wch, W_B_EXIR);
65562306a36Sopenharmony_ci	pr_debug("%s: B%d EXIR %02x\n", card->name, wch->bch.nr, stat);
65662306a36Sopenharmony_ci	if (stat & W_B_EXI_RME) {
65762306a36Sopenharmony_ci		star = ReadW6692B(wch, W_B_STAR);
65862306a36Sopenharmony_ci		if (star & (W_B_STAR_RDOV | W_B_STAR_CRCE | W_B_STAR_RMB)) {
65962306a36Sopenharmony_ci			if ((star & W_B_STAR_RDOV) &&
66062306a36Sopenharmony_ci			    test_bit(FLG_ACTIVE, &wch->bch.Flags)) {
66162306a36Sopenharmony_ci				pr_debug("%s: B%d RDOV proto=%x\n", card->name,
66262306a36Sopenharmony_ci					 wch->bch.nr, wch->bch.state);
66362306a36Sopenharmony_ci#ifdef ERROR_STATISTIC
66462306a36Sopenharmony_ci				wch->bch.err_rdo++;
66562306a36Sopenharmony_ci#endif
66662306a36Sopenharmony_ci			}
66762306a36Sopenharmony_ci			if (test_bit(FLG_HDLC, &wch->bch.Flags)) {
66862306a36Sopenharmony_ci				if (star & W_B_STAR_CRCE) {
66962306a36Sopenharmony_ci					pr_debug("%s: B%d CRC error\n",
67062306a36Sopenharmony_ci						 card->name, wch->bch.nr);
67162306a36Sopenharmony_ci#ifdef ERROR_STATISTIC
67262306a36Sopenharmony_ci					wch->bch.err_crc++;
67362306a36Sopenharmony_ci#endif
67462306a36Sopenharmony_ci				}
67562306a36Sopenharmony_ci				if (star & W_B_STAR_RMB) {
67662306a36Sopenharmony_ci					pr_debug("%s: B%d message abort\n",
67762306a36Sopenharmony_ci						 card->name, wch->bch.nr);
67862306a36Sopenharmony_ci#ifdef ERROR_STATISTIC
67962306a36Sopenharmony_ci					wch->bch.err_inv++;
68062306a36Sopenharmony_ci#endif
68162306a36Sopenharmony_ci				}
68262306a36Sopenharmony_ci			}
68362306a36Sopenharmony_ci			WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK |
68462306a36Sopenharmony_ci				    W_B_CMDR_RRST | W_B_CMDR_RACT);
68562306a36Sopenharmony_ci			if (wch->bch.rx_skb)
68662306a36Sopenharmony_ci				skb_trim(wch->bch.rx_skb, 0);
68762306a36Sopenharmony_ci		} else {
68862306a36Sopenharmony_ci			count = ReadW6692B(wch, W_B_RBCL) &
68962306a36Sopenharmony_ci				(W_B_FIFO_THRESH - 1);
69062306a36Sopenharmony_ci			if (count == 0)
69162306a36Sopenharmony_ci				count = W_B_FIFO_THRESH;
69262306a36Sopenharmony_ci			W6692_empty_Bfifo(wch, count);
69362306a36Sopenharmony_ci			recv_Bchannel(&wch->bch, 0, false);
69462306a36Sopenharmony_ci		}
69562306a36Sopenharmony_ci	}
69662306a36Sopenharmony_ci	if (stat & W_B_EXI_RMR) {
69762306a36Sopenharmony_ci		if (!(stat & W_B_EXI_RME))
69862306a36Sopenharmony_ci			star = ReadW6692B(wch, W_B_STAR);
69962306a36Sopenharmony_ci		if (star & W_B_STAR_RDOV) {
70062306a36Sopenharmony_ci			pr_debug("%s: B%d RDOV proto=%x\n", card->name,
70162306a36Sopenharmony_ci				 wch->bch.nr, wch->bch.state);
70262306a36Sopenharmony_ci#ifdef ERROR_STATISTIC
70362306a36Sopenharmony_ci			wch->bch.err_rdo++;
70462306a36Sopenharmony_ci#endif
70562306a36Sopenharmony_ci			WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK |
70662306a36Sopenharmony_ci				    W_B_CMDR_RRST | W_B_CMDR_RACT);
70762306a36Sopenharmony_ci		} else {
70862306a36Sopenharmony_ci			W6692_empty_Bfifo(wch, W_B_FIFO_THRESH);
70962306a36Sopenharmony_ci			if (test_bit(FLG_TRANSPARENT, &wch->bch.Flags))
71062306a36Sopenharmony_ci				recv_Bchannel(&wch->bch, 0, false);
71162306a36Sopenharmony_ci		}
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci	if (stat & W_B_EXI_RDOV) {
71462306a36Sopenharmony_ci		/* only if it is not handled yet */
71562306a36Sopenharmony_ci		if (!(star & W_B_STAR_RDOV)) {
71662306a36Sopenharmony_ci			pr_debug("%s: B%d RDOV IRQ proto=%x\n", card->name,
71762306a36Sopenharmony_ci				 wch->bch.nr, wch->bch.state);
71862306a36Sopenharmony_ci#ifdef ERROR_STATISTIC
71962306a36Sopenharmony_ci			wch->bch.err_rdo++;
72062306a36Sopenharmony_ci#endif
72162306a36Sopenharmony_ci			WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK |
72262306a36Sopenharmony_ci				    W_B_CMDR_RRST | W_B_CMDR_RACT);
72362306a36Sopenharmony_ci		}
72462306a36Sopenharmony_ci	}
72562306a36Sopenharmony_ci	if (stat & W_B_EXI_XFR) {
72662306a36Sopenharmony_ci		if (!(stat & (W_B_EXI_RME | W_B_EXI_RMR))) {
72762306a36Sopenharmony_ci			star = ReadW6692B(wch, W_B_STAR);
72862306a36Sopenharmony_ci			pr_debug("%s: B%d star %02x\n", card->name,
72962306a36Sopenharmony_ci				 wch->bch.nr, star);
73062306a36Sopenharmony_ci		}
73162306a36Sopenharmony_ci		if (star & W_B_STAR_XDOW) {
73262306a36Sopenharmony_ci			pr_warn("%s: B%d XDOW proto=%x\n", card->name,
73362306a36Sopenharmony_ci				wch->bch.nr, wch->bch.state);
73462306a36Sopenharmony_ci#ifdef ERROR_STATISTIC
73562306a36Sopenharmony_ci			wch->bch.err_xdu++;
73662306a36Sopenharmony_ci#endif
73762306a36Sopenharmony_ci			WriteW6692B(wch, W_B_CMDR, W_B_CMDR_XRST |
73862306a36Sopenharmony_ci				    W_B_CMDR_RACT);
73962306a36Sopenharmony_ci			/* resend */
74062306a36Sopenharmony_ci			if (wch->bch.tx_skb) {
74162306a36Sopenharmony_ci				if (!test_bit(FLG_TRANSPARENT, &wch->bch.Flags))
74262306a36Sopenharmony_ci					wch->bch.tx_idx = 0;
74362306a36Sopenharmony_ci			}
74462306a36Sopenharmony_ci		}
74562306a36Sopenharmony_ci		send_next(wch);
74662306a36Sopenharmony_ci		if (star & W_B_STAR_XDOW)
74762306a36Sopenharmony_ci			return; /* handle XDOW only once */
74862306a36Sopenharmony_ci	}
74962306a36Sopenharmony_ci	if (stat & W_B_EXI_XDUN) {
75062306a36Sopenharmony_ci		pr_warn("%s: B%d XDUN proto=%x\n", card->name,
75162306a36Sopenharmony_ci			wch->bch.nr, wch->bch.state);
75262306a36Sopenharmony_ci#ifdef ERROR_STATISTIC
75362306a36Sopenharmony_ci		wch->bch.err_xdu++;
75462306a36Sopenharmony_ci#endif
75562306a36Sopenharmony_ci		/* resend - no XRST needed */
75662306a36Sopenharmony_ci		if (wch->bch.tx_skb) {
75762306a36Sopenharmony_ci			if (!test_bit(FLG_TRANSPARENT, &wch->bch.Flags))
75862306a36Sopenharmony_ci				wch->bch.tx_idx = 0;
75962306a36Sopenharmony_ci		} else if (test_bit(FLG_FILLEMPTY, &wch->bch.Flags)) {
76062306a36Sopenharmony_ci			test_and_set_bit(FLG_TX_EMPTY, &wch->bch.Flags);
76162306a36Sopenharmony_ci		}
76262306a36Sopenharmony_ci		send_next(wch);
76362306a36Sopenharmony_ci	}
76462306a36Sopenharmony_ci}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_cistatic irqreturn_t
76762306a36Sopenharmony_ciw6692_irq(int intno, void *dev_id)
76862306a36Sopenharmony_ci{
76962306a36Sopenharmony_ci	struct w6692_hw	*card = dev_id;
77062306a36Sopenharmony_ci	u8		ista;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	spin_lock(&card->lock);
77362306a36Sopenharmony_ci	ista = ReadW6692(card, W_ISTA);
77462306a36Sopenharmony_ci	if ((ista | card->imask) == card->imask) {
77562306a36Sopenharmony_ci		/* possible a shared  IRQ reqest */
77662306a36Sopenharmony_ci		spin_unlock(&card->lock);
77762306a36Sopenharmony_ci		return IRQ_NONE;
77862306a36Sopenharmony_ci	}
77962306a36Sopenharmony_ci	card->irqcnt++;
78062306a36Sopenharmony_ci	pr_debug("%s: ista %02x\n", card->name, ista);
78162306a36Sopenharmony_ci	ista &= ~card->imask;
78262306a36Sopenharmony_ci	if (ista & W_INT_B1_EXI)
78362306a36Sopenharmony_ci		W6692B_interrupt(card, 0);
78462306a36Sopenharmony_ci	if (ista & W_INT_B2_EXI)
78562306a36Sopenharmony_ci		W6692B_interrupt(card, 1);
78662306a36Sopenharmony_ci	if (ista & W_INT_D_RME)
78762306a36Sopenharmony_ci		handle_rxD(card);
78862306a36Sopenharmony_ci	if (ista & W_INT_D_RMR)
78962306a36Sopenharmony_ci		W6692_empty_Dfifo(card, W_D_FIFO_THRESH);
79062306a36Sopenharmony_ci	if (ista & W_INT_D_XFR)
79162306a36Sopenharmony_ci		handle_txD(card);
79262306a36Sopenharmony_ci	if (ista & W_INT_D_EXI)
79362306a36Sopenharmony_ci		handle_statusD(card);
79462306a36Sopenharmony_ci	if (ista & (W_INT_XINT0 | W_INT_XINT1)) /* XINT0/1 - never */
79562306a36Sopenharmony_ci		pr_debug("%s: W6692 spurious XINT!\n", card->name);
79662306a36Sopenharmony_ci/* End IRQ Handler */
79762306a36Sopenharmony_ci	spin_unlock(&card->lock);
79862306a36Sopenharmony_ci	return IRQ_HANDLED;
79962306a36Sopenharmony_ci}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_cistatic void
80262306a36Sopenharmony_cidbusy_timer_handler(struct timer_list *t)
80362306a36Sopenharmony_ci{
80462306a36Sopenharmony_ci	struct dchannel *dch = from_timer(dch, t, timer);
80562306a36Sopenharmony_ci	struct w6692_hw	*card = dch->hw;
80662306a36Sopenharmony_ci	int		rbch, star;
80762306a36Sopenharmony_ci	u_long		flags;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	if (test_bit(FLG_BUSY_TIMER, &dch->Flags)) {
81062306a36Sopenharmony_ci		spin_lock_irqsave(&card->lock, flags);
81162306a36Sopenharmony_ci		rbch = ReadW6692(card, W_D_RBCH);
81262306a36Sopenharmony_ci		star = ReadW6692(card, W_D_STAR);
81362306a36Sopenharmony_ci		pr_debug("%s: D-Channel Busy RBCH %02x STAR %02x\n",
81462306a36Sopenharmony_ci			 card->name, rbch, star);
81562306a36Sopenharmony_ci		if (star & W_D_STAR_XBZ)	/* D-Channel Busy */
81662306a36Sopenharmony_ci			test_and_set_bit(FLG_L1_BUSY, &dch->Flags);
81762306a36Sopenharmony_ci		else {
81862306a36Sopenharmony_ci			/* discard frame; reset transceiver */
81962306a36Sopenharmony_ci			test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags);
82062306a36Sopenharmony_ci			if (dch->tx_idx)
82162306a36Sopenharmony_ci				dch->tx_idx = 0;
82262306a36Sopenharmony_ci			else
82362306a36Sopenharmony_ci				pr_info("%s: W6692 D-Channel Busy no tx_idx\n",
82462306a36Sopenharmony_ci					card->name);
82562306a36Sopenharmony_ci			/* Transmitter reset */
82662306a36Sopenharmony_ci			WriteW6692(card, W_D_CMDR, W_D_CMDR_XRST);
82762306a36Sopenharmony_ci		}
82862306a36Sopenharmony_ci		spin_unlock_irqrestore(&card->lock, flags);
82962306a36Sopenharmony_ci	}
83062306a36Sopenharmony_ci}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_cistatic void initW6692(struct w6692_hw *card)
83362306a36Sopenharmony_ci{
83462306a36Sopenharmony_ci	u8	val;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	timer_setup(&card->dch.timer, dbusy_timer_handler, 0);
83762306a36Sopenharmony_ci	w6692_mode(&card->bc[0], ISDN_P_NONE);
83862306a36Sopenharmony_ci	w6692_mode(&card->bc[1], ISDN_P_NONE);
83962306a36Sopenharmony_ci	WriteW6692(card, W_D_CTL, 0x00);
84062306a36Sopenharmony_ci	disable_hwirq(card);
84162306a36Sopenharmony_ci	WriteW6692(card, W_D_SAM, 0xff);
84262306a36Sopenharmony_ci	WriteW6692(card, W_D_TAM, 0xff);
84362306a36Sopenharmony_ci	WriteW6692(card, W_D_MODE, W_D_MODE_RACT);
84462306a36Sopenharmony_ci	card->state = W_L1CMD_RST;
84562306a36Sopenharmony_ci	ph_command(card, W_L1CMD_RST);
84662306a36Sopenharmony_ci	ph_command(card, W_L1CMD_ECK);
84762306a36Sopenharmony_ci	/* enable all IRQ but extern */
84862306a36Sopenharmony_ci	card->imask = 0x18;
84962306a36Sopenharmony_ci	WriteW6692(card, W_D_EXIM, 0x00);
85062306a36Sopenharmony_ci	WriteW6692B(&card->bc[0], W_B_EXIM, 0);
85162306a36Sopenharmony_ci	WriteW6692B(&card->bc[1], W_B_EXIM, 0);
85262306a36Sopenharmony_ci	/* Reset D-chan receiver and transmitter */
85362306a36Sopenharmony_ci	WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST | W_D_CMDR_XRST);
85462306a36Sopenharmony_ci	/* Reset B-chan receiver and transmitter */
85562306a36Sopenharmony_ci	WriteW6692B(&card->bc[0], W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
85662306a36Sopenharmony_ci	WriteW6692B(&card->bc[1], W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
85762306a36Sopenharmony_ci	/* enable peripheral */
85862306a36Sopenharmony_ci	if (card->subtype == W6692_USR) {
85962306a36Sopenharmony_ci		/* seems that USR implemented some power control features
86062306a36Sopenharmony_ci		 * Pin 79 is connected to the oscilator circuit so we
86162306a36Sopenharmony_ci		 * have to handle it here
86262306a36Sopenharmony_ci		 */
86362306a36Sopenharmony_ci		card->pctl = 0x80;
86462306a36Sopenharmony_ci		card->xdata = 0;
86562306a36Sopenharmony_ci		WriteW6692(card, W_PCTL, card->pctl);
86662306a36Sopenharmony_ci		WriteW6692(card, W_XDATA, card->xdata);
86762306a36Sopenharmony_ci	} else {
86862306a36Sopenharmony_ci		card->pctl = W_PCTL_OE5 | W_PCTL_OE4 | W_PCTL_OE2 |
86962306a36Sopenharmony_ci			W_PCTL_OE1 | W_PCTL_OE0;
87062306a36Sopenharmony_ci		card->xaddr = 0x00;/* all sw off */
87162306a36Sopenharmony_ci		if (card->fmask & pots)
87262306a36Sopenharmony_ci			card->xdata |= 0x06;	/*  POWER UP/ LED OFF / ALAW */
87362306a36Sopenharmony_ci		if (card->fmask & led)
87462306a36Sopenharmony_ci			card->xdata |= 0x04;	/* LED OFF */
87562306a36Sopenharmony_ci		if ((card->fmask & pots) || (card->fmask & led)) {
87662306a36Sopenharmony_ci			WriteW6692(card, W_PCTL, card->pctl);
87762306a36Sopenharmony_ci			WriteW6692(card, W_XADDR, card->xaddr);
87862306a36Sopenharmony_ci			WriteW6692(card, W_XDATA, card->xdata);
87962306a36Sopenharmony_ci			val = ReadW6692(card, W_XADDR);
88062306a36Sopenharmony_ci			if (debug & DEBUG_HW)
88162306a36Sopenharmony_ci				pr_notice("%s: W_XADDR=%02x\n",
88262306a36Sopenharmony_ci					  card->name, val);
88362306a36Sopenharmony_ci		}
88462306a36Sopenharmony_ci	}
88562306a36Sopenharmony_ci}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_cistatic void
88862306a36Sopenharmony_cireset_w6692(struct w6692_hw *card)
88962306a36Sopenharmony_ci{
89062306a36Sopenharmony_ci	WriteW6692(card, W_D_CTL, W_D_CTL_SRST);
89162306a36Sopenharmony_ci	mdelay(10);
89262306a36Sopenharmony_ci	WriteW6692(card, W_D_CTL, 0);
89362306a36Sopenharmony_ci}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_cistatic int
89662306a36Sopenharmony_ciinit_card(struct w6692_hw *card)
89762306a36Sopenharmony_ci{
89862306a36Sopenharmony_ci	int	cnt = 3;
89962306a36Sopenharmony_ci	u_long	flags;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	spin_lock_irqsave(&card->lock, flags);
90262306a36Sopenharmony_ci	disable_hwirq(card);
90362306a36Sopenharmony_ci	spin_unlock_irqrestore(&card->lock, flags);
90462306a36Sopenharmony_ci	if (request_irq(card->irq, w6692_irq, IRQF_SHARED, card->name, card)) {
90562306a36Sopenharmony_ci		pr_info("%s: couldn't get interrupt %d\n", card->name,
90662306a36Sopenharmony_ci			card->irq);
90762306a36Sopenharmony_ci		return -EIO;
90862306a36Sopenharmony_ci	}
90962306a36Sopenharmony_ci	while (cnt--) {
91062306a36Sopenharmony_ci		spin_lock_irqsave(&card->lock, flags);
91162306a36Sopenharmony_ci		initW6692(card);
91262306a36Sopenharmony_ci		enable_hwirq(card);
91362306a36Sopenharmony_ci		spin_unlock_irqrestore(&card->lock, flags);
91462306a36Sopenharmony_ci		/* Timeout 10ms */
91562306a36Sopenharmony_ci		msleep_interruptible(10);
91662306a36Sopenharmony_ci		if (debug & DEBUG_HW)
91762306a36Sopenharmony_ci			pr_notice("%s: IRQ %d count %d\n", card->name,
91862306a36Sopenharmony_ci				  card->irq, card->irqcnt);
91962306a36Sopenharmony_ci		if (!card->irqcnt) {
92062306a36Sopenharmony_ci			pr_info("%s: IRQ(%d) getting no IRQs during init %d\n",
92162306a36Sopenharmony_ci				card->name, card->irq, 3 - cnt);
92262306a36Sopenharmony_ci			reset_w6692(card);
92362306a36Sopenharmony_ci		} else
92462306a36Sopenharmony_ci			return 0;
92562306a36Sopenharmony_ci	}
92662306a36Sopenharmony_ci	free_irq(card->irq, card);
92762306a36Sopenharmony_ci	return -EIO;
92862306a36Sopenharmony_ci}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_cistatic int
93162306a36Sopenharmony_ciw6692_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb)
93262306a36Sopenharmony_ci{
93362306a36Sopenharmony_ci	struct bchannel *bch = container_of(ch, struct bchannel, ch);
93462306a36Sopenharmony_ci	struct w6692_ch	*bc = container_of(bch, struct w6692_ch, bch);
93562306a36Sopenharmony_ci	struct w6692_hw *card = bch->hw;
93662306a36Sopenharmony_ci	int ret = -EINVAL;
93762306a36Sopenharmony_ci	struct mISDNhead *hh = mISDN_HEAD_P(skb);
93862306a36Sopenharmony_ci	unsigned long flags;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	switch (hh->prim) {
94162306a36Sopenharmony_ci	case PH_DATA_REQ:
94262306a36Sopenharmony_ci		spin_lock_irqsave(&card->lock, flags);
94362306a36Sopenharmony_ci		ret = bchannel_senddata(bch, skb);
94462306a36Sopenharmony_ci		if (ret > 0) { /* direct TX */
94562306a36Sopenharmony_ci			ret = 0;
94662306a36Sopenharmony_ci			W6692_fill_Bfifo(bc);
94762306a36Sopenharmony_ci		}
94862306a36Sopenharmony_ci		spin_unlock_irqrestore(&card->lock, flags);
94962306a36Sopenharmony_ci		return ret;
95062306a36Sopenharmony_ci	case PH_ACTIVATE_REQ:
95162306a36Sopenharmony_ci		spin_lock_irqsave(&card->lock, flags);
95262306a36Sopenharmony_ci		if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
95362306a36Sopenharmony_ci			ret = w6692_mode(bc, ch->protocol);
95462306a36Sopenharmony_ci		else
95562306a36Sopenharmony_ci			ret = 0;
95662306a36Sopenharmony_ci		spin_unlock_irqrestore(&card->lock, flags);
95762306a36Sopenharmony_ci		if (!ret)
95862306a36Sopenharmony_ci			_queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
95962306a36Sopenharmony_ci				    NULL, GFP_KERNEL);
96062306a36Sopenharmony_ci		break;
96162306a36Sopenharmony_ci	case PH_DEACTIVATE_REQ:
96262306a36Sopenharmony_ci		spin_lock_irqsave(&card->lock, flags);
96362306a36Sopenharmony_ci		mISDN_clear_bchannel(bch);
96462306a36Sopenharmony_ci		w6692_mode(bc, ISDN_P_NONE);
96562306a36Sopenharmony_ci		spin_unlock_irqrestore(&card->lock, flags);
96662306a36Sopenharmony_ci		_queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
96762306a36Sopenharmony_ci			    NULL, GFP_KERNEL);
96862306a36Sopenharmony_ci		ret = 0;
96962306a36Sopenharmony_ci		break;
97062306a36Sopenharmony_ci	default:
97162306a36Sopenharmony_ci		pr_info("%s: %s unknown prim(%x,%x)\n",
97262306a36Sopenharmony_ci			card->name, __func__, hh->prim, hh->id);
97362306a36Sopenharmony_ci		ret = -EINVAL;
97462306a36Sopenharmony_ci	}
97562306a36Sopenharmony_ci	if (!ret)
97662306a36Sopenharmony_ci		dev_kfree_skb(skb);
97762306a36Sopenharmony_ci	return ret;
97862306a36Sopenharmony_ci}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_cistatic int
98162306a36Sopenharmony_cichannel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
98262306a36Sopenharmony_ci{
98362306a36Sopenharmony_ci	return mISDN_ctrl_bchannel(bch, cq);
98462306a36Sopenharmony_ci}
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_cistatic int
98762306a36Sopenharmony_ciopen_bchannel(struct w6692_hw *card, struct channel_req *rq)
98862306a36Sopenharmony_ci{
98962306a36Sopenharmony_ci	struct bchannel *bch;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	if (rq->adr.channel == 0 || rq->adr.channel > 2)
99262306a36Sopenharmony_ci		return -EINVAL;
99362306a36Sopenharmony_ci	if (rq->protocol == ISDN_P_NONE)
99462306a36Sopenharmony_ci		return -EINVAL;
99562306a36Sopenharmony_ci	bch = &card->bc[rq->adr.channel - 1].bch;
99662306a36Sopenharmony_ci	if (test_and_set_bit(FLG_OPEN, &bch->Flags))
99762306a36Sopenharmony_ci		return -EBUSY; /* b-channel can be only open once */
99862306a36Sopenharmony_ci	bch->ch.protocol = rq->protocol;
99962306a36Sopenharmony_ci	rq->ch = &bch->ch;
100062306a36Sopenharmony_ci	return 0;
100162306a36Sopenharmony_ci}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_cistatic int
100462306a36Sopenharmony_cichannel_ctrl(struct w6692_hw *card, struct mISDN_ctrl_req *cq)
100562306a36Sopenharmony_ci{
100662306a36Sopenharmony_ci	int	ret = 0;
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	switch (cq->op) {
100962306a36Sopenharmony_ci	case MISDN_CTRL_GETOP:
101062306a36Sopenharmony_ci		cq->op = MISDN_CTRL_L1_TIMER3;
101162306a36Sopenharmony_ci		break;
101262306a36Sopenharmony_ci	case MISDN_CTRL_L1_TIMER3:
101362306a36Sopenharmony_ci		ret = l1_event(card->dch.l1, HW_TIMER3_VALUE | (cq->p1 & 0xff));
101462306a36Sopenharmony_ci		break;
101562306a36Sopenharmony_ci	default:
101662306a36Sopenharmony_ci		pr_info("%s: unknown CTRL OP %x\n", card->name, cq->op);
101762306a36Sopenharmony_ci		ret = -EINVAL;
101862306a36Sopenharmony_ci		break;
101962306a36Sopenharmony_ci	}
102062306a36Sopenharmony_ci	return ret;
102162306a36Sopenharmony_ci}
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_cistatic int
102462306a36Sopenharmony_ciw6692_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
102562306a36Sopenharmony_ci{
102662306a36Sopenharmony_ci	struct bchannel *bch = container_of(ch, struct bchannel, ch);
102762306a36Sopenharmony_ci	struct w6692_ch *bc = container_of(bch, struct w6692_ch, bch);
102862306a36Sopenharmony_ci	struct w6692_hw *card = bch->hw;
102962306a36Sopenharmony_ci	int ret = -EINVAL;
103062306a36Sopenharmony_ci	u_long flags;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	pr_debug("%s: %s cmd:%x %p\n", card->name, __func__, cmd, arg);
103362306a36Sopenharmony_ci	switch (cmd) {
103462306a36Sopenharmony_ci	case CLOSE_CHANNEL:
103562306a36Sopenharmony_ci		test_and_clear_bit(FLG_OPEN, &bch->Flags);
103662306a36Sopenharmony_ci		cancel_work_sync(&bch->workq);
103762306a36Sopenharmony_ci		spin_lock_irqsave(&card->lock, flags);
103862306a36Sopenharmony_ci		mISDN_clear_bchannel(bch);
103962306a36Sopenharmony_ci		w6692_mode(bc, ISDN_P_NONE);
104062306a36Sopenharmony_ci		spin_unlock_irqrestore(&card->lock, flags);
104162306a36Sopenharmony_ci		ch->protocol = ISDN_P_NONE;
104262306a36Sopenharmony_ci		ch->peer = NULL;
104362306a36Sopenharmony_ci		module_put(THIS_MODULE);
104462306a36Sopenharmony_ci		ret = 0;
104562306a36Sopenharmony_ci		break;
104662306a36Sopenharmony_ci	case CONTROL_CHANNEL:
104762306a36Sopenharmony_ci		ret = channel_bctrl(bch, arg);
104862306a36Sopenharmony_ci		break;
104962306a36Sopenharmony_ci	default:
105062306a36Sopenharmony_ci		pr_info("%s: %s unknown prim(%x)\n",
105162306a36Sopenharmony_ci			card->name, __func__, cmd);
105262306a36Sopenharmony_ci	}
105362306a36Sopenharmony_ci	return ret;
105462306a36Sopenharmony_ci}
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_cistatic int
105762306a36Sopenharmony_ciw6692_l2l1D(struct mISDNchannel *ch, struct sk_buff *skb)
105862306a36Sopenharmony_ci{
105962306a36Sopenharmony_ci	struct mISDNdevice	*dev = container_of(ch, struct mISDNdevice, D);
106062306a36Sopenharmony_ci	struct dchannel		*dch = container_of(dev, struct dchannel, dev);
106162306a36Sopenharmony_ci	struct w6692_hw		*card = container_of(dch, struct w6692_hw, dch);
106262306a36Sopenharmony_ci	int			ret = -EINVAL;
106362306a36Sopenharmony_ci	struct mISDNhead	*hh = mISDN_HEAD_P(skb);
106462306a36Sopenharmony_ci	u32			id;
106562306a36Sopenharmony_ci	u_long			flags;
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	switch (hh->prim) {
106862306a36Sopenharmony_ci	case PH_DATA_REQ:
106962306a36Sopenharmony_ci		spin_lock_irqsave(&card->lock, flags);
107062306a36Sopenharmony_ci		ret = dchannel_senddata(dch, skb);
107162306a36Sopenharmony_ci		if (ret > 0) { /* direct TX */
107262306a36Sopenharmony_ci			id = hh->id; /* skb can be freed */
107362306a36Sopenharmony_ci			W6692_fill_Dfifo(card);
107462306a36Sopenharmony_ci			ret = 0;
107562306a36Sopenharmony_ci			spin_unlock_irqrestore(&card->lock, flags);
107662306a36Sopenharmony_ci			queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
107762306a36Sopenharmony_ci		} else
107862306a36Sopenharmony_ci			spin_unlock_irqrestore(&card->lock, flags);
107962306a36Sopenharmony_ci		return ret;
108062306a36Sopenharmony_ci	case PH_ACTIVATE_REQ:
108162306a36Sopenharmony_ci		ret = l1_event(dch->l1, hh->prim);
108262306a36Sopenharmony_ci		break;
108362306a36Sopenharmony_ci	case PH_DEACTIVATE_REQ:
108462306a36Sopenharmony_ci		test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags);
108562306a36Sopenharmony_ci		ret = l1_event(dch->l1, hh->prim);
108662306a36Sopenharmony_ci		break;
108762306a36Sopenharmony_ci	}
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	if (!ret)
109062306a36Sopenharmony_ci		dev_kfree_skb(skb);
109162306a36Sopenharmony_ci	return ret;
109262306a36Sopenharmony_ci}
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_cistatic int
109562306a36Sopenharmony_ciw6692_l1callback(struct dchannel *dch, u32 cmd)
109662306a36Sopenharmony_ci{
109762306a36Sopenharmony_ci	struct w6692_hw *card = container_of(dch, struct w6692_hw, dch);
109862306a36Sopenharmony_ci	u_long flags;
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	pr_debug("%s: cmd(%x) state(%02x)\n", card->name, cmd, card->state);
110162306a36Sopenharmony_ci	switch (cmd) {
110262306a36Sopenharmony_ci	case INFO3_P8:
110362306a36Sopenharmony_ci		spin_lock_irqsave(&card->lock, flags);
110462306a36Sopenharmony_ci		ph_command(card, W_L1CMD_AR8);
110562306a36Sopenharmony_ci		spin_unlock_irqrestore(&card->lock, flags);
110662306a36Sopenharmony_ci		break;
110762306a36Sopenharmony_ci	case INFO3_P10:
110862306a36Sopenharmony_ci		spin_lock_irqsave(&card->lock, flags);
110962306a36Sopenharmony_ci		ph_command(card, W_L1CMD_AR10);
111062306a36Sopenharmony_ci		spin_unlock_irqrestore(&card->lock, flags);
111162306a36Sopenharmony_ci		break;
111262306a36Sopenharmony_ci	case HW_RESET_REQ:
111362306a36Sopenharmony_ci		spin_lock_irqsave(&card->lock, flags);
111462306a36Sopenharmony_ci		if (card->state != W_L1IND_DRD)
111562306a36Sopenharmony_ci			ph_command(card, W_L1CMD_RST);
111662306a36Sopenharmony_ci		ph_command(card, W_L1CMD_ECK);
111762306a36Sopenharmony_ci		spin_unlock_irqrestore(&card->lock, flags);
111862306a36Sopenharmony_ci		break;
111962306a36Sopenharmony_ci	case HW_DEACT_REQ:
112062306a36Sopenharmony_ci		skb_queue_purge(&dch->squeue);
112162306a36Sopenharmony_ci		if (dch->tx_skb) {
112262306a36Sopenharmony_ci			dev_kfree_skb(dch->tx_skb);
112362306a36Sopenharmony_ci			dch->tx_skb = NULL;
112462306a36Sopenharmony_ci		}
112562306a36Sopenharmony_ci		dch->tx_idx = 0;
112662306a36Sopenharmony_ci		if (dch->rx_skb) {
112762306a36Sopenharmony_ci			dev_kfree_skb(dch->rx_skb);
112862306a36Sopenharmony_ci			dch->rx_skb = NULL;
112962306a36Sopenharmony_ci		}
113062306a36Sopenharmony_ci		test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
113162306a36Sopenharmony_ci		if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
113262306a36Sopenharmony_ci			del_timer(&dch->timer);
113362306a36Sopenharmony_ci		break;
113462306a36Sopenharmony_ci	case HW_POWERUP_REQ:
113562306a36Sopenharmony_ci		spin_lock_irqsave(&card->lock, flags);
113662306a36Sopenharmony_ci		ph_command(card, W_L1CMD_ECK);
113762306a36Sopenharmony_ci		spin_unlock_irqrestore(&card->lock, flags);
113862306a36Sopenharmony_ci		break;
113962306a36Sopenharmony_ci	case PH_ACTIVATE_IND:
114062306a36Sopenharmony_ci		test_and_set_bit(FLG_ACTIVE, &dch->Flags);
114162306a36Sopenharmony_ci		_queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
114262306a36Sopenharmony_ci			    GFP_ATOMIC);
114362306a36Sopenharmony_ci		break;
114462306a36Sopenharmony_ci	case PH_DEACTIVATE_IND:
114562306a36Sopenharmony_ci		test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
114662306a36Sopenharmony_ci		_queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
114762306a36Sopenharmony_ci			    GFP_ATOMIC);
114862306a36Sopenharmony_ci		break;
114962306a36Sopenharmony_ci	default:
115062306a36Sopenharmony_ci		pr_debug("%s: %s unknown command %x\n", card->name,
115162306a36Sopenharmony_ci			 __func__, cmd);
115262306a36Sopenharmony_ci		return -1;
115362306a36Sopenharmony_ci	}
115462306a36Sopenharmony_ci	return 0;
115562306a36Sopenharmony_ci}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_cistatic int
115862306a36Sopenharmony_ciopen_dchannel(struct w6692_hw *card, struct channel_req *rq, void *caller)
115962306a36Sopenharmony_ci{
116062306a36Sopenharmony_ci	pr_debug("%s: %s dev(%d) open from %p\n", card->name, __func__,
116162306a36Sopenharmony_ci		 card->dch.dev.id, caller);
116262306a36Sopenharmony_ci	if (rq->protocol != ISDN_P_TE_S0)
116362306a36Sopenharmony_ci		return -EINVAL;
116462306a36Sopenharmony_ci	if (rq->adr.channel == 1)
116562306a36Sopenharmony_ci		/* E-Channel not supported */
116662306a36Sopenharmony_ci		return -EINVAL;
116762306a36Sopenharmony_ci	rq->ch = &card->dch.dev.D;
116862306a36Sopenharmony_ci	rq->ch->protocol = rq->protocol;
116962306a36Sopenharmony_ci	if (card->dch.state == 7)
117062306a36Sopenharmony_ci		_queue_data(rq->ch, PH_ACTIVATE_IND, MISDN_ID_ANY,
117162306a36Sopenharmony_ci			    0, NULL, GFP_KERNEL);
117262306a36Sopenharmony_ci	return 0;
117362306a36Sopenharmony_ci}
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_cistatic int
117662306a36Sopenharmony_ciw6692_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
117762306a36Sopenharmony_ci{
117862306a36Sopenharmony_ci	struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D);
117962306a36Sopenharmony_ci	struct dchannel *dch = container_of(dev, struct dchannel, dev);
118062306a36Sopenharmony_ci	struct w6692_hw *card = container_of(dch, struct w6692_hw, dch);
118162306a36Sopenharmony_ci	struct channel_req *rq;
118262306a36Sopenharmony_ci	int err = 0;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	pr_debug("%s: DCTRL: %x %p\n", card->name, cmd, arg);
118562306a36Sopenharmony_ci	switch (cmd) {
118662306a36Sopenharmony_ci	case OPEN_CHANNEL:
118762306a36Sopenharmony_ci		rq = arg;
118862306a36Sopenharmony_ci		if (rq->protocol == ISDN_P_TE_S0)
118962306a36Sopenharmony_ci			err = open_dchannel(card, rq, __builtin_return_address(0));
119062306a36Sopenharmony_ci		else
119162306a36Sopenharmony_ci			err = open_bchannel(card, rq);
119262306a36Sopenharmony_ci		if (err)
119362306a36Sopenharmony_ci			break;
119462306a36Sopenharmony_ci		if (!try_module_get(THIS_MODULE))
119562306a36Sopenharmony_ci			pr_info("%s: cannot get module\n", card->name);
119662306a36Sopenharmony_ci		break;
119762306a36Sopenharmony_ci	case CLOSE_CHANNEL:
119862306a36Sopenharmony_ci		pr_debug("%s: dev(%d) close from %p\n", card->name,
119962306a36Sopenharmony_ci			 dch->dev.id, __builtin_return_address(0));
120062306a36Sopenharmony_ci		module_put(THIS_MODULE);
120162306a36Sopenharmony_ci		break;
120262306a36Sopenharmony_ci	case CONTROL_CHANNEL:
120362306a36Sopenharmony_ci		err = channel_ctrl(card, arg);
120462306a36Sopenharmony_ci		break;
120562306a36Sopenharmony_ci	default:
120662306a36Sopenharmony_ci		pr_debug("%s: unknown DCTRL command %x\n", card->name, cmd);
120762306a36Sopenharmony_ci		return -EINVAL;
120862306a36Sopenharmony_ci	}
120962306a36Sopenharmony_ci	return err;
121062306a36Sopenharmony_ci}
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_cistatic int
121362306a36Sopenharmony_cisetup_w6692(struct w6692_hw *card)
121462306a36Sopenharmony_ci{
121562306a36Sopenharmony_ci	u32	val;
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	if (!request_region(card->addr, 256, card->name)) {
121862306a36Sopenharmony_ci		pr_info("%s: config port %x-%x already in use\n", card->name,
121962306a36Sopenharmony_ci			card->addr, card->addr + 255);
122062306a36Sopenharmony_ci		return -EIO;
122162306a36Sopenharmony_ci	}
122262306a36Sopenharmony_ci	W6692Version(card);
122362306a36Sopenharmony_ci	card->bc[0].addr = card->addr;
122462306a36Sopenharmony_ci	card->bc[1].addr = card->addr + 0x40;
122562306a36Sopenharmony_ci	val = ReadW6692(card, W_ISTA);
122662306a36Sopenharmony_ci	if (debug & DEBUG_HW)
122762306a36Sopenharmony_ci		pr_notice("%s ISTA=%02x\n", card->name, val);
122862306a36Sopenharmony_ci	val = ReadW6692(card, W_IMASK);
122962306a36Sopenharmony_ci	if (debug & DEBUG_HW)
123062306a36Sopenharmony_ci		pr_notice("%s IMASK=%02x\n", card->name, val);
123162306a36Sopenharmony_ci	val = ReadW6692(card, W_D_EXIR);
123262306a36Sopenharmony_ci	if (debug & DEBUG_HW)
123362306a36Sopenharmony_ci		pr_notice("%s D_EXIR=%02x\n", card->name, val);
123462306a36Sopenharmony_ci	val = ReadW6692(card, W_D_EXIM);
123562306a36Sopenharmony_ci	if (debug & DEBUG_HW)
123662306a36Sopenharmony_ci		pr_notice("%s D_EXIM=%02x\n", card->name, val);
123762306a36Sopenharmony_ci	val = ReadW6692(card, W_D_RSTA);
123862306a36Sopenharmony_ci	if (debug & DEBUG_HW)
123962306a36Sopenharmony_ci		pr_notice("%s D_RSTA=%02x\n", card->name, val);
124062306a36Sopenharmony_ci	return 0;
124162306a36Sopenharmony_ci}
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_cistatic void
124462306a36Sopenharmony_cirelease_card(struct w6692_hw *card)
124562306a36Sopenharmony_ci{
124662306a36Sopenharmony_ci	u_long	flags;
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	spin_lock_irqsave(&card->lock, flags);
124962306a36Sopenharmony_ci	disable_hwirq(card);
125062306a36Sopenharmony_ci	w6692_mode(&card->bc[0], ISDN_P_NONE);
125162306a36Sopenharmony_ci	w6692_mode(&card->bc[1], ISDN_P_NONE);
125262306a36Sopenharmony_ci	if ((card->fmask & led) || card->subtype == W6692_USR) {
125362306a36Sopenharmony_ci		card->xdata |= 0x04;	/*  LED OFF */
125462306a36Sopenharmony_ci		WriteW6692(card, W_XDATA, card->xdata);
125562306a36Sopenharmony_ci	}
125662306a36Sopenharmony_ci	spin_unlock_irqrestore(&card->lock, flags);
125762306a36Sopenharmony_ci	free_irq(card->irq, card);
125862306a36Sopenharmony_ci	l1_event(card->dch.l1, CLOSE_CHANNEL);
125962306a36Sopenharmony_ci	mISDN_unregister_device(&card->dch.dev);
126062306a36Sopenharmony_ci	release_region(card->addr, 256);
126162306a36Sopenharmony_ci	mISDN_freebchannel(&card->bc[1].bch);
126262306a36Sopenharmony_ci	mISDN_freebchannel(&card->bc[0].bch);
126362306a36Sopenharmony_ci	mISDN_freedchannel(&card->dch);
126462306a36Sopenharmony_ci	write_lock_irqsave(&card_lock, flags);
126562306a36Sopenharmony_ci	list_del(&card->list);
126662306a36Sopenharmony_ci	write_unlock_irqrestore(&card_lock, flags);
126762306a36Sopenharmony_ci	pci_disable_device(card->pdev);
126862306a36Sopenharmony_ci	pci_set_drvdata(card->pdev, NULL);
126962306a36Sopenharmony_ci	kfree(card);
127062306a36Sopenharmony_ci}
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_cistatic int
127362306a36Sopenharmony_cisetup_instance(struct w6692_hw *card)
127462306a36Sopenharmony_ci{
127562306a36Sopenharmony_ci	int		i, err;
127662306a36Sopenharmony_ci	u_long		flags;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	snprintf(card->name, MISDN_MAX_IDLEN - 1, "w6692.%d", w6692_cnt + 1);
127962306a36Sopenharmony_ci	write_lock_irqsave(&card_lock, flags);
128062306a36Sopenharmony_ci	list_add_tail(&card->list, &Cards);
128162306a36Sopenharmony_ci	write_unlock_irqrestore(&card_lock, flags);
128262306a36Sopenharmony_ci	card->fmask = (1 << w6692_cnt);
128362306a36Sopenharmony_ci	_set_debug(card);
128462306a36Sopenharmony_ci	spin_lock_init(&card->lock);
128562306a36Sopenharmony_ci	mISDN_initdchannel(&card->dch, MAX_DFRAME_LEN_L1, W6692_ph_bh);
128662306a36Sopenharmony_ci	card->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0);
128762306a36Sopenharmony_ci	card->dch.dev.D.send = w6692_l2l1D;
128862306a36Sopenharmony_ci	card->dch.dev.D.ctrl = w6692_dctrl;
128962306a36Sopenharmony_ci	card->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
129062306a36Sopenharmony_ci		(1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
129162306a36Sopenharmony_ci	card->dch.hw = card;
129262306a36Sopenharmony_ci	card->dch.dev.nrbchan = 2;
129362306a36Sopenharmony_ci	for (i = 0; i < 2; i++) {
129462306a36Sopenharmony_ci		mISDN_initbchannel(&card->bc[i].bch, MAX_DATA_MEM,
129562306a36Sopenharmony_ci				   W_B_FIFO_THRESH);
129662306a36Sopenharmony_ci		card->bc[i].bch.hw = card;
129762306a36Sopenharmony_ci		card->bc[i].bch.nr = i + 1;
129862306a36Sopenharmony_ci		card->bc[i].bch.ch.nr = i + 1;
129962306a36Sopenharmony_ci		card->bc[i].bch.ch.send = w6692_l2l1B;
130062306a36Sopenharmony_ci		card->bc[i].bch.ch.ctrl = w6692_bctrl;
130162306a36Sopenharmony_ci		set_channelmap(i + 1, card->dch.dev.channelmap);
130262306a36Sopenharmony_ci		list_add(&card->bc[i].bch.ch.list, &card->dch.dev.bchannels);
130362306a36Sopenharmony_ci	}
130462306a36Sopenharmony_ci	err = setup_w6692(card);
130562306a36Sopenharmony_ci	if (err)
130662306a36Sopenharmony_ci		goto error_setup;
130762306a36Sopenharmony_ci	err = mISDN_register_device(&card->dch.dev, &card->pdev->dev,
130862306a36Sopenharmony_ci				    card->name);
130962306a36Sopenharmony_ci	if (err)
131062306a36Sopenharmony_ci		goto error_reg;
131162306a36Sopenharmony_ci	err = init_card(card);
131262306a36Sopenharmony_ci	if (err)
131362306a36Sopenharmony_ci		goto error_init;
131462306a36Sopenharmony_ci	err = create_l1(&card->dch, w6692_l1callback);
131562306a36Sopenharmony_ci	if (!err) {
131662306a36Sopenharmony_ci		w6692_cnt++;
131762306a36Sopenharmony_ci		pr_notice("W6692 %d cards installed\n", w6692_cnt);
131862306a36Sopenharmony_ci		return 0;
131962306a36Sopenharmony_ci	}
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	free_irq(card->irq, card);
132262306a36Sopenharmony_cierror_init:
132362306a36Sopenharmony_ci	mISDN_unregister_device(&card->dch.dev);
132462306a36Sopenharmony_cierror_reg:
132562306a36Sopenharmony_ci	release_region(card->addr, 256);
132662306a36Sopenharmony_cierror_setup:
132762306a36Sopenharmony_ci	mISDN_freebchannel(&card->bc[1].bch);
132862306a36Sopenharmony_ci	mISDN_freebchannel(&card->bc[0].bch);
132962306a36Sopenharmony_ci	mISDN_freedchannel(&card->dch);
133062306a36Sopenharmony_ci	write_lock_irqsave(&card_lock, flags);
133162306a36Sopenharmony_ci	list_del(&card->list);
133262306a36Sopenharmony_ci	write_unlock_irqrestore(&card_lock, flags);
133362306a36Sopenharmony_ci	kfree(card);
133462306a36Sopenharmony_ci	return err;
133562306a36Sopenharmony_ci}
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_cistatic int
133862306a36Sopenharmony_ciw6692_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
133962306a36Sopenharmony_ci{
134062306a36Sopenharmony_ci	int		err = -ENOMEM;
134162306a36Sopenharmony_ci	struct w6692_hw	*card;
134262306a36Sopenharmony_ci	struct w6692map	*m = (struct w6692map *)ent->driver_data;
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	card = kzalloc(sizeof(struct w6692_hw), GFP_KERNEL);
134562306a36Sopenharmony_ci	if (!card) {
134662306a36Sopenharmony_ci		pr_info("No kmem for w6692 card\n");
134762306a36Sopenharmony_ci		return err;
134862306a36Sopenharmony_ci	}
134962306a36Sopenharmony_ci	card->pdev = pdev;
135062306a36Sopenharmony_ci	card->subtype = m->subtype;
135162306a36Sopenharmony_ci	err = pci_enable_device(pdev);
135262306a36Sopenharmony_ci	if (err) {
135362306a36Sopenharmony_ci		kfree(card);
135462306a36Sopenharmony_ci		return err;
135562306a36Sopenharmony_ci	}
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	printk(KERN_INFO "mISDN_w6692: found adapter %s at %s\n",
135862306a36Sopenharmony_ci	       m->name, pci_name(pdev));
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	card->addr = pci_resource_start(pdev, 1);
136162306a36Sopenharmony_ci	card->irq = pdev->irq;
136262306a36Sopenharmony_ci	pci_set_drvdata(pdev, card);
136362306a36Sopenharmony_ci	err = setup_instance(card);
136462306a36Sopenharmony_ci	if (err)
136562306a36Sopenharmony_ci		pci_set_drvdata(pdev, NULL);
136662306a36Sopenharmony_ci	return err;
136762306a36Sopenharmony_ci}
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_cistatic void
137062306a36Sopenharmony_ciw6692_remove_pci(struct pci_dev *pdev)
137162306a36Sopenharmony_ci{
137262306a36Sopenharmony_ci	struct w6692_hw	*card = pci_get_drvdata(pdev);
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	if (card)
137562306a36Sopenharmony_ci		release_card(card);
137662306a36Sopenharmony_ci	else
137762306a36Sopenharmony_ci		if (debug)
137862306a36Sopenharmony_ci			pr_notice("%s: drvdata already removed\n", __func__);
137962306a36Sopenharmony_ci}
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_cistatic const struct pci_device_id w6692_ids[] = {
138262306a36Sopenharmony_ci	{ PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH,
138362306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, (ulong)&w6692_map[0]},
138462306a36Sopenharmony_ci	{ PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692,
138562306a36Sopenharmony_ci	  PCI_VENDOR_ID_USR, PCI_DEVICE_ID_USR_6692, 0, 0,
138662306a36Sopenharmony_ci	  (ulong)&w6692_map[2]},
138762306a36Sopenharmony_ci	{ PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692,
138862306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, (ulong)&w6692_map[1]},
138962306a36Sopenharmony_ci	{ }
139062306a36Sopenharmony_ci};
139162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, w6692_ids);
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_cistatic struct pci_driver w6692_driver = {
139462306a36Sopenharmony_ci	.name =  "w6692",
139562306a36Sopenharmony_ci	.probe = w6692_probe,
139662306a36Sopenharmony_ci	.remove = w6692_remove_pci,
139762306a36Sopenharmony_ci	.id_table = w6692_ids,
139862306a36Sopenharmony_ci};
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_cistatic int __init w6692_init(void)
140162306a36Sopenharmony_ci{
140262306a36Sopenharmony_ci	int err;
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	pr_notice("Winbond W6692 PCI driver Rev. %s\n", W6692_REV);
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci	err = pci_register_driver(&w6692_driver);
140762306a36Sopenharmony_ci	return err;
140862306a36Sopenharmony_ci}
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_cistatic void __exit w6692_cleanup(void)
141162306a36Sopenharmony_ci{
141262306a36Sopenharmony_ci	pci_unregister_driver(&w6692_driver);
141362306a36Sopenharmony_ci}
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_cimodule_init(w6692_init);
141662306a36Sopenharmony_cimodule_exit(w6692_cleanup);
1417