18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * NETJet mISDN driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author       Karsten Keil <keil@isdn4linux.de>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/pci.h>
138c2ecf20Sopenharmony_ci#include <linux/delay.h>
148c2ecf20Sopenharmony_ci#include <linux/mISDNhw.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include "ipac.h"
178c2ecf20Sopenharmony_ci#include "iohelper.h"
188c2ecf20Sopenharmony_ci#include "netjet.h"
198c2ecf20Sopenharmony_ci#include "isdnhdlc.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define NETJET_REV	"2.0"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cienum nj_types {
248c2ecf20Sopenharmony_ci	NETJET_S_TJ300,
258c2ecf20Sopenharmony_ci	NETJET_S_TJ320,
268c2ecf20Sopenharmony_ci	ENTERNOW__TJ320,
278c2ecf20Sopenharmony_ci};
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistruct tiger_dma {
308c2ecf20Sopenharmony_ci	size_t		size;
318c2ecf20Sopenharmony_ci	u32		*start;
328c2ecf20Sopenharmony_ci	int		idx;
338c2ecf20Sopenharmony_ci	u32		dmastart;
348c2ecf20Sopenharmony_ci	u32		dmairq;
358c2ecf20Sopenharmony_ci	u32		dmaend;
368c2ecf20Sopenharmony_ci	u32		dmacur;
378c2ecf20Sopenharmony_ci};
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistruct tiger_hw;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistruct tiger_ch {
428c2ecf20Sopenharmony_ci	struct bchannel		bch;
438c2ecf20Sopenharmony_ci	struct tiger_hw		*nj;
448c2ecf20Sopenharmony_ci	int			idx;
458c2ecf20Sopenharmony_ci	int			free;
468c2ecf20Sopenharmony_ci	int			lastrx;
478c2ecf20Sopenharmony_ci	u16			rxstate;
488c2ecf20Sopenharmony_ci	u16			txstate;
498c2ecf20Sopenharmony_ci	struct isdnhdlc_vars	hsend;
508c2ecf20Sopenharmony_ci	struct isdnhdlc_vars	hrecv;
518c2ecf20Sopenharmony_ci	u8			*hsbuf;
528c2ecf20Sopenharmony_ci	u8			*hrbuf;
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#define TX_INIT		0x0001
568c2ecf20Sopenharmony_ci#define TX_IDLE		0x0002
578c2ecf20Sopenharmony_ci#define TX_RUN		0x0004
588c2ecf20Sopenharmony_ci#define TX_UNDERRUN	0x0100
598c2ecf20Sopenharmony_ci#define RX_OVERRUN	0x0100
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci#define LOG_SIZE	64
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistruct tiger_hw {
648c2ecf20Sopenharmony_ci	struct list_head	list;
658c2ecf20Sopenharmony_ci	struct pci_dev		*pdev;
668c2ecf20Sopenharmony_ci	char			name[MISDN_MAX_IDLEN];
678c2ecf20Sopenharmony_ci	enum nj_types		typ;
688c2ecf20Sopenharmony_ci	int			irq;
698c2ecf20Sopenharmony_ci	u32			irqcnt;
708c2ecf20Sopenharmony_ci	u32			base;
718c2ecf20Sopenharmony_ci	size_t			base_s;
728c2ecf20Sopenharmony_ci	dma_addr_t		dma;
738c2ecf20Sopenharmony_ci	void			*dma_p;
748c2ecf20Sopenharmony_ci	spinlock_t		lock;	/* lock HW */
758c2ecf20Sopenharmony_ci	struct isac_hw		isac;
768c2ecf20Sopenharmony_ci	struct tiger_dma	send;
778c2ecf20Sopenharmony_ci	struct tiger_dma	recv;
788c2ecf20Sopenharmony_ci	struct tiger_ch		bc[2];
798c2ecf20Sopenharmony_ci	u8			ctrlreg;
808c2ecf20Sopenharmony_ci	u8			dmactrl;
818c2ecf20Sopenharmony_ci	u8			auxd;
828c2ecf20Sopenharmony_ci	u8			last_is0;
838c2ecf20Sopenharmony_ci	u8			irqmask0;
848c2ecf20Sopenharmony_ci	char			log[LOG_SIZE];
858c2ecf20Sopenharmony_ci};
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic LIST_HEAD(Cards);
888c2ecf20Sopenharmony_cistatic DEFINE_RWLOCK(card_lock); /* protect Cards */
898c2ecf20Sopenharmony_cistatic u32 debug;
908c2ecf20Sopenharmony_cistatic int nj_cnt;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic void
938c2ecf20Sopenharmony_ci_set_debug(struct tiger_hw *card)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	card->isac.dch.debug = debug;
968c2ecf20Sopenharmony_ci	card->bc[0].bch.debug = debug;
978c2ecf20Sopenharmony_ci	card->bc[1].bch.debug = debug;
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic int
1018c2ecf20Sopenharmony_ciset_debug(const char *val, const struct kernel_param *kp)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	int ret;
1048c2ecf20Sopenharmony_ci	struct tiger_hw *card;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	ret = param_set_uint(val, kp);
1078c2ecf20Sopenharmony_ci	if (!ret) {
1088c2ecf20Sopenharmony_ci		read_lock(&card_lock);
1098c2ecf20Sopenharmony_ci		list_for_each_entry(card, &Cards, list)
1108c2ecf20Sopenharmony_ci			_set_debug(card);
1118c2ecf20Sopenharmony_ci		read_unlock(&card_lock);
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci	return ret;
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ciMODULE_AUTHOR("Karsten Keil");
1178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
1188c2ecf20Sopenharmony_ciMODULE_VERSION(NETJET_REV);
1198c2ecf20Sopenharmony_cimodule_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
1208c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Netjet debug mask");
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic void
1238c2ecf20Sopenharmony_cinj_disable_hwirq(struct tiger_hw *card)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	outb(0, card->base + NJ_IRQMASK0);
1268c2ecf20Sopenharmony_ci	outb(0, card->base + NJ_IRQMASK1);
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic u8
1318c2ecf20Sopenharmony_ciReadISAC_nj(void *p, u8 offset)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct tiger_hw *card = p;
1348c2ecf20Sopenharmony_ci	u8 ret;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	card->auxd &= 0xfc;
1378c2ecf20Sopenharmony_ci	card->auxd |= (offset >> 4) & 3;
1388c2ecf20Sopenharmony_ci	outb(card->auxd, card->base + NJ_AUXDATA);
1398c2ecf20Sopenharmony_ci	ret = inb(card->base + NJ_ISAC_OFF + ((offset & 0x0f) << 2));
1408c2ecf20Sopenharmony_ci	return ret;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic void
1448c2ecf20Sopenharmony_ciWriteISAC_nj(void *p, u8 offset, u8 value)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	struct tiger_hw *card = p;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	card->auxd &= 0xfc;
1498c2ecf20Sopenharmony_ci	card->auxd |= (offset >> 4) & 3;
1508c2ecf20Sopenharmony_ci	outb(card->auxd, card->base + NJ_AUXDATA);
1518c2ecf20Sopenharmony_ci	outb(value, card->base + NJ_ISAC_OFF + ((offset & 0x0f) << 2));
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic void
1558c2ecf20Sopenharmony_ciReadFiFoISAC_nj(void *p, u8 offset, u8 *data, int size)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	struct tiger_hw *card = p;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	card->auxd &= 0xfc;
1608c2ecf20Sopenharmony_ci	outb(card->auxd, card->base + NJ_AUXDATA);
1618c2ecf20Sopenharmony_ci	insb(card->base + NJ_ISAC_OFF, data, size);
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic void
1658c2ecf20Sopenharmony_ciWriteFiFoISAC_nj(void *p, u8 offset, u8 *data, int size)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	struct tiger_hw *card = p;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	card->auxd &= 0xfc;
1708c2ecf20Sopenharmony_ci	outb(card->auxd, card->base + NJ_AUXDATA);
1718c2ecf20Sopenharmony_ci	outsb(card->base + NJ_ISAC_OFF, data, size);
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic void
1758c2ecf20Sopenharmony_cifill_mem(struct tiger_ch *bc, u32 idx, u32 cnt, u32 fill)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	struct tiger_hw *card = bc->bch.hw;
1788c2ecf20Sopenharmony_ci	u32 mask = 0xff, val;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	pr_debug("%s: B%1d fill %02x len %d idx %d/%d\n", card->name,
1818c2ecf20Sopenharmony_ci		 bc->bch.nr, fill, cnt, idx, card->send.idx);
1828c2ecf20Sopenharmony_ci	if (bc->bch.nr & 2) {
1838c2ecf20Sopenharmony_ci		fill  <<= 8;
1848c2ecf20Sopenharmony_ci		mask <<= 8;
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci	mask ^= 0xffffffff;
1878c2ecf20Sopenharmony_ci	while (cnt--) {
1888c2ecf20Sopenharmony_ci		val = card->send.start[idx];
1898c2ecf20Sopenharmony_ci		val &= mask;
1908c2ecf20Sopenharmony_ci		val |= fill;
1918c2ecf20Sopenharmony_ci		card->send.start[idx++] = val;
1928c2ecf20Sopenharmony_ci		if (idx >= card->send.size)
1938c2ecf20Sopenharmony_ci			idx = 0;
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic int
1988c2ecf20Sopenharmony_cimode_tiger(struct tiger_ch *bc, u32 protocol)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	struct tiger_hw *card = bc->bch.hw;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	pr_debug("%s: B%1d protocol %x-->%x\n", card->name,
2038c2ecf20Sopenharmony_ci		 bc->bch.nr, bc->bch.state, protocol);
2048c2ecf20Sopenharmony_ci	switch (protocol) {
2058c2ecf20Sopenharmony_ci	case ISDN_P_NONE:
2068c2ecf20Sopenharmony_ci		if (bc->bch.state == ISDN_P_NONE)
2078c2ecf20Sopenharmony_ci			break;
2088c2ecf20Sopenharmony_ci		fill_mem(bc, 0, card->send.size, 0xff);
2098c2ecf20Sopenharmony_ci		bc->bch.state = protocol;
2108c2ecf20Sopenharmony_ci		/* only stop dma and interrupts if both channels NULL */
2118c2ecf20Sopenharmony_ci		if ((card->bc[0].bch.state == ISDN_P_NONE) &&
2128c2ecf20Sopenharmony_ci		    (card->bc[1].bch.state == ISDN_P_NONE)) {
2138c2ecf20Sopenharmony_ci			card->dmactrl = 0;
2148c2ecf20Sopenharmony_ci			outb(card->dmactrl, card->base + NJ_DMACTRL);
2158c2ecf20Sopenharmony_ci			outb(0, card->base + NJ_IRQMASK0);
2168c2ecf20Sopenharmony_ci		}
2178c2ecf20Sopenharmony_ci		test_and_clear_bit(FLG_HDLC, &bc->bch.Flags);
2188c2ecf20Sopenharmony_ci		test_and_clear_bit(FLG_TRANSPARENT, &bc->bch.Flags);
2198c2ecf20Sopenharmony_ci		bc->txstate = 0;
2208c2ecf20Sopenharmony_ci		bc->rxstate = 0;
2218c2ecf20Sopenharmony_ci		bc->lastrx = -1;
2228c2ecf20Sopenharmony_ci		break;
2238c2ecf20Sopenharmony_ci	case ISDN_P_B_RAW:
2248c2ecf20Sopenharmony_ci		test_and_set_bit(FLG_TRANSPARENT, &bc->bch.Flags);
2258c2ecf20Sopenharmony_ci		bc->bch.state = protocol;
2268c2ecf20Sopenharmony_ci		bc->idx = 0;
2278c2ecf20Sopenharmony_ci		bc->free = card->send.size / 2;
2288c2ecf20Sopenharmony_ci		bc->rxstate = 0;
2298c2ecf20Sopenharmony_ci		bc->txstate = TX_INIT | TX_IDLE;
2308c2ecf20Sopenharmony_ci		bc->lastrx = -1;
2318c2ecf20Sopenharmony_ci		if (!card->dmactrl) {
2328c2ecf20Sopenharmony_ci			card->dmactrl = 1;
2338c2ecf20Sopenharmony_ci			outb(card->dmactrl, card->base + NJ_DMACTRL);
2348c2ecf20Sopenharmony_ci			outb(0x0f, card->base + NJ_IRQMASK0);
2358c2ecf20Sopenharmony_ci		}
2368c2ecf20Sopenharmony_ci		break;
2378c2ecf20Sopenharmony_ci	case ISDN_P_B_HDLC:
2388c2ecf20Sopenharmony_ci		test_and_set_bit(FLG_HDLC, &bc->bch.Flags);
2398c2ecf20Sopenharmony_ci		bc->bch.state = protocol;
2408c2ecf20Sopenharmony_ci		bc->idx = 0;
2418c2ecf20Sopenharmony_ci		bc->free = card->send.size / 2;
2428c2ecf20Sopenharmony_ci		bc->rxstate = 0;
2438c2ecf20Sopenharmony_ci		bc->txstate = TX_INIT | TX_IDLE;
2448c2ecf20Sopenharmony_ci		isdnhdlc_rcv_init(&bc->hrecv, 0);
2458c2ecf20Sopenharmony_ci		isdnhdlc_out_init(&bc->hsend, 0);
2468c2ecf20Sopenharmony_ci		bc->lastrx = -1;
2478c2ecf20Sopenharmony_ci		if (!card->dmactrl) {
2488c2ecf20Sopenharmony_ci			card->dmactrl = 1;
2498c2ecf20Sopenharmony_ci			outb(card->dmactrl, card->base + NJ_DMACTRL);
2508c2ecf20Sopenharmony_ci			outb(0x0f, card->base + NJ_IRQMASK0);
2518c2ecf20Sopenharmony_ci		}
2528c2ecf20Sopenharmony_ci		break;
2538c2ecf20Sopenharmony_ci	default:
2548c2ecf20Sopenharmony_ci		pr_info("%s: %s protocol %x not handled\n", card->name,
2558c2ecf20Sopenharmony_ci			__func__, protocol);
2568c2ecf20Sopenharmony_ci		return -ENOPROTOOPT;
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci	card->send.dmacur = inl(card->base + NJ_DMA_READ_ADR);
2598c2ecf20Sopenharmony_ci	card->recv.dmacur = inl(card->base + NJ_DMA_WRITE_ADR);
2608c2ecf20Sopenharmony_ci	card->send.idx = (card->send.dmacur - card->send.dmastart) >> 2;
2618c2ecf20Sopenharmony_ci	card->recv.idx = (card->recv.dmacur - card->recv.dmastart) >> 2;
2628c2ecf20Sopenharmony_ci	pr_debug("%s: %s ctrl %x irq  %02x/%02x idx %d/%d\n",
2638c2ecf20Sopenharmony_ci		 card->name, __func__,
2648c2ecf20Sopenharmony_ci		 inb(card->base + NJ_DMACTRL),
2658c2ecf20Sopenharmony_ci		 inb(card->base + NJ_IRQMASK0),
2668c2ecf20Sopenharmony_ci		 inb(card->base + NJ_IRQSTAT0),
2678c2ecf20Sopenharmony_ci		 card->send.idx,
2688c2ecf20Sopenharmony_ci		 card->recv.idx);
2698c2ecf20Sopenharmony_ci	return 0;
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic void
2738c2ecf20Sopenharmony_cinj_reset(struct tiger_hw *card)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	outb(0xff, card->base + NJ_CTRL); /* Reset On */
2768c2ecf20Sopenharmony_ci	mdelay(1);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	/* now edge triggered for TJ320 GE 13/07/00 */
2798c2ecf20Sopenharmony_ci	/* see comment in IRQ function */
2808c2ecf20Sopenharmony_ci	if (card->typ == NETJET_S_TJ320) /* TJ320 */
2818c2ecf20Sopenharmony_ci		card->ctrlreg = 0x40;  /* Reset Off and status read clear */
2828c2ecf20Sopenharmony_ci	else
2838c2ecf20Sopenharmony_ci		card->ctrlreg = 0x00;  /* Reset Off and status read clear */
2848c2ecf20Sopenharmony_ci	outb(card->ctrlreg, card->base + NJ_CTRL);
2858c2ecf20Sopenharmony_ci	mdelay(10);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	/* configure AUX pins (all output except ISAC IRQ pin) */
2888c2ecf20Sopenharmony_ci	card->auxd = 0;
2898c2ecf20Sopenharmony_ci	card->dmactrl = 0;
2908c2ecf20Sopenharmony_ci	outb(~NJ_ISACIRQ, card->base + NJ_AUXCTRL);
2918c2ecf20Sopenharmony_ci	outb(NJ_ISACIRQ,  card->base + NJ_IRQMASK1);
2928c2ecf20Sopenharmony_ci	outb(card->auxd, card->base + NJ_AUXDATA);
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_cistatic int
2968c2ecf20Sopenharmony_ciinittiger(struct tiger_hw *card)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	int i;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	card->dma_p = dma_alloc_coherent(&card->pdev->dev, NJ_DMA_SIZE,
3018c2ecf20Sopenharmony_ci					 &card->dma, GFP_ATOMIC);
3028c2ecf20Sopenharmony_ci	if (!card->dma_p) {
3038c2ecf20Sopenharmony_ci		pr_info("%s: No DMA memory\n", card->name);
3048c2ecf20Sopenharmony_ci		return -ENOMEM;
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci	if ((u64)card->dma > 0xffffffff) {
3078c2ecf20Sopenharmony_ci		pr_info("%s: DMA outside 32 bit\n", card->name);
3088c2ecf20Sopenharmony_ci		return -ENOMEM;
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++) {
3118c2ecf20Sopenharmony_ci		card->bc[i].hsbuf = kmalloc(NJ_DMA_TXSIZE, GFP_ATOMIC);
3128c2ecf20Sopenharmony_ci		if (!card->bc[i].hsbuf) {
3138c2ecf20Sopenharmony_ci			pr_info("%s: no B%d send buffer\n", card->name, i + 1);
3148c2ecf20Sopenharmony_ci			return -ENOMEM;
3158c2ecf20Sopenharmony_ci		}
3168c2ecf20Sopenharmony_ci		card->bc[i].hrbuf = kmalloc(NJ_DMA_RXSIZE, GFP_ATOMIC);
3178c2ecf20Sopenharmony_ci		if (!card->bc[i].hrbuf) {
3188c2ecf20Sopenharmony_ci			pr_info("%s: no B%d recv buffer\n", card->name, i + 1);
3198c2ecf20Sopenharmony_ci			return -ENOMEM;
3208c2ecf20Sopenharmony_ci		}
3218c2ecf20Sopenharmony_ci	}
3228c2ecf20Sopenharmony_ci	memset(card->dma_p, 0xff, NJ_DMA_SIZE);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	card->send.start = card->dma_p;
3258c2ecf20Sopenharmony_ci	card->send.dmastart = (u32)card->dma;
3268c2ecf20Sopenharmony_ci	card->send.dmaend = card->send.dmastart +
3278c2ecf20Sopenharmony_ci		(4 * (NJ_DMA_TXSIZE - 1));
3288c2ecf20Sopenharmony_ci	card->send.dmairq = card->send.dmastart +
3298c2ecf20Sopenharmony_ci		(4 * ((NJ_DMA_TXSIZE / 2) - 1));
3308c2ecf20Sopenharmony_ci	card->send.size = NJ_DMA_TXSIZE;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	if (debug & DEBUG_HW)
3338c2ecf20Sopenharmony_ci		pr_notice("%s: send buffer phy %#x - %#x - %#x  virt %p"
3348c2ecf20Sopenharmony_ci			  " size %zu u32\n", card->name,
3358c2ecf20Sopenharmony_ci			  card->send.dmastart, card->send.dmairq,
3368c2ecf20Sopenharmony_ci			  card->send.dmaend, card->send.start, card->send.size);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	outl(card->send.dmastart, card->base + NJ_DMA_READ_START);
3398c2ecf20Sopenharmony_ci	outl(card->send.dmairq, card->base + NJ_DMA_READ_IRQ);
3408c2ecf20Sopenharmony_ci	outl(card->send.dmaend, card->base + NJ_DMA_READ_END);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	card->recv.start = card->dma_p + (NJ_DMA_SIZE / 2);
3438c2ecf20Sopenharmony_ci	card->recv.dmastart = (u32)card->dma  + (NJ_DMA_SIZE / 2);
3448c2ecf20Sopenharmony_ci	card->recv.dmaend = card->recv.dmastart +
3458c2ecf20Sopenharmony_ci		(4 * (NJ_DMA_RXSIZE - 1));
3468c2ecf20Sopenharmony_ci	card->recv.dmairq = card->recv.dmastart +
3478c2ecf20Sopenharmony_ci		(4 * ((NJ_DMA_RXSIZE / 2) - 1));
3488c2ecf20Sopenharmony_ci	card->recv.size = NJ_DMA_RXSIZE;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if (debug & DEBUG_HW)
3518c2ecf20Sopenharmony_ci		pr_notice("%s: recv buffer phy %#x - %#x - %#x  virt %p"
3528c2ecf20Sopenharmony_ci			  " size %zu u32\n", card->name,
3538c2ecf20Sopenharmony_ci			  card->recv.dmastart, card->recv.dmairq,
3548c2ecf20Sopenharmony_ci			  card->recv.dmaend, card->recv.start, card->recv.size);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	outl(card->recv.dmastart, card->base + NJ_DMA_WRITE_START);
3578c2ecf20Sopenharmony_ci	outl(card->recv.dmairq, card->base + NJ_DMA_WRITE_IRQ);
3588c2ecf20Sopenharmony_ci	outl(card->recv.dmaend, card->base + NJ_DMA_WRITE_END);
3598c2ecf20Sopenharmony_ci	return 0;
3608c2ecf20Sopenharmony_ci}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_cistatic void
3638c2ecf20Sopenharmony_ciread_dma(struct tiger_ch *bc, u32 idx, int cnt)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	struct tiger_hw *card = bc->bch.hw;
3668c2ecf20Sopenharmony_ci	int i, stat;
3678c2ecf20Sopenharmony_ci	u32 val;
3688c2ecf20Sopenharmony_ci	u8 *p, *pn;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	if (bc->lastrx == idx) {
3718c2ecf20Sopenharmony_ci		bc->rxstate |= RX_OVERRUN;
3728c2ecf20Sopenharmony_ci		pr_info("%s: B%1d overrun at idx %d\n", card->name,
3738c2ecf20Sopenharmony_ci			bc->bch.nr, idx);
3748c2ecf20Sopenharmony_ci	}
3758c2ecf20Sopenharmony_ci	bc->lastrx = idx;
3768c2ecf20Sopenharmony_ci	if (test_bit(FLG_RX_OFF, &bc->bch.Flags)) {
3778c2ecf20Sopenharmony_ci		bc->bch.dropcnt += cnt;
3788c2ecf20Sopenharmony_ci		return;
3798c2ecf20Sopenharmony_ci	}
3808c2ecf20Sopenharmony_ci	stat = bchannel_get_rxbuf(&bc->bch, cnt);
3818c2ecf20Sopenharmony_ci	/* only transparent use the count here, HDLC overun is detected later */
3828c2ecf20Sopenharmony_ci	if (stat == -ENOMEM) {
3838c2ecf20Sopenharmony_ci		pr_warn("%s.B%d: No memory for %d bytes\n",
3848c2ecf20Sopenharmony_ci			card->name, bc->bch.nr, cnt);
3858c2ecf20Sopenharmony_ci		return;
3868c2ecf20Sopenharmony_ci	}
3878c2ecf20Sopenharmony_ci	if (test_bit(FLG_TRANSPARENT, &bc->bch.Flags))
3888c2ecf20Sopenharmony_ci		p = skb_put(bc->bch.rx_skb, cnt);
3898c2ecf20Sopenharmony_ci	else
3908c2ecf20Sopenharmony_ci		p = bc->hrbuf;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	for (i = 0; i < cnt; i++) {
3938c2ecf20Sopenharmony_ci		val = card->recv.start[idx++];
3948c2ecf20Sopenharmony_ci		if (bc->bch.nr & 2)
3958c2ecf20Sopenharmony_ci			val >>= 8;
3968c2ecf20Sopenharmony_ci		if (idx >= card->recv.size)
3978c2ecf20Sopenharmony_ci			idx = 0;
3988c2ecf20Sopenharmony_ci		p[i] = val & 0xff;
3998c2ecf20Sopenharmony_ci	}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	if (test_bit(FLG_TRANSPARENT, &bc->bch.Flags)) {
4028c2ecf20Sopenharmony_ci		recv_Bchannel(&bc->bch, 0, false);
4038c2ecf20Sopenharmony_ci		return;
4048c2ecf20Sopenharmony_ci	}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	pn = bc->hrbuf;
4078c2ecf20Sopenharmony_ci	while (cnt > 0) {
4088c2ecf20Sopenharmony_ci		stat = isdnhdlc_decode(&bc->hrecv, pn, cnt, &i,
4098c2ecf20Sopenharmony_ci				       bc->bch.rx_skb->data, bc->bch.maxlen);
4108c2ecf20Sopenharmony_ci		if (stat > 0) { /* valid frame received */
4118c2ecf20Sopenharmony_ci			p = skb_put(bc->bch.rx_skb, stat);
4128c2ecf20Sopenharmony_ci			if (debug & DEBUG_HW_BFIFO) {
4138c2ecf20Sopenharmony_ci				snprintf(card->log, LOG_SIZE,
4148c2ecf20Sopenharmony_ci					 "B%1d-recv %s %d ", bc->bch.nr,
4158c2ecf20Sopenharmony_ci					 card->name, stat);
4168c2ecf20Sopenharmony_ci				print_hex_dump_bytes(card->log,
4178c2ecf20Sopenharmony_ci						     DUMP_PREFIX_OFFSET, p,
4188c2ecf20Sopenharmony_ci						     stat);
4198c2ecf20Sopenharmony_ci			}
4208c2ecf20Sopenharmony_ci			recv_Bchannel(&bc->bch, 0, false);
4218c2ecf20Sopenharmony_ci			stat = bchannel_get_rxbuf(&bc->bch, bc->bch.maxlen);
4228c2ecf20Sopenharmony_ci			if (stat < 0) {
4238c2ecf20Sopenharmony_ci				pr_warn("%s.B%d: No memory for %d bytes\n",
4248c2ecf20Sopenharmony_ci					card->name, bc->bch.nr, cnt);
4258c2ecf20Sopenharmony_ci				return;
4268c2ecf20Sopenharmony_ci			}
4278c2ecf20Sopenharmony_ci		} else if (stat == -HDLC_CRC_ERROR) {
4288c2ecf20Sopenharmony_ci			pr_info("%s: B%1d receive frame CRC error\n",
4298c2ecf20Sopenharmony_ci				card->name, bc->bch.nr);
4308c2ecf20Sopenharmony_ci		} else if (stat == -HDLC_FRAMING_ERROR) {
4318c2ecf20Sopenharmony_ci			pr_info("%s: B%1d receive framing error\n",
4328c2ecf20Sopenharmony_ci				card->name, bc->bch.nr);
4338c2ecf20Sopenharmony_ci		} else if (stat == -HDLC_LENGTH_ERROR) {
4348c2ecf20Sopenharmony_ci			pr_info("%s: B%1d receive frame too long (> %d)\n",
4358c2ecf20Sopenharmony_ci				card->name, bc->bch.nr, bc->bch.maxlen);
4368c2ecf20Sopenharmony_ci		}
4378c2ecf20Sopenharmony_ci		pn += i;
4388c2ecf20Sopenharmony_ci		cnt -= i;
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic void
4438c2ecf20Sopenharmony_cirecv_tiger(struct tiger_hw *card, u8 irq_stat)
4448c2ecf20Sopenharmony_ci{
4458c2ecf20Sopenharmony_ci	u32 idx;
4468c2ecf20Sopenharmony_ci	int cnt = card->recv.size / 2;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	/* Note receive is via the WRITE DMA channel */
4498c2ecf20Sopenharmony_ci	card->last_is0 &= ~NJ_IRQM0_WR_MASK;
4508c2ecf20Sopenharmony_ci	card->last_is0 |= (irq_stat & NJ_IRQM0_WR_MASK);
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	if (irq_stat & NJ_IRQM0_WR_END)
4538c2ecf20Sopenharmony_ci		idx = cnt - 1;
4548c2ecf20Sopenharmony_ci	else
4558c2ecf20Sopenharmony_ci		idx = card->recv.size - 1;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	if (test_bit(FLG_ACTIVE, &card->bc[0].bch.Flags))
4588c2ecf20Sopenharmony_ci		read_dma(&card->bc[0], idx, cnt);
4598c2ecf20Sopenharmony_ci	if (test_bit(FLG_ACTIVE, &card->bc[1].bch.Flags))
4608c2ecf20Sopenharmony_ci		read_dma(&card->bc[1], idx, cnt);
4618c2ecf20Sopenharmony_ci}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci/* sync with current DMA address at start or after exception */
4648c2ecf20Sopenharmony_cistatic void
4658c2ecf20Sopenharmony_ciresync(struct tiger_ch *bc, struct tiger_hw *card)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	card->send.dmacur = inl(card->base | NJ_DMA_READ_ADR);
4688c2ecf20Sopenharmony_ci	card->send.idx = (card->send.dmacur - card->send.dmastart) >> 2;
4698c2ecf20Sopenharmony_ci	if (bc->free > card->send.size / 2)
4708c2ecf20Sopenharmony_ci		bc->free = card->send.size / 2;
4718c2ecf20Sopenharmony_ci	/* currently we simple sync to the next complete free area
4728c2ecf20Sopenharmony_ci	 * this hast the advantage that we have always maximum time to
4738c2ecf20Sopenharmony_ci	 * handle TX irq
4748c2ecf20Sopenharmony_ci	 */
4758c2ecf20Sopenharmony_ci	if (card->send.idx < ((card->send.size / 2) - 1))
4768c2ecf20Sopenharmony_ci		bc->idx = (card->recv.size / 2) - 1;
4778c2ecf20Sopenharmony_ci	else
4788c2ecf20Sopenharmony_ci		bc->idx = card->recv.size - 1;
4798c2ecf20Sopenharmony_ci	bc->txstate = TX_RUN;
4808c2ecf20Sopenharmony_ci	pr_debug("%s: %s B%1d free %d idx %d/%d\n", card->name,
4818c2ecf20Sopenharmony_ci		 __func__, bc->bch.nr, bc->free, bc->idx, card->send.idx);
4828c2ecf20Sopenharmony_ci}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cistatic int bc_next_frame(struct tiger_ch *);
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_cistatic void
4878c2ecf20Sopenharmony_cifill_hdlc_flag(struct tiger_ch *bc)
4888c2ecf20Sopenharmony_ci{
4898c2ecf20Sopenharmony_ci	struct tiger_hw *card = bc->bch.hw;
4908c2ecf20Sopenharmony_ci	int count, i;
4918c2ecf20Sopenharmony_ci	u32 m, v;
4928c2ecf20Sopenharmony_ci	u8  *p;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	if (bc->free == 0)
4958c2ecf20Sopenharmony_ci		return;
4968c2ecf20Sopenharmony_ci	pr_debug("%s: %s B%1d %d state %x idx %d/%d\n", card->name,
4978c2ecf20Sopenharmony_ci		 __func__, bc->bch.nr, bc->free, bc->txstate,
4988c2ecf20Sopenharmony_ci		 bc->idx, card->send.idx);
4998c2ecf20Sopenharmony_ci	if (bc->txstate & (TX_IDLE | TX_INIT | TX_UNDERRUN))
5008c2ecf20Sopenharmony_ci		resync(bc, card);
5018c2ecf20Sopenharmony_ci	count = isdnhdlc_encode(&bc->hsend, NULL, 0, &i,
5028c2ecf20Sopenharmony_ci				bc->hsbuf, bc->free);
5038c2ecf20Sopenharmony_ci	pr_debug("%s: B%1d hdlc encoded %d flags\n", card->name,
5048c2ecf20Sopenharmony_ci		 bc->bch.nr, count);
5058c2ecf20Sopenharmony_ci	bc->free -= count;
5068c2ecf20Sopenharmony_ci	p = bc->hsbuf;
5078c2ecf20Sopenharmony_ci	m = (bc->bch.nr & 1) ? 0xffffff00 : 0xffff00ff;
5088c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
5098c2ecf20Sopenharmony_ci		if (bc->idx >= card->send.size)
5108c2ecf20Sopenharmony_ci			bc->idx = 0;
5118c2ecf20Sopenharmony_ci		v = card->send.start[bc->idx];
5128c2ecf20Sopenharmony_ci		v &= m;
5138c2ecf20Sopenharmony_ci		v |= (bc->bch.nr & 1) ? (u32)(p[i]) : ((u32)(p[i])) << 8;
5148c2ecf20Sopenharmony_ci		card->send.start[bc->idx++] = v;
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ci	if (debug & DEBUG_HW_BFIFO) {
5178c2ecf20Sopenharmony_ci		snprintf(card->log, LOG_SIZE, "B%1d-send %s %d ",
5188c2ecf20Sopenharmony_ci			 bc->bch.nr, card->name, count);
5198c2ecf20Sopenharmony_ci		print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, p, count);
5208c2ecf20Sopenharmony_ci	}
5218c2ecf20Sopenharmony_ci}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_cistatic void
5248c2ecf20Sopenharmony_cifill_dma(struct tiger_ch *bc)
5258c2ecf20Sopenharmony_ci{
5268c2ecf20Sopenharmony_ci	struct tiger_hw *card = bc->bch.hw;
5278c2ecf20Sopenharmony_ci	int count, i, fillempty = 0;
5288c2ecf20Sopenharmony_ci	u32 m, v, n = 0;
5298c2ecf20Sopenharmony_ci	u8  *p;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	if (bc->free == 0)
5328c2ecf20Sopenharmony_ci		return;
5338c2ecf20Sopenharmony_ci	if (!bc->bch.tx_skb) {
5348c2ecf20Sopenharmony_ci		if (!test_bit(FLG_TX_EMPTY, &bc->bch.Flags))
5358c2ecf20Sopenharmony_ci			return;
5368c2ecf20Sopenharmony_ci		fillempty = 1;
5378c2ecf20Sopenharmony_ci		count = card->send.size >> 1;
5388c2ecf20Sopenharmony_ci		p = bc->bch.fill;
5398c2ecf20Sopenharmony_ci	} else {
5408c2ecf20Sopenharmony_ci		count = bc->bch.tx_skb->len - bc->bch.tx_idx;
5418c2ecf20Sopenharmony_ci		if (count <= 0)
5428c2ecf20Sopenharmony_ci			return;
5438c2ecf20Sopenharmony_ci		pr_debug("%s: %s B%1d %d/%d/%d/%d state %x idx %d/%d\n",
5448c2ecf20Sopenharmony_ci			 card->name, __func__, bc->bch.nr, count, bc->free,
5458c2ecf20Sopenharmony_ci			 bc->bch.tx_idx, bc->bch.tx_skb->len, bc->txstate,
5468c2ecf20Sopenharmony_ci			 bc->idx, card->send.idx);
5478c2ecf20Sopenharmony_ci		p = bc->bch.tx_skb->data + bc->bch.tx_idx;
5488c2ecf20Sopenharmony_ci	}
5498c2ecf20Sopenharmony_ci	if (bc->txstate & (TX_IDLE | TX_INIT | TX_UNDERRUN))
5508c2ecf20Sopenharmony_ci		resync(bc, card);
5518c2ecf20Sopenharmony_ci	if (test_bit(FLG_HDLC, &bc->bch.Flags) && !fillempty) {
5528c2ecf20Sopenharmony_ci		count = isdnhdlc_encode(&bc->hsend, p, count, &i,
5538c2ecf20Sopenharmony_ci					bc->hsbuf, bc->free);
5548c2ecf20Sopenharmony_ci		pr_debug("%s: B%1d hdlc encoded %d in %d\n", card->name,
5558c2ecf20Sopenharmony_ci			 bc->bch.nr, i, count);
5568c2ecf20Sopenharmony_ci		bc->bch.tx_idx += i;
5578c2ecf20Sopenharmony_ci		bc->free -= count;
5588c2ecf20Sopenharmony_ci		p = bc->hsbuf;
5598c2ecf20Sopenharmony_ci	} else {
5608c2ecf20Sopenharmony_ci		if (count > bc->free)
5618c2ecf20Sopenharmony_ci			count = bc->free;
5628c2ecf20Sopenharmony_ci		if (!fillempty)
5638c2ecf20Sopenharmony_ci			bc->bch.tx_idx += count;
5648c2ecf20Sopenharmony_ci		bc->free -= count;
5658c2ecf20Sopenharmony_ci	}
5668c2ecf20Sopenharmony_ci	m = (bc->bch.nr & 1) ? 0xffffff00 : 0xffff00ff;
5678c2ecf20Sopenharmony_ci	if (fillempty) {
5688c2ecf20Sopenharmony_ci		n = p[0];
5698c2ecf20Sopenharmony_ci		if (!(bc->bch.nr & 1))
5708c2ecf20Sopenharmony_ci			n <<= 8;
5718c2ecf20Sopenharmony_ci		for (i = 0; i < count; i++) {
5728c2ecf20Sopenharmony_ci			if (bc->idx >= card->send.size)
5738c2ecf20Sopenharmony_ci				bc->idx = 0;
5748c2ecf20Sopenharmony_ci			v = card->send.start[bc->idx];
5758c2ecf20Sopenharmony_ci			v &= m;
5768c2ecf20Sopenharmony_ci			v |= n;
5778c2ecf20Sopenharmony_ci			card->send.start[bc->idx++] = v;
5788c2ecf20Sopenharmony_ci		}
5798c2ecf20Sopenharmony_ci	} else {
5808c2ecf20Sopenharmony_ci		for (i = 0; i < count; i++) {
5818c2ecf20Sopenharmony_ci			if (bc->idx >= card->send.size)
5828c2ecf20Sopenharmony_ci				bc->idx = 0;
5838c2ecf20Sopenharmony_ci			v = card->send.start[bc->idx];
5848c2ecf20Sopenharmony_ci			v &= m;
5858c2ecf20Sopenharmony_ci			n = p[i];
5868c2ecf20Sopenharmony_ci			v |= (bc->bch.nr & 1) ? n : n << 8;
5878c2ecf20Sopenharmony_ci			card->send.start[bc->idx++] = v;
5888c2ecf20Sopenharmony_ci		}
5898c2ecf20Sopenharmony_ci	}
5908c2ecf20Sopenharmony_ci	if (debug & DEBUG_HW_BFIFO) {
5918c2ecf20Sopenharmony_ci		snprintf(card->log, LOG_SIZE, "B%1d-send %s %d ",
5928c2ecf20Sopenharmony_ci			 bc->bch.nr, card->name, count);
5938c2ecf20Sopenharmony_ci		print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, p, count);
5948c2ecf20Sopenharmony_ci	}
5958c2ecf20Sopenharmony_ci	if (bc->free)
5968c2ecf20Sopenharmony_ci		bc_next_frame(bc);
5978c2ecf20Sopenharmony_ci}
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_cistatic int
6018c2ecf20Sopenharmony_cibc_next_frame(struct tiger_ch *bc)
6028c2ecf20Sopenharmony_ci{
6038c2ecf20Sopenharmony_ci	int ret = 1;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	if (bc->bch.tx_skb && bc->bch.tx_idx < bc->bch.tx_skb->len) {
6068c2ecf20Sopenharmony_ci		fill_dma(bc);
6078c2ecf20Sopenharmony_ci	} else {
6088c2ecf20Sopenharmony_ci		dev_kfree_skb(bc->bch.tx_skb);
6098c2ecf20Sopenharmony_ci		if (get_next_bframe(&bc->bch)) {
6108c2ecf20Sopenharmony_ci			fill_dma(bc);
6118c2ecf20Sopenharmony_ci			test_and_clear_bit(FLG_TX_EMPTY, &bc->bch.Flags);
6128c2ecf20Sopenharmony_ci		} else if (test_bit(FLG_TX_EMPTY, &bc->bch.Flags)) {
6138c2ecf20Sopenharmony_ci			fill_dma(bc);
6148c2ecf20Sopenharmony_ci		} else if (test_bit(FLG_FILLEMPTY, &bc->bch.Flags)) {
6158c2ecf20Sopenharmony_ci			test_and_set_bit(FLG_TX_EMPTY, &bc->bch.Flags);
6168c2ecf20Sopenharmony_ci			ret = 0;
6178c2ecf20Sopenharmony_ci		} else {
6188c2ecf20Sopenharmony_ci			ret = 0;
6198c2ecf20Sopenharmony_ci		}
6208c2ecf20Sopenharmony_ci	}
6218c2ecf20Sopenharmony_ci	return ret;
6228c2ecf20Sopenharmony_ci}
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_cistatic void
6258c2ecf20Sopenharmony_cisend_tiger_bc(struct tiger_hw *card, struct tiger_ch *bc)
6268c2ecf20Sopenharmony_ci{
6278c2ecf20Sopenharmony_ci	int ret;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	bc->free += card->send.size / 2;
6308c2ecf20Sopenharmony_ci	if (bc->free >= card->send.size) {
6318c2ecf20Sopenharmony_ci		if (!(bc->txstate & (TX_UNDERRUN | TX_INIT))) {
6328c2ecf20Sopenharmony_ci			pr_info("%s: B%1d TX underrun state %x\n", card->name,
6338c2ecf20Sopenharmony_ci				bc->bch.nr, bc->txstate);
6348c2ecf20Sopenharmony_ci			bc->txstate |= TX_UNDERRUN;
6358c2ecf20Sopenharmony_ci		}
6368c2ecf20Sopenharmony_ci		bc->free = card->send.size;
6378c2ecf20Sopenharmony_ci	}
6388c2ecf20Sopenharmony_ci	ret = bc_next_frame(bc);
6398c2ecf20Sopenharmony_ci	if (!ret) {
6408c2ecf20Sopenharmony_ci		if (test_bit(FLG_HDLC, &bc->bch.Flags)) {
6418c2ecf20Sopenharmony_ci			fill_hdlc_flag(bc);
6428c2ecf20Sopenharmony_ci			return;
6438c2ecf20Sopenharmony_ci		}
6448c2ecf20Sopenharmony_ci		pr_debug("%s: B%1d TX no data free %d idx %d/%d\n", card->name,
6458c2ecf20Sopenharmony_ci			 bc->bch.nr, bc->free, bc->idx, card->send.idx);
6468c2ecf20Sopenharmony_ci		if (!(bc->txstate & (TX_IDLE | TX_INIT))) {
6478c2ecf20Sopenharmony_ci			fill_mem(bc, bc->idx, bc->free, 0xff);
6488c2ecf20Sopenharmony_ci			if (bc->free == card->send.size)
6498c2ecf20Sopenharmony_ci				bc->txstate |= TX_IDLE;
6508c2ecf20Sopenharmony_ci		}
6518c2ecf20Sopenharmony_ci	}
6528c2ecf20Sopenharmony_ci}
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_cistatic void
6558c2ecf20Sopenharmony_cisend_tiger(struct tiger_hw *card, u8 irq_stat)
6568c2ecf20Sopenharmony_ci{
6578c2ecf20Sopenharmony_ci	int i;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	/* Note send is via the READ DMA channel */
6608c2ecf20Sopenharmony_ci	if ((irq_stat & card->last_is0) & NJ_IRQM0_RD_MASK) {
6618c2ecf20Sopenharmony_ci		pr_info("%s: tiger warn write double dma %x/%x\n",
6628c2ecf20Sopenharmony_ci			card->name, irq_stat, card->last_is0);
6638c2ecf20Sopenharmony_ci		return;
6648c2ecf20Sopenharmony_ci	} else {
6658c2ecf20Sopenharmony_ci		card->last_is0 &= ~NJ_IRQM0_RD_MASK;
6668c2ecf20Sopenharmony_ci		card->last_is0 |= (irq_stat & NJ_IRQM0_RD_MASK);
6678c2ecf20Sopenharmony_ci	}
6688c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++) {
6698c2ecf20Sopenharmony_ci		if (test_bit(FLG_ACTIVE, &card->bc[i].bch.Flags))
6708c2ecf20Sopenharmony_ci			send_tiger_bc(card, &card->bc[i]);
6718c2ecf20Sopenharmony_ci	}
6728c2ecf20Sopenharmony_ci}
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_cistatic irqreturn_t
6758c2ecf20Sopenharmony_cinj_irq(int intno, void *dev_id)
6768c2ecf20Sopenharmony_ci{
6778c2ecf20Sopenharmony_ci	struct tiger_hw *card = dev_id;
6788c2ecf20Sopenharmony_ci	u8 val, s1val, s0val;
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	spin_lock(&card->lock);
6818c2ecf20Sopenharmony_ci	s0val = inb(card->base | NJ_IRQSTAT0);
6828c2ecf20Sopenharmony_ci	s1val = inb(card->base | NJ_IRQSTAT1);
6838c2ecf20Sopenharmony_ci	if ((s1val & NJ_ISACIRQ) && (s0val == 0)) {
6848c2ecf20Sopenharmony_ci		/* shared IRQ */
6858c2ecf20Sopenharmony_ci		spin_unlock(&card->lock);
6868c2ecf20Sopenharmony_ci		return IRQ_NONE;
6878c2ecf20Sopenharmony_ci	}
6888c2ecf20Sopenharmony_ci	pr_debug("%s: IRQSTAT0 %02x IRQSTAT1 %02x\n", card->name, s0val, s1val);
6898c2ecf20Sopenharmony_ci	card->irqcnt++;
6908c2ecf20Sopenharmony_ci	if (!(s1val & NJ_ISACIRQ)) {
6918c2ecf20Sopenharmony_ci		val = ReadISAC_nj(card, ISAC_ISTA);
6928c2ecf20Sopenharmony_ci		if (val)
6938c2ecf20Sopenharmony_ci			mISDNisac_irq(&card->isac, val);
6948c2ecf20Sopenharmony_ci	}
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	if (s0val)
6978c2ecf20Sopenharmony_ci		/* write to clear */
6988c2ecf20Sopenharmony_ci		outb(s0val, card->base | NJ_IRQSTAT0);
6998c2ecf20Sopenharmony_ci	else
7008c2ecf20Sopenharmony_ci		goto end;
7018c2ecf20Sopenharmony_ci	s1val = s0val;
7028c2ecf20Sopenharmony_ci	/* set bits in sval to indicate which page is free */
7038c2ecf20Sopenharmony_ci	card->recv.dmacur = inl(card->base | NJ_DMA_WRITE_ADR);
7048c2ecf20Sopenharmony_ci	card->recv.idx = (card->recv.dmacur - card->recv.dmastart) >> 2;
7058c2ecf20Sopenharmony_ci	if (card->recv.dmacur < card->recv.dmairq)
7068c2ecf20Sopenharmony_ci		s0val = 0x08;	/* the 2nd write area is free */
7078c2ecf20Sopenharmony_ci	else
7088c2ecf20Sopenharmony_ci		s0val = 0x04;	/* the 1st write area is free */
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	card->send.dmacur = inl(card->base | NJ_DMA_READ_ADR);
7118c2ecf20Sopenharmony_ci	card->send.idx = (card->send.dmacur - card->send.dmastart) >> 2;
7128c2ecf20Sopenharmony_ci	if (card->send.dmacur < card->send.dmairq)
7138c2ecf20Sopenharmony_ci		s0val |= 0x02;	/* the 2nd read area is free */
7148c2ecf20Sopenharmony_ci	else
7158c2ecf20Sopenharmony_ci		s0val |= 0x01;	/* the 1st read area is free */
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	pr_debug("%s: DMA Status %02x/%02x/%02x %d/%d\n", card->name,
7188c2ecf20Sopenharmony_ci		 s1val, s0val, card->last_is0,
7198c2ecf20Sopenharmony_ci		 card->recv.idx, card->send.idx);
7208c2ecf20Sopenharmony_ci	/* test if we have a DMA interrupt */
7218c2ecf20Sopenharmony_ci	if (s0val != card->last_is0) {
7228c2ecf20Sopenharmony_ci		if ((s0val & NJ_IRQM0_RD_MASK) !=
7238c2ecf20Sopenharmony_ci		    (card->last_is0 & NJ_IRQM0_RD_MASK))
7248c2ecf20Sopenharmony_ci			/* got a write dma int */
7258c2ecf20Sopenharmony_ci			send_tiger(card, s0val);
7268c2ecf20Sopenharmony_ci		if ((s0val & NJ_IRQM0_WR_MASK) !=
7278c2ecf20Sopenharmony_ci		    (card->last_is0 & NJ_IRQM0_WR_MASK))
7288c2ecf20Sopenharmony_ci			/* got a read dma int */
7298c2ecf20Sopenharmony_ci			recv_tiger(card, s0val);
7308c2ecf20Sopenharmony_ci	}
7318c2ecf20Sopenharmony_ciend:
7328c2ecf20Sopenharmony_ci	spin_unlock(&card->lock);
7338c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
7348c2ecf20Sopenharmony_ci}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_cistatic int
7378c2ecf20Sopenharmony_cinj_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb)
7388c2ecf20Sopenharmony_ci{
7398c2ecf20Sopenharmony_ci	int ret = -EINVAL;
7408c2ecf20Sopenharmony_ci	struct bchannel *bch = container_of(ch, struct bchannel, ch);
7418c2ecf20Sopenharmony_ci	struct tiger_ch *bc = container_of(bch, struct tiger_ch, bch);
7428c2ecf20Sopenharmony_ci	struct tiger_hw *card = bch->hw;
7438c2ecf20Sopenharmony_ci	struct mISDNhead *hh = mISDN_HEAD_P(skb);
7448c2ecf20Sopenharmony_ci	unsigned long flags;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	switch (hh->prim) {
7478c2ecf20Sopenharmony_ci	case PH_DATA_REQ:
7488c2ecf20Sopenharmony_ci		spin_lock_irqsave(&card->lock, flags);
7498c2ecf20Sopenharmony_ci		ret = bchannel_senddata(bch, skb);
7508c2ecf20Sopenharmony_ci		if (ret > 0) { /* direct TX */
7518c2ecf20Sopenharmony_ci			fill_dma(bc);
7528c2ecf20Sopenharmony_ci			ret = 0;
7538c2ecf20Sopenharmony_ci		}
7548c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&card->lock, flags);
7558c2ecf20Sopenharmony_ci		return ret;
7568c2ecf20Sopenharmony_ci	case PH_ACTIVATE_REQ:
7578c2ecf20Sopenharmony_ci		spin_lock_irqsave(&card->lock, flags);
7588c2ecf20Sopenharmony_ci		if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
7598c2ecf20Sopenharmony_ci			ret = mode_tiger(bc, ch->protocol);
7608c2ecf20Sopenharmony_ci		else
7618c2ecf20Sopenharmony_ci			ret = 0;
7628c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&card->lock, flags);
7638c2ecf20Sopenharmony_ci		if (!ret)
7648c2ecf20Sopenharmony_ci			_queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
7658c2ecf20Sopenharmony_ci				    NULL, GFP_KERNEL);
7668c2ecf20Sopenharmony_ci		break;
7678c2ecf20Sopenharmony_ci	case PH_DEACTIVATE_REQ:
7688c2ecf20Sopenharmony_ci		spin_lock_irqsave(&card->lock, flags);
7698c2ecf20Sopenharmony_ci		mISDN_clear_bchannel(bch);
7708c2ecf20Sopenharmony_ci		mode_tiger(bc, ISDN_P_NONE);
7718c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&card->lock, flags);
7728c2ecf20Sopenharmony_ci		_queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
7738c2ecf20Sopenharmony_ci			    NULL, GFP_KERNEL);
7748c2ecf20Sopenharmony_ci		ret = 0;
7758c2ecf20Sopenharmony_ci		break;
7768c2ecf20Sopenharmony_ci	}
7778c2ecf20Sopenharmony_ci	if (!ret)
7788c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
7798c2ecf20Sopenharmony_ci	return ret;
7808c2ecf20Sopenharmony_ci}
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_cistatic int
7838c2ecf20Sopenharmony_cichannel_bctrl(struct tiger_ch *bc, struct mISDN_ctrl_req *cq)
7848c2ecf20Sopenharmony_ci{
7858c2ecf20Sopenharmony_ci	return mISDN_ctrl_bchannel(&bc->bch, cq);
7868c2ecf20Sopenharmony_ci}
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_cistatic int
7898c2ecf20Sopenharmony_cinj_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
7908c2ecf20Sopenharmony_ci{
7918c2ecf20Sopenharmony_ci	struct bchannel *bch = container_of(ch, struct bchannel, ch);
7928c2ecf20Sopenharmony_ci	struct tiger_ch *bc = container_of(bch, struct tiger_ch, bch);
7938c2ecf20Sopenharmony_ci	struct tiger_hw *card  = bch->hw;
7948c2ecf20Sopenharmony_ci	int ret = -EINVAL;
7958c2ecf20Sopenharmony_ci	u_long flags;
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	pr_debug("%s: %s cmd:%x %p\n", card->name, __func__, cmd, arg);
7988c2ecf20Sopenharmony_ci	switch (cmd) {
7998c2ecf20Sopenharmony_ci	case CLOSE_CHANNEL:
8008c2ecf20Sopenharmony_ci		test_and_clear_bit(FLG_OPEN, &bch->Flags);
8018c2ecf20Sopenharmony_ci		cancel_work_sync(&bch->workq);
8028c2ecf20Sopenharmony_ci		spin_lock_irqsave(&card->lock, flags);
8038c2ecf20Sopenharmony_ci		mISDN_clear_bchannel(bch);
8048c2ecf20Sopenharmony_ci		mode_tiger(bc, ISDN_P_NONE);
8058c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&card->lock, flags);
8068c2ecf20Sopenharmony_ci		ch->protocol = ISDN_P_NONE;
8078c2ecf20Sopenharmony_ci		ch->peer = NULL;
8088c2ecf20Sopenharmony_ci		module_put(THIS_MODULE);
8098c2ecf20Sopenharmony_ci		ret = 0;
8108c2ecf20Sopenharmony_ci		break;
8118c2ecf20Sopenharmony_ci	case CONTROL_CHANNEL:
8128c2ecf20Sopenharmony_ci		ret = channel_bctrl(bc, arg);
8138c2ecf20Sopenharmony_ci		break;
8148c2ecf20Sopenharmony_ci	default:
8158c2ecf20Sopenharmony_ci		pr_info("%s: %s unknown prim(%x)\n", card->name, __func__, cmd);
8168c2ecf20Sopenharmony_ci	}
8178c2ecf20Sopenharmony_ci	return ret;
8188c2ecf20Sopenharmony_ci}
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_cistatic int
8218c2ecf20Sopenharmony_cichannel_ctrl(struct tiger_hw *card, struct mISDN_ctrl_req *cq)
8228c2ecf20Sopenharmony_ci{
8238c2ecf20Sopenharmony_ci	int	ret = 0;
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	switch (cq->op) {
8268c2ecf20Sopenharmony_ci	case MISDN_CTRL_GETOP:
8278c2ecf20Sopenharmony_ci		cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_L1_TIMER3;
8288c2ecf20Sopenharmony_ci		break;
8298c2ecf20Sopenharmony_ci	case MISDN_CTRL_LOOP:
8308c2ecf20Sopenharmony_ci		/* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */
8318c2ecf20Sopenharmony_ci		if (cq->channel < 0 || cq->channel > 3) {
8328c2ecf20Sopenharmony_ci			ret = -EINVAL;
8338c2ecf20Sopenharmony_ci			break;
8348c2ecf20Sopenharmony_ci		}
8358c2ecf20Sopenharmony_ci		ret = card->isac.ctrl(&card->isac, HW_TESTLOOP, cq->channel);
8368c2ecf20Sopenharmony_ci		break;
8378c2ecf20Sopenharmony_ci	case MISDN_CTRL_L1_TIMER3:
8388c2ecf20Sopenharmony_ci		ret = card->isac.ctrl(&card->isac, HW_TIMER3_VALUE, cq->p1);
8398c2ecf20Sopenharmony_ci		break;
8408c2ecf20Sopenharmony_ci	default:
8418c2ecf20Sopenharmony_ci		pr_info("%s: %s unknown Op %x\n", card->name, __func__, cq->op);
8428c2ecf20Sopenharmony_ci		ret = -EINVAL;
8438c2ecf20Sopenharmony_ci		break;
8448c2ecf20Sopenharmony_ci	}
8458c2ecf20Sopenharmony_ci	return ret;
8468c2ecf20Sopenharmony_ci}
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_cistatic int
8498c2ecf20Sopenharmony_ciopen_bchannel(struct tiger_hw *card, struct channel_req *rq)
8508c2ecf20Sopenharmony_ci{
8518c2ecf20Sopenharmony_ci	struct bchannel *bch;
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	if (rq->adr.channel == 0 || rq->adr.channel > 2)
8548c2ecf20Sopenharmony_ci		return -EINVAL;
8558c2ecf20Sopenharmony_ci	if (rq->protocol == ISDN_P_NONE)
8568c2ecf20Sopenharmony_ci		return -EINVAL;
8578c2ecf20Sopenharmony_ci	bch = &card->bc[rq->adr.channel - 1].bch;
8588c2ecf20Sopenharmony_ci	if (test_and_set_bit(FLG_OPEN, &bch->Flags))
8598c2ecf20Sopenharmony_ci		return -EBUSY; /* b-channel can be only open once */
8608c2ecf20Sopenharmony_ci	test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);
8618c2ecf20Sopenharmony_ci	bch->ch.protocol = rq->protocol;
8628c2ecf20Sopenharmony_ci	rq->ch = &bch->ch;
8638c2ecf20Sopenharmony_ci	return 0;
8648c2ecf20Sopenharmony_ci}
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci/*
8678c2ecf20Sopenharmony_ci * device control function
8688c2ecf20Sopenharmony_ci */
8698c2ecf20Sopenharmony_cistatic int
8708c2ecf20Sopenharmony_cinj_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
8718c2ecf20Sopenharmony_ci{
8728c2ecf20Sopenharmony_ci	struct mISDNdevice	*dev = container_of(ch, struct mISDNdevice, D);
8738c2ecf20Sopenharmony_ci	struct dchannel		*dch = container_of(dev, struct dchannel, dev);
8748c2ecf20Sopenharmony_ci	struct tiger_hw	*card = dch->hw;
8758c2ecf20Sopenharmony_ci	struct channel_req	*rq;
8768c2ecf20Sopenharmony_ci	int			err = 0;
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	pr_debug("%s: %s cmd:%x %p\n", card->name, __func__, cmd, arg);
8798c2ecf20Sopenharmony_ci	switch (cmd) {
8808c2ecf20Sopenharmony_ci	case OPEN_CHANNEL:
8818c2ecf20Sopenharmony_ci		rq = arg;
8828c2ecf20Sopenharmony_ci		if (rq->protocol == ISDN_P_TE_S0)
8838c2ecf20Sopenharmony_ci			err = card->isac.open(&card->isac, rq);
8848c2ecf20Sopenharmony_ci		else
8858c2ecf20Sopenharmony_ci			err = open_bchannel(card, rq);
8868c2ecf20Sopenharmony_ci		if (err)
8878c2ecf20Sopenharmony_ci			break;
8888c2ecf20Sopenharmony_ci		if (!try_module_get(THIS_MODULE))
8898c2ecf20Sopenharmony_ci			pr_info("%s: cannot get module\n", card->name);
8908c2ecf20Sopenharmony_ci		break;
8918c2ecf20Sopenharmony_ci	case CLOSE_CHANNEL:
8928c2ecf20Sopenharmony_ci		pr_debug("%s: dev(%d) close from %p\n", card->name, dch->dev.id,
8938c2ecf20Sopenharmony_ci			 __builtin_return_address(0));
8948c2ecf20Sopenharmony_ci		module_put(THIS_MODULE);
8958c2ecf20Sopenharmony_ci		break;
8968c2ecf20Sopenharmony_ci	case CONTROL_CHANNEL:
8978c2ecf20Sopenharmony_ci		err = channel_ctrl(card, arg);
8988c2ecf20Sopenharmony_ci		break;
8998c2ecf20Sopenharmony_ci	default:
9008c2ecf20Sopenharmony_ci		pr_debug("%s: %s unknown command %x\n",
9018c2ecf20Sopenharmony_ci			 card->name, __func__, cmd);
9028c2ecf20Sopenharmony_ci		return -EINVAL;
9038c2ecf20Sopenharmony_ci	}
9048c2ecf20Sopenharmony_ci	return err;
9058c2ecf20Sopenharmony_ci}
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_cistatic int
9088c2ecf20Sopenharmony_cinj_init_card(struct tiger_hw *card)
9098c2ecf20Sopenharmony_ci{
9108c2ecf20Sopenharmony_ci	u_long flags;
9118c2ecf20Sopenharmony_ci	int ret;
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	spin_lock_irqsave(&card->lock, flags);
9148c2ecf20Sopenharmony_ci	nj_disable_hwirq(card);
9158c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&card->lock, flags);
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	card->irq = card->pdev->irq;
9188c2ecf20Sopenharmony_ci	if (request_irq(card->irq, nj_irq, IRQF_SHARED, card->name, card)) {
9198c2ecf20Sopenharmony_ci		pr_info("%s: couldn't get interrupt %d\n",
9208c2ecf20Sopenharmony_ci			card->name, card->irq);
9218c2ecf20Sopenharmony_ci		card->irq = -1;
9228c2ecf20Sopenharmony_ci		return -EIO;
9238c2ecf20Sopenharmony_ci	}
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	spin_lock_irqsave(&card->lock, flags);
9268c2ecf20Sopenharmony_ci	nj_reset(card);
9278c2ecf20Sopenharmony_ci	ret = card->isac.init(&card->isac);
9288c2ecf20Sopenharmony_ci	if (ret)
9298c2ecf20Sopenharmony_ci		goto error;
9308c2ecf20Sopenharmony_ci	ret = inittiger(card);
9318c2ecf20Sopenharmony_ci	if (ret)
9328c2ecf20Sopenharmony_ci		goto error;
9338c2ecf20Sopenharmony_ci	mode_tiger(&card->bc[0], ISDN_P_NONE);
9348c2ecf20Sopenharmony_ci	mode_tiger(&card->bc[1], ISDN_P_NONE);
9358c2ecf20Sopenharmony_cierror:
9368c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&card->lock, flags);
9378c2ecf20Sopenharmony_ci	return ret;
9388c2ecf20Sopenharmony_ci}
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_cistatic void
9428c2ecf20Sopenharmony_cinj_release(struct tiger_hw *card)
9438c2ecf20Sopenharmony_ci{
9448c2ecf20Sopenharmony_ci	u_long flags;
9458c2ecf20Sopenharmony_ci	int i;
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	if (card->base_s) {
9488c2ecf20Sopenharmony_ci		spin_lock_irqsave(&card->lock, flags);
9498c2ecf20Sopenharmony_ci		nj_disable_hwirq(card);
9508c2ecf20Sopenharmony_ci		mode_tiger(&card->bc[0], ISDN_P_NONE);
9518c2ecf20Sopenharmony_ci		mode_tiger(&card->bc[1], ISDN_P_NONE);
9528c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&card->lock, flags);
9538c2ecf20Sopenharmony_ci		card->isac.release(&card->isac);
9548c2ecf20Sopenharmony_ci		release_region(card->base, card->base_s);
9558c2ecf20Sopenharmony_ci		card->base_s = 0;
9568c2ecf20Sopenharmony_ci	}
9578c2ecf20Sopenharmony_ci	if (card->irq > 0)
9588c2ecf20Sopenharmony_ci		free_irq(card->irq, card);
9598c2ecf20Sopenharmony_ci	if (device_is_registered(&card->isac.dch.dev.dev))
9608c2ecf20Sopenharmony_ci		mISDN_unregister_device(&card->isac.dch.dev);
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++) {
9638c2ecf20Sopenharmony_ci		mISDN_freebchannel(&card->bc[i].bch);
9648c2ecf20Sopenharmony_ci		kfree(card->bc[i].hsbuf);
9658c2ecf20Sopenharmony_ci		kfree(card->bc[i].hrbuf);
9668c2ecf20Sopenharmony_ci	}
9678c2ecf20Sopenharmony_ci	if (card->dma_p)
9688c2ecf20Sopenharmony_ci		dma_free_coherent(&card->pdev->dev, NJ_DMA_SIZE, card->dma_p,
9698c2ecf20Sopenharmony_ci				  card->dma);
9708c2ecf20Sopenharmony_ci	write_lock_irqsave(&card_lock, flags);
9718c2ecf20Sopenharmony_ci	list_del(&card->list);
9728c2ecf20Sopenharmony_ci	write_unlock_irqrestore(&card_lock, flags);
9738c2ecf20Sopenharmony_ci	pci_clear_master(card->pdev);
9748c2ecf20Sopenharmony_ci	pci_disable_device(card->pdev);
9758c2ecf20Sopenharmony_ci	pci_set_drvdata(card->pdev, NULL);
9768c2ecf20Sopenharmony_ci	kfree(card);
9778c2ecf20Sopenharmony_ci}
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_cistatic int
9818c2ecf20Sopenharmony_cinj_setup(struct tiger_hw *card)
9828c2ecf20Sopenharmony_ci{
9838c2ecf20Sopenharmony_ci	card->base = pci_resource_start(card->pdev, 0);
9848c2ecf20Sopenharmony_ci	card->base_s = pci_resource_len(card->pdev, 0);
9858c2ecf20Sopenharmony_ci	if (!request_region(card->base, card->base_s, card->name)) {
9868c2ecf20Sopenharmony_ci		pr_info("%s: NETjet config port %#x-%#x already in use\n",
9878c2ecf20Sopenharmony_ci			card->name, card->base,
9888c2ecf20Sopenharmony_ci			(u32)(card->base + card->base_s - 1));
9898c2ecf20Sopenharmony_ci		card->base_s = 0;
9908c2ecf20Sopenharmony_ci		return -EIO;
9918c2ecf20Sopenharmony_ci	}
9928c2ecf20Sopenharmony_ci	ASSIGN_FUNC(nj, ISAC, card->isac);
9938c2ecf20Sopenharmony_ci	return 0;
9948c2ecf20Sopenharmony_ci}
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_cistatic int
9988c2ecf20Sopenharmony_cisetup_instance(struct tiger_hw *card)
9998c2ecf20Sopenharmony_ci{
10008c2ecf20Sopenharmony_ci	int i, err;
10018c2ecf20Sopenharmony_ci	u_long flags;
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci	snprintf(card->name, MISDN_MAX_IDLEN - 1, "netjet.%d", nj_cnt + 1);
10048c2ecf20Sopenharmony_ci	write_lock_irqsave(&card_lock, flags);
10058c2ecf20Sopenharmony_ci	list_add_tail(&card->list, &Cards);
10068c2ecf20Sopenharmony_ci	write_unlock_irqrestore(&card_lock, flags);
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	_set_debug(card);
10098c2ecf20Sopenharmony_ci	card->isac.name = card->name;
10108c2ecf20Sopenharmony_ci	spin_lock_init(&card->lock);
10118c2ecf20Sopenharmony_ci	card->isac.hwlock = &card->lock;
10128c2ecf20Sopenharmony_ci	mISDNisac_init(&card->isac, card);
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	card->isac.dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
10158c2ecf20Sopenharmony_ci		(1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
10168c2ecf20Sopenharmony_ci	card->isac.dch.dev.D.ctrl = nj_dctrl;
10178c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++) {
10188c2ecf20Sopenharmony_ci		card->bc[i].bch.nr = i + 1;
10198c2ecf20Sopenharmony_ci		set_channelmap(i + 1, card->isac.dch.dev.channelmap);
10208c2ecf20Sopenharmony_ci		mISDN_initbchannel(&card->bc[i].bch, MAX_DATA_MEM,
10218c2ecf20Sopenharmony_ci				   NJ_DMA_RXSIZE >> 1);
10228c2ecf20Sopenharmony_ci		card->bc[i].bch.hw = card;
10238c2ecf20Sopenharmony_ci		card->bc[i].bch.ch.send = nj_l2l1B;
10248c2ecf20Sopenharmony_ci		card->bc[i].bch.ch.ctrl = nj_bctrl;
10258c2ecf20Sopenharmony_ci		card->bc[i].bch.ch.nr = i + 1;
10268c2ecf20Sopenharmony_ci		list_add(&card->bc[i].bch.ch.list,
10278c2ecf20Sopenharmony_ci			 &card->isac.dch.dev.bchannels);
10288c2ecf20Sopenharmony_ci		card->bc[i].bch.hw = card;
10298c2ecf20Sopenharmony_ci	}
10308c2ecf20Sopenharmony_ci	err = nj_setup(card);
10318c2ecf20Sopenharmony_ci	if (err)
10328c2ecf20Sopenharmony_ci		goto error;
10338c2ecf20Sopenharmony_ci	err = mISDN_register_device(&card->isac.dch.dev, &card->pdev->dev,
10348c2ecf20Sopenharmony_ci				    card->name);
10358c2ecf20Sopenharmony_ci	if (err)
10368c2ecf20Sopenharmony_ci		goto error;
10378c2ecf20Sopenharmony_ci	err = nj_init_card(card);
10388c2ecf20Sopenharmony_ci	if (!err)  {
10398c2ecf20Sopenharmony_ci		nj_cnt++;
10408c2ecf20Sopenharmony_ci		pr_notice("Netjet %d cards installed\n", nj_cnt);
10418c2ecf20Sopenharmony_ci		return 0;
10428c2ecf20Sopenharmony_ci	}
10438c2ecf20Sopenharmony_cierror:
10448c2ecf20Sopenharmony_ci	nj_release(card);
10458c2ecf20Sopenharmony_ci	return err;
10468c2ecf20Sopenharmony_ci}
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_cistatic int
10498c2ecf20Sopenharmony_cinj_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
10508c2ecf20Sopenharmony_ci{
10518c2ecf20Sopenharmony_ci	int err = -ENOMEM;
10528c2ecf20Sopenharmony_ci	int cfg;
10538c2ecf20Sopenharmony_ci	struct tiger_hw *card;
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	if (pdev->subsystem_vendor == 0x8086 &&
10568c2ecf20Sopenharmony_ci	    pdev->subsystem_device == 0x0003) {
10578c2ecf20Sopenharmony_ci		pr_notice("Netjet: Digium X100P/X101P not handled\n");
10588c2ecf20Sopenharmony_ci		return -ENODEV;
10598c2ecf20Sopenharmony_ci	}
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	if (pdev->subsystem_vendor == 0x55 &&
10628c2ecf20Sopenharmony_ci	    pdev->subsystem_device == 0x02) {
10638c2ecf20Sopenharmony_ci		pr_notice("Netjet: Enter!Now not handled yet\n");
10648c2ecf20Sopenharmony_ci		return -ENODEV;
10658c2ecf20Sopenharmony_ci	}
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	if (pdev->subsystem_vendor == 0xb100 &&
10688c2ecf20Sopenharmony_ci	    pdev->subsystem_device == 0x0003) {
10698c2ecf20Sopenharmony_ci		pr_notice("Netjet: Digium TDM400P not handled yet\n");
10708c2ecf20Sopenharmony_ci		return -ENODEV;
10718c2ecf20Sopenharmony_ci	}
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	card = kzalloc(sizeof(struct tiger_hw), GFP_KERNEL);
10748c2ecf20Sopenharmony_ci	if (!card) {
10758c2ecf20Sopenharmony_ci		pr_info("No kmem for Netjet\n");
10768c2ecf20Sopenharmony_ci		return err;
10778c2ecf20Sopenharmony_ci	}
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci	card->pdev = pdev;
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	err = pci_enable_device(pdev);
10828c2ecf20Sopenharmony_ci	if (err) {
10838c2ecf20Sopenharmony_ci		kfree(card);
10848c2ecf20Sopenharmony_ci		return err;
10858c2ecf20Sopenharmony_ci	}
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	printk(KERN_INFO "nj_probe(mISDN): found adapter at %s\n",
10888c2ecf20Sopenharmony_ci	       pci_name(pdev));
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	pci_set_master(pdev);
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	/* the TJ300 and TJ320 must be detected, the IRQ handling is different
10938c2ecf20Sopenharmony_ci	 * unfortunately the chips use the same device ID, but the TJ320 has
10948c2ecf20Sopenharmony_ci	 * the bit20 in status PCI cfg register set
10958c2ecf20Sopenharmony_ci	 */
10968c2ecf20Sopenharmony_ci	pci_read_config_dword(pdev, 0x04, &cfg);
10978c2ecf20Sopenharmony_ci	if (cfg & 0x00100000)
10988c2ecf20Sopenharmony_ci		card->typ = NETJET_S_TJ320;
10998c2ecf20Sopenharmony_ci	else
11008c2ecf20Sopenharmony_ci		card->typ = NETJET_S_TJ300;
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	card->base = pci_resource_start(pdev, 0);
11038c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, card);
11048c2ecf20Sopenharmony_ci	err = setup_instance(card);
11058c2ecf20Sopenharmony_ci	if (err)
11068c2ecf20Sopenharmony_ci		pci_set_drvdata(pdev, NULL);
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	return err;
11098c2ecf20Sopenharmony_ci}
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_cistatic void nj_remove(struct pci_dev *pdev)
11138c2ecf20Sopenharmony_ci{
11148c2ecf20Sopenharmony_ci	struct tiger_hw *card = pci_get_drvdata(pdev);
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	if (card)
11178c2ecf20Sopenharmony_ci		nj_release(card);
11188c2ecf20Sopenharmony_ci	else
11198c2ecf20Sopenharmony_ci		pr_info("%s drvdata already removed\n", __func__);
11208c2ecf20Sopenharmony_ci}
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci/* We cannot select cards with PCI_SUB... IDs, since here are cards with
11238c2ecf20Sopenharmony_ci * SUB IDs set to PCI_ANY_ID, so we need to match all and reject
11248c2ecf20Sopenharmony_ci * known other cards which not work with this driver - see probe function */
11258c2ecf20Sopenharmony_cistatic const struct pci_device_id nj_pci_ids[] = {
11268c2ecf20Sopenharmony_ci	{ PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_300,
11278c2ecf20Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
11288c2ecf20Sopenharmony_ci	{ }
11298c2ecf20Sopenharmony_ci};
11308c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, nj_pci_ids);
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_cistatic struct pci_driver nj_driver = {
11338c2ecf20Sopenharmony_ci	.name = "netjet",
11348c2ecf20Sopenharmony_ci	.probe = nj_probe,
11358c2ecf20Sopenharmony_ci	.remove = nj_remove,
11368c2ecf20Sopenharmony_ci	.id_table = nj_pci_ids,
11378c2ecf20Sopenharmony_ci};
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_cistatic int __init nj_init(void)
11408c2ecf20Sopenharmony_ci{
11418c2ecf20Sopenharmony_ci	int err;
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci	pr_notice("Netjet PCI driver Rev. %s\n", NETJET_REV);
11448c2ecf20Sopenharmony_ci	err = pci_register_driver(&nj_driver);
11458c2ecf20Sopenharmony_ci	return err;
11468c2ecf20Sopenharmony_ci}
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_cistatic void __exit nj_cleanup(void)
11498c2ecf20Sopenharmony_ci{
11508c2ecf20Sopenharmony_ci	pci_unregister_driver(&nj_driver);
11518c2ecf20Sopenharmony_ci}
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_cimodule_init(nj_init);
11548c2ecf20Sopenharmony_cimodule_exit(nj_cleanup);
1155