162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * isac.c   ISAC specific routines
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/irqreturn.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/mISDNhw.h>
1462306a36Sopenharmony_ci#include "ipac.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define DBUSY_TIMER_VALUE	80
1862306a36Sopenharmony_ci#define ARCOFI_USE		1
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define ISAC_REV		"2.0"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ciMODULE_AUTHOR("Karsten Keil");
2362306a36Sopenharmony_ciMODULE_VERSION(ISAC_REV);
2462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define ReadISAC(is, o)		(is->read_reg(is->dch.hw, o + is->off))
2762306a36Sopenharmony_ci#define	WriteISAC(is, o, v)	(is->write_reg(is->dch.hw, o + is->off, v))
2862306a36Sopenharmony_ci#define ReadHSCX(h, o)		(h->ip->read_reg(h->ip->hw, h->off + o))
2962306a36Sopenharmony_ci#define WriteHSCX(h, o, v)	(h->ip->write_reg(h->ip->hw, h->off + o, v))
3062306a36Sopenharmony_ci#define ReadIPAC(ip, o)		(ip->read_reg(ip->hw, o))
3162306a36Sopenharmony_ci#define WriteIPAC(ip, o, v)	(ip->write_reg(ip->hw, o, v))
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic inline void
3462306a36Sopenharmony_ciph_command(struct isac_hw *isac, u8 command)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	pr_debug("%s: ph_command %x\n", isac->name, command);
3762306a36Sopenharmony_ci	if (isac->type & IPAC_TYPE_ISACX)
3862306a36Sopenharmony_ci		WriteISAC(isac, ISACX_CIX0, (command << 4) | 0xE);
3962306a36Sopenharmony_ci	else
4062306a36Sopenharmony_ci		WriteISAC(isac, ISAC_CIX0, (command << 2) | 3);
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic void
4462306a36Sopenharmony_ciisac_ph_state_change(struct isac_hw *isac)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	switch (isac->state) {
4762306a36Sopenharmony_ci	case (ISAC_IND_RS):
4862306a36Sopenharmony_ci	case (ISAC_IND_EI):
4962306a36Sopenharmony_ci		ph_command(isac, ISAC_CMD_DUI);
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ci	schedule_event(&isac->dch, FLG_PHCHANGE);
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic void
5562306a36Sopenharmony_ciisac_ph_state_bh(struct dchannel *dch)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	struct isac_hw *isac = container_of(dch, struct isac_hw, dch);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	switch (isac->state) {
6062306a36Sopenharmony_ci	case ISAC_IND_RS:
6162306a36Sopenharmony_ci	case ISAC_IND_EI:
6262306a36Sopenharmony_ci		dch->state = 0;
6362306a36Sopenharmony_ci		l1_event(dch->l1, HW_RESET_IND);
6462306a36Sopenharmony_ci		break;
6562306a36Sopenharmony_ci	case ISAC_IND_DID:
6662306a36Sopenharmony_ci		dch->state = 3;
6762306a36Sopenharmony_ci		l1_event(dch->l1, HW_DEACT_CNF);
6862306a36Sopenharmony_ci		break;
6962306a36Sopenharmony_ci	case ISAC_IND_DR:
7062306a36Sopenharmony_ci	case ISAC_IND_DR6:
7162306a36Sopenharmony_ci		dch->state = 3;
7262306a36Sopenharmony_ci		l1_event(dch->l1, HW_DEACT_IND);
7362306a36Sopenharmony_ci		break;
7462306a36Sopenharmony_ci	case ISAC_IND_PU:
7562306a36Sopenharmony_ci		dch->state = 4;
7662306a36Sopenharmony_ci		l1_event(dch->l1, HW_POWERUP_IND);
7762306a36Sopenharmony_ci		break;
7862306a36Sopenharmony_ci	case ISAC_IND_RSY:
7962306a36Sopenharmony_ci		if (dch->state <= 5) {
8062306a36Sopenharmony_ci			dch->state = 5;
8162306a36Sopenharmony_ci			l1_event(dch->l1, ANYSIGNAL);
8262306a36Sopenharmony_ci		} else {
8362306a36Sopenharmony_ci			dch->state = 8;
8462306a36Sopenharmony_ci			l1_event(dch->l1, LOSTFRAMING);
8562306a36Sopenharmony_ci		}
8662306a36Sopenharmony_ci		break;
8762306a36Sopenharmony_ci	case ISAC_IND_ARD:
8862306a36Sopenharmony_ci		dch->state = 6;
8962306a36Sopenharmony_ci		l1_event(dch->l1, INFO2);
9062306a36Sopenharmony_ci		break;
9162306a36Sopenharmony_ci	case ISAC_IND_AI8:
9262306a36Sopenharmony_ci		dch->state = 7;
9362306a36Sopenharmony_ci		l1_event(dch->l1, INFO4_P8);
9462306a36Sopenharmony_ci		break;
9562306a36Sopenharmony_ci	case ISAC_IND_AI10:
9662306a36Sopenharmony_ci		dch->state = 7;
9762306a36Sopenharmony_ci		l1_event(dch->l1, INFO4_P10);
9862306a36Sopenharmony_ci		break;
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci	pr_debug("%s: TE newstate %x\n", isac->name, dch->state);
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic void
10462306a36Sopenharmony_ciisac_empty_fifo(struct isac_hw *isac, int count)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	u8 *ptr;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	pr_debug("%s: %s  %d\n", isac->name, __func__, count);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	if (!isac->dch.rx_skb) {
11162306a36Sopenharmony_ci		isac->dch.rx_skb = mI_alloc_skb(isac->dch.maxlen, GFP_ATOMIC);
11262306a36Sopenharmony_ci		if (!isac->dch.rx_skb) {
11362306a36Sopenharmony_ci			pr_info("%s: D receive out of memory\n", isac->name);
11462306a36Sopenharmony_ci			WriteISAC(isac, ISAC_CMDR, 0x80);
11562306a36Sopenharmony_ci			return;
11662306a36Sopenharmony_ci		}
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci	if ((isac->dch.rx_skb->len + count) >= isac->dch.maxlen) {
11962306a36Sopenharmony_ci		pr_debug("%s: %s overrun %d\n", isac->name, __func__,
12062306a36Sopenharmony_ci			 isac->dch.rx_skb->len + count);
12162306a36Sopenharmony_ci		WriteISAC(isac, ISAC_CMDR, 0x80);
12262306a36Sopenharmony_ci		return;
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci	ptr = skb_put(isac->dch.rx_skb, count);
12562306a36Sopenharmony_ci	isac->read_fifo(isac->dch.hw, isac->off, ptr, count);
12662306a36Sopenharmony_ci	WriteISAC(isac, ISAC_CMDR, 0x80);
12762306a36Sopenharmony_ci	if (isac->dch.debug & DEBUG_HW_DFIFO) {
12862306a36Sopenharmony_ci		char	pfx[MISDN_MAX_IDLEN + 16];
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci		snprintf(pfx, MISDN_MAX_IDLEN + 15, "D-recv %s %d ",
13162306a36Sopenharmony_ci			 isac->name, count);
13262306a36Sopenharmony_ci		print_hex_dump_bytes(pfx, DUMP_PREFIX_OFFSET, ptr, count);
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic void
13762306a36Sopenharmony_ciisac_fill_fifo(struct isac_hw *isac)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	int count, more;
14062306a36Sopenharmony_ci	u8 *ptr;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	if (!isac->dch.tx_skb)
14362306a36Sopenharmony_ci		return;
14462306a36Sopenharmony_ci	count = isac->dch.tx_skb->len - isac->dch.tx_idx;
14562306a36Sopenharmony_ci	if (count <= 0)
14662306a36Sopenharmony_ci		return;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	more = 0;
14962306a36Sopenharmony_ci	if (count > 32) {
15062306a36Sopenharmony_ci		more = !0;
15162306a36Sopenharmony_ci		count = 32;
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci	pr_debug("%s: %s  %d\n", isac->name, __func__, count);
15462306a36Sopenharmony_ci	ptr = isac->dch.tx_skb->data + isac->dch.tx_idx;
15562306a36Sopenharmony_ci	isac->dch.tx_idx += count;
15662306a36Sopenharmony_ci	isac->write_fifo(isac->dch.hw, isac->off, ptr, count);
15762306a36Sopenharmony_ci	WriteISAC(isac, ISAC_CMDR, more ? 0x8 : 0xa);
15862306a36Sopenharmony_ci	if (test_and_set_bit(FLG_BUSY_TIMER, &isac->dch.Flags)) {
15962306a36Sopenharmony_ci		pr_debug("%s: %s dbusytimer running\n", isac->name, __func__);
16062306a36Sopenharmony_ci		del_timer(&isac->dch.timer);
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci	isac->dch.timer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
16362306a36Sopenharmony_ci	add_timer(&isac->dch.timer);
16462306a36Sopenharmony_ci	if (isac->dch.debug & DEBUG_HW_DFIFO) {
16562306a36Sopenharmony_ci		char	pfx[MISDN_MAX_IDLEN + 16];
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci		snprintf(pfx, MISDN_MAX_IDLEN + 15, "D-send %s %d ",
16862306a36Sopenharmony_ci			 isac->name, count);
16962306a36Sopenharmony_ci		print_hex_dump_bytes(pfx, DUMP_PREFIX_OFFSET, ptr, count);
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic void
17462306a36Sopenharmony_ciisac_rme_irq(struct isac_hw *isac)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	u8 val, count;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	val = ReadISAC(isac, ISAC_RSTA);
17962306a36Sopenharmony_ci	if ((val & 0x70) != 0x20) {
18062306a36Sopenharmony_ci		if (val & 0x40) {
18162306a36Sopenharmony_ci			pr_debug("%s: ISAC RDO\n", isac->name);
18262306a36Sopenharmony_ci#ifdef ERROR_STATISTIC
18362306a36Sopenharmony_ci			isac->dch.err_rx++;
18462306a36Sopenharmony_ci#endif
18562306a36Sopenharmony_ci		}
18662306a36Sopenharmony_ci		if (!(val & 0x20)) {
18762306a36Sopenharmony_ci			pr_debug("%s: ISAC CRC error\n", isac->name);
18862306a36Sopenharmony_ci#ifdef ERROR_STATISTIC
18962306a36Sopenharmony_ci			isac->dch.err_crc++;
19062306a36Sopenharmony_ci#endif
19162306a36Sopenharmony_ci		}
19262306a36Sopenharmony_ci		WriteISAC(isac, ISAC_CMDR, 0x80);
19362306a36Sopenharmony_ci		dev_kfree_skb(isac->dch.rx_skb);
19462306a36Sopenharmony_ci		isac->dch.rx_skb = NULL;
19562306a36Sopenharmony_ci	} else {
19662306a36Sopenharmony_ci		count = ReadISAC(isac, ISAC_RBCL) & 0x1f;
19762306a36Sopenharmony_ci		if (count == 0)
19862306a36Sopenharmony_ci			count = 32;
19962306a36Sopenharmony_ci		isac_empty_fifo(isac, count);
20062306a36Sopenharmony_ci		recv_Dchannel(&isac->dch);
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic void
20562306a36Sopenharmony_ciisac_xpr_irq(struct isac_hw *isac)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	if (test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags))
20862306a36Sopenharmony_ci		del_timer(&isac->dch.timer);
20962306a36Sopenharmony_ci	if (isac->dch.tx_skb && isac->dch.tx_idx < isac->dch.tx_skb->len) {
21062306a36Sopenharmony_ci		isac_fill_fifo(isac);
21162306a36Sopenharmony_ci	} else {
21262306a36Sopenharmony_ci		dev_kfree_skb(isac->dch.tx_skb);
21362306a36Sopenharmony_ci		if (get_next_dframe(&isac->dch))
21462306a36Sopenharmony_ci			isac_fill_fifo(isac);
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic void
21962306a36Sopenharmony_ciisac_retransmit(struct isac_hw *isac)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	if (test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags))
22262306a36Sopenharmony_ci		del_timer(&isac->dch.timer);
22362306a36Sopenharmony_ci	if (test_bit(FLG_TX_BUSY, &isac->dch.Flags)) {
22462306a36Sopenharmony_ci		/* Restart frame */
22562306a36Sopenharmony_ci		isac->dch.tx_idx = 0;
22662306a36Sopenharmony_ci		isac_fill_fifo(isac);
22762306a36Sopenharmony_ci	} else if (isac->dch.tx_skb) { /* should not happen */
22862306a36Sopenharmony_ci		pr_info("%s: tx_skb exist but not busy\n", isac->name);
22962306a36Sopenharmony_ci		test_and_set_bit(FLG_TX_BUSY, &isac->dch.Flags);
23062306a36Sopenharmony_ci		isac->dch.tx_idx = 0;
23162306a36Sopenharmony_ci		isac_fill_fifo(isac);
23262306a36Sopenharmony_ci	} else {
23362306a36Sopenharmony_ci		pr_info("%s: ISAC XDU no TX_BUSY\n", isac->name);
23462306a36Sopenharmony_ci		if (get_next_dframe(&isac->dch))
23562306a36Sopenharmony_ci			isac_fill_fifo(isac);
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic void
24062306a36Sopenharmony_ciisac_mos_irq(struct isac_hw *isac)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	u8 val;
24362306a36Sopenharmony_ci	int ret;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	val = ReadISAC(isac, ISAC_MOSR);
24662306a36Sopenharmony_ci	pr_debug("%s: ISAC MOSR %02x\n", isac->name, val);
24762306a36Sopenharmony_ci#if ARCOFI_USE
24862306a36Sopenharmony_ci	if (val & 0x08) {
24962306a36Sopenharmony_ci		if (!isac->mon_rx) {
25062306a36Sopenharmony_ci			isac->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC);
25162306a36Sopenharmony_ci			if (!isac->mon_rx) {
25262306a36Sopenharmony_ci				pr_info("%s: ISAC MON RX out of memory!\n",
25362306a36Sopenharmony_ci					isac->name);
25462306a36Sopenharmony_ci				isac->mocr &= 0xf0;
25562306a36Sopenharmony_ci				isac->mocr |= 0x0a;
25662306a36Sopenharmony_ci				WriteISAC(isac, ISAC_MOCR, isac->mocr);
25762306a36Sopenharmony_ci				goto afterMONR0;
25862306a36Sopenharmony_ci			} else
25962306a36Sopenharmony_ci				isac->mon_rxp = 0;
26062306a36Sopenharmony_ci		}
26162306a36Sopenharmony_ci		if (isac->mon_rxp >= MAX_MON_FRAME) {
26262306a36Sopenharmony_ci			isac->mocr &= 0xf0;
26362306a36Sopenharmony_ci			isac->mocr |= 0x0a;
26462306a36Sopenharmony_ci			WriteISAC(isac, ISAC_MOCR, isac->mocr);
26562306a36Sopenharmony_ci			isac->mon_rxp = 0;
26662306a36Sopenharmony_ci			pr_debug("%s: ISAC MON RX overflow!\n", isac->name);
26762306a36Sopenharmony_ci			goto afterMONR0;
26862306a36Sopenharmony_ci		}
26962306a36Sopenharmony_ci		isac->mon_rx[isac->mon_rxp++] = ReadISAC(isac, ISAC_MOR0);
27062306a36Sopenharmony_ci		pr_debug("%s: ISAC MOR0 %02x\n", isac->name,
27162306a36Sopenharmony_ci			 isac->mon_rx[isac->mon_rxp - 1]);
27262306a36Sopenharmony_ci		if (isac->mon_rxp == 1) {
27362306a36Sopenharmony_ci			isac->mocr |= 0x04;
27462306a36Sopenharmony_ci			WriteISAC(isac, ISAC_MOCR, isac->mocr);
27562306a36Sopenharmony_ci		}
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ciafterMONR0:
27862306a36Sopenharmony_ci	if (val & 0x80) {
27962306a36Sopenharmony_ci		if (!isac->mon_rx) {
28062306a36Sopenharmony_ci			isac->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC);
28162306a36Sopenharmony_ci			if (!isac->mon_rx) {
28262306a36Sopenharmony_ci				pr_info("%s: ISAC MON RX out of memory!\n",
28362306a36Sopenharmony_ci					isac->name);
28462306a36Sopenharmony_ci				isac->mocr &= 0x0f;
28562306a36Sopenharmony_ci				isac->mocr |= 0xa0;
28662306a36Sopenharmony_ci				WriteISAC(isac, ISAC_MOCR, isac->mocr);
28762306a36Sopenharmony_ci				goto afterMONR1;
28862306a36Sopenharmony_ci			} else
28962306a36Sopenharmony_ci				isac->mon_rxp = 0;
29062306a36Sopenharmony_ci		}
29162306a36Sopenharmony_ci		if (isac->mon_rxp >= MAX_MON_FRAME) {
29262306a36Sopenharmony_ci			isac->mocr &= 0x0f;
29362306a36Sopenharmony_ci			isac->mocr |= 0xa0;
29462306a36Sopenharmony_ci			WriteISAC(isac, ISAC_MOCR, isac->mocr);
29562306a36Sopenharmony_ci			isac->mon_rxp = 0;
29662306a36Sopenharmony_ci			pr_debug("%s: ISAC MON RX overflow!\n", isac->name);
29762306a36Sopenharmony_ci			goto afterMONR1;
29862306a36Sopenharmony_ci		}
29962306a36Sopenharmony_ci		isac->mon_rx[isac->mon_rxp++] = ReadISAC(isac, ISAC_MOR1);
30062306a36Sopenharmony_ci		pr_debug("%s: ISAC MOR1 %02x\n", isac->name,
30162306a36Sopenharmony_ci			 isac->mon_rx[isac->mon_rxp - 1]);
30262306a36Sopenharmony_ci		isac->mocr |= 0x40;
30362306a36Sopenharmony_ci		WriteISAC(isac, ISAC_MOCR, isac->mocr);
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ciafterMONR1:
30662306a36Sopenharmony_ci	if (val & 0x04) {
30762306a36Sopenharmony_ci		isac->mocr &= 0xf0;
30862306a36Sopenharmony_ci		WriteISAC(isac, ISAC_MOCR, isac->mocr);
30962306a36Sopenharmony_ci		isac->mocr |= 0x0a;
31062306a36Sopenharmony_ci		WriteISAC(isac, ISAC_MOCR, isac->mocr);
31162306a36Sopenharmony_ci		if (isac->monitor) {
31262306a36Sopenharmony_ci			ret = isac->monitor(isac->dch.hw, MONITOR_RX_0,
31362306a36Sopenharmony_ci					    isac->mon_rx, isac->mon_rxp);
31462306a36Sopenharmony_ci			if (ret)
31562306a36Sopenharmony_ci				kfree(isac->mon_rx);
31662306a36Sopenharmony_ci		} else {
31762306a36Sopenharmony_ci			pr_info("%s: MONITOR 0 received %d but no user\n",
31862306a36Sopenharmony_ci				isac->name, isac->mon_rxp);
31962306a36Sopenharmony_ci			kfree(isac->mon_rx);
32062306a36Sopenharmony_ci		}
32162306a36Sopenharmony_ci		isac->mon_rx = NULL;
32262306a36Sopenharmony_ci		isac->mon_rxp = 0;
32362306a36Sopenharmony_ci	}
32462306a36Sopenharmony_ci	if (val & 0x40) {
32562306a36Sopenharmony_ci		isac->mocr &= 0x0f;
32662306a36Sopenharmony_ci		WriteISAC(isac, ISAC_MOCR, isac->mocr);
32762306a36Sopenharmony_ci		isac->mocr |= 0xa0;
32862306a36Sopenharmony_ci		WriteISAC(isac, ISAC_MOCR, isac->mocr);
32962306a36Sopenharmony_ci		if (isac->monitor) {
33062306a36Sopenharmony_ci			ret = isac->monitor(isac->dch.hw, MONITOR_RX_1,
33162306a36Sopenharmony_ci					    isac->mon_rx, isac->mon_rxp);
33262306a36Sopenharmony_ci			if (ret)
33362306a36Sopenharmony_ci				kfree(isac->mon_rx);
33462306a36Sopenharmony_ci		} else {
33562306a36Sopenharmony_ci			pr_info("%s: MONITOR 1 received %d but no user\n",
33662306a36Sopenharmony_ci				isac->name, isac->mon_rxp);
33762306a36Sopenharmony_ci			kfree(isac->mon_rx);
33862306a36Sopenharmony_ci		}
33962306a36Sopenharmony_ci		isac->mon_rx = NULL;
34062306a36Sopenharmony_ci		isac->mon_rxp = 0;
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci	if (val & 0x02) {
34362306a36Sopenharmony_ci		if ((!isac->mon_tx) || (isac->mon_txc &&
34462306a36Sopenharmony_ci					(isac->mon_txp >= isac->mon_txc) && !(val & 0x08))) {
34562306a36Sopenharmony_ci			isac->mocr &= 0xf0;
34662306a36Sopenharmony_ci			WriteISAC(isac, ISAC_MOCR, isac->mocr);
34762306a36Sopenharmony_ci			isac->mocr |= 0x0a;
34862306a36Sopenharmony_ci			WriteISAC(isac, ISAC_MOCR, isac->mocr);
34962306a36Sopenharmony_ci			if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {
35062306a36Sopenharmony_ci				if (isac->monitor)
35162306a36Sopenharmony_ci					isac->monitor(isac->dch.hw,
35262306a36Sopenharmony_ci						      MONITOR_TX_0, NULL, 0);
35362306a36Sopenharmony_ci			}
35462306a36Sopenharmony_ci			kfree(isac->mon_tx);
35562306a36Sopenharmony_ci			isac->mon_tx = NULL;
35662306a36Sopenharmony_ci			isac->mon_txc = 0;
35762306a36Sopenharmony_ci			isac->mon_txp = 0;
35862306a36Sopenharmony_ci			goto AfterMOX0;
35962306a36Sopenharmony_ci		}
36062306a36Sopenharmony_ci		if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {
36162306a36Sopenharmony_ci			if (isac->monitor)
36262306a36Sopenharmony_ci				isac->monitor(isac->dch.hw,
36362306a36Sopenharmony_ci					      MONITOR_TX_0, NULL, 0);
36462306a36Sopenharmony_ci			kfree(isac->mon_tx);
36562306a36Sopenharmony_ci			isac->mon_tx = NULL;
36662306a36Sopenharmony_ci			isac->mon_txc = 0;
36762306a36Sopenharmony_ci			isac->mon_txp = 0;
36862306a36Sopenharmony_ci			goto AfterMOX0;
36962306a36Sopenharmony_ci		}
37062306a36Sopenharmony_ci		WriteISAC(isac, ISAC_MOX0, isac->mon_tx[isac->mon_txp++]);
37162306a36Sopenharmony_ci		pr_debug("%s: ISAC %02x -> MOX0\n", isac->name,
37262306a36Sopenharmony_ci			 isac->mon_tx[isac->mon_txp - 1]);
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ciAfterMOX0:
37562306a36Sopenharmony_ci	if (val & 0x20) {
37662306a36Sopenharmony_ci		if ((!isac->mon_tx) || (isac->mon_txc &&
37762306a36Sopenharmony_ci					(isac->mon_txp >= isac->mon_txc) && !(val & 0x80))) {
37862306a36Sopenharmony_ci			isac->mocr &= 0x0f;
37962306a36Sopenharmony_ci			WriteISAC(isac, ISAC_MOCR, isac->mocr);
38062306a36Sopenharmony_ci			isac->mocr |= 0xa0;
38162306a36Sopenharmony_ci			WriteISAC(isac, ISAC_MOCR, isac->mocr);
38262306a36Sopenharmony_ci			if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {
38362306a36Sopenharmony_ci				if (isac->monitor)
38462306a36Sopenharmony_ci					isac->monitor(isac->dch.hw,
38562306a36Sopenharmony_ci						      MONITOR_TX_1, NULL, 0);
38662306a36Sopenharmony_ci			}
38762306a36Sopenharmony_ci			kfree(isac->mon_tx);
38862306a36Sopenharmony_ci			isac->mon_tx = NULL;
38962306a36Sopenharmony_ci			isac->mon_txc = 0;
39062306a36Sopenharmony_ci			isac->mon_txp = 0;
39162306a36Sopenharmony_ci			goto AfterMOX1;
39262306a36Sopenharmony_ci		}
39362306a36Sopenharmony_ci		if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {
39462306a36Sopenharmony_ci			if (isac->monitor)
39562306a36Sopenharmony_ci				isac->monitor(isac->dch.hw,
39662306a36Sopenharmony_ci					      MONITOR_TX_1, NULL, 0);
39762306a36Sopenharmony_ci			kfree(isac->mon_tx);
39862306a36Sopenharmony_ci			isac->mon_tx = NULL;
39962306a36Sopenharmony_ci			isac->mon_txc = 0;
40062306a36Sopenharmony_ci			isac->mon_txp = 0;
40162306a36Sopenharmony_ci			goto AfterMOX1;
40262306a36Sopenharmony_ci		}
40362306a36Sopenharmony_ci		WriteISAC(isac, ISAC_MOX1, isac->mon_tx[isac->mon_txp++]);
40462306a36Sopenharmony_ci		pr_debug("%s: ISAC %02x -> MOX1\n", isac->name,
40562306a36Sopenharmony_ci			 isac->mon_tx[isac->mon_txp - 1]);
40662306a36Sopenharmony_ci	}
40762306a36Sopenharmony_ciAfterMOX1:
40862306a36Sopenharmony_ci	val = 0; /* dummy to avoid warning */
40962306a36Sopenharmony_ci#endif
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_cistatic void
41362306a36Sopenharmony_ciisac_cisq_irq(struct isac_hw *isac) {
41462306a36Sopenharmony_ci	u8 val;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	val = ReadISAC(isac, ISAC_CIR0);
41762306a36Sopenharmony_ci	pr_debug("%s: ISAC CIR0 %02X\n", isac->name, val);
41862306a36Sopenharmony_ci	if (val & 2) {
41962306a36Sopenharmony_ci		pr_debug("%s: ph_state change %x->%x\n", isac->name,
42062306a36Sopenharmony_ci			 isac->state, (val >> 2) & 0xf);
42162306a36Sopenharmony_ci		isac->state = (val >> 2) & 0xf;
42262306a36Sopenharmony_ci		isac_ph_state_change(isac);
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci	if (val & 1) {
42562306a36Sopenharmony_ci		val = ReadISAC(isac, ISAC_CIR1);
42662306a36Sopenharmony_ci		pr_debug("%s: ISAC CIR1 %02X\n", isac->name, val);
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_cistatic void
43162306a36Sopenharmony_ciisacsx_cic_irq(struct isac_hw *isac)
43262306a36Sopenharmony_ci{
43362306a36Sopenharmony_ci	u8 val;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	val = ReadISAC(isac, ISACX_CIR0);
43662306a36Sopenharmony_ci	pr_debug("%s: ISACX CIR0 %02X\n", isac->name, val);
43762306a36Sopenharmony_ci	if (val & ISACX_CIR0_CIC0) {
43862306a36Sopenharmony_ci		pr_debug("%s: ph_state change %x->%x\n", isac->name,
43962306a36Sopenharmony_ci			 isac->state, val >> 4);
44062306a36Sopenharmony_ci		isac->state = val >> 4;
44162306a36Sopenharmony_ci		isac_ph_state_change(isac);
44262306a36Sopenharmony_ci	}
44362306a36Sopenharmony_ci}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_cistatic void
44662306a36Sopenharmony_ciisacsx_rme_irq(struct isac_hw *isac)
44762306a36Sopenharmony_ci{
44862306a36Sopenharmony_ci	int count;
44962306a36Sopenharmony_ci	u8 val;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	val = ReadISAC(isac, ISACX_RSTAD);
45262306a36Sopenharmony_ci	if ((val & (ISACX_RSTAD_VFR |
45362306a36Sopenharmony_ci		    ISACX_RSTAD_RDO |
45462306a36Sopenharmony_ci		    ISACX_RSTAD_CRC |
45562306a36Sopenharmony_ci		    ISACX_RSTAD_RAB))
45662306a36Sopenharmony_ci	    != (ISACX_RSTAD_VFR | ISACX_RSTAD_CRC)) {
45762306a36Sopenharmony_ci		pr_debug("%s: RSTAD %#x, dropped\n", isac->name, val);
45862306a36Sopenharmony_ci#ifdef ERROR_STATISTIC
45962306a36Sopenharmony_ci		if (val & ISACX_RSTAD_CRC)
46062306a36Sopenharmony_ci			isac->dch.err_rx++;
46162306a36Sopenharmony_ci		else
46262306a36Sopenharmony_ci			isac->dch.err_crc++;
46362306a36Sopenharmony_ci#endif
46462306a36Sopenharmony_ci		WriteISAC(isac, ISACX_CMDRD, ISACX_CMDRD_RMC);
46562306a36Sopenharmony_ci		dev_kfree_skb(isac->dch.rx_skb);
46662306a36Sopenharmony_ci		isac->dch.rx_skb = NULL;
46762306a36Sopenharmony_ci	} else {
46862306a36Sopenharmony_ci		count = ReadISAC(isac, ISACX_RBCLD) & 0x1f;
46962306a36Sopenharmony_ci		if (count == 0)
47062306a36Sopenharmony_ci			count = 32;
47162306a36Sopenharmony_ci		isac_empty_fifo(isac, count);
47262306a36Sopenharmony_ci		if (isac->dch.rx_skb) {
47362306a36Sopenharmony_ci			skb_trim(isac->dch.rx_skb, isac->dch.rx_skb->len - 1);
47462306a36Sopenharmony_ci			pr_debug("%s: dchannel received %d\n", isac->name,
47562306a36Sopenharmony_ci				 isac->dch.rx_skb->len);
47662306a36Sopenharmony_ci			recv_Dchannel(&isac->dch);
47762306a36Sopenharmony_ci		}
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ciirqreturn_t
48262306a36Sopenharmony_cimISDNisac_irq(struct isac_hw *isac, u8 val)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	if (unlikely(!val))
48562306a36Sopenharmony_ci		return IRQ_NONE;
48662306a36Sopenharmony_ci	pr_debug("%s: ISAC interrupt %02x\n", isac->name, val);
48762306a36Sopenharmony_ci	if (isac->type & IPAC_TYPE_ISACX) {
48862306a36Sopenharmony_ci		if (val & ISACX__CIC)
48962306a36Sopenharmony_ci			isacsx_cic_irq(isac);
49062306a36Sopenharmony_ci		if (val & ISACX__ICD) {
49162306a36Sopenharmony_ci			val = ReadISAC(isac, ISACX_ISTAD);
49262306a36Sopenharmony_ci			pr_debug("%s: ISTAD %02x\n", isac->name, val);
49362306a36Sopenharmony_ci			if (val & ISACX_D_XDU) {
49462306a36Sopenharmony_ci				pr_debug("%s: ISAC XDU\n", isac->name);
49562306a36Sopenharmony_ci#ifdef ERROR_STATISTIC
49662306a36Sopenharmony_ci				isac->dch.err_tx++;
49762306a36Sopenharmony_ci#endif
49862306a36Sopenharmony_ci				isac_retransmit(isac);
49962306a36Sopenharmony_ci			}
50062306a36Sopenharmony_ci			if (val & ISACX_D_XMR) {
50162306a36Sopenharmony_ci				pr_debug("%s: ISAC XMR\n", isac->name);
50262306a36Sopenharmony_ci#ifdef ERROR_STATISTIC
50362306a36Sopenharmony_ci				isac->dch.err_tx++;
50462306a36Sopenharmony_ci#endif
50562306a36Sopenharmony_ci				isac_retransmit(isac);
50662306a36Sopenharmony_ci			}
50762306a36Sopenharmony_ci			if (val & ISACX_D_XPR)
50862306a36Sopenharmony_ci				isac_xpr_irq(isac);
50962306a36Sopenharmony_ci			if (val & ISACX_D_RFO) {
51062306a36Sopenharmony_ci				pr_debug("%s: ISAC RFO\n", isac->name);
51162306a36Sopenharmony_ci				WriteISAC(isac, ISACX_CMDRD, ISACX_CMDRD_RMC);
51262306a36Sopenharmony_ci			}
51362306a36Sopenharmony_ci			if (val & ISACX_D_RME)
51462306a36Sopenharmony_ci				isacsx_rme_irq(isac);
51562306a36Sopenharmony_ci			if (val & ISACX_D_RPF)
51662306a36Sopenharmony_ci				isac_empty_fifo(isac, 0x20);
51762306a36Sopenharmony_ci		}
51862306a36Sopenharmony_ci	} else {
51962306a36Sopenharmony_ci		if (val & 0x80)	/* RME */
52062306a36Sopenharmony_ci			isac_rme_irq(isac);
52162306a36Sopenharmony_ci		if (val & 0x40)	/* RPF */
52262306a36Sopenharmony_ci			isac_empty_fifo(isac, 32);
52362306a36Sopenharmony_ci		if (val & 0x10)	/* XPR */
52462306a36Sopenharmony_ci			isac_xpr_irq(isac);
52562306a36Sopenharmony_ci		if (val & 0x04)	/* CISQ */
52662306a36Sopenharmony_ci			isac_cisq_irq(isac);
52762306a36Sopenharmony_ci		if (val & 0x20)	/* RSC - never */
52862306a36Sopenharmony_ci			pr_debug("%s: ISAC RSC interrupt\n", isac->name);
52962306a36Sopenharmony_ci		if (val & 0x02)	/* SIN - never */
53062306a36Sopenharmony_ci			pr_debug("%s: ISAC SIN interrupt\n", isac->name);
53162306a36Sopenharmony_ci		if (val & 0x01) {	/* EXI */
53262306a36Sopenharmony_ci			val = ReadISAC(isac, ISAC_EXIR);
53362306a36Sopenharmony_ci			pr_debug("%s: ISAC EXIR %02x\n", isac->name, val);
53462306a36Sopenharmony_ci			if (val & 0x80)	/* XMR */
53562306a36Sopenharmony_ci				pr_debug("%s: ISAC XMR\n", isac->name);
53662306a36Sopenharmony_ci			if (val & 0x40) { /* XDU */
53762306a36Sopenharmony_ci				pr_debug("%s: ISAC XDU\n", isac->name);
53862306a36Sopenharmony_ci#ifdef ERROR_STATISTIC
53962306a36Sopenharmony_ci				isac->dch.err_tx++;
54062306a36Sopenharmony_ci#endif
54162306a36Sopenharmony_ci				isac_retransmit(isac);
54262306a36Sopenharmony_ci			}
54362306a36Sopenharmony_ci			if (val & 0x04)	/* MOS */
54462306a36Sopenharmony_ci				isac_mos_irq(isac);
54562306a36Sopenharmony_ci		}
54662306a36Sopenharmony_ci	}
54762306a36Sopenharmony_ci	return IRQ_HANDLED;
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ciEXPORT_SYMBOL(mISDNisac_irq);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_cistatic int
55262306a36Sopenharmony_ciisac_l1hw(struct mISDNchannel *ch, struct sk_buff *skb)
55362306a36Sopenharmony_ci{
55462306a36Sopenharmony_ci	struct mISDNdevice	*dev = container_of(ch, struct mISDNdevice, D);
55562306a36Sopenharmony_ci	struct dchannel		*dch = container_of(dev, struct dchannel, dev);
55662306a36Sopenharmony_ci	struct isac_hw		*isac = container_of(dch, struct isac_hw, dch);
55762306a36Sopenharmony_ci	int			ret = -EINVAL;
55862306a36Sopenharmony_ci	struct mISDNhead	*hh = mISDN_HEAD_P(skb);
55962306a36Sopenharmony_ci	u32			id;
56062306a36Sopenharmony_ci	u_long			flags;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	switch (hh->prim) {
56362306a36Sopenharmony_ci	case PH_DATA_REQ:
56462306a36Sopenharmony_ci		spin_lock_irqsave(isac->hwlock, flags);
56562306a36Sopenharmony_ci		ret = dchannel_senddata(dch, skb);
56662306a36Sopenharmony_ci		if (ret > 0) { /* direct TX */
56762306a36Sopenharmony_ci			id = hh->id; /* skb can be freed */
56862306a36Sopenharmony_ci			isac_fill_fifo(isac);
56962306a36Sopenharmony_ci			ret = 0;
57062306a36Sopenharmony_ci			spin_unlock_irqrestore(isac->hwlock, flags);
57162306a36Sopenharmony_ci			queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
57262306a36Sopenharmony_ci		} else
57362306a36Sopenharmony_ci			spin_unlock_irqrestore(isac->hwlock, flags);
57462306a36Sopenharmony_ci		return ret;
57562306a36Sopenharmony_ci	case PH_ACTIVATE_REQ:
57662306a36Sopenharmony_ci		ret = l1_event(dch->l1, hh->prim);
57762306a36Sopenharmony_ci		break;
57862306a36Sopenharmony_ci	case PH_DEACTIVATE_REQ:
57962306a36Sopenharmony_ci		test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags);
58062306a36Sopenharmony_ci		ret = l1_event(dch->l1, hh->prim);
58162306a36Sopenharmony_ci		break;
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	if (!ret)
58562306a36Sopenharmony_ci		dev_kfree_skb(skb);
58662306a36Sopenharmony_ci	return ret;
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_cistatic int
59062306a36Sopenharmony_ciisac_ctrl(struct isac_hw *isac, u32 cmd, unsigned long para)
59162306a36Sopenharmony_ci{
59262306a36Sopenharmony_ci	u8 tl = 0;
59362306a36Sopenharmony_ci	unsigned long flags;
59462306a36Sopenharmony_ci	int ret = 0;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	switch (cmd) {
59762306a36Sopenharmony_ci	case HW_TESTLOOP:
59862306a36Sopenharmony_ci		spin_lock_irqsave(isac->hwlock, flags);
59962306a36Sopenharmony_ci		if (!(isac->type & IPAC_TYPE_ISACX)) {
60062306a36Sopenharmony_ci			/* TODO: implement for IPAC_TYPE_ISACX */
60162306a36Sopenharmony_ci			if (para & 1) /* B1 */
60262306a36Sopenharmony_ci				tl |= 0x0c;
60362306a36Sopenharmony_ci			else if (para & 2) /* B2 */
60462306a36Sopenharmony_ci				tl |= 0x3;
60562306a36Sopenharmony_ci			/* we only support IOM2 mode */
60662306a36Sopenharmony_ci			WriteISAC(isac, ISAC_SPCR, tl);
60762306a36Sopenharmony_ci			if (tl)
60862306a36Sopenharmony_ci				WriteISAC(isac, ISAC_ADF1, 0x8);
60962306a36Sopenharmony_ci			else
61062306a36Sopenharmony_ci				WriteISAC(isac, ISAC_ADF1, 0x0);
61162306a36Sopenharmony_ci		}
61262306a36Sopenharmony_ci		spin_unlock_irqrestore(isac->hwlock, flags);
61362306a36Sopenharmony_ci		break;
61462306a36Sopenharmony_ci	case HW_TIMER3_VALUE:
61562306a36Sopenharmony_ci		ret = l1_event(isac->dch.l1, HW_TIMER3_VALUE | (para & 0xff));
61662306a36Sopenharmony_ci		break;
61762306a36Sopenharmony_ci	default:
61862306a36Sopenharmony_ci		pr_debug("%s: %s unknown command %x %lx\n", isac->name,
61962306a36Sopenharmony_ci			 __func__, cmd, para);
62062306a36Sopenharmony_ci		ret = -1;
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci	return ret;
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cistatic int
62662306a36Sopenharmony_ciisac_l1cmd(struct dchannel *dch, u32 cmd)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci	struct isac_hw *isac = container_of(dch, struct isac_hw, dch);
62962306a36Sopenharmony_ci	u_long flags;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	pr_debug("%s: cmd(%x) state(%02x)\n", isac->name, cmd, isac->state);
63262306a36Sopenharmony_ci	switch (cmd) {
63362306a36Sopenharmony_ci	case INFO3_P8:
63462306a36Sopenharmony_ci		spin_lock_irqsave(isac->hwlock, flags);
63562306a36Sopenharmony_ci		ph_command(isac, ISAC_CMD_AR8);
63662306a36Sopenharmony_ci		spin_unlock_irqrestore(isac->hwlock, flags);
63762306a36Sopenharmony_ci		break;
63862306a36Sopenharmony_ci	case INFO3_P10:
63962306a36Sopenharmony_ci		spin_lock_irqsave(isac->hwlock, flags);
64062306a36Sopenharmony_ci		ph_command(isac, ISAC_CMD_AR10);
64162306a36Sopenharmony_ci		spin_unlock_irqrestore(isac->hwlock, flags);
64262306a36Sopenharmony_ci		break;
64362306a36Sopenharmony_ci	case HW_RESET_REQ:
64462306a36Sopenharmony_ci		spin_lock_irqsave(isac->hwlock, flags);
64562306a36Sopenharmony_ci		if ((isac->state == ISAC_IND_EI) ||
64662306a36Sopenharmony_ci		    (isac->state == ISAC_IND_DR) ||
64762306a36Sopenharmony_ci		    (isac->state == ISAC_IND_DR6) ||
64862306a36Sopenharmony_ci		    (isac->state == ISAC_IND_RS))
64962306a36Sopenharmony_ci			ph_command(isac, ISAC_CMD_TIM);
65062306a36Sopenharmony_ci		else
65162306a36Sopenharmony_ci			ph_command(isac, ISAC_CMD_RS);
65262306a36Sopenharmony_ci		spin_unlock_irqrestore(isac->hwlock, flags);
65362306a36Sopenharmony_ci		break;
65462306a36Sopenharmony_ci	case HW_DEACT_REQ:
65562306a36Sopenharmony_ci		skb_queue_purge(&dch->squeue);
65662306a36Sopenharmony_ci		if (dch->tx_skb) {
65762306a36Sopenharmony_ci			dev_kfree_skb(dch->tx_skb);
65862306a36Sopenharmony_ci			dch->tx_skb = NULL;
65962306a36Sopenharmony_ci		}
66062306a36Sopenharmony_ci		dch->tx_idx = 0;
66162306a36Sopenharmony_ci		if (dch->rx_skb) {
66262306a36Sopenharmony_ci			dev_kfree_skb(dch->rx_skb);
66362306a36Sopenharmony_ci			dch->rx_skb = NULL;
66462306a36Sopenharmony_ci		}
66562306a36Sopenharmony_ci		test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
66662306a36Sopenharmony_ci		if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
66762306a36Sopenharmony_ci			del_timer(&dch->timer);
66862306a36Sopenharmony_ci		break;
66962306a36Sopenharmony_ci	case HW_POWERUP_REQ:
67062306a36Sopenharmony_ci		spin_lock_irqsave(isac->hwlock, flags);
67162306a36Sopenharmony_ci		ph_command(isac, ISAC_CMD_TIM);
67262306a36Sopenharmony_ci		spin_unlock_irqrestore(isac->hwlock, flags);
67362306a36Sopenharmony_ci		break;
67462306a36Sopenharmony_ci	case PH_ACTIVATE_IND:
67562306a36Sopenharmony_ci		test_and_set_bit(FLG_ACTIVE, &dch->Flags);
67662306a36Sopenharmony_ci		_queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
67762306a36Sopenharmony_ci			    GFP_ATOMIC);
67862306a36Sopenharmony_ci		break;
67962306a36Sopenharmony_ci	case PH_DEACTIVATE_IND:
68062306a36Sopenharmony_ci		test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
68162306a36Sopenharmony_ci		_queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
68262306a36Sopenharmony_ci			    GFP_ATOMIC);
68362306a36Sopenharmony_ci		break;
68462306a36Sopenharmony_ci	default:
68562306a36Sopenharmony_ci		pr_debug("%s: %s unknown command %x\n", isac->name,
68662306a36Sopenharmony_ci			 __func__, cmd);
68762306a36Sopenharmony_ci		return -1;
68862306a36Sopenharmony_ci	}
68962306a36Sopenharmony_ci	return 0;
69062306a36Sopenharmony_ci}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_cistatic void
69362306a36Sopenharmony_ciisac_release(struct isac_hw *isac)
69462306a36Sopenharmony_ci{
69562306a36Sopenharmony_ci	if (isac->type & IPAC_TYPE_ISACX)
69662306a36Sopenharmony_ci		WriteISAC(isac, ISACX_MASK, 0xff);
69762306a36Sopenharmony_ci	else if (isac->type != 0)
69862306a36Sopenharmony_ci		WriteISAC(isac, ISAC_MASK, 0xff);
69962306a36Sopenharmony_ci	if (isac->dch.timer.function != NULL) {
70062306a36Sopenharmony_ci		del_timer(&isac->dch.timer);
70162306a36Sopenharmony_ci		isac->dch.timer.function = NULL;
70262306a36Sopenharmony_ci	}
70362306a36Sopenharmony_ci	kfree(isac->mon_rx);
70462306a36Sopenharmony_ci	isac->mon_rx = NULL;
70562306a36Sopenharmony_ci	kfree(isac->mon_tx);
70662306a36Sopenharmony_ci	isac->mon_tx = NULL;
70762306a36Sopenharmony_ci	if (isac->dch.l1)
70862306a36Sopenharmony_ci		l1_event(isac->dch.l1, CLOSE_CHANNEL);
70962306a36Sopenharmony_ci	mISDN_freedchannel(&isac->dch);
71062306a36Sopenharmony_ci}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_cistatic void
71362306a36Sopenharmony_cidbusy_timer_handler(struct timer_list *t)
71462306a36Sopenharmony_ci{
71562306a36Sopenharmony_ci	struct isac_hw *isac = from_timer(isac, t, dch.timer);
71662306a36Sopenharmony_ci	int rbch, star;
71762306a36Sopenharmony_ci	u_long flags;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	if (test_bit(FLG_BUSY_TIMER, &isac->dch.Flags)) {
72062306a36Sopenharmony_ci		spin_lock_irqsave(isac->hwlock, flags);
72162306a36Sopenharmony_ci		rbch = ReadISAC(isac, ISAC_RBCH);
72262306a36Sopenharmony_ci		star = ReadISAC(isac, ISAC_STAR);
72362306a36Sopenharmony_ci		pr_debug("%s: D-Channel Busy RBCH %02x STAR %02x\n",
72462306a36Sopenharmony_ci			 isac->name, rbch, star);
72562306a36Sopenharmony_ci		if (rbch & ISAC_RBCH_XAC) /* D-Channel Busy */
72662306a36Sopenharmony_ci			test_and_set_bit(FLG_L1_BUSY, &isac->dch.Flags);
72762306a36Sopenharmony_ci		else {
72862306a36Sopenharmony_ci			/* discard frame; reset transceiver */
72962306a36Sopenharmony_ci			test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags);
73062306a36Sopenharmony_ci			if (isac->dch.tx_idx)
73162306a36Sopenharmony_ci				isac->dch.tx_idx = 0;
73262306a36Sopenharmony_ci			else
73362306a36Sopenharmony_ci				pr_info("%s: ISAC D-Channel Busy no tx_idx\n",
73462306a36Sopenharmony_ci					isac->name);
73562306a36Sopenharmony_ci			/* Transmitter reset */
73662306a36Sopenharmony_ci			WriteISAC(isac, ISAC_CMDR, 0x01);
73762306a36Sopenharmony_ci		}
73862306a36Sopenharmony_ci		spin_unlock_irqrestore(isac->hwlock, flags);
73962306a36Sopenharmony_ci	}
74062306a36Sopenharmony_ci}
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_cistatic int
74362306a36Sopenharmony_ciopen_dchannel_caller(struct isac_hw *isac, struct channel_req *rq, void *caller)
74462306a36Sopenharmony_ci{
74562306a36Sopenharmony_ci	pr_debug("%s: %s dev(%d) open from %p\n", isac->name, __func__,
74662306a36Sopenharmony_ci		 isac->dch.dev.id, caller);
74762306a36Sopenharmony_ci	if (rq->protocol != ISDN_P_TE_S0)
74862306a36Sopenharmony_ci		return -EINVAL;
74962306a36Sopenharmony_ci	if (rq->adr.channel == 1)
75062306a36Sopenharmony_ci		/* E-Channel not supported */
75162306a36Sopenharmony_ci		return -EINVAL;
75262306a36Sopenharmony_ci	rq->ch = &isac->dch.dev.D;
75362306a36Sopenharmony_ci	rq->ch->protocol = rq->protocol;
75462306a36Sopenharmony_ci	if (isac->dch.state == 7)
75562306a36Sopenharmony_ci		_queue_data(rq->ch, PH_ACTIVATE_IND, MISDN_ID_ANY,
75662306a36Sopenharmony_ci			    0, NULL, GFP_KERNEL);
75762306a36Sopenharmony_ci	return 0;
75862306a36Sopenharmony_ci}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_cistatic int
76162306a36Sopenharmony_ciopen_dchannel(struct isac_hw *isac, struct channel_req *rq)
76262306a36Sopenharmony_ci{
76362306a36Sopenharmony_ci	return open_dchannel_caller(isac, rq, __builtin_return_address(0));
76462306a36Sopenharmony_ci}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_cistatic const char *ISACVer[] =
76762306a36Sopenharmony_ci{"2086/2186 V1.1", "2085 B1", "2085 B2",
76862306a36Sopenharmony_ci "2085 V2.3"};
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_cistatic int
77162306a36Sopenharmony_ciisac_init(struct isac_hw *isac)
77262306a36Sopenharmony_ci{
77362306a36Sopenharmony_ci	u8 val;
77462306a36Sopenharmony_ci	int err = 0;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	if (!isac->dch.l1) {
77762306a36Sopenharmony_ci		err = create_l1(&isac->dch, isac_l1cmd);
77862306a36Sopenharmony_ci		if (err)
77962306a36Sopenharmony_ci			return err;
78062306a36Sopenharmony_ci	}
78162306a36Sopenharmony_ci	isac->mon_tx = NULL;
78262306a36Sopenharmony_ci	isac->mon_rx = NULL;
78362306a36Sopenharmony_ci	timer_setup(&isac->dch.timer, dbusy_timer_handler, 0);
78462306a36Sopenharmony_ci	isac->mocr = 0xaa;
78562306a36Sopenharmony_ci	if (isac->type & IPAC_TYPE_ISACX) {
78662306a36Sopenharmony_ci		/* Disable all IRQ */
78762306a36Sopenharmony_ci		WriteISAC(isac, ISACX_MASK, 0xff);
78862306a36Sopenharmony_ci		val = ReadISAC(isac, ISACX_STARD);
78962306a36Sopenharmony_ci		pr_debug("%s: ISACX STARD %x\n", isac->name, val);
79062306a36Sopenharmony_ci		val = ReadISAC(isac, ISACX_ISTAD);
79162306a36Sopenharmony_ci		pr_debug("%s: ISACX ISTAD %x\n", isac->name, val);
79262306a36Sopenharmony_ci		val = ReadISAC(isac, ISACX_ISTA);
79362306a36Sopenharmony_ci		pr_debug("%s: ISACX ISTA %x\n", isac->name, val);
79462306a36Sopenharmony_ci		/* clear LDD */
79562306a36Sopenharmony_ci		WriteISAC(isac, ISACX_TR_CONF0, 0x00);
79662306a36Sopenharmony_ci		/* enable transmitter */
79762306a36Sopenharmony_ci		WriteISAC(isac, ISACX_TR_CONF2, 0x00);
79862306a36Sopenharmony_ci		/* transparent mode 0, RAC, stop/go */
79962306a36Sopenharmony_ci		WriteISAC(isac, ISACX_MODED, 0xc9);
80062306a36Sopenharmony_ci		/* all HDLC IRQ unmasked */
80162306a36Sopenharmony_ci		val = ReadISAC(isac, ISACX_ID);
80262306a36Sopenharmony_ci		if (isac->dch.debug & DEBUG_HW)
80362306a36Sopenharmony_ci			pr_notice("%s: ISACX Design ID %x\n",
80462306a36Sopenharmony_ci				  isac->name, val & 0x3f);
80562306a36Sopenharmony_ci		val = ReadISAC(isac, ISACX_CIR0);
80662306a36Sopenharmony_ci		pr_debug("%s: ISACX CIR0 %02X\n", isac->name, val);
80762306a36Sopenharmony_ci		isac->state = val >> 4;
80862306a36Sopenharmony_ci		isac_ph_state_change(isac);
80962306a36Sopenharmony_ci		ph_command(isac, ISAC_CMD_RS);
81062306a36Sopenharmony_ci		WriteISAC(isac, ISACX_MASK, IPACX__ON);
81162306a36Sopenharmony_ci		WriteISAC(isac, ISACX_MASKD, 0x00);
81262306a36Sopenharmony_ci	} else { /* old isac */
81362306a36Sopenharmony_ci		WriteISAC(isac, ISAC_MASK, 0xff);
81462306a36Sopenharmony_ci		val = ReadISAC(isac, ISAC_STAR);
81562306a36Sopenharmony_ci		pr_debug("%s: ISAC STAR %x\n", isac->name, val);
81662306a36Sopenharmony_ci		val = ReadISAC(isac, ISAC_MODE);
81762306a36Sopenharmony_ci		pr_debug("%s: ISAC MODE %x\n", isac->name, val);
81862306a36Sopenharmony_ci		val = ReadISAC(isac, ISAC_ADF2);
81962306a36Sopenharmony_ci		pr_debug("%s: ISAC ADF2 %x\n", isac->name, val);
82062306a36Sopenharmony_ci		val = ReadISAC(isac, ISAC_ISTA);
82162306a36Sopenharmony_ci		pr_debug("%s: ISAC ISTA %x\n", isac->name, val);
82262306a36Sopenharmony_ci		if (val & 0x01) {
82362306a36Sopenharmony_ci			val = ReadISAC(isac, ISAC_EXIR);
82462306a36Sopenharmony_ci			pr_debug("%s: ISAC EXIR %x\n", isac->name, val);
82562306a36Sopenharmony_ci		}
82662306a36Sopenharmony_ci		val = ReadISAC(isac, ISAC_RBCH);
82762306a36Sopenharmony_ci		if (isac->dch.debug & DEBUG_HW)
82862306a36Sopenharmony_ci			pr_notice("%s: ISAC version (%x): %s\n", isac->name,
82962306a36Sopenharmony_ci				  val, ISACVer[(val >> 5) & 3]);
83062306a36Sopenharmony_ci		isac->type |= ((val >> 5) & 3);
83162306a36Sopenharmony_ci		if (!isac->adf2)
83262306a36Sopenharmony_ci			isac->adf2 = 0x80;
83362306a36Sopenharmony_ci		if (!(isac->adf2 & 0x80)) { /* only IOM 2 Mode */
83462306a36Sopenharmony_ci			pr_info("%s: only support IOM2 mode but adf2=%02x\n",
83562306a36Sopenharmony_ci				isac->name, isac->adf2);
83662306a36Sopenharmony_ci			isac_release(isac);
83762306a36Sopenharmony_ci			return -EINVAL;
83862306a36Sopenharmony_ci		}
83962306a36Sopenharmony_ci		WriteISAC(isac, ISAC_ADF2, isac->adf2);
84062306a36Sopenharmony_ci		WriteISAC(isac, ISAC_SQXR, 0x2f);
84162306a36Sopenharmony_ci		WriteISAC(isac, ISAC_SPCR, 0x00);
84262306a36Sopenharmony_ci		WriteISAC(isac, ISAC_STCR, 0x70);
84362306a36Sopenharmony_ci		WriteISAC(isac, ISAC_MODE, 0xc9);
84462306a36Sopenharmony_ci		WriteISAC(isac, ISAC_TIMR, 0x00);
84562306a36Sopenharmony_ci		WriteISAC(isac, ISAC_ADF1, 0x00);
84662306a36Sopenharmony_ci		val = ReadISAC(isac, ISAC_CIR0);
84762306a36Sopenharmony_ci		pr_debug("%s: ISAC CIR0 %x\n", isac->name, val);
84862306a36Sopenharmony_ci		isac->state = (val >> 2) & 0xf;
84962306a36Sopenharmony_ci		isac_ph_state_change(isac);
85062306a36Sopenharmony_ci		ph_command(isac, ISAC_CMD_RS);
85162306a36Sopenharmony_ci		WriteISAC(isac, ISAC_MASK, 0);
85262306a36Sopenharmony_ci	}
85362306a36Sopenharmony_ci	return err;
85462306a36Sopenharmony_ci}
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ciint
85762306a36Sopenharmony_cimISDNisac_init(struct isac_hw *isac, void *hw)
85862306a36Sopenharmony_ci{
85962306a36Sopenharmony_ci	mISDN_initdchannel(&isac->dch, MAX_DFRAME_LEN_L1, isac_ph_state_bh);
86062306a36Sopenharmony_ci	isac->dch.hw = hw;
86162306a36Sopenharmony_ci	isac->dch.dev.D.send = isac_l1hw;
86262306a36Sopenharmony_ci	isac->init = isac_init;
86362306a36Sopenharmony_ci	isac->release = isac_release;
86462306a36Sopenharmony_ci	isac->ctrl = isac_ctrl;
86562306a36Sopenharmony_ci	isac->open = open_dchannel;
86662306a36Sopenharmony_ci	isac->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0);
86762306a36Sopenharmony_ci	isac->dch.dev.nrbchan = 2;
86862306a36Sopenharmony_ci	return 0;
86962306a36Sopenharmony_ci}
87062306a36Sopenharmony_ciEXPORT_SYMBOL(mISDNisac_init);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_cistatic void
87362306a36Sopenharmony_ciwaitforCEC(struct hscx_hw *hx)
87462306a36Sopenharmony_ci{
87562306a36Sopenharmony_ci	u8 starb, to = 50;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	while (to) {
87862306a36Sopenharmony_ci		starb = ReadHSCX(hx, IPAC_STARB);
87962306a36Sopenharmony_ci		if (!(starb & 0x04))
88062306a36Sopenharmony_ci			break;
88162306a36Sopenharmony_ci		udelay(1);
88262306a36Sopenharmony_ci		to--;
88362306a36Sopenharmony_ci	}
88462306a36Sopenharmony_ci	if (to < 50)
88562306a36Sopenharmony_ci		pr_debug("%s: B%1d CEC %d us\n", hx->ip->name, hx->bch.nr,
88662306a36Sopenharmony_ci			 50 - to);
88762306a36Sopenharmony_ci	if (!to)
88862306a36Sopenharmony_ci		pr_info("%s: B%1d CEC timeout\n", hx->ip->name, hx->bch.nr);
88962306a36Sopenharmony_ci}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_cistatic void
89362306a36Sopenharmony_ciwaitforXFW(struct hscx_hw *hx)
89462306a36Sopenharmony_ci{
89562306a36Sopenharmony_ci	u8 starb, to = 50;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	while (to) {
89862306a36Sopenharmony_ci		starb = ReadHSCX(hx, IPAC_STARB);
89962306a36Sopenharmony_ci		if ((starb & 0x44) == 0x40)
90062306a36Sopenharmony_ci			break;
90162306a36Sopenharmony_ci		udelay(1);
90262306a36Sopenharmony_ci		to--;
90362306a36Sopenharmony_ci	}
90462306a36Sopenharmony_ci	if (to < 50)
90562306a36Sopenharmony_ci		pr_debug("%s: B%1d XFW %d us\n", hx->ip->name, hx->bch.nr,
90662306a36Sopenharmony_ci			 50 - to);
90762306a36Sopenharmony_ci	if (!to)
90862306a36Sopenharmony_ci		pr_info("%s: B%1d XFW timeout\n", hx->ip->name, hx->bch.nr);
90962306a36Sopenharmony_ci}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_cistatic void
91262306a36Sopenharmony_cihscx_cmdr(struct hscx_hw *hx, u8 cmd)
91362306a36Sopenharmony_ci{
91462306a36Sopenharmony_ci	if (hx->ip->type & IPAC_TYPE_IPACX)
91562306a36Sopenharmony_ci		WriteHSCX(hx, IPACX_CMDRB, cmd);
91662306a36Sopenharmony_ci	else {
91762306a36Sopenharmony_ci		waitforCEC(hx);
91862306a36Sopenharmony_ci		WriteHSCX(hx, IPAC_CMDRB, cmd);
91962306a36Sopenharmony_ci	}
92062306a36Sopenharmony_ci}
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_cistatic void
92362306a36Sopenharmony_cihscx_empty_fifo(struct hscx_hw *hscx, u8 count)
92462306a36Sopenharmony_ci{
92562306a36Sopenharmony_ci	u8 *p;
92662306a36Sopenharmony_ci	int maxlen;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	pr_debug("%s: B%1d %d\n", hscx->ip->name, hscx->bch.nr, count);
92962306a36Sopenharmony_ci	if (test_bit(FLG_RX_OFF, &hscx->bch.Flags)) {
93062306a36Sopenharmony_ci		hscx->bch.dropcnt += count;
93162306a36Sopenharmony_ci		hscx_cmdr(hscx, 0x80); /* RMC */
93262306a36Sopenharmony_ci		return;
93362306a36Sopenharmony_ci	}
93462306a36Sopenharmony_ci	maxlen = bchannel_get_rxbuf(&hscx->bch, count);
93562306a36Sopenharmony_ci	if (maxlen < 0) {
93662306a36Sopenharmony_ci		hscx_cmdr(hscx, 0x80); /* RMC */
93762306a36Sopenharmony_ci		if (hscx->bch.rx_skb)
93862306a36Sopenharmony_ci			skb_trim(hscx->bch.rx_skb, 0);
93962306a36Sopenharmony_ci		pr_warn("%s.B%d: No bufferspace for %d bytes\n",
94062306a36Sopenharmony_ci			hscx->ip->name, hscx->bch.nr, count);
94162306a36Sopenharmony_ci		return;
94262306a36Sopenharmony_ci	}
94362306a36Sopenharmony_ci	p = skb_put(hscx->bch.rx_skb, count);
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	if (hscx->ip->type & IPAC_TYPE_IPACX)
94662306a36Sopenharmony_ci		hscx->ip->read_fifo(hscx->ip->hw,
94762306a36Sopenharmony_ci				    hscx->off + IPACX_RFIFOB, p, count);
94862306a36Sopenharmony_ci	else
94962306a36Sopenharmony_ci		hscx->ip->read_fifo(hscx->ip->hw,
95062306a36Sopenharmony_ci				    hscx->off, p, count);
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	hscx_cmdr(hscx, 0x80); /* RMC */
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	if (hscx->bch.debug & DEBUG_HW_BFIFO) {
95562306a36Sopenharmony_ci		snprintf(hscx->log, 64, "B%1d-recv %s %d ",
95662306a36Sopenharmony_ci			 hscx->bch.nr, hscx->ip->name, count);
95762306a36Sopenharmony_ci		print_hex_dump_bytes(hscx->log, DUMP_PREFIX_OFFSET, p, count);
95862306a36Sopenharmony_ci	}
95962306a36Sopenharmony_ci}
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_cistatic void
96262306a36Sopenharmony_cihscx_fill_fifo(struct hscx_hw *hscx)
96362306a36Sopenharmony_ci{
96462306a36Sopenharmony_ci	int count, more;
96562306a36Sopenharmony_ci	u8 *p;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	if (!hscx->bch.tx_skb) {
96862306a36Sopenharmony_ci		if (!test_bit(FLG_TX_EMPTY, &hscx->bch.Flags))
96962306a36Sopenharmony_ci			return;
97062306a36Sopenharmony_ci		count = hscx->fifo_size;
97162306a36Sopenharmony_ci		more = 1;
97262306a36Sopenharmony_ci		p = hscx->log;
97362306a36Sopenharmony_ci		memset(p, hscx->bch.fill[0], count);
97462306a36Sopenharmony_ci	} else {
97562306a36Sopenharmony_ci		count = hscx->bch.tx_skb->len - hscx->bch.tx_idx;
97662306a36Sopenharmony_ci		if (count <= 0)
97762306a36Sopenharmony_ci			return;
97862306a36Sopenharmony_ci		p = hscx->bch.tx_skb->data + hscx->bch.tx_idx;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci		more = test_bit(FLG_TRANSPARENT, &hscx->bch.Flags) ? 1 : 0;
98162306a36Sopenharmony_ci		if (count > hscx->fifo_size) {
98262306a36Sopenharmony_ci			count = hscx->fifo_size;
98362306a36Sopenharmony_ci			more = 1;
98462306a36Sopenharmony_ci		}
98562306a36Sopenharmony_ci		pr_debug("%s: B%1d %d/%d/%d\n", hscx->ip->name, hscx->bch.nr,
98662306a36Sopenharmony_ci			 count, hscx->bch.tx_idx, hscx->bch.tx_skb->len);
98762306a36Sopenharmony_ci		hscx->bch.tx_idx += count;
98862306a36Sopenharmony_ci	}
98962306a36Sopenharmony_ci	if (hscx->ip->type & IPAC_TYPE_IPACX)
99062306a36Sopenharmony_ci		hscx->ip->write_fifo(hscx->ip->hw,
99162306a36Sopenharmony_ci				     hscx->off + IPACX_XFIFOB, p, count);
99262306a36Sopenharmony_ci	else {
99362306a36Sopenharmony_ci		waitforXFW(hscx);
99462306a36Sopenharmony_ci		hscx->ip->write_fifo(hscx->ip->hw,
99562306a36Sopenharmony_ci				     hscx->off, p, count);
99662306a36Sopenharmony_ci	}
99762306a36Sopenharmony_ci	hscx_cmdr(hscx, more ? 0x08 : 0x0a);
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	if (hscx->bch.tx_skb && (hscx->bch.debug & DEBUG_HW_BFIFO)) {
100062306a36Sopenharmony_ci		snprintf(hscx->log, 64, "B%1d-send %s %d ",
100162306a36Sopenharmony_ci			 hscx->bch.nr, hscx->ip->name, count);
100262306a36Sopenharmony_ci		print_hex_dump_bytes(hscx->log, DUMP_PREFIX_OFFSET, p, count);
100362306a36Sopenharmony_ci	}
100462306a36Sopenharmony_ci}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_cistatic void
100762306a36Sopenharmony_cihscx_xpr(struct hscx_hw *hx)
100862306a36Sopenharmony_ci{
100962306a36Sopenharmony_ci	if (hx->bch.tx_skb && hx->bch.tx_idx < hx->bch.tx_skb->len) {
101062306a36Sopenharmony_ci		hscx_fill_fifo(hx);
101162306a36Sopenharmony_ci	} else {
101262306a36Sopenharmony_ci		dev_kfree_skb(hx->bch.tx_skb);
101362306a36Sopenharmony_ci		if (get_next_bframe(&hx->bch)) {
101462306a36Sopenharmony_ci			hscx_fill_fifo(hx);
101562306a36Sopenharmony_ci			test_and_clear_bit(FLG_TX_EMPTY, &hx->bch.Flags);
101662306a36Sopenharmony_ci		} else if (test_bit(FLG_TX_EMPTY, &hx->bch.Flags)) {
101762306a36Sopenharmony_ci			hscx_fill_fifo(hx);
101862306a36Sopenharmony_ci		}
101962306a36Sopenharmony_ci	}
102062306a36Sopenharmony_ci}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_cistatic void
102362306a36Sopenharmony_ciipac_rme(struct hscx_hw *hx)
102462306a36Sopenharmony_ci{
102562306a36Sopenharmony_ci	int count;
102662306a36Sopenharmony_ci	u8 rstab;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	if (hx->ip->type & IPAC_TYPE_IPACX)
102962306a36Sopenharmony_ci		rstab = ReadHSCX(hx, IPACX_RSTAB);
103062306a36Sopenharmony_ci	else
103162306a36Sopenharmony_ci		rstab = ReadHSCX(hx, IPAC_RSTAB);
103262306a36Sopenharmony_ci	pr_debug("%s: B%1d RSTAB %02x\n", hx->ip->name, hx->bch.nr, rstab);
103362306a36Sopenharmony_ci	if ((rstab & 0xf0) != 0xa0) {
103462306a36Sopenharmony_ci		/* !(VFR && !RDO && CRC && !RAB) */
103562306a36Sopenharmony_ci		if (!(rstab & 0x80)) {
103662306a36Sopenharmony_ci			if (hx->bch.debug & DEBUG_HW_BCHANNEL)
103762306a36Sopenharmony_ci				pr_notice("%s: B%1d invalid frame\n",
103862306a36Sopenharmony_ci					  hx->ip->name, hx->bch.nr);
103962306a36Sopenharmony_ci		}
104062306a36Sopenharmony_ci		if (rstab & 0x40) {
104162306a36Sopenharmony_ci			if (hx->bch.debug & DEBUG_HW_BCHANNEL)
104262306a36Sopenharmony_ci				pr_notice("%s: B%1d RDO proto=%x\n",
104362306a36Sopenharmony_ci					  hx->ip->name, hx->bch.nr,
104462306a36Sopenharmony_ci					  hx->bch.state);
104562306a36Sopenharmony_ci		}
104662306a36Sopenharmony_ci		if (!(rstab & 0x20)) {
104762306a36Sopenharmony_ci			if (hx->bch.debug & DEBUG_HW_BCHANNEL)
104862306a36Sopenharmony_ci				pr_notice("%s: B%1d CRC error\n",
104962306a36Sopenharmony_ci					  hx->ip->name, hx->bch.nr);
105062306a36Sopenharmony_ci		}
105162306a36Sopenharmony_ci		hscx_cmdr(hx, 0x80); /* Do RMC */
105262306a36Sopenharmony_ci		return;
105362306a36Sopenharmony_ci	}
105462306a36Sopenharmony_ci	if (hx->ip->type & IPAC_TYPE_IPACX)
105562306a36Sopenharmony_ci		count = ReadHSCX(hx, IPACX_RBCLB);
105662306a36Sopenharmony_ci	else
105762306a36Sopenharmony_ci		count = ReadHSCX(hx, IPAC_RBCLB);
105862306a36Sopenharmony_ci	count &= (hx->fifo_size - 1);
105962306a36Sopenharmony_ci	if (count == 0)
106062306a36Sopenharmony_ci		count = hx->fifo_size;
106162306a36Sopenharmony_ci	hscx_empty_fifo(hx, count);
106262306a36Sopenharmony_ci	if (!hx->bch.rx_skb)
106362306a36Sopenharmony_ci		return;
106462306a36Sopenharmony_ci	if (hx->bch.rx_skb->len < 2) {
106562306a36Sopenharmony_ci		pr_debug("%s: B%1d frame too short %d\n",
106662306a36Sopenharmony_ci			 hx->ip->name, hx->bch.nr, hx->bch.rx_skb->len);
106762306a36Sopenharmony_ci		skb_trim(hx->bch.rx_skb, 0);
106862306a36Sopenharmony_ci	} else {
106962306a36Sopenharmony_ci		skb_trim(hx->bch.rx_skb, hx->bch.rx_skb->len - 1);
107062306a36Sopenharmony_ci		recv_Bchannel(&hx->bch, 0, false);
107162306a36Sopenharmony_ci	}
107262306a36Sopenharmony_ci}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_cistatic void
107562306a36Sopenharmony_ciipac_irq(struct hscx_hw *hx, u8 ista)
107662306a36Sopenharmony_ci{
107762306a36Sopenharmony_ci	u8 istab, m, exirb = 0;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	if (hx->ip->type & IPAC_TYPE_IPACX)
108062306a36Sopenharmony_ci		istab = ReadHSCX(hx, IPACX_ISTAB);
108162306a36Sopenharmony_ci	else if (hx->ip->type & IPAC_TYPE_IPAC) {
108262306a36Sopenharmony_ci		istab = ReadHSCX(hx, IPAC_ISTAB);
108362306a36Sopenharmony_ci		m = (hx->bch.nr & 1) ? IPAC__EXA : IPAC__EXB;
108462306a36Sopenharmony_ci		if (m & ista) {
108562306a36Sopenharmony_ci			exirb = ReadHSCX(hx, IPAC_EXIRB);
108662306a36Sopenharmony_ci			pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name,
108762306a36Sopenharmony_ci				 hx->bch.nr, exirb);
108862306a36Sopenharmony_ci		}
108962306a36Sopenharmony_ci	} else if (hx->bch.nr & 2) { /* HSCX B */
109062306a36Sopenharmony_ci		if (ista & (HSCX__EXA | HSCX__ICA))
109162306a36Sopenharmony_ci			ipac_irq(&hx->ip->hscx[0], ista);
109262306a36Sopenharmony_ci		if (ista & HSCX__EXB) {
109362306a36Sopenharmony_ci			exirb = ReadHSCX(hx, IPAC_EXIRB);
109462306a36Sopenharmony_ci			pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name,
109562306a36Sopenharmony_ci				 hx->bch.nr, exirb);
109662306a36Sopenharmony_ci		}
109762306a36Sopenharmony_ci		istab = ista & 0xF8;
109862306a36Sopenharmony_ci	} else { /* HSCX A */
109962306a36Sopenharmony_ci		istab = ReadHSCX(hx, IPAC_ISTAB);
110062306a36Sopenharmony_ci		if (ista & HSCX__EXA) {
110162306a36Sopenharmony_ci			exirb = ReadHSCX(hx, IPAC_EXIRB);
110262306a36Sopenharmony_ci			pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name,
110362306a36Sopenharmony_ci				 hx->bch.nr, exirb);
110462306a36Sopenharmony_ci		}
110562306a36Sopenharmony_ci		istab = istab & 0xF8;
110662306a36Sopenharmony_ci	}
110762306a36Sopenharmony_ci	if (exirb & IPAC_B_XDU)
110862306a36Sopenharmony_ci		istab |= IPACX_B_XDU;
110962306a36Sopenharmony_ci	if (exirb & IPAC_B_RFO)
111062306a36Sopenharmony_ci		istab |= IPACX_B_RFO;
111162306a36Sopenharmony_ci	pr_debug("%s: B%1d ISTAB %02x\n", hx->ip->name, hx->bch.nr, istab);
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	if (!test_bit(FLG_ACTIVE, &hx->bch.Flags))
111462306a36Sopenharmony_ci		return;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	if (istab & IPACX_B_RME)
111762306a36Sopenharmony_ci		ipac_rme(hx);
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	if (istab & IPACX_B_RPF) {
112062306a36Sopenharmony_ci		hscx_empty_fifo(hx, hx->fifo_size);
112162306a36Sopenharmony_ci		if (test_bit(FLG_TRANSPARENT, &hx->bch.Flags))
112262306a36Sopenharmony_ci			recv_Bchannel(&hx->bch, 0, false);
112362306a36Sopenharmony_ci	}
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	if (istab & IPACX_B_RFO) {
112662306a36Sopenharmony_ci		pr_debug("%s: B%1d RFO error\n", hx->ip->name, hx->bch.nr);
112762306a36Sopenharmony_ci		hscx_cmdr(hx, 0x40);	/* RRES */
112862306a36Sopenharmony_ci	}
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	if (istab & IPACX_B_XPR)
113162306a36Sopenharmony_ci		hscx_xpr(hx);
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	if (istab & IPACX_B_XDU) {
113462306a36Sopenharmony_ci		if (test_bit(FLG_TRANSPARENT, &hx->bch.Flags)) {
113562306a36Sopenharmony_ci			if (test_bit(FLG_FILLEMPTY, &hx->bch.Flags))
113662306a36Sopenharmony_ci				test_and_set_bit(FLG_TX_EMPTY, &hx->bch.Flags);
113762306a36Sopenharmony_ci			hscx_xpr(hx);
113862306a36Sopenharmony_ci			return;
113962306a36Sopenharmony_ci		}
114062306a36Sopenharmony_ci		pr_debug("%s: B%1d XDU error at len %d\n", hx->ip->name,
114162306a36Sopenharmony_ci			 hx->bch.nr, hx->bch.tx_idx);
114262306a36Sopenharmony_ci		hx->bch.tx_idx = 0;
114362306a36Sopenharmony_ci		hscx_cmdr(hx, 0x01);	/* XRES */
114462306a36Sopenharmony_ci	}
114562306a36Sopenharmony_ci}
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ciirqreturn_t
114862306a36Sopenharmony_cimISDNipac_irq(struct ipac_hw *ipac, int maxloop)
114962306a36Sopenharmony_ci{
115062306a36Sopenharmony_ci	int cnt = maxloop + 1;
115162306a36Sopenharmony_ci	u8 ista, istad;
115262306a36Sopenharmony_ci	struct isac_hw  *isac = &ipac->isac;
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	if (ipac->type & IPAC_TYPE_IPACX) {
115562306a36Sopenharmony_ci		ista = ReadIPAC(ipac, ISACX_ISTA);
115662306a36Sopenharmony_ci		while (ista && --cnt) {
115762306a36Sopenharmony_ci			pr_debug("%s: ISTA %02x\n", ipac->name, ista);
115862306a36Sopenharmony_ci			if (ista & IPACX__ICA)
115962306a36Sopenharmony_ci				ipac_irq(&ipac->hscx[0], ista);
116062306a36Sopenharmony_ci			if (ista & IPACX__ICB)
116162306a36Sopenharmony_ci				ipac_irq(&ipac->hscx[1], ista);
116262306a36Sopenharmony_ci			if (ista & (ISACX__ICD | ISACX__CIC))
116362306a36Sopenharmony_ci				mISDNisac_irq(&ipac->isac, ista);
116462306a36Sopenharmony_ci			ista = ReadIPAC(ipac, ISACX_ISTA);
116562306a36Sopenharmony_ci		}
116662306a36Sopenharmony_ci	} else if (ipac->type & IPAC_TYPE_IPAC) {
116762306a36Sopenharmony_ci		ista = ReadIPAC(ipac, IPAC_ISTA);
116862306a36Sopenharmony_ci		while (ista && --cnt) {
116962306a36Sopenharmony_ci			pr_debug("%s: ISTA %02x\n", ipac->name, ista);
117062306a36Sopenharmony_ci			if (ista & (IPAC__ICD | IPAC__EXD)) {
117162306a36Sopenharmony_ci				istad = ReadISAC(isac, ISAC_ISTA);
117262306a36Sopenharmony_ci				pr_debug("%s: ISTAD %02x\n", ipac->name, istad);
117362306a36Sopenharmony_ci				if (istad & IPAC_D_TIN2)
117462306a36Sopenharmony_ci					pr_debug("%s TIN2 irq\n", ipac->name);
117562306a36Sopenharmony_ci				if (ista & IPAC__EXD)
117662306a36Sopenharmony_ci					istad |= 1; /* ISAC EXI */
117762306a36Sopenharmony_ci				mISDNisac_irq(isac, istad);
117862306a36Sopenharmony_ci			}
117962306a36Sopenharmony_ci			if (ista & (IPAC__ICA | IPAC__EXA))
118062306a36Sopenharmony_ci				ipac_irq(&ipac->hscx[0], ista);
118162306a36Sopenharmony_ci			if (ista & (IPAC__ICB | IPAC__EXB))
118262306a36Sopenharmony_ci				ipac_irq(&ipac->hscx[1], ista);
118362306a36Sopenharmony_ci			ista = ReadIPAC(ipac, IPAC_ISTA);
118462306a36Sopenharmony_ci		}
118562306a36Sopenharmony_ci	} else if (ipac->type & IPAC_TYPE_HSCX) {
118662306a36Sopenharmony_ci		while (--cnt) {
118762306a36Sopenharmony_ci			ista = ReadIPAC(ipac, IPAC_ISTAB + ipac->hscx[1].off);
118862306a36Sopenharmony_ci			pr_debug("%s: B2 ISTA %02x\n", ipac->name, ista);
118962306a36Sopenharmony_ci			if (ista)
119062306a36Sopenharmony_ci				ipac_irq(&ipac->hscx[1], ista);
119162306a36Sopenharmony_ci			istad = ReadISAC(isac, ISAC_ISTA);
119262306a36Sopenharmony_ci			pr_debug("%s: ISTAD %02x\n", ipac->name, istad);
119362306a36Sopenharmony_ci			if (istad)
119462306a36Sopenharmony_ci				mISDNisac_irq(isac, istad);
119562306a36Sopenharmony_ci			if (0 == (ista | istad))
119662306a36Sopenharmony_ci				break;
119762306a36Sopenharmony_ci		}
119862306a36Sopenharmony_ci	}
119962306a36Sopenharmony_ci	if (cnt > maxloop) /* only for ISAC/HSCX without PCI IRQ test */
120062306a36Sopenharmony_ci		return IRQ_NONE;
120162306a36Sopenharmony_ci	if (cnt < maxloop)
120262306a36Sopenharmony_ci		pr_debug("%s: %d irqloops cpu%d\n", ipac->name,
120362306a36Sopenharmony_ci			 maxloop - cnt, smp_processor_id());
120462306a36Sopenharmony_ci	if (maxloop && !cnt)
120562306a36Sopenharmony_ci		pr_notice("%s: %d IRQ LOOP cpu%d\n", ipac->name,
120662306a36Sopenharmony_ci			  maxloop, smp_processor_id());
120762306a36Sopenharmony_ci	return IRQ_HANDLED;
120862306a36Sopenharmony_ci}
120962306a36Sopenharmony_ciEXPORT_SYMBOL(mISDNipac_irq);
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_cistatic int
121262306a36Sopenharmony_cihscx_mode(struct hscx_hw *hscx, u32 bprotocol)
121362306a36Sopenharmony_ci{
121462306a36Sopenharmony_ci	pr_debug("%s: HSCX %c protocol %x-->%x ch %d\n", hscx->ip->name,
121562306a36Sopenharmony_ci		 '@' + hscx->bch.nr, hscx->bch.state, bprotocol, hscx->bch.nr);
121662306a36Sopenharmony_ci	if (hscx->ip->type & IPAC_TYPE_IPACX) {
121762306a36Sopenharmony_ci		if (hscx->bch.nr & 1) { /* B1 and ICA */
121862306a36Sopenharmony_ci			WriteIPAC(hscx->ip, ISACX_BCHA_TSDP_BC1, 0x80);
121962306a36Sopenharmony_ci			WriteIPAC(hscx->ip, ISACX_BCHA_CR, 0x88);
122062306a36Sopenharmony_ci		} else { /* B2 and ICB */
122162306a36Sopenharmony_ci			WriteIPAC(hscx->ip, ISACX_BCHB_TSDP_BC1, 0x81);
122262306a36Sopenharmony_ci			WriteIPAC(hscx->ip, ISACX_BCHB_CR, 0x88);
122362306a36Sopenharmony_ci		}
122462306a36Sopenharmony_ci		switch (bprotocol) {
122562306a36Sopenharmony_ci		case ISDN_P_NONE: /* init */
122662306a36Sopenharmony_ci			WriteHSCX(hscx, IPACX_MODEB, 0xC0);	/* rec off */
122762306a36Sopenharmony_ci			WriteHSCX(hscx, IPACX_EXMB,  0x30);	/* std adj. */
122862306a36Sopenharmony_ci			WriteHSCX(hscx, IPACX_MASKB, 0xFF);	/* ints off */
122962306a36Sopenharmony_ci			hscx_cmdr(hscx, 0x41);
123062306a36Sopenharmony_ci			test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags);
123162306a36Sopenharmony_ci			test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
123262306a36Sopenharmony_ci			break;
123362306a36Sopenharmony_ci		case ISDN_P_B_RAW:
123462306a36Sopenharmony_ci			WriteHSCX(hscx, IPACX_MODEB, 0x88);	/* ex trans */
123562306a36Sopenharmony_ci			WriteHSCX(hscx, IPACX_EXMB,  0x00);	/* trans */
123662306a36Sopenharmony_ci			hscx_cmdr(hscx, 0x41);
123762306a36Sopenharmony_ci			WriteHSCX(hscx, IPACX_MASKB, IPACX_B_ON);
123862306a36Sopenharmony_ci			test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
123962306a36Sopenharmony_ci			break;
124062306a36Sopenharmony_ci		case ISDN_P_B_HDLC:
124162306a36Sopenharmony_ci			WriteHSCX(hscx, IPACX_MODEB, 0xC0);	/* trans */
124262306a36Sopenharmony_ci			WriteHSCX(hscx, IPACX_EXMB,  0x00);	/* hdlc,crc */
124362306a36Sopenharmony_ci			hscx_cmdr(hscx, 0x41);
124462306a36Sopenharmony_ci			WriteHSCX(hscx, IPACX_MASKB, IPACX_B_ON);
124562306a36Sopenharmony_ci			test_and_set_bit(FLG_HDLC, &hscx->bch.Flags);
124662306a36Sopenharmony_ci			break;
124762306a36Sopenharmony_ci		default:
124862306a36Sopenharmony_ci			pr_info("%s: protocol not known %x\n", hscx->ip->name,
124962306a36Sopenharmony_ci				bprotocol);
125062306a36Sopenharmony_ci			return -ENOPROTOOPT;
125162306a36Sopenharmony_ci		}
125262306a36Sopenharmony_ci	} else if (hscx->ip->type & IPAC_TYPE_IPAC) { /* IPAC */
125362306a36Sopenharmony_ci		WriteHSCX(hscx, IPAC_CCR1, 0x82);
125462306a36Sopenharmony_ci		WriteHSCX(hscx, IPAC_CCR2, 0x30);
125562306a36Sopenharmony_ci		WriteHSCX(hscx, IPAC_XCCR, 0x07);
125662306a36Sopenharmony_ci		WriteHSCX(hscx, IPAC_RCCR, 0x07);
125762306a36Sopenharmony_ci		WriteHSCX(hscx, IPAC_TSAX, hscx->slot);
125862306a36Sopenharmony_ci		WriteHSCX(hscx, IPAC_TSAR, hscx->slot);
125962306a36Sopenharmony_ci		switch (bprotocol) {
126062306a36Sopenharmony_ci		case ISDN_P_NONE:
126162306a36Sopenharmony_ci			WriteHSCX(hscx, IPAC_TSAX, 0x1F);
126262306a36Sopenharmony_ci			WriteHSCX(hscx, IPAC_TSAR, 0x1F);
126362306a36Sopenharmony_ci			WriteHSCX(hscx, IPAC_MODEB, 0x84);
126462306a36Sopenharmony_ci			WriteHSCX(hscx, IPAC_CCR1, 0x82);
126562306a36Sopenharmony_ci			WriteHSCX(hscx, IPAC_MASKB, 0xFF);	/* ints off */
126662306a36Sopenharmony_ci			test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags);
126762306a36Sopenharmony_ci			test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
126862306a36Sopenharmony_ci			break;
126962306a36Sopenharmony_ci		case ISDN_P_B_RAW:
127062306a36Sopenharmony_ci			WriteHSCX(hscx, IPAC_MODEB, 0xe4);	/* ex trans */
127162306a36Sopenharmony_ci			WriteHSCX(hscx, IPAC_CCR1, 0x82);
127262306a36Sopenharmony_ci			hscx_cmdr(hscx, 0x41);
127362306a36Sopenharmony_ci			WriteHSCX(hscx, IPAC_MASKB, 0);
127462306a36Sopenharmony_ci			test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
127562306a36Sopenharmony_ci			break;
127662306a36Sopenharmony_ci		case ISDN_P_B_HDLC:
127762306a36Sopenharmony_ci			WriteHSCX(hscx, IPAC_MODEB, 0x8c);
127862306a36Sopenharmony_ci			WriteHSCX(hscx, IPAC_CCR1, 0x8a);
127962306a36Sopenharmony_ci			hscx_cmdr(hscx, 0x41);
128062306a36Sopenharmony_ci			WriteHSCX(hscx, IPAC_MASKB, 0);
128162306a36Sopenharmony_ci			test_and_set_bit(FLG_HDLC, &hscx->bch.Flags);
128262306a36Sopenharmony_ci			break;
128362306a36Sopenharmony_ci		default:
128462306a36Sopenharmony_ci			pr_info("%s: protocol not known %x\n", hscx->ip->name,
128562306a36Sopenharmony_ci				bprotocol);
128662306a36Sopenharmony_ci			return -ENOPROTOOPT;
128762306a36Sopenharmony_ci		}
128862306a36Sopenharmony_ci	} else if (hscx->ip->type & IPAC_TYPE_HSCX) { /* HSCX */
128962306a36Sopenharmony_ci		WriteHSCX(hscx, IPAC_CCR1, 0x85);
129062306a36Sopenharmony_ci		WriteHSCX(hscx, IPAC_CCR2, 0x30);
129162306a36Sopenharmony_ci		WriteHSCX(hscx, IPAC_XCCR, 0x07);
129262306a36Sopenharmony_ci		WriteHSCX(hscx, IPAC_RCCR, 0x07);
129362306a36Sopenharmony_ci		WriteHSCX(hscx, IPAC_TSAX, hscx->slot);
129462306a36Sopenharmony_ci		WriteHSCX(hscx, IPAC_TSAR, hscx->slot);
129562306a36Sopenharmony_ci		switch (bprotocol) {
129662306a36Sopenharmony_ci		case ISDN_P_NONE:
129762306a36Sopenharmony_ci			WriteHSCX(hscx, IPAC_TSAX, 0x1F);
129862306a36Sopenharmony_ci			WriteHSCX(hscx, IPAC_TSAR, 0x1F);
129962306a36Sopenharmony_ci			WriteHSCX(hscx, IPAC_MODEB, 0x84);
130062306a36Sopenharmony_ci			WriteHSCX(hscx, IPAC_CCR1, 0x85);
130162306a36Sopenharmony_ci			WriteHSCX(hscx, IPAC_MASKB, 0xFF);	/* ints off */
130262306a36Sopenharmony_ci			test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags);
130362306a36Sopenharmony_ci			test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
130462306a36Sopenharmony_ci			break;
130562306a36Sopenharmony_ci		case ISDN_P_B_RAW:
130662306a36Sopenharmony_ci			WriteHSCX(hscx, IPAC_MODEB, 0xe4);	/* ex trans */
130762306a36Sopenharmony_ci			WriteHSCX(hscx, IPAC_CCR1, 0x85);
130862306a36Sopenharmony_ci			hscx_cmdr(hscx, 0x41);
130962306a36Sopenharmony_ci			WriteHSCX(hscx, IPAC_MASKB, 0);
131062306a36Sopenharmony_ci			test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
131162306a36Sopenharmony_ci			break;
131262306a36Sopenharmony_ci		case ISDN_P_B_HDLC:
131362306a36Sopenharmony_ci			WriteHSCX(hscx, IPAC_MODEB, 0x8c);
131462306a36Sopenharmony_ci			WriteHSCX(hscx, IPAC_CCR1, 0x8d);
131562306a36Sopenharmony_ci			hscx_cmdr(hscx, 0x41);
131662306a36Sopenharmony_ci			WriteHSCX(hscx, IPAC_MASKB, 0);
131762306a36Sopenharmony_ci			test_and_set_bit(FLG_HDLC, &hscx->bch.Flags);
131862306a36Sopenharmony_ci			break;
131962306a36Sopenharmony_ci		default:
132062306a36Sopenharmony_ci			pr_info("%s: protocol not known %x\n", hscx->ip->name,
132162306a36Sopenharmony_ci				bprotocol);
132262306a36Sopenharmony_ci			return -ENOPROTOOPT;
132362306a36Sopenharmony_ci		}
132462306a36Sopenharmony_ci	} else
132562306a36Sopenharmony_ci		return -EINVAL;
132662306a36Sopenharmony_ci	hscx->bch.state = bprotocol;
132762306a36Sopenharmony_ci	return 0;
132862306a36Sopenharmony_ci}
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_cistatic int
133162306a36Sopenharmony_cihscx_l2l1(struct mISDNchannel *ch, struct sk_buff *skb)
133262306a36Sopenharmony_ci{
133362306a36Sopenharmony_ci	struct bchannel *bch = container_of(ch, struct bchannel, ch);
133462306a36Sopenharmony_ci	struct hscx_hw	*hx = container_of(bch, struct hscx_hw, bch);
133562306a36Sopenharmony_ci	int ret = -EINVAL;
133662306a36Sopenharmony_ci	struct mISDNhead *hh = mISDN_HEAD_P(skb);
133762306a36Sopenharmony_ci	unsigned long flags;
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	switch (hh->prim) {
134062306a36Sopenharmony_ci	case PH_DATA_REQ:
134162306a36Sopenharmony_ci		spin_lock_irqsave(hx->ip->hwlock, flags);
134262306a36Sopenharmony_ci		ret = bchannel_senddata(bch, skb);
134362306a36Sopenharmony_ci		if (ret > 0) { /* direct TX */
134462306a36Sopenharmony_ci			ret = 0;
134562306a36Sopenharmony_ci			hscx_fill_fifo(hx);
134662306a36Sopenharmony_ci		}
134762306a36Sopenharmony_ci		spin_unlock_irqrestore(hx->ip->hwlock, flags);
134862306a36Sopenharmony_ci		return ret;
134962306a36Sopenharmony_ci	case PH_ACTIVATE_REQ:
135062306a36Sopenharmony_ci		spin_lock_irqsave(hx->ip->hwlock, flags);
135162306a36Sopenharmony_ci		if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
135262306a36Sopenharmony_ci			ret = hscx_mode(hx, ch->protocol);
135362306a36Sopenharmony_ci		else
135462306a36Sopenharmony_ci			ret = 0;
135562306a36Sopenharmony_ci		spin_unlock_irqrestore(hx->ip->hwlock, flags);
135662306a36Sopenharmony_ci		if (!ret)
135762306a36Sopenharmony_ci			_queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
135862306a36Sopenharmony_ci				    NULL, GFP_KERNEL);
135962306a36Sopenharmony_ci		break;
136062306a36Sopenharmony_ci	case PH_DEACTIVATE_REQ:
136162306a36Sopenharmony_ci		spin_lock_irqsave(hx->ip->hwlock, flags);
136262306a36Sopenharmony_ci		mISDN_clear_bchannel(bch);
136362306a36Sopenharmony_ci		hscx_mode(hx, ISDN_P_NONE);
136462306a36Sopenharmony_ci		spin_unlock_irqrestore(hx->ip->hwlock, flags);
136562306a36Sopenharmony_ci		_queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
136662306a36Sopenharmony_ci			    NULL, GFP_KERNEL);
136762306a36Sopenharmony_ci		ret = 0;
136862306a36Sopenharmony_ci		break;
136962306a36Sopenharmony_ci	default:
137062306a36Sopenharmony_ci		pr_info("%s: %s unknown prim(%x,%x)\n",
137162306a36Sopenharmony_ci			hx->ip->name, __func__, hh->prim, hh->id);
137262306a36Sopenharmony_ci		ret = -EINVAL;
137362306a36Sopenharmony_ci	}
137462306a36Sopenharmony_ci	if (!ret)
137562306a36Sopenharmony_ci		dev_kfree_skb(skb);
137662306a36Sopenharmony_ci	return ret;
137762306a36Sopenharmony_ci}
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_cistatic int
138062306a36Sopenharmony_cichannel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
138162306a36Sopenharmony_ci{
138262306a36Sopenharmony_ci	return mISDN_ctrl_bchannel(bch, cq);
138362306a36Sopenharmony_ci}
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_cistatic int
138662306a36Sopenharmony_cihscx_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
138762306a36Sopenharmony_ci{
138862306a36Sopenharmony_ci	struct bchannel *bch = container_of(ch, struct bchannel, ch);
138962306a36Sopenharmony_ci	struct hscx_hw	*hx = container_of(bch, struct hscx_hw, bch);
139062306a36Sopenharmony_ci	int ret = -EINVAL;
139162306a36Sopenharmony_ci	u_long flags;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	pr_debug("%s: %s cmd:%x %p\n", hx->ip->name, __func__, cmd, arg);
139462306a36Sopenharmony_ci	switch (cmd) {
139562306a36Sopenharmony_ci	case CLOSE_CHANNEL:
139662306a36Sopenharmony_ci		test_and_clear_bit(FLG_OPEN, &bch->Flags);
139762306a36Sopenharmony_ci		cancel_work_sync(&bch->workq);
139862306a36Sopenharmony_ci		spin_lock_irqsave(hx->ip->hwlock, flags);
139962306a36Sopenharmony_ci		mISDN_clear_bchannel(bch);
140062306a36Sopenharmony_ci		hscx_mode(hx, ISDN_P_NONE);
140162306a36Sopenharmony_ci		spin_unlock_irqrestore(hx->ip->hwlock, flags);
140262306a36Sopenharmony_ci		ch->protocol = ISDN_P_NONE;
140362306a36Sopenharmony_ci		ch->peer = NULL;
140462306a36Sopenharmony_ci		module_put(hx->ip->owner);
140562306a36Sopenharmony_ci		ret = 0;
140662306a36Sopenharmony_ci		break;
140762306a36Sopenharmony_ci	case CONTROL_CHANNEL:
140862306a36Sopenharmony_ci		ret = channel_bctrl(bch, arg);
140962306a36Sopenharmony_ci		break;
141062306a36Sopenharmony_ci	default:
141162306a36Sopenharmony_ci		pr_info("%s: %s unknown prim(%x)\n",
141262306a36Sopenharmony_ci			hx->ip->name, __func__, cmd);
141362306a36Sopenharmony_ci	}
141462306a36Sopenharmony_ci	return ret;
141562306a36Sopenharmony_ci}
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_cistatic void
141862306a36Sopenharmony_cifree_ipac(struct ipac_hw *ipac)
141962306a36Sopenharmony_ci{
142062306a36Sopenharmony_ci	isac_release(&ipac->isac);
142162306a36Sopenharmony_ci}
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_cistatic const char *HSCXVer[] =
142462306a36Sopenharmony_ci{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7",
142562306a36Sopenharmony_ci "?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"};
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_cistatic void
143062306a36Sopenharmony_cihscx_init(struct hscx_hw *hx)
143162306a36Sopenharmony_ci{
143262306a36Sopenharmony_ci	u8 val;
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	WriteHSCX(hx, IPAC_RAH2, 0xFF);
143562306a36Sopenharmony_ci	WriteHSCX(hx, IPAC_XBCH, 0x00);
143662306a36Sopenharmony_ci	WriteHSCX(hx, IPAC_RLCR, 0x00);
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	if (hx->ip->type & IPAC_TYPE_HSCX) {
143962306a36Sopenharmony_ci		WriteHSCX(hx, IPAC_CCR1, 0x85);
144062306a36Sopenharmony_ci		val = ReadHSCX(hx, HSCX_VSTR);
144162306a36Sopenharmony_ci		pr_debug("%s: HSCX VSTR %02x\n", hx->ip->name, val);
144262306a36Sopenharmony_ci		if (hx->bch.debug & DEBUG_HW)
144362306a36Sopenharmony_ci			pr_notice("%s: HSCX version %s\n", hx->ip->name,
144462306a36Sopenharmony_ci				  HSCXVer[val & 0x0f]);
144562306a36Sopenharmony_ci	} else
144662306a36Sopenharmony_ci		WriteHSCX(hx, IPAC_CCR1, 0x82);
144762306a36Sopenharmony_ci	WriteHSCX(hx, IPAC_CCR2, 0x30);
144862306a36Sopenharmony_ci	WriteHSCX(hx, IPAC_XCCR, 0x07);
144962306a36Sopenharmony_ci	WriteHSCX(hx, IPAC_RCCR, 0x07);
145062306a36Sopenharmony_ci}
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_cistatic int
145362306a36Sopenharmony_ciipac_init(struct ipac_hw *ipac)
145462306a36Sopenharmony_ci{
145562306a36Sopenharmony_ci	u8 val;
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	if (ipac->type & IPAC_TYPE_HSCX) {
145862306a36Sopenharmony_ci		hscx_init(&ipac->hscx[0]);
145962306a36Sopenharmony_ci		hscx_init(&ipac->hscx[1]);
146062306a36Sopenharmony_ci		val = ReadIPAC(ipac, IPAC_ID);
146162306a36Sopenharmony_ci	} else if (ipac->type & IPAC_TYPE_IPAC) {
146262306a36Sopenharmony_ci		hscx_init(&ipac->hscx[0]);
146362306a36Sopenharmony_ci		hscx_init(&ipac->hscx[1]);
146462306a36Sopenharmony_ci		WriteIPAC(ipac, IPAC_MASK, IPAC__ON);
146562306a36Sopenharmony_ci		val = ReadIPAC(ipac, IPAC_CONF);
146662306a36Sopenharmony_ci		/* conf is default 0, but can be overwritten by card setup */
146762306a36Sopenharmony_ci		pr_debug("%s: IPAC CONF %02x/%02x\n", ipac->name,
146862306a36Sopenharmony_ci			 val, ipac->conf);
146962306a36Sopenharmony_ci		WriteIPAC(ipac, IPAC_CONF, ipac->conf);
147062306a36Sopenharmony_ci		val = ReadIPAC(ipac, IPAC_ID);
147162306a36Sopenharmony_ci		if (ipac->hscx[0].bch.debug & DEBUG_HW)
147262306a36Sopenharmony_ci			pr_notice("%s: IPAC Design ID %02x\n", ipac->name, val);
147362306a36Sopenharmony_ci	}
147462306a36Sopenharmony_ci	/* nothing special for IPACX to do here */
147562306a36Sopenharmony_ci	return isac_init(&ipac->isac);
147662306a36Sopenharmony_ci}
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_cistatic int
147962306a36Sopenharmony_ciopen_bchannel(struct ipac_hw *ipac, struct channel_req *rq)
148062306a36Sopenharmony_ci{
148162306a36Sopenharmony_ci	struct bchannel		*bch;
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	if (rq->adr.channel == 0 || rq->adr.channel > 2)
148462306a36Sopenharmony_ci		return -EINVAL;
148562306a36Sopenharmony_ci	if (rq->protocol == ISDN_P_NONE)
148662306a36Sopenharmony_ci		return -EINVAL;
148762306a36Sopenharmony_ci	bch = &ipac->hscx[rq->adr.channel - 1].bch;
148862306a36Sopenharmony_ci	if (test_and_set_bit(FLG_OPEN, &bch->Flags))
148962306a36Sopenharmony_ci		return -EBUSY; /* b-channel can be only open once */
149062306a36Sopenharmony_ci	test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);
149162306a36Sopenharmony_ci	bch->ch.protocol = rq->protocol;
149262306a36Sopenharmony_ci	rq->ch = &bch->ch;
149362306a36Sopenharmony_ci	return 0;
149462306a36Sopenharmony_ci}
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_cistatic int
149762306a36Sopenharmony_cichannel_ctrl(struct ipac_hw *ipac, struct mISDN_ctrl_req *cq)
149862306a36Sopenharmony_ci{
149962306a36Sopenharmony_ci	int	ret = 0;
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	switch (cq->op) {
150262306a36Sopenharmony_ci	case MISDN_CTRL_GETOP:
150362306a36Sopenharmony_ci		cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_L1_TIMER3;
150462306a36Sopenharmony_ci		break;
150562306a36Sopenharmony_ci	case MISDN_CTRL_LOOP:
150662306a36Sopenharmony_ci		/* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */
150762306a36Sopenharmony_ci		if (cq->channel < 0 || cq->channel > 3) {
150862306a36Sopenharmony_ci			ret = -EINVAL;
150962306a36Sopenharmony_ci			break;
151062306a36Sopenharmony_ci		}
151162306a36Sopenharmony_ci		ret = ipac->ctrl(ipac, HW_TESTLOOP, cq->channel);
151262306a36Sopenharmony_ci		break;
151362306a36Sopenharmony_ci	case MISDN_CTRL_L1_TIMER3:
151462306a36Sopenharmony_ci		ret = ipac->isac.ctrl(&ipac->isac, HW_TIMER3_VALUE, cq->p1);
151562306a36Sopenharmony_ci		break;
151662306a36Sopenharmony_ci	default:
151762306a36Sopenharmony_ci		pr_info("%s: unknown CTRL OP %x\n", ipac->name, cq->op);
151862306a36Sopenharmony_ci		ret = -EINVAL;
151962306a36Sopenharmony_ci		break;
152062306a36Sopenharmony_ci	}
152162306a36Sopenharmony_ci	return ret;
152262306a36Sopenharmony_ci}
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_cistatic int
152562306a36Sopenharmony_ciipac_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
152662306a36Sopenharmony_ci{
152762306a36Sopenharmony_ci	struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D);
152862306a36Sopenharmony_ci	struct dchannel *dch = container_of(dev, struct dchannel, dev);
152962306a36Sopenharmony_ci	struct isac_hw *isac = container_of(dch, struct isac_hw, dch);
153062306a36Sopenharmony_ci	struct ipac_hw *ipac = container_of(isac, struct ipac_hw, isac);
153162306a36Sopenharmony_ci	struct channel_req *rq;
153262306a36Sopenharmony_ci	int err = 0;
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	pr_debug("%s: DCTRL: %x %p\n", ipac->name, cmd, arg);
153562306a36Sopenharmony_ci	switch (cmd) {
153662306a36Sopenharmony_ci	case OPEN_CHANNEL:
153762306a36Sopenharmony_ci		rq = arg;
153862306a36Sopenharmony_ci		if (rq->protocol == ISDN_P_TE_S0)
153962306a36Sopenharmony_ci			err = open_dchannel_caller(isac, rq, __builtin_return_address(0));
154062306a36Sopenharmony_ci		else
154162306a36Sopenharmony_ci			err = open_bchannel(ipac, rq);
154262306a36Sopenharmony_ci		if (err)
154362306a36Sopenharmony_ci			break;
154462306a36Sopenharmony_ci		if (!try_module_get(ipac->owner))
154562306a36Sopenharmony_ci			pr_info("%s: cannot get module\n", ipac->name);
154662306a36Sopenharmony_ci		break;
154762306a36Sopenharmony_ci	case CLOSE_CHANNEL:
154862306a36Sopenharmony_ci		pr_debug("%s: dev(%d) close from %p\n", ipac->name,
154962306a36Sopenharmony_ci			 dch->dev.id, __builtin_return_address(0));
155062306a36Sopenharmony_ci		module_put(ipac->owner);
155162306a36Sopenharmony_ci		break;
155262306a36Sopenharmony_ci	case CONTROL_CHANNEL:
155362306a36Sopenharmony_ci		err = channel_ctrl(ipac, arg);
155462306a36Sopenharmony_ci		break;
155562306a36Sopenharmony_ci	default:
155662306a36Sopenharmony_ci		pr_debug("%s: unknown DCTRL command %x\n", ipac->name, cmd);
155762306a36Sopenharmony_ci		return -EINVAL;
155862306a36Sopenharmony_ci	}
155962306a36Sopenharmony_ci	return err;
156062306a36Sopenharmony_ci}
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ciu32
156362306a36Sopenharmony_cimISDNipac_init(struct ipac_hw *ipac, void *hw)
156462306a36Sopenharmony_ci{
156562306a36Sopenharmony_ci	u32 ret;
156662306a36Sopenharmony_ci	u8 i;
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	ipac->hw = hw;
156962306a36Sopenharmony_ci	if (ipac->isac.dch.debug & DEBUG_HW)
157062306a36Sopenharmony_ci		pr_notice("%s: ipac type %x\n", ipac->name, ipac->type);
157162306a36Sopenharmony_ci	if (ipac->type & IPAC_TYPE_HSCX) {
157262306a36Sopenharmony_ci		ipac->isac.type = IPAC_TYPE_ISAC;
157362306a36Sopenharmony_ci		ipac->hscx[0].off = 0;
157462306a36Sopenharmony_ci		ipac->hscx[1].off = 0x40;
157562306a36Sopenharmony_ci		ipac->hscx[0].fifo_size = 32;
157662306a36Sopenharmony_ci		ipac->hscx[1].fifo_size = 32;
157762306a36Sopenharmony_ci	} else if (ipac->type & IPAC_TYPE_IPAC) {
157862306a36Sopenharmony_ci		ipac->isac.type = IPAC_TYPE_IPAC | IPAC_TYPE_ISAC;
157962306a36Sopenharmony_ci		ipac->hscx[0].off = 0;
158062306a36Sopenharmony_ci		ipac->hscx[1].off = 0x40;
158162306a36Sopenharmony_ci		ipac->hscx[0].fifo_size = 64;
158262306a36Sopenharmony_ci		ipac->hscx[1].fifo_size = 64;
158362306a36Sopenharmony_ci	} else if (ipac->type & IPAC_TYPE_IPACX) {
158462306a36Sopenharmony_ci		ipac->isac.type = IPAC_TYPE_IPACX | IPAC_TYPE_ISACX;
158562306a36Sopenharmony_ci		ipac->hscx[0].off = IPACX_OFF_ICA;
158662306a36Sopenharmony_ci		ipac->hscx[1].off = IPACX_OFF_ICB;
158762306a36Sopenharmony_ci		ipac->hscx[0].fifo_size = 64;
158862306a36Sopenharmony_ci		ipac->hscx[1].fifo_size = 64;
158962306a36Sopenharmony_ci	} else
159062306a36Sopenharmony_ci		return 0;
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	mISDNisac_init(&ipac->isac, hw);
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci	ipac->isac.dch.dev.D.ctrl = ipac_dctrl;
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	for (i = 0; i < 2; i++) {
159762306a36Sopenharmony_ci		ipac->hscx[i].bch.nr = i + 1;
159862306a36Sopenharmony_ci		set_channelmap(i + 1, ipac->isac.dch.dev.channelmap);
159962306a36Sopenharmony_ci		list_add(&ipac->hscx[i].bch.ch.list,
160062306a36Sopenharmony_ci			 &ipac->isac.dch.dev.bchannels);
160162306a36Sopenharmony_ci		mISDN_initbchannel(&ipac->hscx[i].bch, MAX_DATA_MEM,
160262306a36Sopenharmony_ci				   ipac->hscx[i].fifo_size);
160362306a36Sopenharmony_ci		ipac->hscx[i].bch.ch.nr = i + 1;
160462306a36Sopenharmony_ci		ipac->hscx[i].bch.ch.send = &hscx_l2l1;
160562306a36Sopenharmony_ci		ipac->hscx[i].bch.ch.ctrl = hscx_bctrl;
160662306a36Sopenharmony_ci		ipac->hscx[i].bch.hw = hw;
160762306a36Sopenharmony_ci		ipac->hscx[i].ip = ipac;
160862306a36Sopenharmony_ci		/* default values for IOM time slots
160962306a36Sopenharmony_ci		 * can be overwritten by card */
161062306a36Sopenharmony_ci		ipac->hscx[i].slot = (i == 0) ? 0x2f : 0x03;
161162306a36Sopenharmony_ci	}
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci	ipac->init = ipac_init;
161462306a36Sopenharmony_ci	ipac->release = free_ipac;
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci	ret =	(1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
161762306a36Sopenharmony_ci		(1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
161862306a36Sopenharmony_ci	return ret;
161962306a36Sopenharmony_ci}
162062306a36Sopenharmony_ciEXPORT_SYMBOL(mISDNipac_init);
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_cistatic int __init
162362306a36Sopenharmony_ciisac_mod_init(void)
162462306a36Sopenharmony_ci{
162562306a36Sopenharmony_ci	pr_notice("mISDNipac module version %s\n", ISAC_REV);
162662306a36Sopenharmony_ci	return 0;
162762306a36Sopenharmony_ci}
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_cistatic void __exit
163062306a36Sopenharmony_ciisac_mod_cleanup(void)
163162306a36Sopenharmony_ci{
163262306a36Sopenharmony_ci	pr_notice("mISDNipac module unloaded\n");
163362306a36Sopenharmony_ci}
163462306a36Sopenharmony_cimodule_init(isac_mod_init);
163562306a36Sopenharmony_cimodule_exit(isac_mod_cleanup);
1636