162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * NETJet mISDN driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author       Karsten Keil <keil@isdn4linux.de>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/interrupt.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/pci.h>
1362306a36Sopenharmony_ci#include <linux/delay.h>
1462306a36Sopenharmony_ci#include <linux/mISDNhw.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include "ipac.h"
1762306a36Sopenharmony_ci#include "iohelper.h"
1862306a36Sopenharmony_ci#include "netjet.h"
1962306a36Sopenharmony_ci#include "isdnhdlc.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define NETJET_REV	"2.0"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cienum nj_types {
2462306a36Sopenharmony_ci	NETJET_S_TJ300,
2562306a36Sopenharmony_ci	NETJET_S_TJ320,
2662306a36Sopenharmony_ci	ENTERNOW__TJ320,
2762306a36Sopenharmony_ci};
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistruct tiger_dma {
3062306a36Sopenharmony_ci	size_t		size;
3162306a36Sopenharmony_ci	u32		*start;
3262306a36Sopenharmony_ci	int		idx;
3362306a36Sopenharmony_ci	u32		dmastart;
3462306a36Sopenharmony_ci	u32		dmairq;
3562306a36Sopenharmony_ci	u32		dmaend;
3662306a36Sopenharmony_ci	u32		dmacur;
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistruct tiger_hw;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistruct tiger_ch {
4262306a36Sopenharmony_ci	struct bchannel		bch;
4362306a36Sopenharmony_ci	struct tiger_hw		*nj;
4462306a36Sopenharmony_ci	int			idx;
4562306a36Sopenharmony_ci	int			free;
4662306a36Sopenharmony_ci	int			lastrx;
4762306a36Sopenharmony_ci	u16			rxstate;
4862306a36Sopenharmony_ci	u16			txstate;
4962306a36Sopenharmony_ci	struct isdnhdlc_vars	hsend;
5062306a36Sopenharmony_ci	struct isdnhdlc_vars	hrecv;
5162306a36Sopenharmony_ci	u8			*hsbuf;
5262306a36Sopenharmony_ci	u8			*hrbuf;
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#define TX_INIT		0x0001
5662306a36Sopenharmony_ci#define TX_IDLE		0x0002
5762306a36Sopenharmony_ci#define TX_RUN		0x0004
5862306a36Sopenharmony_ci#define TX_UNDERRUN	0x0100
5962306a36Sopenharmony_ci#define RX_OVERRUN	0x0100
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#define LOG_SIZE	64
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistruct tiger_hw {
6462306a36Sopenharmony_ci	struct list_head	list;
6562306a36Sopenharmony_ci	struct pci_dev		*pdev;
6662306a36Sopenharmony_ci	char			name[MISDN_MAX_IDLEN];
6762306a36Sopenharmony_ci	enum nj_types		typ;
6862306a36Sopenharmony_ci	int			irq;
6962306a36Sopenharmony_ci	u32			irqcnt;
7062306a36Sopenharmony_ci	u32			base;
7162306a36Sopenharmony_ci	size_t			base_s;
7262306a36Sopenharmony_ci	dma_addr_t		dma;
7362306a36Sopenharmony_ci	void			*dma_p;
7462306a36Sopenharmony_ci	spinlock_t		lock;	/* lock HW */
7562306a36Sopenharmony_ci	struct isac_hw		isac;
7662306a36Sopenharmony_ci	struct tiger_dma	send;
7762306a36Sopenharmony_ci	struct tiger_dma	recv;
7862306a36Sopenharmony_ci	struct tiger_ch		bc[2];
7962306a36Sopenharmony_ci	u8			ctrlreg;
8062306a36Sopenharmony_ci	u8			dmactrl;
8162306a36Sopenharmony_ci	u8			auxd;
8262306a36Sopenharmony_ci	u8			last_is0;
8362306a36Sopenharmony_ci	u8			irqmask0;
8462306a36Sopenharmony_ci	char			log[LOG_SIZE];
8562306a36Sopenharmony_ci};
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic LIST_HEAD(Cards);
8862306a36Sopenharmony_cistatic DEFINE_RWLOCK(card_lock); /* protect Cards */
8962306a36Sopenharmony_cistatic u32 debug;
9062306a36Sopenharmony_cistatic int nj_cnt;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic void
9362306a36Sopenharmony_ci_set_debug(struct tiger_hw *card)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	card->isac.dch.debug = debug;
9662306a36Sopenharmony_ci	card->bc[0].bch.debug = debug;
9762306a36Sopenharmony_ci	card->bc[1].bch.debug = debug;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic int
10162306a36Sopenharmony_ciset_debug(const char *val, const struct kernel_param *kp)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	int ret;
10462306a36Sopenharmony_ci	struct tiger_hw *card;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	ret = param_set_uint(val, kp);
10762306a36Sopenharmony_ci	if (!ret) {
10862306a36Sopenharmony_ci		read_lock(&card_lock);
10962306a36Sopenharmony_ci		list_for_each_entry(card, &Cards, list)
11062306a36Sopenharmony_ci			_set_debug(card);
11162306a36Sopenharmony_ci		read_unlock(&card_lock);
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci	return ret;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ciMODULE_AUTHOR("Karsten Keil");
11762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
11862306a36Sopenharmony_ciMODULE_VERSION(NETJET_REV);
11962306a36Sopenharmony_cimodule_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
12062306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "Netjet debug mask");
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic void
12362306a36Sopenharmony_cinj_disable_hwirq(struct tiger_hw *card)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	outb(0, card->base + NJ_IRQMASK0);
12662306a36Sopenharmony_ci	outb(0, card->base + NJ_IRQMASK1);
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic u8
13162306a36Sopenharmony_ciReadISAC_nj(void *p, u8 offset)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	struct tiger_hw *card = p;
13462306a36Sopenharmony_ci	u8 ret;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	card->auxd &= 0xfc;
13762306a36Sopenharmony_ci	card->auxd |= (offset >> 4) & 3;
13862306a36Sopenharmony_ci	outb(card->auxd, card->base + NJ_AUXDATA);
13962306a36Sopenharmony_ci	ret = inb(card->base + NJ_ISAC_OFF + ((offset & 0x0f) << 2));
14062306a36Sopenharmony_ci	return ret;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic void
14462306a36Sopenharmony_ciWriteISAC_nj(void *p, u8 offset, u8 value)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct tiger_hw *card = p;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	card->auxd &= 0xfc;
14962306a36Sopenharmony_ci	card->auxd |= (offset >> 4) & 3;
15062306a36Sopenharmony_ci	outb(card->auxd, card->base + NJ_AUXDATA);
15162306a36Sopenharmony_ci	outb(value, card->base + NJ_ISAC_OFF + ((offset & 0x0f) << 2));
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic void
15562306a36Sopenharmony_ciReadFiFoISAC_nj(void *p, u8 offset, u8 *data, int size)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	struct tiger_hw *card = p;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	card->auxd &= 0xfc;
16062306a36Sopenharmony_ci	outb(card->auxd, card->base + NJ_AUXDATA);
16162306a36Sopenharmony_ci	insb(card->base + NJ_ISAC_OFF, data, size);
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic void
16562306a36Sopenharmony_ciWriteFiFoISAC_nj(void *p, u8 offset, u8 *data, int size)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	struct tiger_hw *card = p;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	card->auxd &= 0xfc;
17062306a36Sopenharmony_ci	outb(card->auxd, card->base + NJ_AUXDATA);
17162306a36Sopenharmony_ci	outsb(card->base + NJ_ISAC_OFF, data, size);
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic void
17562306a36Sopenharmony_cifill_mem(struct tiger_ch *bc, u32 idx, u32 cnt, u32 fill)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	struct tiger_hw *card = bc->bch.hw;
17862306a36Sopenharmony_ci	u32 mask = 0xff, val;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	pr_debug("%s: B%1d fill %02x len %d idx %d/%d\n", card->name,
18162306a36Sopenharmony_ci		 bc->bch.nr, fill, cnt, idx, card->send.idx);
18262306a36Sopenharmony_ci	if (bc->bch.nr & 2) {
18362306a36Sopenharmony_ci		fill  <<= 8;
18462306a36Sopenharmony_ci		mask <<= 8;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci	mask ^= 0xffffffff;
18762306a36Sopenharmony_ci	while (cnt--) {
18862306a36Sopenharmony_ci		val = card->send.start[idx];
18962306a36Sopenharmony_ci		val &= mask;
19062306a36Sopenharmony_ci		val |= fill;
19162306a36Sopenharmony_ci		card->send.start[idx++] = val;
19262306a36Sopenharmony_ci		if (idx >= card->send.size)
19362306a36Sopenharmony_ci			idx = 0;
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic int
19862306a36Sopenharmony_cimode_tiger(struct tiger_ch *bc, u32 protocol)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	struct tiger_hw *card = bc->bch.hw;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	pr_debug("%s: B%1d protocol %x-->%x\n", card->name,
20362306a36Sopenharmony_ci		 bc->bch.nr, bc->bch.state, protocol);
20462306a36Sopenharmony_ci	switch (protocol) {
20562306a36Sopenharmony_ci	case ISDN_P_NONE:
20662306a36Sopenharmony_ci		if (bc->bch.state == ISDN_P_NONE)
20762306a36Sopenharmony_ci			break;
20862306a36Sopenharmony_ci		fill_mem(bc, 0, card->send.size, 0xff);
20962306a36Sopenharmony_ci		bc->bch.state = protocol;
21062306a36Sopenharmony_ci		/* only stop dma and interrupts if both channels NULL */
21162306a36Sopenharmony_ci		if ((card->bc[0].bch.state == ISDN_P_NONE) &&
21262306a36Sopenharmony_ci		    (card->bc[1].bch.state == ISDN_P_NONE)) {
21362306a36Sopenharmony_ci			card->dmactrl = 0;
21462306a36Sopenharmony_ci			outb(card->dmactrl, card->base + NJ_DMACTRL);
21562306a36Sopenharmony_ci			outb(0, card->base + NJ_IRQMASK0);
21662306a36Sopenharmony_ci		}
21762306a36Sopenharmony_ci		test_and_clear_bit(FLG_HDLC, &bc->bch.Flags);
21862306a36Sopenharmony_ci		test_and_clear_bit(FLG_TRANSPARENT, &bc->bch.Flags);
21962306a36Sopenharmony_ci		bc->txstate = 0;
22062306a36Sopenharmony_ci		bc->rxstate = 0;
22162306a36Sopenharmony_ci		bc->lastrx = -1;
22262306a36Sopenharmony_ci		break;
22362306a36Sopenharmony_ci	case ISDN_P_B_RAW:
22462306a36Sopenharmony_ci		test_and_set_bit(FLG_TRANSPARENT, &bc->bch.Flags);
22562306a36Sopenharmony_ci		bc->bch.state = protocol;
22662306a36Sopenharmony_ci		bc->idx = 0;
22762306a36Sopenharmony_ci		bc->free = card->send.size / 2;
22862306a36Sopenharmony_ci		bc->rxstate = 0;
22962306a36Sopenharmony_ci		bc->txstate = TX_INIT | TX_IDLE;
23062306a36Sopenharmony_ci		bc->lastrx = -1;
23162306a36Sopenharmony_ci		if (!card->dmactrl) {
23262306a36Sopenharmony_ci			card->dmactrl = 1;
23362306a36Sopenharmony_ci			outb(card->dmactrl, card->base + NJ_DMACTRL);
23462306a36Sopenharmony_ci			outb(0x0f, card->base + NJ_IRQMASK0);
23562306a36Sopenharmony_ci		}
23662306a36Sopenharmony_ci		break;
23762306a36Sopenharmony_ci	case ISDN_P_B_HDLC:
23862306a36Sopenharmony_ci		test_and_set_bit(FLG_HDLC, &bc->bch.Flags);
23962306a36Sopenharmony_ci		bc->bch.state = protocol;
24062306a36Sopenharmony_ci		bc->idx = 0;
24162306a36Sopenharmony_ci		bc->free = card->send.size / 2;
24262306a36Sopenharmony_ci		bc->rxstate = 0;
24362306a36Sopenharmony_ci		bc->txstate = TX_INIT | TX_IDLE;
24462306a36Sopenharmony_ci		isdnhdlc_rcv_init(&bc->hrecv, 0);
24562306a36Sopenharmony_ci		isdnhdlc_out_init(&bc->hsend, 0);
24662306a36Sopenharmony_ci		bc->lastrx = -1;
24762306a36Sopenharmony_ci		if (!card->dmactrl) {
24862306a36Sopenharmony_ci			card->dmactrl = 1;
24962306a36Sopenharmony_ci			outb(card->dmactrl, card->base + NJ_DMACTRL);
25062306a36Sopenharmony_ci			outb(0x0f, card->base + NJ_IRQMASK0);
25162306a36Sopenharmony_ci		}
25262306a36Sopenharmony_ci		break;
25362306a36Sopenharmony_ci	default:
25462306a36Sopenharmony_ci		pr_info("%s: %s protocol %x not handled\n", card->name,
25562306a36Sopenharmony_ci			__func__, protocol);
25662306a36Sopenharmony_ci		return -ENOPROTOOPT;
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci	card->send.dmacur = inl(card->base + NJ_DMA_READ_ADR);
25962306a36Sopenharmony_ci	card->recv.dmacur = inl(card->base + NJ_DMA_WRITE_ADR);
26062306a36Sopenharmony_ci	card->send.idx = (card->send.dmacur - card->send.dmastart) >> 2;
26162306a36Sopenharmony_ci	card->recv.idx = (card->recv.dmacur - card->recv.dmastart) >> 2;
26262306a36Sopenharmony_ci	pr_debug("%s: %s ctrl %x irq  %02x/%02x idx %d/%d\n",
26362306a36Sopenharmony_ci		 card->name, __func__,
26462306a36Sopenharmony_ci		 inb(card->base + NJ_DMACTRL),
26562306a36Sopenharmony_ci		 inb(card->base + NJ_IRQMASK0),
26662306a36Sopenharmony_ci		 inb(card->base + NJ_IRQSTAT0),
26762306a36Sopenharmony_ci		 card->send.idx,
26862306a36Sopenharmony_ci		 card->recv.idx);
26962306a36Sopenharmony_ci	return 0;
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic void
27362306a36Sopenharmony_cinj_reset(struct tiger_hw *card)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	outb(0xff, card->base + NJ_CTRL); /* Reset On */
27662306a36Sopenharmony_ci	mdelay(1);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	/* now edge triggered for TJ320 GE 13/07/00 */
27962306a36Sopenharmony_ci	/* see comment in IRQ function */
28062306a36Sopenharmony_ci	if (card->typ == NETJET_S_TJ320) /* TJ320 */
28162306a36Sopenharmony_ci		card->ctrlreg = 0x40;  /* Reset Off and status read clear */
28262306a36Sopenharmony_ci	else
28362306a36Sopenharmony_ci		card->ctrlreg = 0x00;  /* Reset Off and status read clear */
28462306a36Sopenharmony_ci	outb(card->ctrlreg, card->base + NJ_CTRL);
28562306a36Sopenharmony_ci	mdelay(10);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	/* configure AUX pins (all output except ISAC IRQ pin) */
28862306a36Sopenharmony_ci	card->auxd = 0;
28962306a36Sopenharmony_ci	card->dmactrl = 0;
29062306a36Sopenharmony_ci	outb(~NJ_ISACIRQ, card->base + NJ_AUXCTRL);
29162306a36Sopenharmony_ci	outb(NJ_ISACIRQ,  card->base + NJ_IRQMASK1);
29262306a36Sopenharmony_ci	outb(card->auxd, card->base + NJ_AUXDATA);
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic int
29662306a36Sopenharmony_ciinittiger(struct tiger_hw *card)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	int i;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	card->dma_p = dma_alloc_coherent(&card->pdev->dev, NJ_DMA_SIZE,
30162306a36Sopenharmony_ci					 &card->dma, GFP_ATOMIC);
30262306a36Sopenharmony_ci	if (!card->dma_p) {
30362306a36Sopenharmony_ci		pr_info("%s: No DMA memory\n", card->name);
30462306a36Sopenharmony_ci		return -ENOMEM;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci	if ((u64)card->dma > 0xffffffff) {
30762306a36Sopenharmony_ci		pr_info("%s: DMA outside 32 bit\n", card->name);
30862306a36Sopenharmony_ci		return -ENOMEM;
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci	for (i = 0; i < 2; i++) {
31162306a36Sopenharmony_ci		card->bc[i].hsbuf = kmalloc(NJ_DMA_TXSIZE, GFP_ATOMIC);
31262306a36Sopenharmony_ci		if (!card->bc[i].hsbuf) {
31362306a36Sopenharmony_ci			pr_info("%s: no B%d send buffer\n", card->name, i + 1);
31462306a36Sopenharmony_ci			return -ENOMEM;
31562306a36Sopenharmony_ci		}
31662306a36Sopenharmony_ci		card->bc[i].hrbuf = kmalloc(NJ_DMA_RXSIZE, GFP_ATOMIC);
31762306a36Sopenharmony_ci		if (!card->bc[i].hrbuf) {
31862306a36Sopenharmony_ci			pr_info("%s: no B%d recv buffer\n", card->name, i + 1);
31962306a36Sopenharmony_ci			return -ENOMEM;
32062306a36Sopenharmony_ci		}
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci	memset(card->dma_p, 0xff, NJ_DMA_SIZE);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	card->send.start = card->dma_p;
32562306a36Sopenharmony_ci	card->send.dmastart = (u32)card->dma;
32662306a36Sopenharmony_ci	card->send.dmaend = card->send.dmastart +
32762306a36Sopenharmony_ci		(4 * (NJ_DMA_TXSIZE - 1));
32862306a36Sopenharmony_ci	card->send.dmairq = card->send.dmastart +
32962306a36Sopenharmony_ci		(4 * ((NJ_DMA_TXSIZE / 2) - 1));
33062306a36Sopenharmony_ci	card->send.size = NJ_DMA_TXSIZE;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	if (debug & DEBUG_HW)
33362306a36Sopenharmony_ci		pr_notice("%s: send buffer phy %#x - %#x - %#x  virt %p"
33462306a36Sopenharmony_ci			  " size %zu u32\n", card->name,
33562306a36Sopenharmony_ci			  card->send.dmastart, card->send.dmairq,
33662306a36Sopenharmony_ci			  card->send.dmaend, card->send.start, card->send.size);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	outl(card->send.dmastart, card->base + NJ_DMA_READ_START);
33962306a36Sopenharmony_ci	outl(card->send.dmairq, card->base + NJ_DMA_READ_IRQ);
34062306a36Sopenharmony_ci	outl(card->send.dmaend, card->base + NJ_DMA_READ_END);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	card->recv.start = card->dma_p + (NJ_DMA_SIZE / 2);
34362306a36Sopenharmony_ci	card->recv.dmastart = (u32)card->dma  + (NJ_DMA_SIZE / 2);
34462306a36Sopenharmony_ci	card->recv.dmaend = card->recv.dmastart +
34562306a36Sopenharmony_ci		(4 * (NJ_DMA_RXSIZE - 1));
34662306a36Sopenharmony_ci	card->recv.dmairq = card->recv.dmastart +
34762306a36Sopenharmony_ci		(4 * ((NJ_DMA_RXSIZE / 2) - 1));
34862306a36Sopenharmony_ci	card->recv.size = NJ_DMA_RXSIZE;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	if (debug & DEBUG_HW)
35162306a36Sopenharmony_ci		pr_notice("%s: recv buffer phy %#x - %#x - %#x  virt %p"
35262306a36Sopenharmony_ci			  " size %zu u32\n", card->name,
35362306a36Sopenharmony_ci			  card->recv.dmastart, card->recv.dmairq,
35462306a36Sopenharmony_ci			  card->recv.dmaend, card->recv.start, card->recv.size);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	outl(card->recv.dmastart, card->base + NJ_DMA_WRITE_START);
35762306a36Sopenharmony_ci	outl(card->recv.dmairq, card->base + NJ_DMA_WRITE_IRQ);
35862306a36Sopenharmony_ci	outl(card->recv.dmaend, card->base + NJ_DMA_WRITE_END);
35962306a36Sopenharmony_ci	return 0;
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic void
36362306a36Sopenharmony_ciread_dma(struct tiger_ch *bc, u32 idx, int cnt)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	struct tiger_hw *card = bc->bch.hw;
36662306a36Sopenharmony_ci	int i, stat;
36762306a36Sopenharmony_ci	u32 val;
36862306a36Sopenharmony_ci	u8 *p, *pn;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	if (bc->lastrx == idx) {
37162306a36Sopenharmony_ci		bc->rxstate |= RX_OVERRUN;
37262306a36Sopenharmony_ci		pr_info("%s: B%1d overrun at idx %d\n", card->name,
37362306a36Sopenharmony_ci			bc->bch.nr, idx);
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci	bc->lastrx = idx;
37662306a36Sopenharmony_ci	if (test_bit(FLG_RX_OFF, &bc->bch.Flags)) {
37762306a36Sopenharmony_ci		bc->bch.dropcnt += cnt;
37862306a36Sopenharmony_ci		return;
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci	stat = bchannel_get_rxbuf(&bc->bch, cnt);
38162306a36Sopenharmony_ci	/* only transparent use the count here, HDLC overun is detected later */
38262306a36Sopenharmony_ci	if (stat == -ENOMEM) {
38362306a36Sopenharmony_ci		pr_warn("%s.B%d: No memory for %d bytes\n",
38462306a36Sopenharmony_ci			card->name, bc->bch.nr, cnt);
38562306a36Sopenharmony_ci		return;
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci	if (test_bit(FLG_TRANSPARENT, &bc->bch.Flags))
38862306a36Sopenharmony_ci		p = skb_put(bc->bch.rx_skb, cnt);
38962306a36Sopenharmony_ci	else
39062306a36Sopenharmony_ci		p = bc->hrbuf;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	for (i = 0; i < cnt; i++) {
39362306a36Sopenharmony_ci		val = card->recv.start[idx++];
39462306a36Sopenharmony_ci		if (bc->bch.nr & 2)
39562306a36Sopenharmony_ci			val >>= 8;
39662306a36Sopenharmony_ci		if (idx >= card->recv.size)
39762306a36Sopenharmony_ci			idx = 0;
39862306a36Sopenharmony_ci		p[i] = val & 0xff;
39962306a36Sopenharmony_ci	}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	if (test_bit(FLG_TRANSPARENT, &bc->bch.Flags)) {
40262306a36Sopenharmony_ci		recv_Bchannel(&bc->bch, 0, false);
40362306a36Sopenharmony_ci		return;
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	pn = bc->hrbuf;
40762306a36Sopenharmony_ci	while (cnt > 0) {
40862306a36Sopenharmony_ci		stat = isdnhdlc_decode(&bc->hrecv, pn, cnt, &i,
40962306a36Sopenharmony_ci				       bc->bch.rx_skb->data, bc->bch.maxlen);
41062306a36Sopenharmony_ci		if (stat > 0) { /* valid frame received */
41162306a36Sopenharmony_ci			p = skb_put(bc->bch.rx_skb, stat);
41262306a36Sopenharmony_ci			if (debug & DEBUG_HW_BFIFO) {
41362306a36Sopenharmony_ci				snprintf(card->log, LOG_SIZE,
41462306a36Sopenharmony_ci					 "B%1d-recv %s %d ", bc->bch.nr,
41562306a36Sopenharmony_ci					 card->name, stat);
41662306a36Sopenharmony_ci				print_hex_dump_bytes(card->log,
41762306a36Sopenharmony_ci						     DUMP_PREFIX_OFFSET, p,
41862306a36Sopenharmony_ci						     stat);
41962306a36Sopenharmony_ci			}
42062306a36Sopenharmony_ci			recv_Bchannel(&bc->bch, 0, false);
42162306a36Sopenharmony_ci			stat = bchannel_get_rxbuf(&bc->bch, bc->bch.maxlen);
42262306a36Sopenharmony_ci			if (stat < 0) {
42362306a36Sopenharmony_ci				pr_warn("%s.B%d: No memory for %d bytes\n",
42462306a36Sopenharmony_ci					card->name, bc->bch.nr, cnt);
42562306a36Sopenharmony_ci				return;
42662306a36Sopenharmony_ci			}
42762306a36Sopenharmony_ci		} else if (stat == -HDLC_CRC_ERROR) {
42862306a36Sopenharmony_ci			pr_info("%s: B%1d receive frame CRC error\n",
42962306a36Sopenharmony_ci				card->name, bc->bch.nr);
43062306a36Sopenharmony_ci		} else if (stat == -HDLC_FRAMING_ERROR) {
43162306a36Sopenharmony_ci			pr_info("%s: B%1d receive framing error\n",
43262306a36Sopenharmony_ci				card->name, bc->bch.nr);
43362306a36Sopenharmony_ci		} else if (stat == -HDLC_LENGTH_ERROR) {
43462306a36Sopenharmony_ci			pr_info("%s: B%1d receive frame too long (> %d)\n",
43562306a36Sopenharmony_ci				card->name, bc->bch.nr, bc->bch.maxlen);
43662306a36Sopenharmony_ci		}
43762306a36Sopenharmony_ci		pn += i;
43862306a36Sopenharmony_ci		cnt -= i;
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic void
44362306a36Sopenharmony_cirecv_tiger(struct tiger_hw *card, u8 irq_stat)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	u32 idx;
44662306a36Sopenharmony_ci	int cnt = card->recv.size / 2;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	/* Note receive is via the WRITE DMA channel */
44962306a36Sopenharmony_ci	card->last_is0 &= ~NJ_IRQM0_WR_MASK;
45062306a36Sopenharmony_ci	card->last_is0 |= (irq_stat & NJ_IRQM0_WR_MASK);
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	if (irq_stat & NJ_IRQM0_WR_END)
45362306a36Sopenharmony_ci		idx = cnt - 1;
45462306a36Sopenharmony_ci	else
45562306a36Sopenharmony_ci		idx = card->recv.size - 1;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	if (test_bit(FLG_ACTIVE, &card->bc[0].bch.Flags))
45862306a36Sopenharmony_ci		read_dma(&card->bc[0], idx, cnt);
45962306a36Sopenharmony_ci	if (test_bit(FLG_ACTIVE, &card->bc[1].bch.Flags))
46062306a36Sopenharmony_ci		read_dma(&card->bc[1], idx, cnt);
46162306a36Sopenharmony_ci}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci/* sync with current DMA address at start or after exception */
46462306a36Sopenharmony_cistatic void
46562306a36Sopenharmony_ciresync(struct tiger_ch *bc, struct tiger_hw *card)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	card->send.dmacur = inl(card->base | NJ_DMA_READ_ADR);
46862306a36Sopenharmony_ci	card->send.idx = (card->send.dmacur - card->send.dmastart) >> 2;
46962306a36Sopenharmony_ci	if (bc->free > card->send.size / 2)
47062306a36Sopenharmony_ci		bc->free = card->send.size / 2;
47162306a36Sopenharmony_ci	/* currently we simple sync to the next complete free area
47262306a36Sopenharmony_ci	 * this hast the advantage that we have always maximum time to
47362306a36Sopenharmony_ci	 * handle TX irq
47462306a36Sopenharmony_ci	 */
47562306a36Sopenharmony_ci	if (card->send.idx < ((card->send.size / 2) - 1))
47662306a36Sopenharmony_ci		bc->idx = (card->recv.size / 2) - 1;
47762306a36Sopenharmony_ci	else
47862306a36Sopenharmony_ci		bc->idx = card->recv.size - 1;
47962306a36Sopenharmony_ci	bc->txstate = TX_RUN;
48062306a36Sopenharmony_ci	pr_debug("%s: %s B%1d free %d idx %d/%d\n", card->name,
48162306a36Sopenharmony_ci		 __func__, bc->bch.nr, bc->free, bc->idx, card->send.idx);
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic int bc_next_frame(struct tiger_ch *);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_cistatic void
48762306a36Sopenharmony_cifill_hdlc_flag(struct tiger_ch *bc)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	struct tiger_hw *card = bc->bch.hw;
49062306a36Sopenharmony_ci	int count, i;
49162306a36Sopenharmony_ci	u32 m, v;
49262306a36Sopenharmony_ci	u8  *p;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	if (bc->free == 0)
49562306a36Sopenharmony_ci		return;
49662306a36Sopenharmony_ci	pr_debug("%s: %s B%1d %d state %x idx %d/%d\n", card->name,
49762306a36Sopenharmony_ci		 __func__, bc->bch.nr, bc->free, bc->txstate,
49862306a36Sopenharmony_ci		 bc->idx, card->send.idx);
49962306a36Sopenharmony_ci	if (bc->txstate & (TX_IDLE | TX_INIT | TX_UNDERRUN))
50062306a36Sopenharmony_ci		resync(bc, card);
50162306a36Sopenharmony_ci	count = isdnhdlc_encode(&bc->hsend, NULL, 0, &i,
50262306a36Sopenharmony_ci				bc->hsbuf, bc->free);
50362306a36Sopenharmony_ci	pr_debug("%s: B%1d hdlc encoded %d flags\n", card->name,
50462306a36Sopenharmony_ci		 bc->bch.nr, count);
50562306a36Sopenharmony_ci	bc->free -= count;
50662306a36Sopenharmony_ci	p = bc->hsbuf;
50762306a36Sopenharmony_ci	m = (bc->bch.nr & 1) ? 0xffffff00 : 0xffff00ff;
50862306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
50962306a36Sopenharmony_ci		if (bc->idx >= card->send.size)
51062306a36Sopenharmony_ci			bc->idx = 0;
51162306a36Sopenharmony_ci		v = card->send.start[bc->idx];
51262306a36Sopenharmony_ci		v &= m;
51362306a36Sopenharmony_ci		v |= (bc->bch.nr & 1) ? (u32)(p[i]) : ((u32)(p[i])) << 8;
51462306a36Sopenharmony_ci		card->send.start[bc->idx++] = v;
51562306a36Sopenharmony_ci	}
51662306a36Sopenharmony_ci	if (debug & DEBUG_HW_BFIFO) {
51762306a36Sopenharmony_ci		snprintf(card->log, LOG_SIZE, "B%1d-send %s %d ",
51862306a36Sopenharmony_ci			 bc->bch.nr, card->name, count);
51962306a36Sopenharmony_ci		print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, p, count);
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_cistatic void
52462306a36Sopenharmony_cifill_dma(struct tiger_ch *bc)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	struct tiger_hw *card = bc->bch.hw;
52762306a36Sopenharmony_ci	int count, i, fillempty = 0;
52862306a36Sopenharmony_ci	u32 m, v, n = 0;
52962306a36Sopenharmony_ci	u8  *p;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	if (bc->free == 0)
53262306a36Sopenharmony_ci		return;
53362306a36Sopenharmony_ci	if (!bc->bch.tx_skb) {
53462306a36Sopenharmony_ci		if (!test_bit(FLG_TX_EMPTY, &bc->bch.Flags))
53562306a36Sopenharmony_ci			return;
53662306a36Sopenharmony_ci		fillempty = 1;
53762306a36Sopenharmony_ci		count = card->send.size >> 1;
53862306a36Sopenharmony_ci		p = bc->bch.fill;
53962306a36Sopenharmony_ci	} else {
54062306a36Sopenharmony_ci		count = bc->bch.tx_skb->len - bc->bch.tx_idx;
54162306a36Sopenharmony_ci		if (count <= 0)
54262306a36Sopenharmony_ci			return;
54362306a36Sopenharmony_ci		pr_debug("%s: %s B%1d %d/%d/%d/%d state %x idx %d/%d\n",
54462306a36Sopenharmony_ci			 card->name, __func__, bc->bch.nr, count, bc->free,
54562306a36Sopenharmony_ci			 bc->bch.tx_idx, bc->bch.tx_skb->len, bc->txstate,
54662306a36Sopenharmony_ci			 bc->idx, card->send.idx);
54762306a36Sopenharmony_ci		p = bc->bch.tx_skb->data + bc->bch.tx_idx;
54862306a36Sopenharmony_ci	}
54962306a36Sopenharmony_ci	if (bc->txstate & (TX_IDLE | TX_INIT | TX_UNDERRUN))
55062306a36Sopenharmony_ci		resync(bc, card);
55162306a36Sopenharmony_ci	if (test_bit(FLG_HDLC, &bc->bch.Flags) && !fillempty) {
55262306a36Sopenharmony_ci		count = isdnhdlc_encode(&bc->hsend, p, count, &i,
55362306a36Sopenharmony_ci					bc->hsbuf, bc->free);
55462306a36Sopenharmony_ci		pr_debug("%s: B%1d hdlc encoded %d in %d\n", card->name,
55562306a36Sopenharmony_ci			 bc->bch.nr, i, count);
55662306a36Sopenharmony_ci		bc->bch.tx_idx += i;
55762306a36Sopenharmony_ci		bc->free -= count;
55862306a36Sopenharmony_ci		p = bc->hsbuf;
55962306a36Sopenharmony_ci	} else {
56062306a36Sopenharmony_ci		if (count > bc->free)
56162306a36Sopenharmony_ci			count = bc->free;
56262306a36Sopenharmony_ci		if (!fillempty)
56362306a36Sopenharmony_ci			bc->bch.tx_idx += count;
56462306a36Sopenharmony_ci		bc->free -= count;
56562306a36Sopenharmony_ci	}
56662306a36Sopenharmony_ci	m = (bc->bch.nr & 1) ? 0xffffff00 : 0xffff00ff;
56762306a36Sopenharmony_ci	if (fillempty) {
56862306a36Sopenharmony_ci		n = p[0];
56962306a36Sopenharmony_ci		if (!(bc->bch.nr & 1))
57062306a36Sopenharmony_ci			n <<= 8;
57162306a36Sopenharmony_ci		for (i = 0; i < count; i++) {
57262306a36Sopenharmony_ci			if (bc->idx >= card->send.size)
57362306a36Sopenharmony_ci				bc->idx = 0;
57462306a36Sopenharmony_ci			v = card->send.start[bc->idx];
57562306a36Sopenharmony_ci			v &= m;
57662306a36Sopenharmony_ci			v |= n;
57762306a36Sopenharmony_ci			card->send.start[bc->idx++] = v;
57862306a36Sopenharmony_ci		}
57962306a36Sopenharmony_ci	} else {
58062306a36Sopenharmony_ci		for (i = 0; i < count; i++) {
58162306a36Sopenharmony_ci			if (bc->idx >= card->send.size)
58262306a36Sopenharmony_ci				bc->idx = 0;
58362306a36Sopenharmony_ci			v = card->send.start[bc->idx];
58462306a36Sopenharmony_ci			v &= m;
58562306a36Sopenharmony_ci			n = p[i];
58662306a36Sopenharmony_ci			v |= (bc->bch.nr & 1) ? n : n << 8;
58762306a36Sopenharmony_ci			card->send.start[bc->idx++] = v;
58862306a36Sopenharmony_ci		}
58962306a36Sopenharmony_ci	}
59062306a36Sopenharmony_ci	if (debug & DEBUG_HW_BFIFO) {
59162306a36Sopenharmony_ci		snprintf(card->log, LOG_SIZE, "B%1d-send %s %d ",
59262306a36Sopenharmony_ci			 bc->bch.nr, card->name, count);
59362306a36Sopenharmony_ci		print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, p, count);
59462306a36Sopenharmony_ci	}
59562306a36Sopenharmony_ci	if (bc->free)
59662306a36Sopenharmony_ci		bc_next_frame(bc);
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_cistatic int
60162306a36Sopenharmony_cibc_next_frame(struct tiger_ch *bc)
60262306a36Sopenharmony_ci{
60362306a36Sopenharmony_ci	int ret = 1;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	if (bc->bch.tx_skb && bc->bch.tx_idx < bc->bch.tx_skb->len) {
60662306a36Sopenharmony_ci		fill_dma(bc);
60762306a36Sopenharmony_ci	} else {
60862306a36Sopenharmony_ci		dev_kfree_skb(bc->bch.tx_skb);
60962306a36Sopenharmony_ci		if (get_next_bframe(&bc->bch)) {
61062306a36Sopenharmony_ci			fill_dma(bc);
61162306a36Sopenharmony_ci			test_and_clear_bit(FLG_TX_EMPTY, &bc->bch.Flags);
61262306a36Sopenharmony_ci		} else if (test_bit(FLG_TX_EMPTY, &bc->bch.Flags)) {
61362306a36Sopenharmony_ci			fill_dma(bc);
61462306a36Sopenharmony_ci		} else if (test_bit(FLG_FILLEMPTY, &bc->bch.Flags)) {
61562306a36Sopenharmony_ci			test_and_set_bit(FLG_TX_EMPTY, &bc->bch.Flags);
61662306a36Sopenharmony_ci			ret = 0;
61762306a36Sopenharmony_ci		} else {
61862306a36Sopenharmony_ci			ret = 0;
61962306a36Sopenharmony_ci		}
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci	return ret;
62262306a36Sopenharmony_ci}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_cistatic void
62562306a36Sopenharmony_cisend_tiger_bc(struct tiger_hw *card, struct tiger_ch *bc)
62662306a36Sopenharmony_ci{
62762306a36Sopenharmony_ci	int ret;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	bc->free += card->send.size / 2;
63062306a36Sopenharmony_ci	if (bc->free >= card->send.size) {
63162306a36Sopenharmony_ci		if (!(bc->txstate & (TX_UNDERRUN | TX_INIT))) {
63262306a36Sopenharmony_ci			pr_info("%s: B%1d TX underrun state %x\n", card->name,
63362306a36Sopenharmony_ci				bc->bch.nr, bc->txstate);
63462306a36Sopenharmony_ci			bc->txstate |= TX_UNDERRUN;
63562306a36Sopenharmony_ci		}
63662306a36Sopenharmony_ci		bc->free = card->send.size;
63762306a36Sopenharmony_ci	}
63862306a36Sopenharmony_ci	ret = bc_next_frame(bc);
63962306a36Sopenharmony_ci	if (!ret) {
64062306a36Sopenharmony_ci		if (test_bit(FLG_HDLC, &bc->bch.Flags)) {
64162306a36Sopenharmony_ci			fill_hdlc_flag(bc);
64262306a36Sopenharmony_ci			return;
64362306a36Sopenharmony_ci		}
64462306a36Sopenharmony_ci		pr_debug("%s: B%1d TX no data free %d idx %d/%d\n", card->name,
64562306a36Sopenharmony_ci			 bc->bch.nr, bc->free, bc->idx, card->send.idx);
64662306a36Sopenharmony_ci		if (!(bc->txstate & (TX_IDLE | TX_INIT))) {
64762306a36Sopenharmony_ci			fill_mem(bc, bc->idx, bc->free, 0xff);
64862306a36Sopenharmony_ci			if (bc->free == card->send.size)
64962306a36Sopenharmony_ci				bc->txstate |= TX_IDLE;
65062306a36Sopenharmony_ci		}
65162306a36Sopenharmony_ci	}
65262306a36Sopenharmony_ci}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_cistatic void
65562306a36Sopenharmony_cisend_tiger(struct tiger_hw *card, u8 irq_stat)
65662306a36Sopenharmony_ci{
65762306a36Sopenharmony_ci	int i;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	/* Note send is via the READ DMA channel */
66062306a36Sopenharmony_ci	if ((irq_stat & card->last_is0) & NJ_IRQM0_RD_MASK) {
66162306a36Sopenharmony_ci		pr_info("%s: tiger warn write double dma %x/%x\n",
66262306a36Sopenharmony_ci			card->name, irq_stat, card->last_is0);
66362306a36Sopenharmony_ci		return;
66462306a36Sopenharmony_ci	} else {
66562306a36Sopenharmony_ci		card->last_is0 &= ~NJ_IRQM0_RD_MASK;
66662306a36Sopenharmony_ci		card->last_is0 |= (irq_stat & NJ_IRQM0_RD_MASK);
66762306a36Sopenharmony_ci	}
66862306a36Sopenharmony_ci	for (i = 0; i < 2; i++) {
66962306a36Sopenharmony_ci		if (test_bit(FLG_ACTIVE, &card->bc[i].bch.Flags))
67062306a36Sopenharmony_ci			send_tiger_bc(card, &card->bc[i]);
67162306a36Sopenharmony_ci	}
67262306a36Sopenharmony_ci}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_cistatic irqreturn_t
67562306a36Sopenharmony_cinj_irq(int intno, void *dev_id)
67662306a36Sopenharmony_ci{
67762306a36Sopenharmony_ci	struct tiger_hw *card = dev_id;
67862306a36Sopenharmony_ci	u8 val, s1val, s0val;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	spin_lock(&card->lock);
68162306a36Sopenharmony_ci	s0val = inb(card->base | NJ_IRQSTAT0);
68262306a36Sopenharmony_ci	s1val = inb(card->base | NJ_IRQSTAT1);
68362306a36Sopenharmony_ci	if ((s1val & NJ_ISACIRQ) && (s0val == 0)) {
68462306a36Sopenharmony_ci		/* shared IRQ */
68562306a36Sopenharmony_ci		spin_unlock(&card->lock);
68662306a36Sopenharmony_ci		return IRQ_NONE;
68762306a36Sopenharmony_ci	}
68862306a36Sopenharmony_ci	pr_debug("%s: IRQSTAT0 %02x IRQSTAT1 %02x\n", card->name, s0val, s1val);
68962306a36Sopenharmony_ci	card->irqcnt++;
69062306a36Sopenharmony_ci	if (!(s1val & NJ_ISACIRQ)) {
69162306a36Sopenharmony_ci		val = ReadISAC_nj(card, ISAC_ISTA);
69262306a36Sopenharmony_ci		if (val)
69362306a36Sopenharmony_ci			mISDNisac_irq(&card->isac, val);
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	if (s0val)
69762306a36Sopenharmony_ci		/* write to clear */
69862306a36Sopenharmony_ci		outb(s0val, card->base | NJ_IRQSTAT0);
69962306a36Sopenharmony_ci	else
70062306a36Sopenharmony_ci		goto end;
70162306a36Sopenharmony_ci	s1val = s0val;
70262306a36Sopenharmony_ci	/* set bits in sval to indicate which page is free */
70362306a36Sopenharmony_ci	card->recv.dmacur = inl(card->base | NJ_DMA_WRITE_ADR);
70462306a36Sopenharmony_ci	card->recv.idx = (card->recv.dmacur - card->recv.dmastart) >> 2;
70562306a36Sopenharmony_ci	if (card->recv.dmacur < card->recv.dmairq)
70662306a36Sopenharmony_ci		s0val = 0x08;	/* the 2nd write area is free */
70762306a36Sopenharmony_ci	else
70862306a36Sopenharmony_ci		s0val = 0x04;	/* the 1st write area is free */
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	card->send.dmacur = inl(card->base | NJ_DMA_READ_ADR);
71162306a36Sopenharmony_ci	card->send.idx = (card->send.dmacur - card->send.dmastart) >> 2;
71262306a36Sopenharmony_ci	if (card->send.dmacur < card->send.dmairq)
71362306a36Sopenharmony_ci		s0val |= 0x02;	/* the 2nd read area is free */
71462306a36Sopenharmony_ci	else
71562306a36Sopenharmony_ci		s0val |= 0x01;	/* the 1st read area is free */
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	pr_debug("%s: DMA Status %02x/%02x/%02x %d/%d\n", card->name,
71862306a36Sopenharmony_ci		 s1val, s0val, card->last_is0,
71962306a36Sopenharmony_ci		 card->recv.idx, card->send.idx);
72062306a36Sopenharmony_ci	/* test if we have a DMA interrupt */
72162306a36Sopenharmony_ci	if (s0val != card->last_is0) {
72262306a36Sopenharmony_ci		if ((s0val & NJ_IRQM0_RD_MASK) !=
72362306a36Sopenharmony_ci		    (card->last_is0 & NJ_IRQM0_RD_MASK))
72462306a36Sopenharmony_ci			/* got a write dma int */
72562306a36Sopenharmony_ci			send_tiger(card, s0val);
72662306a36Sopenharmony_ci		if ((s0val & NJ_IRQM0_WR_MASK) !=
72762306a36Sopenharmony_ci		    (card->last_is0 & NJ_IRQM0_WR_MASK))
72862306a36Sopenharmony_ci			/* got a read dma int */
72962306a36Sopenharmony_ci			recv_tiger(card, s0val);
73062306a36Sopenharmony_ci	}
73162306a36Sopenharmony_ciend:
73262306a36Sopenharmony_ci	spin_unlock(&card->lock);
73362306a36Sopenharmony_ci	return IRQ_HANDLED;
73462306a36Sopenharmony_ci}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_cistatic int
73762306a36Sopenharmony_cinj_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb)
73862306a36Sopenharmony_ci{
73962306a36Sopenharmony_ci	int ret = -EINVAL;
74062306a36Sopenharmony_ci	struct bchannel *bch = container_of(ch, struct bchannel, ch);
74162306a36Sopenharmony_ci	struct tiger_ch *bc = container_of(bch, struct tiger_ch, bch);
74262306a36Sopenharmony_ci	struct tiger_hw *card = bch->hw;
74362306a36Sopenharmony_ci	struct mISDNhead *hh = mISDN_HEAD_P(skb);
74462306a36Sopenharmony_ci	unsigned long flags;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	switch (hh->prim) {
74762306a36Sopenharmony_ci	case PH_DATA_REQ:
74862306a36Sopenharmony_ci		spin_lock_irqsave(&card->lock, flags);
74962306a36Sopenharmony_ci		ret = bchannel_senddata(bch, skb);
75062306a36Sopenharmony_ci		if (ret > 0) { /* direct TX */
75162306a36Sopenharmony_ci			fill_dma(bc);
75262306a36Sopenharmony_ci			ret = 0;
75362306a36Sopenharmony_ci		}
75462306a36Sopenharmony_ci		spin_unlock_irqrestore(&card->lock, flags);
75562306a36Sopenharmony_ci		return ret;
75662306a36Sopenharmony_ci	case PH_ACTIVATE_REQ:
75762306a36Sopenharmony_ci		spin_lock_irqsave(&card->lock, flags);
75862306a36Sopenharmony_ci		if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
75962306a36Sopenharmony_ci			ret = mode_tiger(bc, ch->protocol);
76062306a36Sopenharmony_ci		else
76162306a36Sopenharmony_ci			ret = 0;
76262306a36Sopenharmony_ci		spin_unlock_irqrestore(&card->lock, flags);
76362306a36Sopenharmony_ci		if (!ret)
76462306a36Sopenharmony_ci			_queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
76562306a36Sopenharmony_ci				    NULL, GFP_KERNEL);
76662306a36Sopenharmony_ci		break;
76762306a36Sopenharmony_ci	case PH_DEACTIVATE_REQ:
76862306a36Sopenharmony_ci		spin_lock_irqsave(&card->lock, flags);
76962306a36Sopenharmony_ci		mISDN_clear_bchannel(bch);
77062306a36Sopenharmony_ci		mode_tiger(bc, ISDN_P_NONE);
77162306a36Sopenharmony_ci		spin_unlock_irqrestore(&card->lock, flags);
77262306a36Sopenharmony_ci		_queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
77362306a36Sopenharmony_ci			    NULL, GFP_KERNEL);
77462306a36Sopenharmony_ci		ret = 0;
77562306a36Sopenharmony_ci		break;
77662306a36Sopenharmony_ci	}
77762306a36Sopenharmony_ci	if (!ret)
77862306a36Sopenharmony_ci		dev_kfree_skb(skb);
77962306a36Sopenharmony_ci	return ret;
78062306a36Sopenharmony_ci}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_cistatic int
78362306a36Sopenharmony_cichannel_bctrl(struct tiger_ch *bc, struct mISDN_ctrl_req *cq)
78462306a36Sopenharmony_ci{
78562306a36Sopenharmony_ci	return mISDN_ctrl_bchannel(&bc->bch, cq);
78662306a36Sopenharmony_ci}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_cistatic int
78962306a36Sopenharmony_cinj_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
79062306a36Sopenharmony_ci{
79162306a36Sopenharmony_ci	struct bchannel *bch = container_of(ch, struct bchannel, ch);
79262306a36Sopenharmony_ci	struct tiger_ch *bc = container_of(bch, struct tiger_ch, bch);
79362306a36Sopenharmony_ci	struct tiger_hw *card  = bch->hw;
79462306a36Sopenharmony_ci	int ret = -EINVAL;
79562306a36Sopenharmony_ci	u_long flags;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	pr_debug("%s: %s cmd:%x %p\n", card->name, __func__, cmd, arg);
79862306a36Sopenharmony_ci	switch (cmd) {
79962306a36Sopenharmony_ci	case CLOSE_CHANNEL:
80062306a36Sopenharmony_ci		test_and_clear_bit(FLG_OPEN, &bch->Flags);
80162306a36Sopenharmony_ci		cancel_work_sync(&bch->workq);
80262306a36Sopenharmony_ci		spin_lock_irqsave(&card->lock, flags);
80362306a36Sopenharmony_ci		mISDN_clear_bchannel(bch);
80462306a36Sopenharmony_ci		mode_tiger(bc, ISDN_P_NONE);
80562306a36Sopenharmony_ci		spin_unlock_irqrestore(&card->lock, flags);
80662306a36Sopenharmony_ci		ch->protocol = ISDN_P_NONE;
80762306a36Sopenharmony_ci		ch->peer = NULL;
80862306a36Sopenharmony_ci		module_put(THIS_MODULE);
80962306a36Sopenharmony_ci		ret = 0;
81062306a36Sopenharmony_ci		break;
81162306a36Sopenharmony_ci	case CONTROL_CHANNEL:
81262306a36Sopenharmony_ci		ret = channel_bctrl(bc, arg);
81362306a36Sopenharmony_ci		break;
81462306a36Sopenharmony_ci	default:
81562306a36Sopenharmony_ci		pr_info("%s: %s unknown prim(%x)\n", card->name, __func__, cmd);
81662306a36Sopenharmony_ci	}
81762306a36Sopenharmony_ci	return ret;
81862306a36Sopenharmony_ci}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_cistatic int
82162306a36Sopenharmony_cichannel_ctrl(struct tiger_hw *card, struct mISDN_ctrl_req *cq)
82262306a36Sopenharmony_ci{
82362306a36Sopenharmony_ci	int	ret = 0;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	switch (cq->op) {
82662306a36Sopenharmony_ci	case MISDN_CTRL_GETOP:
82762306a36Sopenharmony_ci		cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_L1_TIMER3;
82862306a36Sopenharmony_ci		break;
82962306a36Sopenharmony_ci	case MISDN_CTRL_LOOP:
83062306a36Sopenharmony_ci		/* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */
83162306a36Sopenharmony_ci		if (cq->channel < 0 || cq->channel > 3) {
83262306a36Sopenharmony_ci			ret = -EINVAL;
83362306a36Sopenharmony_ci			break;
83462306a36Sopenharmony_ci		}
83562306a36Sopenharmony_ci		ret = card->isac.ctrl(&card->isac, HW_TESTLOOP, cq->channel);
83662306a36Sopenharmony_ci		break;
83762306a36Sopenharmony_ci	case MISDN_CTRL_L1_TIMER3:
83862306a36Sopenharmony_ci		ret = card->isac.ctrl(&card->isac, HW_TIMER3_VALUE, cq->p1);
83962306a36Sopenharmony_ci		break;
84062306a36Sopenharmony_ci	default:
84162306a36Sopenharmony_ci		pr_info("%s: %s unknown Op %x\n", card->name, __func__, cq->op);
84262306a36Sopenharmony_ci		ret = -EINVAL;
84362306a36Sopenharmony_ci		break;
84462306a36Sopenharmony_ci	}
84562306a36Sopenharmony_ci	return ret;
84662306a36Sopenharmony_ci}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_cistatic int
84962306a36Sopenharmony_ciopen_bchannel(struct tiger_hw *card, struct channel_req *rq)
85062306a36Sopenharmony_ci{
85162306a36Sopenharmony_ci	struct bchannel *bch;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	if (rq->adr.channel == 0 || rq->adr.channel > 2)
85462306a36Sopenharmony_ci		return -EINVAL;
85562306a36Sopenharmony_ci	if (rq->protocol == ISDN_P_NONE)
85662306a36Sopenharmony_ci		return -EINVAL;
85762306a36Sopenharmony_ci	bch = &card->bc[rq->adr.channel - 1].bch;
85862306a36Sopenharmony_ci	if (test_and_set_bit(FLG_OPEN, &bch->Flags))
85962306a36Sopenharmony_ci		return -EBUSY; /* b-channel can be only open once */
86062306a36Sopenharmony_ci	test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);
86162306a36Sopenharmony_ci	bch->ch.protocol = rq->protocol;
86262306a36Sopenharmony_ci	rq->ch = &bch->ch;
86362306a36Sopenharmony_ci	return 0;
86462306a36Sopenharmony_ci}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci/*
86762306a36Sopenharmony_ci * device control function
86862306a36Sopenharmony_ci */
86962306a36Sopenharmony_cistatic int
87062306a36Sopenharmony_cinj_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
87162306a36Sopenharmony_ci{
87262306a36Sopenharmony_ci	struct mISDNdevice	*dev = container_of(ch, struct mISDNdevice, D);
87362306a36Sopenharmony_ci	struct dchannel		*dch = container_of(dev, struct dchannel, dev);
87462306a36Sopenharmony_ci	struct tiger_hw	*card = dch->hw;
87562306a36Sopenharmony_ci	struct channel_req	*rq;
87662306a36Sopenharmony_ci	int			err = 0;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	pr_debug("%s: %s cmd:%x %p\n", card->name, __func__, cmd, arg);
87962306a36Sopenharmony_ci	switch (cmd) {
88062306a36Sopenharmony_ci	case OPEN_CHANNEL:
88162306a36Sopenharmony_ci		rq = arg;
88262306a36Sopenharmony_ci		if (rq->protocol == ISDN_P_TE_S0)
88362306a36Sopenharmony_ci			err = card->isac.open(&card->isac, rq);
88462306a36Sopenharmony_ci		else
88562306a36Sopenharmony_ci			err = open_bchannel(card, rq);
88662306a36Sopenharmony_ci		if (err)
88762306a36Sopenharmony_ci			break;
88862306a36Sopenharmony_ci		if (!try_module_get(THIS_MODULE))
88962306a36Sopenharmony_ci			pr_info("%s: cannot get module\n", card->name);
89062306a36Sopenharmony_ci		break;
89162306a36Sopenharmony_ci	case CLOSE_CHANNEL:
89262306a36Sopenharmony_ci		pr_debug("%s: dev(%d) close from %p\n", card->name, dch->dev.id,
89362306a36Sopenharmony_ci			 __builtin_return_address(0));
89462306a36Sopenharmony_ci		module_put(THIS_MODULE);
89562306a36Sopenharmony_ci		break;
89662306a36Sopenharmony_ci	case CONTROL_CHANNEL:
89762306a36Sopenharmony_ci		err = channel_ctrl(card, arg);
89862306a36Sopenharmony_ci		break;
89962306a36Sopenharmony_ci	default:
90062306a36Sopenharmony_ci		pr_debug("%s: %s unknown command %x\n",
90162306a36Sopenharmony_ci			 card->name, __func__, cmd);
90262306a36Sopenharmony_ci		return -EINVAL;
90362306a36Sopenharmony_ci	}
90462306a36Sopenharmony_ci	return err;
90562306a36Sopenharmony_ci}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_cistatic int
90862306a36Sopenharmony_cinj_init_card(struct tiger_hw *card)
90962306a36Sopenharmony_ci{
91062306a36Sopenharmony_ci	u_long flags;
91162306a36Sopenharmony_ci	int ret;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	spin_lock_irqsave(&card->lock, flags);
91462306a36Sopenharmony_ci	nj_disable_hwirq(card);
91562306a36Sopenharmony_ci	spin_unlock_irqrestore(&card->lock, flags);
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	card->irq = card->pdev->irq;
91862306a36Sopenharmony_ci	if (request_irq(card->irq, nj_irq, IRQF_SHARED, card->name, card)) {
91962306a36Sopenharmony_ci		pr_info("%s: couldn't get interrupt %d\n",
92062306a36Sopenharmony_ci			card->name, card->irq);
92162306a36Sopenharmony_ci		card->irq = -1;
92262306a36Sopenharmony_ci		return -EIO;
92362306a36Sopenharmony_ci	}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	spin_lock_irqsave(&card->lock, flags);
92662306a36Sopenharmony_ci	nj_reset(card);
92762306a36Sopenharmony_ci	ret = card->isac.init(&card->isac);
92862306a36Sopenharmony_ci	if (ret)
92962306a36Sopenharmony_ci		goto error;
93062306a36Sopenharmony_ci	ret = inittiger(card);
93162306a36Sopenharmony_ci	if (ret)
93262306a36Sopenharmony_ci		goto error;
93362306a36Sopenharmony_ci	mode_tiger(&card->bc[0], ISDN_P_NONE);
93462306a36Sopenharmony_ci	mode_tiger(&card->bc[1], ISDN_P_NONE);
93562306a36Sopenharmony_cierror:
93662306a36Sopenharmony_ci	spin_unlock_irqrestore(&card->lock, flags);
93762306a36Sopenharmony_ci	return ret;
93862306a36Sopenharmony_ci}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_cistatic void
94262306a36Sopenharmony_cinj_release(struct tiger_hw *card)
94362306a36Sopenharmony_ci{
94462306a36Sopenharmony_ci	u_long flags;
94562306a36Sopenharmony_ci	int i;
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	if (card->base_s) {
94862306a36Sopenharmony_ci		spin_lock_irqsave(&card->lock, flags);
94962306a36Sopenharmony_ci		nj_disable_hwirq(card);
95062306a36Sopenharmony_ci		mode_tiger(&card->bc[0], ISDN_P_NONE);
95162306a36Sopenharmony_ci		mode_tiger(&card->bc[1], ISDN_P_NONE);
95262306a36Sopenharmony_ci		spin_unlock_irqrestore(&card->lock, flags);
95362306a36Sopenharmony_ci		card->isac.release(&card->isac);
95462306a36Sopenharmony_ci		release_region(card->base, card->base_s);
95562306a36Sopenharmony_ci		card->base_s = 0;
95662306a36Sopenharmony_ci	}
95762306a36Sopenharmony_ci	if (card->irq > 0)
95862306a36Sopenharmony_ci		free_irq(card->irq, card);
95962306a36Sopenharmony_ci	if (device_is_registered(&card->isac.dch.dev.dev))
96062306a36Sopenharmony_ci		mISDN_unregister_device(&card->isac.dch.dev);
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	for (i = 0; i < 2; i++) {
96362306a36Sopenharmony_ci		mISDN_freebchannel(&card->bc[i].bch);
96462306a36Sopenharmony_ci		kfree(card->bc[i].hsbuf);
96562306a36Sopenharmony_ci		kfree(card->bc[i].hrbuf);
96662306a36Sopenharmony_ci	}
96762306a36Sopenharmony_ci	if (card->dma_p)
96862306a36Sopenharmony_ci		dma_free_coherent(&card->pdev->dev, NJ_DMA_SIZE, card->dma_p,
96962306a36Sopenharmony_ci				  card->dma);
97062306a36Sopenharmony_ci	write_lock_irqsave(&card_lock, flags);
97162306a36Sopenharmony_ci	list_del(&card->list);
97262306a36Sopenharmony_ci	write_unlock_irqrestore(&card_lock, flags);
97362306a36Sopenharmony_ci	pci_disable_device(card->pdev);
97462306a36Sopenharmony_ci	pci_set_drvdata(card->pdev, NULL);
97562306a36Sopenharmony_ci	kfree(card);
97662306a36Sopenharmony_ci}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_cistatic int
98062306a36Sopenharmony_cinj_setup(struct tiger_hw *card)
98162306a36Sopenharmony_ci{
98262306a36Sopenharmony_ci	card->base = pci_resource_start(card->pdev, 0);
98362306a36Sopenharmony_ci	card->base_s = pci_resource_len(card->pdev, 0);
98462306a36Sopenharmony_ci	if (!request_region(card->base, card->base_s, card->name)) {
98562306a36Sopenharmony_ci		pr_info("%s: NETjet config port %#x-%#x already in use\n",
98662306a36Sopenharmony_ci			card->name, card->base,
98762306a36Sopenharmony_ci			(u32)(card->base + card->base_s - 1));
98862306a36Sopenharmony_ci		card->base_s = 0;
98962306a36Sopenharmony_ci		return -EIO;
99062306a36Sopenharmony_ci	}
99162306a36Sopenharmony_ci	ASSIGN_FUNC(nj, ISAC, card->isac);
99262306a36Sopenharmony_ci	return 0;
99362306a36Sopenharmony_ci}
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_cistatic int
99762306a36Sopenharmony_cisetup_instance(struct tiger_hw *card)
99862306a36Sopenharmony_ci{
99962306a36Sopenharmony_ci	int i, err;
100062306a36Sopenharmony_ci	u_long flags;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	snprintf(card->name, MISDN_MAX_IDLEN - 1, "netjet.%d", nj_cnt + 1);
100362306a36Sopenharmony_ci	write_lock_irqsave(&card_lock, flags);
100462306a36Sopenharmony_ci	list_add_tail(&card->list, &Cards);
100562306a36Sopenharmony_ci	write_unlock_irqrestore(&card_lock, flags);
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	_set_debug(card);
100862306a36Sopenharmony_ci	card->isac.name = card->name;
100962306a36Sopenharmony_ci	spin_lock_init(&card->lock);
101062306a36Sopenharmony_ci	card->isac.hwlock = &card->lock;
101162306a36Sopenharmony_ci	mISDNisac_init(&card->isac, card);
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	card->isac.dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
101462306a36Sopenharmony_ci		(1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
101562306a36Sopenharmony_ci	card->isac.dch.dev.D.ctrl = nj_dctrl;
101662306a36Sopenharmony_ci	for (i = 0; i < 2; i++) {
101762306a36Sopenharmony_ci		card->bc[i].bch.nr = i + 1;
101862306a36Sopenharmony_ci		set_channelmap(i + 1, card->isac.dch.dev.channelmap);
101962306a36Sopenharmony_ci		mISDN_initbchannel(&card->bc[i].bch, MAX_DATA_MEM,
102062306a36Sopenharmony_ci				   NJ_DMA_RXSIZE >> 1);
102162306a36Sopenharmony_ci		card->bc[i].bch.hw = card;
102262306a36Sopenharmony_ci		card->bc[i].bch.ch.send = nj_l2l1B;
102362306a36Sopenharmony_ci		card->bc[i].bch.ch.ctrl = nj_bctrl;
102462306a36Sopenharmony_ci		card->bc[i].bch.ch.nr = i + 1;
102562306a36Sopenharmony_ci		list_add(&card->bc[i].bch.ch.list,
102662306a36Sopenharmony_ci			 &card->isac.dch.dev.bchannels);
102762306a36Sopenharmony_ci		card->bc[i].bch.hw = card;
102862306a36Sopenharmony_ci	}
102962306a36Sopenharmony_ci	err = nj_setup(card);
103062306a36Sopenharmony_ci	if (err)
103162306a36Sopenharmony_ci		goto error;
103262306a36Sopenharmony_ci	err = mISDN_register_device(&card->isac.dch.dev, &card->pdev->dev,
103362306a36Sopenharmony_ci				    card->name);
103462306a36Sopenharmony_ci	if (err)
103562306a36Sopenharmony_ci		goto error;
103662306a36Sopenharmony_ci	err = nj_init_card(card);
103762306a36Sopenharmony_ci	if (!err)  {
103862306a36Sopenharmony_ci		nj_cnt++;
103962306a36Sopenharmony_ci		pr_notice("Netjet %d cards installed\n", nj_cnt);
104062306a36Sopenharmony_ci		return 0;
104162306a36Sopenharmony_ci	}
104262306a36Sopenharmony_cierror:
104362306a36Sopenharmony_ci	nj_release(card);
104462306a36Sopenharmony_ci	return err;
104562306a36Sopenharmony_ci}
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_cistatic int
104862306a36Sopenharmony_cinj_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
104962306a36Sopenharmony_ci{
105062306a36Sopenharmony_ci	int err = -ENOMEM;
105162306a36Sopenharmony_ci	int cfg;
105262306a36Sopenharmony_ci	struct tiger_hw *card;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	if (pdev->subsystem_vendor == 0x8086 &&
105562306a36Sopenharmony_ci	    pdev->subsystem_device == 0x0003) {
105662306a36Sopenharmony_ci		pr_notice("Netjet: Digium X100P/X101P not handled\n");
105762306a36Sopenharmony_ci		return -ENODEV;
105862306a36Sopenharmony_ci	}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	if (pdev->subsystem_vendor == 0x55 &&
106162306a36Sopenharmony_ci	    pdev->subsystem_device == 0x02) {
106262306a36Sopenharmony_ci		pr_notice("Netjet: Enter!Now not handled yet\n");
106362306a36Sopenharmony_ci		return -ENODEV;
106462306a36Sopenharmony_ci	}
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	if (pdev->subsystem_vendor == 0xb100 &&
106762306a36Sopenharmony_ci	    pdev->subsystem_device == 0x0003) {
106862306a36Sopenharmony_ci		pr_notice("Netjet: Digium TDM400P not handled yet\n");
106962306a36Sopenharmony_ci		return -ENODEV;
107062306a36Sopenharmony_ci	}
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	card = kzalloc(sizeof(struct tiger_hw), GFP_KERNEL);
107362306a36Sopenharmony_ci	if (!card) {
107462306a36Sopenharmony_ci		pr_info("No kmem for Netjet\n");
107562306a36Sopenharmony_ci		return err;
107662306a36Sopenharmony_ci	}
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	card->pdev = pdev;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	err = pci_enable_device(pdev);
108162306a36Sopenharmony_ci	if (err) {
108262306a36Sopenharmony_ci		kfree(card);
108362306a36Sopenharmony_ci		return err;
108462306a36Sopenharmony_ci	}
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	printk(KERN_INFO "nj_probe(mISDN): found adapter at %s\n",
108762306a36Sopenharmony_ci	       pci_name(pdev));
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	pci_set_master(pdev);
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	/* the TJ300 and TJ320 must be detected, the IRQ handling is different
109262306a36Sopenharmony_ci	 * unfortunately the chips use the same device ID, but the TJ320 has
109362306a36Sopenharmony_ci	 * the bit20 in status PCI cfg register set
109462306a36Sopenharmony_ci	 */
109562306a36Sopenharmony_ci	pci_read_config_dword(pdev, 0x04, &cfg);
109662306a36Sopenharmony_ci	if (cfg & 0x00100000)
109762306a36Sopenharmony_ci		card->typ = NETJET_S_TJ320;
109862306a36Sopenharmony_ci	else
109962306a36Sopenharmony_ci		card->typ = NETJET_S_TJ300;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	card->base = pci_resource_start(pdev, 0);
110262306a36Sopenharmony_ci	pci_set_drvdata(pdev, card);
110362306a36Sopenharmony_ci	err = setup_instance(card);
110462306a36Sopenharmony_ci	if (err)
110562306a36Sopenharmony_ci		pci_set_drvdata(pdev, NULL);
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	return err;
110862306a36Sopenharmony_ci}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_cistatic void nj_remove(struct pci_dev *pdev)
111262306a36Sopenharmony_ci{
111362306a36Sopenharmony_ci	struct tiger_hw *card = pci_get_drvdata(pdev);
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	if (card)
111662306a36Sopenharmony_ci		nj_release(card);
111762306a36Sopenharmony_ci	else
111862306a36Sopenharmony_ci		pr_info("%s drvdata already removed\n", __func__);
111962306a36Sopenharmony_ci}
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci/* We cannot select cards with PCI_SUB... IDs, since here are cards with
112262306a36Sopenharmony_ci * SUB IDs set to PCI_ANY_ID, so we need to match all and reject
112362306a36Sopenharmony_ci * known other cards which not work with this driver - see probe function */
112462306a36Sopenharmony_cistatic const struct pci_device_id nj_pci_ids[] = {
112562306a36Sopenharmony_ci	{ PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_300,
112662306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
112762306a36Sopenharmony_ci	{ }
112862306a36Sopenharmony_ci};
112962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, nj_pci_ids);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_cistatic struct pci_driver nj_driver = {
113262306a36Sopenharmony_ci	.name = "netjet",
113362306a36Sopenharmony_ci	.probe = nj_probe,
113462306a36Sopenharmony_ci	.remove = nj_remove,
113562306a36Sopenharmony_ci	.id_table = nj_pci_ids,
113662306a36Sopenharmony_ci};
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_cistatic int __init nj_init(void)
113962306a36Sopenharmony_ci{
114062306a36Sopenharmony_ci	int err;
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	pr_notice("Netjet PCI driver Rev. %s\n", NETJET_REV);
114362306a36Sopenharmony_ci	err = pci_register_driver(&nj_driver);
114462306a36Sopenharmony_ci	return err;
114562306a36Sopenharmony_ci}
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_cistatic void __exit nj_cleanup(void)
114862306a36Sopenharmony_ci{
114962306a36Sopenharmony_ci	pci_unregister_driver(&nj_driver);
115062306a36Sopenharmony_ci}
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_cimodule_init(nj_init);
115362306a36Sopenharmony_cimodule_exit(nj_cleanup);
1154