18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * isac.c   ISAC specific routines
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/irqreturn.h>
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/mISDNhw.h>
148c2ecf20Sopenharmony_ci#include "ipac.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define DBUSY_TIMER_VALUE	80
188c2ecf20Sopenharmony_ci#define ARCOFI_USE		1
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define ISAC_REV		"2.0"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ciMODULE_AUTHOR("Karsten Keil");
238c2ecf20Sopenharmony_ciMODULE_VERSION(ISAC_REV);
248c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define ReadISAC(is, o)		(is->read_reg(is->dch.hw, o + is->off))
278c2ecf20Sopenharmony_ci#define	WriteISAC(is, o, v)	(is->write_reg(is->dch.hw, o + is->off, v))
288c2ecf20Sopenharmony_ci#define ReadHSCX(h, o)		(h->ip->read_reg(h->ip->hw, h->off + o))
298c2ecf20Sopenharmony_ci#define WriteHSCX(h, o, v)	(h->ip->write_reg(h->ip->hw, h->off + o, v))
308c2ecf20Sopenharmony_ci#define ReadIPAC(ip, o)		(ip->read_reg(ip->hw, o))
318c2ecf20Sopenharmony_ci#define WriteIPAC(ip, o, v)	(ip->write_reg(ip->hw, o, v))
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic inline void
348c2ecf20Sopenharmony_ciph_command(struct isac_hw *isac, u8 command)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	pr_debug("%s: ph_command %x\n", isac->name, command);
378c2ecf20Sopenharmony_ci	if (isac->type & IPAC_TYPE_ISACX)
388c2ecf20Sopenharmony_ci		WriteISAC(isac, ISACX_CIX0, (command << 4) | 0xE);
398c2ecf20Sopenharmony_ci	else
408c2ecf20Sopenharmony_ci		WriteISAC(isac, ISAC_CIX0, (command << 2) | 3);
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic void
448c2ecf20Sopenharmony_ciisac_ph_state_change(struct isac_hw *isac)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	switch (isac->state) {
478c2ecf20Sopenharmony_ci	case (ISAC_IND_RS):
488c2ecf20Sopenharmony_ci	case (ISAC_IND_EI):
498c2ecf20Sopenharmony_ci		ph_command(isac, ISAC_CMD_DUI);
508c2ecf20Sopenharmony_ci	}
518c2ecf20Sopenharmony_ci	schedule_event(&isac->dch, FLG_PHCHANGE);
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic void
558c2ecf20Sopenharmony_ciisac_ph_state_bh(struct dchannel *dch)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	struct isac_hw *isac = container_of(dch, struct isac_hw, dch);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	switch (isac->state) {
608c2ecf20Sopenharmony_ci	case ISAC_IND_RS:
618c2ecf20Sopenharmony_ci	case ISAC_IND_EI:
628c2ecf20Sopenharmony_ci		dch->state = 0;
638c2ecf20Sopenharmony_ci		l1_event(dch->l1, HW_RESET_IND);
648c2ecf20Sopenharmony_ci		break;
658c2ecf20Sopenharmony_ci	case ISAC_IND_DID:
668c2ecf20Sopenharmony_ci		dch->state = 3;
678c2ecf20Sopenharmony_ci		l1_event(dch->l1, HW_DEACT_CNF);
688c2ecf20Sopenharmony_ci		break;
698c2ecf20Sopenharmony_ci	case ISAC_IND_DR:
708c2ecf20Sopenharmony_ci	case ISAC_IND_DR6:
718c2ecf20Sopenharmony_ci		dch->state = 3;
728c2ecf20Sopenharmony_ci		l1_event(dch->l1, HW_DEACT_IND);
738c2ecf20Sopenharmony_ci		break;
748c2ecf20Sopenharmony_ci	case ISAC_IND_PU:
758c2ecf20Sopenharmony_ci		dch->state = 4;
768c2ecf20Sopenharmony_ci		l1_event(dch->l1, HW_POWERUP_IND);
778c2ecf20Sopenharmony_ci		break;
788c2ecf20Sopenharmony_ci	case ISAC_IND_RSY:
798c2ecf20Sopenharmony_ci		if (dch->state <= 5) {
808c2ecf20Sopenharmony_ci			dch->state = 5;
818c2ecf20Sopenharmony_ci			l1_event(dch->l1, ANYSIGNAL);
828c2ecf20Sopenharmony_ci		} else {
838c2ecf20Sopenharmony_ci			dch->state = 8;
848c2ecf20Sopenharmony_ci			l1_event(dch->l1, LOSTFRAMING);
858c2ecf20Sopenharmony_ci		}
868c2ecf20Sopenharmony_ci		break;
878c2ecf20Sopenharmony_ci	case ISAC_IND_ARD:
888c2ecf20Sopenharmony_ci		dch->state = 6;
898c2ecf20Sopenharmony_ci		l1_event(dch->l1, INFO2);
908c2ecf20Sopenharmony_ci		break;
918c2ecf20Sopenharmony_ci	case ISAC_IND_AI8:
928c2ecf20Sopenharmony_ci		dch->state = 7;
938c2ecf20Sopenharmony_ci		l1_event(dch->l1, INFO4_P8);
948c2ecf20Sopenharmony_ci		break;
958c2ecf20Sopenharmony_ci	case ISAC_IND_AI10:
968c2ecf20Sopenharmony_ci		dch->state = 7;
978c2ecf20Sopenharmony_ci		l1_event(dch->l1, INFO4_P10);
988c2ecf20Sopenharmony_ci		break;
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci	pr_debug("%s: TE newstate %x\n", isac->name, dch->state);
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic void
1048c2ecf20Sopenharmony_ciisac_empty_fifo(struct isac_hw *isac, int count)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	u8 *ptr;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	pr_debug("%s: %s  %d\n", isac->name, __func__, count);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	if (!isac->dch.rx_skb) {
1118c2ecf20Sopenharmony_ci		isac->dch.rx_skb = mI_alloc_skb(isac->dch.maxlen, GFP_ATOMIC);
1128c2ecf20Sopenharmony_ci		if (!isac->dch.rx_skb) {
1138c2ecf20Sopenharmony_ci			pr_info("%s: D receive out of memory\n", isac->name);
1148c2ecf20Sopenharmony_ci			WriteISAC(isac, ISAC_CMDR, 0x80);
1158c2ecf20Sopenharmony_ci			return;
1168c2ecf20Sopenharmony_ci		}
1178c2ecf20Sopenharmony_ci	}
1188c2ecf20Sopenharmony_ci	if ((isac->dch.rx_skb->len + count) >= isac->dch.maxlen) {
1198c2ecf20Sopenharmony_ci		pr_debug("%s: %s overrun %d\n", isac->name, __func__,
1208c2ecf20Sopenharmony_ci			 isac->dch.rx_skb->len + count);
1218c2ecf20Sopenharmony_ci		WriteISAC(isac, ISAC_CMDR, 0x80);
1228c2ecf20Sopenharmony_ci		return;
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci	ptr = skb_put(isac->dch.rx_skb, count);
1258c2ecf20Sopenharmony_ci	isac->read_fifo(isac->dch.hw, isac->off, ptr, count);
1268c2ecf20Sopenharmony_ci	WriteISAC(isac, ISAC_CMDR, 0x80);
1278c2ecf20Sopenharmony_ci	if (isac->dch.debug & DEBUG_HW_DFIFO) {
1288c2ecf20Sopenharmony_ci		char	pfx[MISDN_MAX_IDLEN + 16];
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci		snprintf(pfx, MISDN_MAX_IDLEN + 15, "D-recv %s %d ",
1318c2ecf20Sopenharmony_ci			 isac->name, count);
1328c2ecf20Sopenharmony_ci		print_hex_dump_bytes(pfx, DUMP_PREFIX_OFFSET, ptr, count);
1338c2ecf20Sopenharmony_ci	}
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic void
1378c2ecf20Sopenharmony_ciisac_fill_fifo(struct isac_hw *isac)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	int count, more;
1408c2ecf20Sopenharmony_ci	u8 *ptr;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if (!isac->dch.tx_skb)
1438c2ecf20Sopenharmony_ci		return;
1448c2ecf20Sopenharmony_ci	count = isac->dch.tx_skb->len - isac->dch.tx_idx;
1458c2ecf20Sopenharmony_ci	if (count <= 0)
1468c2ecf20Sopenharmony_ci		return;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	more = 0;
1498c2ecf20Sopenharmony_ci	if (count > 32) {
1508c2ecf20Sopenharmony_ci		more = !0;
1518c2ecf20Sopenharmony_ci		count = 32;
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci	pr_debug("%s: %s  %d\n", isac->name, __func__, count);
1548c2ecf20Sopenharmony_ci	ptr = isac->dch.tx_skb->data + isac->dch.tx_idx;
1558c2ecf20Sopenharmony_ci	isac->dch.tx_idx += count;
1568c2ecf20Sopenharmony_ci	isac->write_fifo(isac->dch.hw, isac->off, ptr, count);
1578c2ecf20Sopenharmony_ci	WriteISAC(isac, ISAC_CMDR, more ? 0x8 : 0xa);
1588c2ecf20Sopenharmony_ci	if (test_and_set_bit(FLG_BUSY_TIMER, &isac->dch.Flags)) {
1598c2ecf20Sopenharmony_ci		pr_debug("%s: %s dbusytimer running\n", isac->name, __func__);
1608c2ecf20Sopenharmony_ci		del_timer(&isac->dch.timer);
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci	isac->dch.timer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
1638c2ecf20Sopenharmony_ci	add_timer(&isac->dch.timer);
1648c2ecf20Sopenharmony_ci	if (isac->dch.debug & DEBUG_HW_DFIFO) {
1658c2ecf20Sopenharmony_ci		char	pfx[MISDN_MAX_IDLEN + 16];
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci		snprintf(pfx, MISDN_MAX_IDLEN + 15, "D-send %s %d ",
1688c2ecf20Sopenharmony_ci			 isac->name, count);
1698c2ecf20Sopenharmony_ci		print_hex_dump_bytes(pfx, DUMP_PREFIX_OFFSET, ptr, count);
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic void
1748c2ecf20Sopenharmony_ciisac_rme_irq(struct isac_hw *isac)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	u8 val, count;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	val = ReadISAC(isac, ISAC_RSTA);
1798c2ecf20Sopenharmony_ci	if ((val & 0x70) != 0x20) {
1808c2ecf20Sopenharmony_ci		if (val & 0x40) {
1818c2ecf20Sopenharmony_ci			pr_debug("%s: ISAC RDO\n", isac->name);
1828c2ecf20Sopenharmony_ci#ifdef ERROR_STATISTIC
1838c2ecf20Sopenharmony_ci			isac->dch.err_rx++;
1848c2ecf20Sopenharmony_ci#endif
1858c2ecf20Sopenharmony_ci		}
1868c2ecf20Sopenharmony_ci		if (!(val & 0x20)) {
1878c2ecf20Sopenharmony_ci			pr_debug("%s: ISAC CRC error\n", isac->name);
1888c2ecf20Sopenharmony_ci#ifdef ERROR_STATISTIC
1898c2ecf20Sopenharmony_ci			isac->dch.err_crc++;
1908c2ecf20Sopenharmony_ci#endif
1918c2ecf20Sopenharmony_ci		}
1928c2ecf20Sopenharmony_ci		WriteISAC(isac, ISAC_CMDR, 0x80);
1938c2ecf20Sopenharmony_ci		dev_kfree_skb(isac->dch.rx_skb);
1948c2ecf20Sopenharmony_ci		isac->dch.rx_skb = NULL;
1958c2ecf20Sopenharmony_ci	} else {
1968c2ecf20Sopenharmony_ci		count = ReadISAC(isac, ISAC_RBCL) & 0x1f;
1978c2ecf20Sopenharmony_ci		if (count == 0)
1988c2ecf20Sopenharmony_ci			count = 32;
1998c2ecf20Sopenharmony_ci		isac_empty_fifo(isac, count);
2008c2ecf20Sopenharmony_ci		recv_Dchannel(&isac->dch);
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic void
2058c2ecf20Sopenharmony_ciisac_xpr_irq(struct isac_hw *isac)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	if (test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags))
2088c2ecf20Sopenharmony_ci		del_timer(&isac->dch.timer);
2098c2ecf20Sopenharmony_ci	if (isac->dch.tx_skb && isac->dch.tx_idx < isac->dch.tx_skb->len) {
2108c2ecf20Sopenharmony_ci		isac_fill_fifo(isac);
2118c2ecf20Sopenharmony_ci	} else {
2128c2ecf20Sopenharmony_ci		dev_kfree_skb(isac->dch.tx_skb);
2138c2ecf20Sopenharmony_ci		if (get_next_dframe(&isac->dch))
2148c2ecf20Sopenharmony_ci			isac_fill_fifo(isac);
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistatic void
2198c2ecf20Sopenharmony_ciisac_retransmit(struct isac_hw *isac)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	if (test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags))
2228c2ecf20Sopenharmony_ci		del_timer(&isac->dch.timer);
2238c2ecf20Sopenharmony_ci	if (test_bit(FLG_TX_BUSY, &isac->dch.Flags)) {
2248c2ecf20Sopenharmony_ci		/* Restart frame */
2258c2ecf20Sopenharmony_ci		isac->dch.tx_idx = 0;
2268c2ecf20Sopenharmony_ci		isac_fill_fifo(isac);
2278c2ecf20Sopenharmony_ci	} else if (isac->dch.tx_skb) { /* should not happen */
2288c2ecf20Sopenharmony_ci		pr_info("%s: tx_skb exist but not busy\n", isac->name);
2298c2ecf20Sopenharmony_ci		test_and_set_bit(FLG_TX_BUSY, &isac->dch.Flags);
2308c2ecf20Sopenharmony_ci		isac->dch.tx_idx = 0;
2318c2ecf20Sopenharmony_ci		isac_fill_fifo(isac);
2328c2ecf20Sopenharmony_ci	} else {
2338c2ecf20Sopenharmony_ci		pr_info("%s: ISAC XDU no TX_BUSY\n", isac->name);
2348c2ecf20Sopenharmony_ci		if (get_next_dframe(&isac->dch))
2358c2ecf20Sopenharmony_ci			isac_fill_fifo(isac);
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic void
2408c2ecf20Sopenharmony_ciisac_mos_irq(struct isac_hw *isac)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	u8 val;
2438c2ecf20Sopenharmony_ci	int ret;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	val = ReadISAC(isac, ISAC_MOSR);
2468c2ecf20Sopenharmony_ci	pr_debug("%s: ISAC MOSR %02x\n", isac->name, val);
2478c2ecf20Sopenharmony_ci#if ARCOFI_USE
2488c2ecf20Sopenharmony_ci	if (val & 0x08) {
2498c2ecf20Sopenharmony_ci		if (!isac->mon_rx) {
2508c2ecf20Sopenharmony_ci			isac->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC);
2518c2ecf20Sopenharmony_ci			if (!isac->mon_rx) {
2528c2ecf20Sopenharmony_ci				pr_info("%s: ISAC MON RX out of memory!\n",
2538c2ecf20Sopenharmony_ci					isac->name);
2548c2ecf20Sopenharmony_ci				isac->mocr &= 0xf0;
2558c2ecf20Sopenharmony_ci				isac->mocr |= 0x0a;
2568c2ecf20Sopenharmony_ci				WriteISAC(isac, ISAC_MOCR, isac->mocr);
2578c2ecf20Sopenharmony_ci				goto afterMONR0;
2588c2ecf20Sopenharmony_ci			} else
2598c2ecf20Sopenharmony_ci				isac->mon_rxp = 0;
2608c2ecf20Sopenharmony_ci		}
2618c2ecf20Sopenharmony_ci		if (isac->mon_rxp >= MAX_MON_FRAME) {
2628c2ecf20Sopenharmony_ci			isac->mocr &= 0xf0;
2638c2ecf20Sopenharmony_ci			isac->mocr |= 0x0a;
2648c2ecf20Sopenharmony_ci			WriteISAC(isac, ISAC_MOCR, isac->mocr);
2658c2ecf20Sopenharmony_ci			isac->mon_rxp = 0;
2668c2ecf20Sopenharmony_ci			pr_debug("%s: ISAC MON RX overflow!\n", isac->name);
2678c2ecf20Sopenharmony_ci			goto afterMONR0;
2688c2ecf20Sopenharmony_ci		}
2698c2ecf20Sopenharmony_ci		isac->mon_rx[isac->mon_rxp++] = ReadISAC(isac, ISAC_MOR0);
2708c2ecf20Sopenharmony_ci		pr_debug("%s: ISAC MOR0 %02x\n", isac->name,
2718c2ecf20Sopenharmony_ci			 isac->mon_rx[isac->mon_rxp - 1]);
2728c2ecf20Sopenharmony_ci		if (isac->mon_rxp == 1) {
2738c2ecf20Sopenharmony_ci			isac->mocr |= 0x04;
2748c2ecf20Sopenharmony_ci			WriteISAC(isac, ISAC_MOCR, isac->mocr);
2758c2ecf20Sopenharmony_ci		}
2768c2ecf20Sopenharmony_ci	}
2778c2ecf20Sopenharmony_ciafterMONR0:
2788c2ecf20Sopenharmony_ci	if (val & 0x80) {
2798c2ecf20Sopenharmony_ci		if (!isac->mon_rx) {
2808c2ecf20Sopenharmony_ci			isac->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC);
2818c2ecf20Sopenharmony_ci			if (!isac->mon_rx) {
2828c2ecf20Sopenharmony_ci				pr_info("%s: ISAC MON RX out of memory!\n",
2838c2ecf20Sopenharmony_ci					isac->name);
2848c2ecf20Sopenharmony_ci				isac->mocr &= 0x0f;
2858c2ecf20Sopenharmony_ci				isac->mocr |= 0xa0;
2868c2ecf20Sopenharmony_ci				WriteISAC(isac, ISAC_MOCR, isac->mocr);
2878c2ecf20Sopenharmony_ci				goto afterMONR1;
2888c2ecf20Sopenharmony_ci			} else
2898c2ecf20Sopenharmony_ci				isac->mon_rxp = 0;
2908c2ecf20Sopenharmony_ci		}
2918c2ecf20Sopenharmony_ci		if (isac->mon_rxp >= MAX_MON_FRAME) {
2928c2ecf20Sopenharmony_ci			isac->mocr &= 0x0f;
2938c2ecf20Sopenharmony_ci			isac->mocr |= 0xa0;
2948c2ecf20Sopenharmony_ci			WriteISAC(isac, ISAC_MOCR, isac->mocr);
2958c2ecf20Sopenharmony_ci			isac->mon_rxp = 0;
2968c2ecf20Sopenharmony_ci			pr_debug("%s: ISAC MON RX overflow!\n", isac->name);
2978c2ecf20Sopenharmony_ci			goto afterMONR1;
2988c2ecf20Sopenharmony_ci		}
2998c2ecf20Sopenharmony_ci		isac->mon_rx[isac->mon_rxp++] = ReadISAC(isac, ISAC_MOR1);
3008c2ecf20Sopenharmony_ci		pr_debug("%s: ISAC MOR1 %02x\n", isac->name,
3018c2ecf20Sopenharmony_ci			 isac->mon_rx[isac->mon_rxp - 1]);
3028c2ecf20Sopenharmony_ci		isac->mocr |= 0x40;
3038c2ecf20Sopenharmony_ci		WriteISAC(isac, ISAC_MOCR, isac->mocr);
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ciafterMONR1:
3068c2ecf20Sopenharmony_ci	if (val & 0x04) {
3078c2ecf20Sopenharmony_ci		isac->mocr &= 0xf0;
3088c2ecf20Sopenharmony_ci		WriteISAC(isac, ISAC_MOCR, isac->mocr);
3098c2ecf20Sopenharmony_ci		isac->mocr |= 0x0a;
3108c2ecf20Sopenharmony_ci		WriteISAC(isac, ISAC_MOCR, isac->mocr);
3118c2ecf20Sopenharmony_ci		if (isac->monitor) {
3128c2ecf20Sopenharmony_ci			ret = isac->monitor(isac->dch.hw, MONITOR_RX_0,
3138c2ecf20Sopenharmony_ci					    isac->mon_rx, isac->mon_rxp);
3148c2ecf20Sopenharmony_ci			if (ret)
3158c2ecf20Sopenharmony_ci				kfree(isac->mon_rx);
3168c2ecf20Sopenharmony_ci		} else {
3178c2ecf20Sopenharmony_ci			pr_info("%s: MONITOR 0 received %d but no user\n",
3188c2ecf20Sopenharmony_ci				isac->name, isac->mon_rxp);
3198c2ecf20Sopenharmony_ci			kfree(isac->mon_rx);
3208c2ecf20Sopenharmony_ci		}
3218c2ecf20Sopenharmony_ci		isac->mon_rx = NULL;
3228c2ecf20Sopenharmony_ci		isac->mon_rxp = 0;
3238c2ecf20Sopenharmony_ci	}
3248c2ecf20Sopenharmony_ci	if (val & 0x40) {
3258c2ecf20Sopenharmony_ci		isac->mocr &= 0x0f;
3268c2ecf20Sopenharmony_ci		WriteISAC(isac, ISAC_MOCR, isac->mocr);
3278c2ecf20Sopenharmony_ci		isac->mocr |= 0xa0;
3288c2ecf20Sopenharmony_ci		WriteISAC(isac, ISAC_MOCR, isac->mocr);
3298c2ecf20Sopenharmony_ci		if (isac->monitor) {
3308c2ecf20Sopenharmony_ci			ret = isac->monitor(isac->dch.hw, MONITOR_RX_1,
3318c2ecf20Sopenharmony_ci					    isac->mon_rx, isac->mon_rxp);
3328c2ecf20Sopenharmony_ci			if (ret)
3338c2ecf20Sopenharmony_ci				kfree(isac->mon_rx);
3348c2ecf20Sopenharmony_ci		} else {
3358c2ecf20Sopenharmony_ci			pr_info("%s: MONITOR 1 received %d but no user\n",
3368c2ecf20Sopenharmony_ci				isac->name, isac->mon_rxp);
3378c2ecf20Sopenharmony_ci			kfree(isac->mon_rx);
3388c2ecf20Sopenharmony_ci		}
3398c2ecf20Sopenharmony_ci		isac->mon_rx = NULL;
3408c2ecf20Sopenharmony_ci		isac->mon_rxp = 0;
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci	if (val & 0x02) {
3438c2ecf20Sopenharmony_ci		if ((!isac->mon_tx) || (isac->mon_txc &&
3448c2ecf20Sopenharmony_ci					(isac->mon_txp >= isac->mon_txc) && !(val & 0x08))) {
3458c2ecf20Sopenharmony_ci			isac->mocr &= 0xf0;
3468c2ecf20Sopenharmony_ci			WriteISAC(isac, ISAC_MOCR, isac->mocr);
3478c2ecf20Sopenharmony_ci			isac->mocr |= 0x0a;
3488c2ecf20Sopenharmony_ci			WriteISAC(isac, ISAC_MOCR, isac->mocr);
3498c2ecf20Sopenharmony_ci			if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {
3508c2ecf20Sopenharmony_ci				if (isac->monitor)
3518c2ecf20Sopenharmony_ci					isac->monitor(isac->dch.hw,
3528c2ecf20Sopenharmony_ci						      MONITOR_TX_0, NULL, 0);
3538c2ecf20Sopenharmony_ci			}
3548c2ecf20Sopenharmony_ci			kfree(isac->mon_tx);
3558c2ecf20Sopenharmony_ci			isac->mon_tx = NULL;
3568c2ecf20Sopenharmony_ci			isac->mon_txc = 0;
3578c2ecf20Sopenharmony_ci			isac->mon_txp = 0;
3588c2ecf20Sopenharmony_ci			goto AfterMOX0;
3598c2ecf20Sopenharmony_ci		}
3608c2ecf20Sopenharmony_ci		if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {
3618c2ecf20Sopenharmony_ci			if (isac->monitor)
3628c2ecf20Sopenharmony_ci				isac->monitor(isac->dch.hw,
3638c2ecf20Sopenharmony_ci					      MONITOR_TX_0, NULL, 0);
3648c2ecf20Sopenharmony_ci			kfree(isac->mon_tx);
3658c2ecf20Sopenharmony_ci			isac->mon_tx = NULL;
3668c2ecf20Sopenharmony_ci			isac->mon_txc = 0;
3678c2ecf20Sopenharmony_ci			isac->mon_txp = 0;
3688c2ecf20Sopenharmony_ci			goto AfterMOX0;
3698c2ecf20Sopenharmony_ci		}
3708c2ecf20Sopenharmony_ci		WriteISAC(isac, ISAC_MOX0, isac->mon_tx[isac->mon_txp++]);
3718c2ecf20Sopenharmony_ci		pr_debug("%s: ISAC %02x -> MOX0\n", isac->name,
3728c2ecf20Sopenharmony_ci			 isac->mon_tx[isac->mon_txp - 1]);
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ciAfterMOX0:
3758c2ecf20Sopenharmony_ci	if (val & 0x20) {
3768c2ecf20Sopenharmony_ci		if ((!isac->mon_tx) || (isac->mon_txc &&
3778c2ecf20Sopenharmony_ci					(isac->mon_txp >= isac->mon_txc) && !(val & 0x80))) {
3788c2ecf20Sopenharmony_ci			isac->mocr &= 0x0f;
3798c2ecf20Sopenharmony_ci			WriteISAC(isac, ISAC_MOCR, isac->mocr);
3808c2ecf20Sopenharmony_ci			isac->mocr |= 0xa0;
3818c2ecf20Sopenharmony_ci			WriteISAC(isac, ISAC_MOCR, isac->mocr);
3828c2ecf20Sopenharmony_ci			if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {
3838c2ecf20Sopenharmony_ci				if (isac->monitor)
3848c2ecf20Sopenharmony_ci					isac->monitor(isac->dch.hw,
3858c2ecf20Sopenharmony_ci						      MONITOR_TX_1, NULL, 0);
3868c2ecf20Sopenharmony_ci			}
3878c2ecf20Sopenharmony_ci			kfree(isac->mon_tx);
3888c2ecf20Sopenharmony_ci			isac->mon_tx = NULL;
3898c2ecf20Sopenharmony_ci			isac->mon_txc = 0;
3908c2ecf20Sopenharmony_ci			isac->mon_txp = 0;
3918c2ecf20Sopenharmony_ci			goto AfterMOX1;
3928c2ecf20Sopenharmony_ci		}
3938c2ecf20Sopenharmony_ci		if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {
3948c2ecf20Sopenharmony_ci			if (isac->monitor)
3958c2ecf20Sopenharmony_ci				isac->monitor(isac->dch.hw,
3968c2ecf20Sopenharmony_ci					      MONITOR_TX_1, NULL, 0);
3978c2ecf20Sopenharmony_ci			kfree(isac->mon_tx);
3988c2ecf20Sopenharmony_ci			isac->mon_tx = NULL;
3998c2ecf20Sopenharmony_ci			isac->mon_txc = 0;
4008c2ecf20Sopenharmony_ci			isac->mon_txp = 0;
4018c2ecf20Sopenharmony_ci			goto AfterMOX1;
4028c2ecf20Sopenharmony_ci		}
4038c2ecf20Sopenharmony_ci		WriteISAC(isac, ISAC_MOX1, isac->mon_tx[isac->mon_txp++]);
4048c2ecf20Sopenharmony_ci		pr_debug("%s: ISAC %02x -> MOX1\n", isac->name,
4058c2ecf20Sopenharmony_ci			 isac->mon_tx[isac->mon_txp - 1]);
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ciAfterMOX1:
4088c2ecf20Sopenharmony_ci	val = 0; /* dummy to avoid warning */
4098c2ecf20Sopenharmony_ci#endif
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic void
4138c2ecf20Sopenharmony_ciisac_cisq_irq(struct isac_hw *isac) {
4148c2ecf20Sopenharmony_ci	u8 val;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	val = ReadISAC(isac, ISAC_CIR0);
4178c2ecf20Sopenharmony_ci	pr_debug("%s: ISAC CIR0 %02X\n", isac->name, val);
4188c2ecf20Sopenharmony_ci	if (val & 2) {
4198c2ecf20Sopenharmony_ci		pr_debug("%s: ph_state change %x->%x\n", isac->name,
4208c2ecf20Sopenharmony_ci			 isac->state, (val >> 2) & 0xf);
4218c2ecf20Sopenharmony_ci		isac->state = (val >> 2) & 0xf;
4228c2ecf20Sopenharmony_ci		isac_ph_state_change(isac);
4238c2ecf20Sopenharmony_ci	}
4248c2ecf20Sopenharmony_ci	if (val & 1) {
4258c2ecf20Sopenharmony_ci		val = ReadISAC(isac, ISAC_CIR1);
4268c2ecf20Sopenharmony_ci		pr_debug("%s: ISAC CIR1 %02X\n", isac->name, val);
4278c2ecf20Sopenharmony_ci	}
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic void
4318c2ecf20Sopenharmony_ciisacsx_cic_irq(struct isac_hw *isac)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	u8 val;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	val = ReadISAC(isac, ISACX_CIR0);
4368c2ecf20Sopenharmony_ci	pr_debug("%s: ISACX CIR0 %02X\n", isac->name, val);
4378c2ecf20Sopenharmony_ci	if (val & ISACX_CIR0_CIC0) {
4388c2ecf20Sopenharmony_ci		pr_debug("%s: ph_state change %x->%x\n", isac->name,
4398c2ecf20Sopenharmony_ci			 isac->state, val >> 4);
4408c2ecf20Sopenharmony_ci		isac->state = val >> 4;
4418c2ecf20Sopenharmony_ci		isac_ph_state_change(isac);
4428c2ecf20Sopenharmony_ci	}
4438c2ecf20Sopenharmony_ci}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_cistatic void
4468c2ecf20Sopenharmony_ciisacsx_rme_irq(struct isac_hw *isac)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	int count;
4498c2ecf20Sopenharmony_ci	u8 val;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	val = ReadISAC(isac, ISACX_RSTAD);
4528c2ecf20Sopenharmony_ci	if ((val & (ISACX_RSTAD_VFR |
4538c2ecf20Sopenharmony_ci		    ISACX_RSTAD_RDO |
4548c2ecf20Sopenharmony_ci		    ISACX_RSTAD_CRC |
4558c2ecf20Sopenharmony_ci		    ISACX_RSTAD_RAB))
4568c2ecf20Sopenharmony_ci	    != (ISACX_RSTAD_VFR | ISACX_RSTAD_CRC)) {
4578c2ecf20Sopenharmony_ci		pr_debug("%s: RSTAD %#x, dropped\n", isac->name, val);
4588c2ecf20Sopenharmony_ci#ifdef ERROR_STATISTIC
4598c2ecf20Sopenharmony_ci		if (val & ISACX_RSTAD_CRC)
4608c2ecf20Sopenharmony_ci			isac->dch.err_rx++;
4618c2ecf20Sopenharmony_ci		else
4628c2ecf20Sopenharmony_ci			isac->dch.err_crc++;
4638c2ecf20Sopenharmony_ci#endif
4648c2ecf20Sopenharmony_ci		WriteISAC(isac, ISACX_CMDRD, ISACX_CMDRD_RMC);
4658c2ecf20Sopenharmony_ci		dev_kfree_skb(isac->dch.rx_skb);
4668c2ecf20Sopenharmony_ci		isac->dch.rx_skb = NULL;
4678c2ecf20Sopenharmony_ci	} else {
4688c2ecf20Sopenharmony_ci		count = ReadISAC(isac, ISACX_RBCLD) & 0x1f;
4698c2ecf20Sopenharmony_ci		if (count == 0)
4708c2ecf20Sopenharmony_ci			count = 32;
4718c2ecf20Sopenharmony_ci		isac_empty_fifo(isac, count);
4728c2ecf20Sopenharmony_ci		if (isac->dch.rx_skb) {
4738c2ecf20Sopenharmony_ci			skb_trim(isac->dch.rx_skb, isac->dch.rx_skb->len - 1);
4748c2ecf20Sopenharmony_ci			pr_debug("%s: dchannel received %d\n", isac->name,
4758c2ecf20Sopenharmony_ci				 isac->dch.rx_skb->len);
4768c2ecf20Sopenharmony_ci			recv_Dchannel(&isac->dch);
4778c2ecf20Sopenharmony_ci		}
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ciirqreturn_t
4828c2ecf20Sopenharmony_cimISDNisac_irq(struct isac_hw *isac, u8 val)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	if (unlikely(!val))
4858c2ecf20Sopenharmony_ci		return IRQ_NONE;
4868c2ecf20Sopenharmony_ci	pr_debug("%s: ISAC interrupt %02x\n", isac->name, val);
4878c2ecf20Sopenharmony_ci	if (isac->type & IPAC_TYPE_ISACX) {
4888c2ecf20Sopenharmony_ci		if (val & ISACX__CIC)
4898c2ecf20Sopenharmony_ci			isacsx_cic_irq(isac);
4908c2ecf20Sopenharmony_ci		if (val & ISACX__ICD) {
4918c2ecf20Sopenharmony_ci			val = ReadISAC(isac, ISACX_ISTAD);
4928c2ecf20Sopenharmony_ci			pr_debug("%s: ISTAD %02x\n", isac->name, val);
4938c2ecf20Sopenharmony_ci			if (val & ISACX_D_XDU) {
4948c2ecf20Sopenharmony_ci				pr_debug("%s: ISAC XDU\n", isac->name);
4958c2ecf20Sopenharmony_ci#ifdef ERROR_STATISTIC
4968c2ecf20Sopenharmony_ci				isac->dch.err_tx++;
4978c2ecf20Sopenharmony_ci#endif
4988c2ecf20Sopenharmony_ci				isac_retransmit(isac);
4998c2ecf20Sopenharmony_ci			}
5008c2ecf20Sopenharmony_ci			if (val & ISACX_D_XMR) {
5018c2ecf20Sopenharmony_ci				pr_debug("%s: ISAC XMR\n", isac->name);
5028c2ecf20Sopenharmony_ci#ifdef ERROR_STATISTIC
5038c2ecf20Sopenharmony_ci				isac->dch.err_tx++;
5048c2ecf20Sopenharmony_ci#endif
5058c2ecf20Sopenharmony_ci				isac_retransmit(isac);
5068c2ecf20Sopenharmony_ci			}
5078c2ecf20Sopenharmony_ci			if (val & ISACX_D_XPR)
5088c2ecf20Sopenharmony_ci				isac_xpr_irq(isac);
5098c2ecf20Sopenharmony_ci			if (val & ISACX_D_RFO) {
5108c2ecf20Sopenharmony_ci				pr_debug("%s: ISAC RFO\n", isac->name);
5118c2ecf20Sopenharmony_ci				WriteISAC(isac, ISACX_CMDRD, ISACX_CMDRD_RMC);
5128c2ecf20Sopenharmony_ci			}
5138c2ecf20Sopenharmony_ci			if (val & ISACX_D_RME)
5148c2ecf20Sopenharmony_ci				isacsx_rme_irq(isac);
5158c2ecf20Sopenharmony_ci			if (val & ISACX_D_RPF)
5168c2ecf20Sopenharmony_ci				isac_empty_fifo(isac, 0x20);
5178c2ecf20Sopenharmony_ci		}
5188c2ecf20Sopenharmony_ci	} else {
5198c2ecf20Sopenharmony_ci		if (val & 0x80)	/* RME */
5208c2ecf20Sopenharmony_ci			isac_rme_irq(isac);
5218c2ecf20Sopenharmony_ci		if (val & 0x40)	/* RPF */
5228c2ecf20Sopenharmony_ci			isac_empty_fifo(isac, 32);
5238c2ecf20Sopenharmony_ci		if (val & 0x10)	/* XPR */
5248c2ecf20Sopenharmony_ci			isac_xpr_irq(isac);
5258c2ecf20Sopenharmony_ci		if (val & 0x04)	/* CISQ */
5268c2ecf20Sopenharmony_ci			isac_cisq_irq(isac);
5278c2ecf20Sopenharmony_ci		if (val & 0x20)	/* RSC - never */
5288c2ecf20Sopenharmony_ci			pr_debug("%s: ISAC RSC interrupt\n", isac->name);
5298c2ecf20Sopenharmony_ci		if (val & 0x02)	/* SIN - never */
5308c2ecf20Sopenharmony_ci			pr_debug("%s: ISAC SIN interrupt\n", isac->name);
5318c2ecf20Sopenharmony_ci		if (val & 0x01) {	/* EXI */
5328c2ecf20Sopenharmony_ci			val = ReadISAC(isac, ISAC_EXIR);
5338c2ecf20Sopenharmony_ci			pr_debug("%s: ISAC EXIR %02x\n", isac->name, val);
5348c2ecf20Sopenharmony_ci			if (val & 0x80)	/* XMR */
5358c2ecf20Sopenharmony_ci				pr_debug("%s: ISAC XMR\n", isac->name);
5368c2ecf20Sopenharmony_ci			if (val & 0x40) { /* XDU */
5378c2ecf20Sopenharmony_ci				pr_debug("%s: ISAC XDU\n", isac->name);
5388c2ecf20Sopenharmony_ci#ifdef ERROR_STATISTIC
5398c2ecf20Sopenharmony_ci				isac->dch.err_tx++;
5408c2ecf20Sopenharmony_ci#endif
5418c2ecf20Sopenharmony_ci				isac_retransmit(isac);
5428c2ecf20Sopenharmony_ci			}
5438c2ecf20Sopenharmony_ci			if (val & 0x04)	/* MOS */
5448c2ecf20Sopenharmony_ci				isac_mos_irq(isac);
5458c2ecf20Sopenharmony_ci		}
5468c2ecf20Sopenharmony_ci	}
5478c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
5488c2ecf20Sopenharmony_ci}
5498c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDNisac_irq);
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_cistatic int
5528c2ecf20Sopenharmony_ciisac_l1hw(struct mISDNchannel *ch, struct sk_buff *skb)
5538c2ecf20Sopenharmony_ci{
5548c2ecf20Sopenharmony_ci	struct mISDNdevice	*dev = container_of(ch, struct mISDNdevice, D);
5558c2ecf20Sopenharmony_ci	struct dchannel		*dch = container_of(dev, struct dchannel, dev);
5568c2ecf20Sopenharmony_ci	struct isac_hw		*isac = container_of(dch, struct isac_hw, dch);
5578c2ecf20Sopenharmony_ci	int			ret = -EINVAL;
5588c2ecf20Sopenharmony_ci	struct mISDNhead	*hh = mISDN_HEAD_P(skb);
5598c2ecf20Sopenharmony_ci	u32			id;
5608c2ecf20Sopenharmony_ci	u_long			flags;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	switch (hh->prim) {
5638c2ecf20Sopenharmony_ci	case PH_DATA_REQ:
5648c2ecf20Sopenharmony_ci		spin_lock_irqsave(isac->hwlock, flags);
5658c2ecf20Sopenharmony_ci		ret = dchannel_senddata(dch, skb);
5668c2ecf20Sopenharmony_ci		if (ret > 0) { /* direct TX */
5678c2ecf20Sopenharmony_ci			id = hh->id; /* skb can be freed */
5688c2ecf20Sopenharmony_ci			isac_fill_fifo(isac);
5698c2ecf20Sopenharmony_ci			ret = 0;
5708c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(isac->hwlock, flags);
5718c2ecf20Sopenharmony_ci			queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
5728c2ecf20Sopenharmony_ci		} else
5738c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(isac->hwlock, flags);
5748c2ecf20Sopenharmony_ci		return ret;
5758c2ecf20Sopenharmony_ci	case PH_ACTIVATE_REQ:
5768c2ecf20Sopenharmony_ci		ret = l1_event(dch->l1, hh->prim);
5778c2ecf20Sopenharmony_ci		break;
5788c2ecf20Sopenharmony_ci	case PH_DEACTIVATE_REQ:
5798c2ecf20Sopenharmony_ci		test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags);
5808c2ecf20Sopenharmony_ci		ret = l1_event(dch->l1, hh->prim);
5818c2ecf20Sopenharmony_ci		break;
5828c2ecf20Sopenharmony_ci	}
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	if (!ret)
5858c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
5868c2ecf20Sopenharmony_ci	return ret;
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_cistatic int
5908c2ecf20Sopenharmony_ciisac_ctrl(struct isac_hw *isac, u32 cmd, unsigned long para)
5918c2ecf20Sopenharmony_ci{
5928c2ecf20Sopenharmony_ci	u8 tl = 0;
5938c2ecf20Sopenharmony_ci	unsigned long flags;
5948c2ecf20Sopenharmony_ci	int ret = 0;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	switch (cmd) {
5978c2ecf20Sopenharmony_ci	case HW_TESTLOOP:
5988c2ecf20Sopenharmony_ci		spin_lock_irqsave(isac->hwlock, flags);
5998c2ecf20Sopenharmony_ci		if (!(isac->type & IPAC_TYPE_ISACX)) {
6008c2ecf20Sopenharmony_ci			/* TODO: implement for IPAC_TYPE_ISACX */
6018c2ecf20Sopenharmony_ci			if (para & 1) /* B1 */
6028c2ecf20Sopenharmony_ci				tl |= 0x0c;
6038c2ecf20Sopenharmony_ci			else if (para & 2) /* B2 */
6048c2ecf20Sopenharmony_ci				tl |= 0x3;
6058c2ecf20Sopenharmony_ci			/* we only support IOM2 mode */
6068c2ecf20Sopenharmony_ci			WriteISAC(isac, ISAC_SPCR, tl);
6078c2ecf20Sopenharmony_ci			if (tl)
6088c2ecf20Sopenharmony_ci				WriteISAC(isac, ISAC_ADF1, 0x8);
6098c2ecf20Sopenharmony_ci			else
6108c2ecf20Sopenharmony_ci				WriteISAC(isac, ISAC_ADF1, 0x0);
6118c2ecf20Sopenharmony_ci		}
6128c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(isac->hwlock, flags);
6138c2ecf20Sopenharmony_ci		break;
6148c2ecf20Sopenharmony_ci	case HW_TIMER3_VALUE:
6158c2ecf20Sopenharmony_ci		ret = l1_event(isac->dch.l1, HW_TIMER3_VALUE | (para & 0xff));
6168c2ecf20Sopenharmony_ci		break;
6178c2ecf20Sopenharmony_ci	default:
6188c2ecf20Sopenharmony_ci		pr_debug("%s: %s unknown command %x %lx\n", isac->name,
6198c2ecf20Sopenharmony_ci			 __func__, cmd, para);
6208c2ecf20Sopenharmony_ci		ret = -1;
6218c2ecf20Sopenharmony_ci	}
6228c2ecf20Sopenharmony_ci	return ret;
6238c2ecf20Sopenharmony_ci}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_cistatic int
6268c2ecf20Sopenharmony_ciisac_l1cmd(struct dchannel *dch, u32 cmd)
6278c2ecf20Sopenharmony_ci{
6288c2ecf20Sopenharmony_ci	struct isac_hw *isac = container_of(dch, struct isac_hw, dch);
6298c2ecf20Sopenharmony_ci	u_long flags;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	pr_debug("%s: cmd(%x) state(%02x)\n", isac->name, cmd, isac->state);
6328c2ecf20Sopenharmony_ci	switch (cmd) {
6338c2ecf20Sopenharmony_ci	case INFO3_P8:
6348c2ecf20Sopenharmony_ci		spin_lock_irqsave(isac->hwlock, flags);
6358c2ecf20Sopenharmony_ci		ph_command(isac, ISAC_CMD_AR8);
6368c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(isac->hwlock, flags);
6378c2ecf20Sopenharmony_ci		break;
6388c2ecf20Sopenharmony_ci	case INFO3_P10:
6398c2ecf20Sopenharmony_ci		spin_lock_irqsave(isac->hwlock, flags);
6408c2ecf20Sopenharmony_ci		ph_command(isac, ISAC_CMD_AR10);
6418c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(isac->hwlock, flags);
6428c2ecf20Sopenharmony_ci		break;
6438c2ecf20Sopenharmony_ci	case HW_RESET_REQ:
6448c2ecf20Sopenharmony_ci		spin_lock_irqsave(isac->hwlock, flags);
6458c2ecf20Sopenharmony_ci		if ((isac->state == ISAC_IND_EI) ||
6468c2ecf20Sopenharmony_ci		    (isac->state == ISAC_IND_DR) ||
6478c2ecf20Sopenharmony_ci		    (isac->state == ISAC_IND_DR6) ||
6488c2ecf20Sopenharmony_ci		    (isac->state == ISAC_IND_RS))
6498c2ecf20Sopenharmony_ci			ph_command(isac, ISAC_CMD_TIM);
6508c2ecf20Sopenharmony_ci		else
6518c2ecf20Sopenharmony_ci			ph_command(isac, ISAC_CMD_RS);
6528c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(isac->hwlock, flags);
6538c2ecf20Sopenharmony_ci		break;
6548c2ecf20Sopenharmony_ci	case HW_DEACT_REQ:
6558c2ecf20Sopenharmony_ci		skb_queue_purge(&dch->squeue);
6568c2ecf20Sopenharmony_ci		if (dch->tx_skb) {
6578c2ecf20Sopenharmony_ci			dev_kfree_skb(dch->tx_skb);
6588c2ecf20Sopenharmony_ci			dch->tx_skb = NULL;
6598c2ecf20Sopenharmony_ci		}
6608c2ecf20Sopenharmony_ci		dch->tx_idx = 0;
6618c2ecf20Sopenharmony_ci		if (dch->rx_skb) {
6628c2ecf20Sopenharmony_ci			dev_kfree_skb(dch->rx_skb);
6638c2ecf20Sopenharmony_ci			dch->rx_skb = NULL;
6648c2ecf20Sopenharmony_ci		}
6658c2ecf20Sopenharmony_ci		test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
6668c2ecf20Sopenharmony_ci		if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
6678c2ecf20Sopenharmony_ci			del_timer(&dch->timer);
6688c2ecf20Sopenharmony_ci		break;
6698c2ecf20Sopenharmony_ci	case HW_POWERUP_REQ:
6708c2ecf20Sopenharmony_ci		spin_lock_irqsave(isac->hwlock, flags);
6718c2ecf20Sopenharmony_ci		ph_command(isac, ISAC_CMD_TIM);
6728c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(isac->hwlock, flags);
6738c2ecf20Sopenharmony_ci		break;
6748c2ecf20Sopenharmony_ci	case PH_ACTIVATE_IND:
6758c2ecf20Sopenharmony_ci		test_and_set_bit(FLG_ACTIVE, &dch->Flags);
6768c2ecf20Sopenharmony_ci		_queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
6778c2ecf20Sopenharmony_ci			    GFP_ATOMIC);
6788c2ecf20Sopenharmony_ci		break;
6798c2ecf20Sopenharmony_ci	case PH_DEACTIVATE_IND:
6808c2ecf20Sopenharmony_ci		test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
6818c2ecf20Sopenharmony_ci		_queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
6828c2ecf20Sopenharmony_ci			    GFP_ATOMIC);
6838c2ecf20Sopenharmony_ci		break;
6848c2ecf20Sopenharmony_ci	default:
6858c2ecf20Sopenharmony_ci		pr_debug("%s: %s unknown command %x\n", isac->name,
6868c2ecf20Sopenharmony_ci			 __func__, cmd);
6878c2ecf20Sopenharmony_ci		return -1;
6888c2ecf20Sopenharmony_ci	}
6898c2ecf20Sopenharmony_ci	return 0;
6908c2ecf20Sopenharmony_ci}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_cistatic void
6938c2ecf20Sopenharmony_ciisac_release(struct isac_hw *isac)
6948c2ecf20Sopenharmony_ci{
6958c2ecf20Sopenharmony_ci	if (isac->type & IPAC_TYPE_ISACX)
6968c2ecf20Sopenharmony_ci		WriteISAC(isac, ISACX_MASK, 0xff);
6978c2ecf20Sopenharmony_ci	else if (isac->type != 0)
6988c2ecf20Sopenharmony_ci		WriteISAC(isac, ISAC_MASK, 0xff);
6998c2ecf20Sopenharmony_ci	if (isac->dch.timer.function != NULL) {
7008c2ecf20Sopenharmony_ci		del_timer(&isac->dch.timer);
7018c2ecf20Sopenharmony_ci		isac->dch.timer.function = NULL;
7028c2ecf20Sopenharmony_ci	}
7038c2ecf20Sopenharmony_ci	kfree(isac->mon_rx);
7048c2ecf20Sopenharmony_ci	isac->mon_rx = NULL;
7058c2ecf20Sopenharmony_ci	kfree(isac->mon_tx);
7068c2ecf20Sopenharmony_ci	isac->mon_tx = NULL;
7078c2ecf20Sopenharmony_ci	if (isac->dch.l1)
7088c2ecf20Sopenharmony_ci		l1_event(isac->dch.l1, CLOSE_CHANNEL);
7098c2ecf20Sopenharmony_ci	mISDN_freedchannel(&isac->dch);
7108c2ecf20Sopenharmony_ci}
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_cistatic void
7138c2ecf20Sopenharmony_cidbusy_timer_handler(struct timer_list *t)
7148c2ecf20Sopenharmony_ci{
7158c2ecf20Sopenharmony_ci	struct isac_hw *isac = from_timer(isac, t, dch.timer);
7168c2ecf20Sopenharmony_ci	int rbch, star;
7178c2ecf20Sopenharmony_ci	u_long flags;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	if (test_bit(FLG_BUSY_TIMER, &isac->dch.Flags)) {
7208c2ecf20Sopenharmony_ci		spin_lock_irqsave(isac->hwlock, flags);
7218c2ecf20Sopenharmony_ci		rbch = ReadISAC(isac, ISAC_RBCH);
7228c2ecf20Sopenharmony_ci		star = ReadISAC(isac, ISAC_STAR);
7238c2ecf20Sopenharmony_ci		pr_debug("%s: D-Channel Busy RBCH %02x STAR %02x\n",
7248c2ecf20Sopenharmony_ci			 isac->name, rbch, star);
7258c2ecf20Sopenharmony_ci		if (rbch & ISAC_RBCH_XAC) /* D-Channel Busy */
7268c2ecf20Sopenharmony_ci			test_and_set_bit(FLG_L1_BUSY, &isac->dch.Flags);
7278c2ecf20Sopenharmony_ci		else {
7288c2ecf20Sopenharmony_ci			/* discard frame; reset transceiver */
7298c2ecf20Sopenharmony_ci			test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags);
7308c2ecf20Sopenharmony_ci			if (isac->dch.tx_idx)
7318c2ecf20Sopenharmony_ci				isac->dch.tx_idx = 0;
7328c2ecf20Sopenharmony_ci			else
7338c2ecf20Sopenharmony_ci				pr_info("%s: ISAC D-Channel Busy no tx_idx\n",
7348c2ecf20Sopenharmony_ci					isac->name);
7358c2ecf20Sopenharmony_ci			/* Transmitter reset */
7368c2ecf20Sopenharmony_ci			WriteISAC(isac, ISAC_CMDR, 0x01);
7378c2ecf20Sopenharmony_ci		}
7388c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(isac->hwlock, flags);
7398c2ecf20Sopenharmony_ci	}
7408c2ecf20Sopenharmony_ci}
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_cistatic int
7438c2ecf20Sopenharmony_ciopen_dchannel_caller(struct isac_hw *isac, struct channel_req *rq, void *caller)
7448c2ecf20Sopenharmony_ci{
7458c2ecf20Sopenharmony_ci	pr_debug("%s: %s dev(%d) open from %p\n", isac->name, __func__,
7468c2ecf20Sopenharmony_ci		 isac->dch.dev.id, caller);
7478c2ecf20Sopenharmony_ci	if (rq->protocol != ISDN_P_TE_S0)
7488c2ecf20Sopenharmony_ci		return -EINVAL;
7498c2ecf20Sopenharmony_ci	if (rq->adr.channel == 1)
7508c2ecf20Sopenharmony_ci		/* E-Channel not supported */
7518c2ecf20Sopenharmony_ci		return -EINVAL;
7528c2ecf20Sopenharmony_ci	rq->ch = &isac->dch.dev.D;
7538c2ecf20Sopenharmony_ci	rq->ch->protocol = rq->protocol;
7548c2ecf20Sopenharmony_ci	if (isac->dch.state == 7)
7558c2ecf20Sopenharmony_ci		_queue_data(rq->ch, PH_ACTIVATE_IND, MISDN_ID_ANY,
7568c2ecf20Sopenharmony_ci			    0, NULL, GFP_KERNEL);
7578c2ecf20Sopenharmony_ci	return 0;
7588c2ecf20Sopenharmony_ci}
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_cistatic int
7618c2ecf20Sopenharmony_ciopen_dchannel(struct isac_hw *isac, struct channel_req *rq)
7628c2ecf20Sopenharmony_ci{
7638c2ecf20Sopenharmony_ci	return open_dchannel_caller(isac, rq, __builtin_return_address(0));
7648c2ecf20Sopenharmony_ci}
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_cistatic const char *ISACVer[] =
7678c2ecf20Sopenharmony_ci{"2086/2186 V1.1", "2085 B1", "2085 B2",
7688c2ecf20Sopenharmony_ci "2085 V2.3"};
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_cistatic int
7718c2ecf20Sopenharmony_ciisac_init(struct isac_hw *isac)
7728c2ecf20Sopenharmony_ci{
7738c2ecf20Sopenharmony_ci	u8 val;
7748c2ecf20Sopenharmony_ci	int err = 0;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	if (!isac->dch.l1) {
7778c2ecf20Sopenharmony_ci		err = create_l1(&isac->dch, isac_l1cmd);
7788c2ecf20Sopenharmony_ci		if (err)
7798c2ecf20Sopenharmony_ci			return err;
7808c2ecf20Sopenharmony_ci	}
7818c2ecf20Sopenharmony_ci	isac->mon_tx = NULL;
7828c2ecf20Sopenharmony_ci	isac->mon_rx = NULL;
7838c2ecf20Sopenharmony_ci	timer_setup(&isac->dch.timer, dbusy_timer_handler, 0);
7848c2ecf20Sopenharmony_ci	isac->mocr = 0xaa;
7858c2ecf20Sopenharmony_ci	if (isac->type & IPAC_TYPE_ISACX) {
7868c2ecf20Sopenharmony_ci		/* Disable all IRQ */
7878c2ecf20Sopenharmony_ci		WriteISAC(isac, ISACX_MASK, 0xff);
7888c2ecf20Sopenharmony_ci		val = ReadISAC(isac, ISACX_STARD);
7898c2ecf20Sopenharmony_ci		pr_debug("%s: ISACX STARD %x\n", isac->name, val);
7908c2ecf20Sopenharmony_ci		val = ReadISAC(isac, ISACX_ISTAD);
7918c2ecf20Sopenharmony_ci		pr_debug("%s: ISACX ISTAD %x\n", isac->name, val);
7928c2ecf20Sopenharmony_ci		val = ReadISAC(isac, ISACX_ISTA);
7938c2ecf20Sopenharmony_ci		pr_debug("%s: ISACX ISTA %x\n", isac->name, val);
7948c2ecf20Sopenharmony_ci		/* clear LDD */
7958c2ecf20Sopenharmony_ci		WriteISAC(isac, ISACX_TR_CONF0, 0x00);
7968c2ecf20Sopenharmony_ci		/* enable transmitter */
7978c2ecf20Sopenharmony_ci		WriteISAC(isac, ISACX_TR_CONF2, 0x00);
7988c2ecf20Sopenharmony_ci		/* transparent mode 0, RAC, stop/go */
7998c2ecf20Sopenharmony_ci		WriteISAC(isac, ISACX_MODED, 0xc9);
8008c2ecf20Sopenharmony_ci		/* all HDLC IRQ unmasked */
8018c2ecf20Sopenharmony_ci		val = ReadISAC(isac, ISACX_ID);
8028c2ecf20Sopenharmony_ci		if (isac->dch.debug & DEBUG_HW)
8038c2ecf20Sopenharmony_ci			pr_notice("%s: ISACX Design ID %x\n",
8048c2ecf20Sopenharmony_ci				  isac->name, val & 0x3f);
8058c2ecf20Sopenharmony_ci		val = ReadISAC(isac, ISACX_CIR0);
8068c2ecf20Sopenharmony_ci		pr_debug("%s: ISACX CIR0 %02X\n", isac->name, val);
8078c2ecf20Sopenharmony_ci		isac->state = val >> 4;
8088c2ecf20Sopenharmony_ci		isac_ph_state_change(isac);
8098c2ecf20Sopenharmony_ci		ph_command(isac, ISAC_CMD_RS);
8108c2ecf20Sopenharmony_ci		WriteISAC(isac, ISACX_MASK, IPACX__ON);
8118c2ecf20Sopenharmony_ci		WriteISAC(isac, ISACX_MASKD, 0x00);
8128c2ecf20Sopenharmony_ci	} else { /* old isac */
8138c2ecf20Sopenharmony_ci		WriteISAC(isac, ISAC_MASK, 0xff);
8148c2ecf20Sopenharmony_ci		val = ReadISAC(isac, ISAC_STAR);
8158c2ecf20Sopenharmony_ci		pr_debug("%s: ISAC STAR %x\n", isac->name, val);
8168c2ecf20Sopenharmony_ci		val = ReadISAC(isac, ISAC_MODE);
8178c2ecf20Sopenharmony_ci		pr_debug("%s: ISAC MODE %x\n", isac->name, val);
8188c2ecf20Sopenharmony_ci		val = ReadISAC(isac, ISAC_ADF2);
8198c2ecf20Sopenharmony_ci		pr_debug("%s: ISAC ADF2 %x\n", isac->name, val);
8208c2ecf20Sopenharmony_ci		val = ReadISAC(isac, ISAC_ISTA);
8218c2ecf20Sopenharmony_ci		pr_debug("%s: ISAC ISTA %x\n", isac->name, val);
8228c2ecf20Sopenharmony_ci		if (val & 0x01) {
8238c2ecf20Sopenharmony_ci			val = ReadISAC(isac, ISAC_EXIR);
8248c2ecf20Sopenharmony_ci			pr_debug("%s: ISAC EXIR %x\n", isac->name, val);
8258c2ecf20Sopenharmony_ci		}
8268c2ecf20Sopenharmony_ci		val = ReadISAC(isac, ISAC_RBCH);
8278c2ecf20Sopenharmony_ci		if (isac->dch.debug & DEBUG_HW)
8288c2ecf20Sopenharmony_ci			pr_notice("%s: ISAC version (%x): %s\n", isac->name,
8298c2ecf20Sopenharmony_ci				  val, ISACVer[(val >> 5) & 3]);
8308c2ecf20Sopenharmony_ci		isac->type |= ((val >> 5) & 3);
8318c2ecf20Sopenharmony_ci		if (!isac->adf2)
8328c2ecf20Sopenharmony_ci			isac->adf2 = 0x80;
8338c2ecf20Sopenharmony_ci		if (!(isac->adf2 & 0x80)) { /* only IOM 2 Mode */
8348c2ecf20Sopenharmony_ci			pr_info("%s: only support IOM2 mode but adf2=%02x\n",
8358c2ecf20Sopenharmony_ci				isac->name, isac->adf2);
8368c2ecf20Sopenharmony_ci			isac_release(isac);
8378c2ecf20Sopenharmony_ci			return -EINVAL;
8388c2ecf20Sopenharmony_ci		}
8398c2ecf20Sopenharmony_ci		WriteISAC(isac, ISAC_ADF2, isac->adf2);
8408c2ecf20Sopenharmony_ci		WriteISAC(isac, ISAC_SQXR, 0x2f);
8418c2ecf20Sopenharmony_ci		WriteISAC(isac, ISAC_SPCR, 0x00);
8428c2ecf20Sopenharmony_ci		WriteISAC(isac, ISAC_STCR, 0x70);
8438c2ecf20Sopenharmony_ci		WriteISAC(isac, ISAC_MODE, 0xc9);
8448c2ecf20Sopenharmony_ci		WriteISAC(isac, ISAC_TIMR, 0x00);
8458c2ecf20Sopenharmony_ci		WriteISAC(isac, ISAC_ADF1, 0x00);
8468c2ecf20Sopenharmony_ci		val = ReadISAC(isac, ISAC_CIR0);
8478c2ecf20Sopenharmony_ci		pr_debug("%s: ISAC CIR0 %x\n", isac->name, val);
8488c2ecf20Sopenharmony_ci		isac->state = (val >> 2) & 0xf;
8498c2ecf20Sopenharmony_ci		isac_ph_state_change(isac);
8508c2ecf20Sopenharmony_ci		ph_command(isac, ISAC_CMD_RS);
8518c2ecf20Sopenharmony_ci		WriteISAC(isac, ISAC_MASK, 0);
8528c2ecf20Sopenharmony_ci	}
8538c2ecf20Sopenharmony_ci	return err;
8548c2ecf20Sopenharmony_ci}
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ciint
8578c2ecf20Sopenharmony_cimISDNisac_init(struct isac_hw *isac, void *hw)
8588c2ecf20Sopenharmony_ci{
8598c2ecf20Sopenharmony_ci	mISDN_initdchannel(&isac->dch, MAX_DFRAME_LEN_L1, isac_ph_state_bh);
8608c2ecf20Sopenharmony_ci	isac->dch.hw = hw;
8618c2ecf20Sopenharmony_ci	isac->dch.dev.D.send = isac_l1hw;
8628c2ecf20Sopenharmony_ci	isac->init = isac_init;
8638c2ecf20Sopenharmony_ci	isac->release = isac_release;
8648c2ecf20Sopenharmony_ci	isac->ctrl = isac_ctrl;
8658c2ecf20Sopenharmony_ci	isac->open = open_dchannel;
8668c2ecf20Sopenharmony_ci	isac->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0);
8678c2ecf20Sopenharmony_ci	isac->dch.dev.nrbchan = 2;
8688c2ecf20Sopenharmony_ci	return 0;
8698c2ecf20Sopenharmony_ci}
8708c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDNisac_init);
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_cistatic void
8738c2ecf20Sopenharmony_ciwaitforCEC(struct hscx_hw *hx)
8748c2ecf20Sopenharmony_ci{
8758c2ecf20Sopenharmony_ci	u8 starb, to = 50;
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	while (to) {
8788c2ecf20Sopenharmony_ci		starb = ReadHSCX(hx, IPAC_STARB);
8798c2ecf20Sopenharmony_ci		if (!(starb & 0x04))
8808c2ecf20Sopenharmony_ci			break;
8818c2ecf20Sopenharmony_ci		udelay(1);
8828c2ecf20Sopenharmony_ci		to--;
8838c2ecf20Sopenharmony_ci	}
8848c2ecf20Sopenharmony_ci	if (to < 50)
8858c2ecf20Sopenharmony_ci		pr_debug("%s: B%1d CEC %d us\n", hx->ip->name, hx->bch.nr,
8868c2ecf20Sopenharmony_ci			 50 - to);
8878c2ecf20Sopenharmony_ci	if (!to)
8888c2ecf20Sopenharmony_ci		pr_info("%s: B%1d CEC timeout\n", hx->ip->name, hx->bch.nr);
8898c2ecf20Sopenharmony_ci}
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_cistatic void
8938c2ecf20Sopenharmony_ciwaitforXFW(struct hscx_hw *hx)
8948c2ecf20Sopenharmony_ci{
8958c2ecf20Sopenharmony_ci	u8 starb, to = 50;
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	while (to) {
8988c2ecf20Sopenharmony_ci		starb = ReadHSCX(hx, IPAC_STARB);
8998c2ecf20Sopenharmony_ci		if ((starb & 0x44) == 0x40)
9008c2ecf20Sopenharmony_ci			break;
9018c2ecf20Sopenharmony_ci		udelay(1);
9028c2ecf20Sopenharmony_ci		to--;
9038c2ecf20Sopenharmony_ci	}
9048c2ecf20Sopenharmony_ci	if (to < 50)
9058c2ecf20Sopenharmony_ci		pr_debug("%s: B%1d XFW %d us\n", hx->ip->name, hx->bch.nr,
9068c2ecf20Sopenharmony_ci			 50 - to);
9078c2ecf20Sopenharmony_ci	if (!to)
9088c2ecf20Sopenharmony_ci		pr_info("%s: B%1d XFW timeout\n", hx->ip->name, hx->bch.nr);
9098c2ecf20Sopenharmony_ci}
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_cistatic void
9128c2ecf20Sopenharmony_cihscx_cmdr(struct hscx_hw *hx, u8 cmd)
9138c2ecf20Sopenharmony_ci{
9148c2ecf20Sopenharmony_ci	if (hx->ip->type & IPAC_TYPE_IPACX)
9158c2ecf20Sopenharmony_ci		WriteHSCX(hx, IPACX_CMDRB, cmd);
9168c2ecf20Sopenharmony_ci	else {
9178c2ecf20Sopenharmony_ci		waitforCEC(hx);
9188c2ecf20Sopenharmony_ci		WriteHSCX(hx, IPAC_CMDRB, cmd);
9198c2ecf20Sopenharmony_ci	}
9208c2ecf20Sopenharmony_ci}
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_cistatic void
9238c2ecf20Sopenharmony_cihscx_empty_fifo(struct hscx_hw *hscx, u8 count)
9248c2ecf20Sopenharmony_ci{
9258c2ecf20Sopenharmony_ci	u8 *p;
9268c2ecf20Sopenharmony_ci	int maxlen;
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	pr_debug("%s: B%1d %d\n", hscx->ip->name, hscx->bch.nr, count);
9298c2ecf20Sopenharmony_ci	if (test_bit(FLG_RX_OFF, &hscx->bch.Flags)) {
9308c2ecf20Sopenharmony_ci		hscx->bch.dropcnt += count;
9318c2ecf20Sopenharmony_ci		hscx_cmdr(hscx, 0x80); /* RMC */
9328c2ecf20Sopenharmony_ci		return;
9338c2ecf20Sopenharmony_ci	}
9348c2ecf20Sopenharmony_ci	maxlen = bchannel_get_rxbuf(&hscx->bch, count);
9358c2ecf20Sopenharmony_ci	if (maxlen < 0) {
9368c2ecf20Sopenharmony_ci		hscx_cmdr(hscx, 0x80); /* RMC */
9378c2ecf20Sopenharmony_ci		if (hscx->bch.rx_skb)
9388c2ecf20Sopenharmony_ci			skb_trim(hscx->bch.rx_skb, 0);
9398c2ecf20Sopenharmony_ci		pr_warn("%s.B%d: No bufferspace for %d bytes\n",
9408c2ecf20Sopenharmony_ci			hscx->ip->name, hscx->bch.nr, count);
9418c2ecf20Sopenharmony_ci		return;
9428c2ecf20Sopenharmony_ci	}
9438c2ecf20Sopenharmony_ci	p = skb_put(hscx->bch.rx_skb, count);
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	if (hscx->ip->type & IPAC_TYPE_IPACX)
9468c2ecf20Sopenharmony_ci		hscx->ip->read_fifo(hscx->ip->hw,
9478c2ecf20Sopenharmony_ci				    hscx->off + IPACX_RFIFOB, p, count);
9488c2ecf20Sopenharmony_ci	else
9498c2ecf20Sopenharmony_ci		hscx->ip->read_fifo(hscx->ip->hw,
9508c2ecf20Sopenharmony_ci				    hscx->off, p, count);
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	hscx_cmdr(hscx, 0x80); /* RMC */
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	if (hscx->bch.debug & DEBUG_HW_BFIFO) {
9558c2ecf20Sopenharmony_ci		snprintf(hscx->log, 64, "B%1d-recv %s %d ",
9568c2ecf20Sopenharmony_ci			 hscx->bch.nr, hscx->ip->name, count);
9578c2ecf20Sopenharmony_ci		print_hex_dump_bytes(hscx->log, DUMP_PREFIX_OFFSET, p, count);
9588c2ecf20Sopenharmony_ci	}
9598c2ecf20Sopenharmony_ci}
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_cistatic void
9628c2ecf20Sopenharmony_cihscx_fill_fifo(struct hscx_hw *hscx)
9638c2ecf20Sopenharmony_ci{
9648c2ecf20Sopenharmony_ci	int count, more;
9658c2ecf20Sopenharmony_ci	u8 *p;
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	if (!hscx->bch.tx_skb) {
9688c2ecf20Sopenharmony_ci		if (!test_bit(FLG_TX_EMPTY, &hscx->bch.Flags))
9698c2ecf20Sopenharmony_ci			return;
9708c2ecf20Sopenharmony_ci		count = hscx->fifo_size;
9718c2ecf20Sopenharmony_ci		more = 1;
9728c2ecf20Sopenharmony_ci		p = hscx->log;
9738c2ecf20Sopenharmony_ci		memset(p, hscx->bch.fill[0], count);
9748c2ecf20Sopenharmony_ci	} else {
9758c2ecf20Sopenharmony_ci		count = hscx->bch.tx_skb->len - hscx->bch.tx_idx;
9768c2ecf20Sopenharmony_ci		if (count <= 0)
9778c2ecf20Sopenharmony_ci			return;
9788c2ecf20Sopenharmony_ci		p = hscx->bch.tx_skb->data + hscx->bch.tx_idx;
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci		more = test_bit(FLG_TRANSPARENT, &hscx->bch.Flags) ? 1 : 0;
9818c2ecf20Sopenharmony_ci		if (count > hscx->fifo_size) {
9828c2ecf20Sopenharmony_ci			count = hscx->fifo_size;
9838c2ecf20Sopenharmony_ci			more = 1;
9848c2ecf20Sopenharmony_ci		}
9858c2ecf20Sopenharmony_ci		pr_debug("%s: B%1d %d/%d/%d\n", hscx->ip->name, hscx->bch.nr,
9868c2ecf20Sopenharmony_ci			 count, hscx->bch.tx_idx, hscx->bch.tx_skb->len);
9878c2ecf20Sopenharmony_ci		hscx->bch.tx_idx += count;
9888c2ecf20Sopenharmony_ci	}
9898c2ecf20Sopenharmony_ci	if (hscx->ip->type & IPAC_TYPE_IPACX)
9908c2ecf20Sopenharmony_ci		hscx->ip->write_fifo(hscx->ip->hw,
9918c2ecf20Sopenharmony_ci				     hscx->off + IPACX_XFIFOB, p, count);
9928c2ecf20Sopenharmony_ci	else {
9938c2ecf20Sopenharmony_ci		waitforXFW(hscx);
9948c2ecf20Sopenharmony_ci		hscx->ip->write_fifo(hscx->ip->hw,
9958c2ecf20Sopenharmony_ci				     hscx->off, p, count);
9968c2ecf20Sopenharmony_ci	}
9978c2ecf20Sopenharmony_ci	hscx_cmdr(hscx, more ? 0x08 : 0x0a);
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	if (hscx->bch.tx_skb && (hscx->bch.debug & DEBUG_HW_BFIFO)) {
10008c2ecf20Sopenharmony_ci		snprintf(hscx->log, 64, "B%1d-send %s %d ",
10018c2ecf20Sopenharmony_ci			 hscx->bch.nr, hscx->ip->name, count);
10028c2ecf20Sopenharmony_ci		print_hex_dump_bytes(hscx->log, DUMP_PREFIX_OFFSET, p, count);
10038c2ecf20Sopenharmony_ci	}
10048c2ecf20Sopenharmony_ci}
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_cistatic void
10078c2ecf20Sopenharmony_cihscx_xpr(struct hscx_hw *hx)
10088c2ecf20Sopenharmony_ci{
10098c2ecf20Sopenharmony_ci	if (hx->bch.tx_skb && hx->bch.tx_idx < hx->bch.tx_skb->len) {
10108c2ecf20Sopenharmony_ci		hscx_fill_fifo(hx);
10118c2ecf20Sopenharmony_ci	} else {
10128c2ecf20Sopenharmony_ci		dev_kfree_skb(hx->bch.tx_skb);
10138c2ecf20Sopenharmony_ci		if (get_next_bframe(&hx->bch)) {
10148c2ecf20Sopenharmony_ci			hscx_fill_fifo(hx);
10158c2ecf20Sopenharmony_ci			test_and_clear_bit(FLG_TX_EMPTY, &hx->bch.Flags);
10168c2ecf20Sopenharmony_ci		} else if (test_bit(FLG_TX_EMPTY, &hx->bch.Flags)) {
10178c2ecf20Sopenharmony_ci			hscx_fill_fifo(hx);
10188c2ecf20Sopenharmony_ci		}
10198c2ecf20Sopenharmony_ci	}
10208c2ecf20Sopenharmony_ci}
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_cistatic void
10238c2ecf20Sopenharmony_ciipac_rme(struct hscx_hw *hx)
10248c2ecf20Sopenharmony_ci{
10258c2ecf20Sopenharmony_ci	int count;
10268c2ecf20Sopenharmony_ci	u8 rstab;
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	if (hx->ip->type & IPAC_TYPE_IPACX)
10298c2ecf20Sopenharmony_ci		rstab = ReadHSCX(hx, IPACX_RSTAB);
10308c2ecf20Sopenharmony_ci	else
10318c2ecf20Sopenharmony_ci		rstab = ReadHSCX(hx, IPAC_RSTAB);
10328c2ecf20Sopenharmony_ci	pr_debug("%s: B%1d RSTAB %02x\n", hx->ip->name, hx->bch.nr, rstab);
10338c2ecf20Sopenharmony_ci	if ((rstab & 0xf0) != 0xa0) {
10348c2ecf20Sopenharmony_ci		/* !(VFR && !RDO && CRC && !RAB) */
10358c2ecf20Sopenharmony_ci		if (!(rstab & 0x80)) {
10368c2ecf20Sopenharmony_ci			if (hx->bch.debug & DEBUG_HW_BCHANNEL)
10378c2ecf20Sopenharmony_ci				pr_notice("%s: B%1d invalid frame\n",
10388c2ecf20Sopenharmony_ci					  hx->ip->name, hx->bch.nr);
10398c2ecf20Sopenharmony_ci		}
10408c2ecf20Sopenharmony_ci		if (rstab & 0x40) {
10418c2ecf20Sopenharmony_ci			if (hx->bch.debug & DEBUG_HW_BCHANNEL)
10428c2ecf20Sopenharmony_ci				pr_notice("%s: B%1d RDO proto=%x\n",
10438c2ecf20Sopenharmony_ci					  hx->ip->name, hx->bch.nr,
10448c2ecf20Sopenharmony_ci					  hx->bch.state);
10458c2ecf20Sopenharmony_ci		}
10468c2ecf20Sopenharmony_ci		if (!(rstab & 0x20)) {
10478c2ecf20Sopenharmony_ci			if (hx->bch.debug & DEBUG_HW_BCHANNEL)
10488c2ecf20Sopenharmony_ci				pr_notice("%s: B%1d CRC error\n",
10498c2ecf20Sopenharmony_ci					  hx->ip->name, hx->bch.nr);
10508c2ecf20Sopenharmony_ci		}
10518c2ecf20Sopenharmony_ci		hscx_cmdr(hx, 0x80); /* Do RMC */
10528c2ecf20Sopenharmony_ci		return;
10538c2ecf20Sopenharmony_ci	}
10548c2ecf20Sopenharmony_ci	if (hx->ip->type & IPAC_TYPE_IPACX)
10558c2ecf20Sopenharmony_ci		count = ReadHSCX(hx, IPACX_RBCLB);
10568c2ecf20Sopenharmony_ci	else
10578c2ecf20Sopenharmony_ci		count = ReadHSCX(hx, IPAC_RBCLB);
10588c2ecf20Sopenharmony_ci	count &= (hx->fifo_size - 1);
10598c2ecf20Sopenharmony_ci	if (count == 0)
10608c2ecf20Sopenharmony_ci		count = hx->fifo_size;
10618c2ecf20Sopenharmony_ci	hscx_empty_fifo(hx, count);
10628c2ecf20Sopenharmony_ci	if (!hx->bch.rx_skb)
10638c2ecf20Sopenharmony_ci		return;
10648c2ecf20Sopenharmony_ci	if (hx->bch.rx_skb->len < 2) {
10658c2ecf20Sopenharmony_ci		pr_debug("%s: B%1d frame to short %d\n",
10668c2ecf20Sopenharmony_ci			 hx->ip->name, hx->bch.nr, hx->bch.rx_skb->len);
10678c2ecf20Sopenharmony_ci		skb_trim(hx->bch.rx_skb, 0);
10688c2ecf20Sopenharmony_ci	} else {
10698c2ecf20Sopenharmony_ci		skb_trim(hx->bch.rx_skb, hx->bch.rx_skb->len - 1);
10708c2ecf20Sopenharmony_ci		recv_Bchannel(&hx->bch, 0, false);
10718c2ecf20Sopenharmony_ci	}
10728c2ecf20Sopenharmony_ci}
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_cistatic void
10758c2ecf20Sopenharmony_ciipac_irq(struct hscx_hw *hx, u8 ista)
10768c2ecf20Sopenharmony_ci{
10778c2ecf20Sopenharmony_ci	u8 istab, m, exirb = 0;
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci	if (hx->ip->type & IPAC_TYPE_IPACX)
10808c2ecf20Sopenharmony_ci		istab = ReadHSCX(hx, IPACX_ISTAB);
10818c2ecf20Sopenharmony_ci	else if (hx->ip->type & IPAC_TYPE_IPAC) {
10828c2ecf20Sopenharmony_ci		istab = ReadHSCX(hx, IPAC_ISTAB);
10838c2ecf20Sopenharmony_ci		m = (hx->bch.nr & 1) ? IPAC__EXA : IPAC__EXB;
10848c2ecf20Sopenharmony_ci		if (m & ista) {
10858c2ecf20Sopenharmony_ci			exirb = ReadHSCX(hx, IPAC_EXIRB);
10868c2ecf20Sopenharmony_ci			pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name,
10878c2ecf20Sopenharmony_ci				 hx->bch.nr, exirb);
10888c2ecf20Sopenharmony_ci		}
10898c2ecf20Sopenharmony_ci	} else if (hx->bch.nr & 2) { /* HSCX B */
10908c2ecf20Sopenharmony_ci		if (ista & (HSCX__EXA | HSCX__ICA))
10918c2ecf20Sopenharmony_ci			ipac_irq(&hx->ip->hscx[0], ista);
10928c2ecf20Sopenharmony_ci		if (ista & HSCX__EXB) {
10938c2ecf20Sopenharmony_ci			exirb = ReadHSCX(hx, IPAC_EXIRB);
10948c2ecf20Sopenharmony_ci			pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name,
10958c2ecf20Sopenharmony_ci				 hx->bch.nr, exirb);
10968c2ecf20Sopenharmony_ci		}
10978c2ecf20Sopenharmony_ci		istab = ista & 0xF8;
10988c2ecf20Sopenharmony_ci	} else { /* HSCX A */
10998c2ecf20Sopenharmony_ci		istab = ReadHSCX(hx, IPAC_ISTAB);
11008c2ecf20Sopenharmony_ci		if (ista & HSCX__EXA) {
11018c2ecf20Sopenharmony_ci			exirb = ReadHSCX(hx, IPAC_EXIRB);
11028c2ecf20Sopenharmony_ci			pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name,
11038c2ecf20Sopenharmony_ci				 hx->bch.nr, exirb);
11048c2ecf20Sopenharmony_ci		}
11058c2ecf20Sopenharmony_ci		istab = istab & 0xF8;
11068c2ecf20Sopenharmony_ci	}
11078c2ecf20Sopenharmony_ci	if (exirb & IPAC_B_XDU)
11088c2ecf20Sopenharmony_ci		istab |= IPACX_B_XDU;
11098c2ecf20Sopenharmony_ci	if (exirb & IPAC_B_RFO)
11108c2ecf20Sopenharmony_ci		istab |= IPACX_B_RFO;
11118c2ecf20Sopenharmony_ci	pr_debug("%s: B%1d ISTAB %02x\n", hx->ip->name, hx->bch.nr, istab);
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	if (!test_bit(FLG_ACTIVE, &hx->bch.Flags))
11148c2ecf20Sopenharmony_ci		return;
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	if (istab & IPACX_B_RME)
11178c2ecf20Sopenharmony_ci		ipac_rme(hx);
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci	if (istab & IPACX_B_RPF) {
11208c2ecf20Sopenharmony_ci		hscx_empty_fifo(hx, hx->fifo_size);
11218c2ecf20Sopenharmony_ci		if (test_bit(FLG_TRANSPARENT, &hx->bch.Flags))
11228c2ecf20Sopenharmony_ci			recv_Bchannel(&hx->bch, 0, false);
11238c2ecf20Sopenharmony_ci	}
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	if (istab & IPACX_B_RFO) {
11268c2ecf20Sopenharmony_ci		pr_debug("%s: B%1d RFO error\n", hx->ip->name, hx->bch.nr);
11278c2ecf20Sopenharmony_ci		hscx_cmdr(hx, 0x40);	/* RRES */
11288c2ecf20Sopenharmony_ci	}
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	if (istab & IPACX_B_XPR)
11318c2ecf20Sopenharmony_ci		hscx_xpr(hx);
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci	if (istab & IPACX_B_XDU) {
11348c2ecf20Sopenharmony_ci		if (test_bit(FLG_TRANSPARENT, &hx->bch.Flags)) {
11358c2ecf20Sopenharmony_ci			if (test_bit(FLG_FILLEMPTY, &hx->bch.Flags))
11368c2ecf20Sopenharmony_ci				test_and_set_bit(FLG_TX_EMPTY, &hx->bch.Flags);
11378c2ecf20Sopenharmony_ci			hscx_xpr(hx);
11388c2ecf20Sopenharmony_ci			return;
11398c2ecf20Sopenharmony_ci		}
11408c2ecf20Sopenharmony_ci		pr_debug("%s: B%1d XDU error at len %d\n", hx->ip->name,
11418c2ecf20Sopenharmony_ci			 hx->bch.nr, hx->bch.tx_idx);
11428c2ecf20Sopenharmony_ci		hx->bch.tx_idx = 0;
11438c2ecf20Sopenharmony_ci		hscx_cmdr(hx, 0x01);	/* XRES */
11448c2ecf20Sopenharmony_ci	}
11458c2ecf20Sopenharmony_ci}
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ciirqreturn_t
11488c2ecf20Sopenharmony_cimISDNipac_irq(struct ipac_hw *ipac, int maxloop)
11498c2ecf20Sopenharmony_ci{
11508c2ecf20Sopenharmony_ci	int cnt = maxloop + 1;
11518c2ecf20Sopenharmony_ci	u8 ista, istad;
11528c2ecf20Sopenharmony_ci	struct isac_hw  *isac = &ipac->isac;
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	if (ipac->type & IPAC_TYPE_IPACX) {
11558c2ecf20Sopenharmony_ci		ista = ReadIPAC(ipac, ISACX_ISTA);
11568c2ecf20Sopenharmony_ci		while (ista && --cnt) {
11578c2ecf20Sopenharmony_ci			pr_debug("%s: ISTA %02x\n", ipac->name, ista);
11588c2ecf20Sopenharmony_ci			if (ista & IPACX__ICA)
11598c2ecf20Sopenharmony_ci				ipac_irq(&ipac->hscx[0], ista);
11608c2ecf20Sopenharmony_ci			if (ista & IPACX__ICB)
11618c2ecf20Sopenharmony_ci				ipac_irq(&ipac->hscx[1], ista);
11628c2ecf20Sopenharmony_ci			if (ista & (ISACX__ICD | ISACX__CIC))
11638c2ecf20Sopenharmony_ci				mISDNisac_irq(&ipac->isac, ista);
11648c2ecf20Sopenharmony_ci			ista = ReadIPAC(ipac, ISACX_ISTA);
11658c2ecf20Sopenharmony_ci		}
11668c2ecf20Sopenharmony_ci	} else if (ipac->type & IPAC_TYPE_IPAC) {
11678c2ecf20Sopenharmony_ci		ista = ReadIPAC(ipac, IPAC_ISTA);
11688c2ecf20Sopenharmony_ci		while (ista && --cnt) {
11698c2ecf20Sopenharmony_ci			pr_debug("%s: ISTA %02x\n", ipac->name, ista);
11708c2ecf20Sopenharmony_ci			if (ista & (IPAC__ICD | IPAC__EXD)) {
11718c2ecf20Sopenharmony_ci				istad = ReadISAC(isac, ISAC_ISTA);
11728c2ecf20Sopenharmony_ci				pr_debug("%s: ISTAD %02x\n", ipac->name, istad);
11738c2ecf20Sopenharmony_ci				if (istad & IPAC_D_TIN2)
11748c2ecf20Sopenharmony_ci					pr_debug("%s TIN2 irq\n", ipac->name);
11758c2ecf20Sopenharmony_ci				if (ista & IPAC__EXD)
11768c2ecf20Sopenharmony_ci					istad |= 1; /* ISAC EXI */
11778c2ecf20Sopenharmony_ci				mISDNisac_irq(isac, istad);
11788c2ecf20Sopenharmony_ci			}
11798c2ecf20Sopenharmony_ci			if (ista & (IPAC__ICA | IPAC__EXA))
11808c2ecf20Sopenharmony_ci				ipac_irq(&ipac->hscx[0], ista);
11818c2ecf20Sopenharmony_ci			if (ista & (IPAC__ICB | IPAC__EXB))
11828c2ecf20Sopenharmony_ci				ipac_irq(&ipac->hscx[1], ista);
11838c2ecf20Sopenharmony_ci			ista = ReadIPAC(ipac, IPAC_ISTA);
11848c2ecf20Sopenharmony_ci		}
11858c2ecf20Sopenharmony_ci	} else if (ipac->type & IPAC_TYPE_HSCX) {
11868c2ecf20Sopenharmony_ci		while (--cnt) {
11878c2ecf20Sopenharmony_ci			ista = ReadIPAC(ipac, IPAC_ISTAB + ipac->hscx[1].off);
11888c2ecf20Sopenharmony_ci			pr_debug("%s: B2 ISTA %02x\n", ipac->name, ista);
11898c2ecf20Sopenharmony_ci			if (ista)
11908c2ecf20Sopenharmony_ci				ipac_irq(&ipac->hscx[1], ista);
11918c2ecf20Sopenharmony_ci			istad = ReadISAC(isac, ISAC_ISTA);
11928c2ecf20Sopenharmony_ci			pr_debug("%s: ISTAD %02x\n", ipac->name, istad);
11938c2ecf20Sopenharmony_ci			if (istad)
11948c2ecf20Sopenharmony_ci				mISDNisac_irq(isac, istad);
11958c2ecf20Sopenharmony_ci			if (0 == (ista | istad))
11968c2ecf20Sopenharmony_ci				break;
11978c2ecf20Sopenharmony_ci		}
11988c2ecf20Sopenharmony_ci	}
11998c2ecf20Sopenharmony_ci	if (cnt > maxloop) /* only for ISAC/HSCX without PCI IRQ test */
12008c2ecf20Sopenharmony_ci		return IRQ_NONE;
12018c2ecf20Sopenharmony_ci	if (cnt < maxloop)
12028c2ecf20Sopenharmony_ci		pr_debug("%s: %d irqloops cpu%d\n", ipac->name,
12038c2ecf20Sopenharmony_ci			 maxloop - cnt, smp_processor_id());
12048c2ecf20Sopenharmony_ci	if (maxloop && !cnt)
12058c2ecf20Sopenharmony_ci		pr_notice("%s: %d IRQ LOOP cpu%d\n", ipac->name,
12068c2ecf20Sopenharmony_ci			  maxloop, smp_processor_id());
12078c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
12088c2ecf20Sopenharmony_ci}
12098c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDNipac_irq);
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_cistatic int
12128c2ecf20Sopenharmony_cihscx_mode(struct hscx_hw *hscx, u32 bprotocol)
12138c2ecf20Sopenharmony_ci{
12148c2ecf20Sopenharmony_ci	pr_debug("%s: HSCX %c protocol %x-->%x ch %d\n", hscx->ip->name,
12158c2ecf20Sopenharmony_ci		 '@' + hscx->bch.nr, hscx->bch.state, bprotocol, hscx->bch.nr);
12168c2ecf20Sopenharmony_ci	if (hscx->ip->type & IPAC_TYPE_IPACX) {
12178c2ecf20Sopenharmony_ci		if (hscx->bch.nr & 1) { /* B1 and ICA */
12188c2ecf20Sopenharmony_ci			WriteIPAC(hscx->ip, ISACX_BCHA_TSDP_BC1, 0x80);
12198c2ecf20Sopenharmony_ci			WriteIPAC(hscx->ip, ISACX_BCHA_CR, 0x88);
12208c2ecf20Sopenharmony_ci		} else { /* B2 and ICB */
12218c2ecf20Sopenharmony_ci			WriteIPAC(hscx->ip, ISACX_BCHB_TSDP_BC1, 0x81);
12228c2ecf20Sopenharmony_ci			WriteIPAC(hscx->ip, ISACX_BCHB_CR, 0x88);
12238c2ecf20Sopenharmony_ci		}
12248c2ecf20Sopenharmony_ci		switch (bprotocol) {
12258c2ecf20Sopenharmony_ci		case ISDN_P_NONE: /* init */
12268c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPACX_MODEB, 0xC0);	/* rec off */
12278c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPACX_EXMB,  0x30);	/* std adj. */
12288c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPACX_MASKB, 0xFF);	/* ints off */
12298c2ecf20Sopenharmony_ci			hscx_cmdr(hscx, 0x41);
12308c2ecf20Sopenharmony_ci			test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags);
12318c2ecf20Sopenharmony_ci			test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
12328c2ecf20Sopenharmony_ci			break;
12338c2ecf20Sopenharmony_ci		case ISDN_P_B_RAW:
12348c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPACX_MODEB, 0x88);	/* ex trans */
12358c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPACX_EXMB,  0x00);	/* trans */
12368c2ecf20Sopenharmony_ci			hscx_cmdr(hscx, 0x41);
12378c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPACX_MASKB, IPACX_B_ON);
12388c2ecf20Sopenharmony_ci			test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
12398c2ecf20Sopenharmony_ci			break;
12408c2ecf20Sopenharmony_ci		case ISDN_P_B_HDLC:
12418c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPACX_MODEB, 0xC0);	/* trans */
12428c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPACX_EXMB,  0x00);	/* hdlc,crc */
12438c2ecf20Sopenharmony_ci			hscx_cmdr(hscx, 0x41);
12448c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPACX_MASKB, IPACX_B_ON);
12458c2ecf20Sopenharmony_ci			test_and_set_bit(FLG_HDLC, &hscx->bch.Flags);
12468c2ecf20Sopenharmony_ci			break;
12478c2ecf20Sopenharmony_ci		default:
12488c2ecf20Sopenharmony_ci			pr_info("%s: protocol not known %x\n", hscx->ip->name,
12498c2ecf20Sopenharmony_ci				bprotocol);
12508c2ecf20Sopenharmony_ci			return -ENOPROTOOPT;
12518c2ecf20Sopenharmony_ci		}
12528c2ecf20Sopenharmony_ci	} else if (hscx->ip->type & IPAC_TYPE_IPAC) { /* IPAC */
12538c2ecf20Sopenharmony_ci		WriteHSCX(hscx, IPAC_CCR1, 0x82);
12548c2ecf20Sopenharmony_ci		WriteHSCX(hscx, IPAC_CCR2, 0x30);
12558c2ecf20Sopenharmony_ci		WriteHSCX(hscx, IPAC_XCCR, 0x07);
12568c2ecf20Sopenharmony_ci		WriteHSCX(hscx, IPAC_RCCR, 0x07);
12578c2ecf20Sopenharmony_ci		WriteHSCX(hscx, IPAC_TSAX, hscx->slot);
12588c2ecf20Sopenharmony_ci		WriteHSCX(hscx, IPAC_TSAR, hscx->slot);
12598c2ecf20Sopenharmony_ci		switch (bprotocol) {
12608c2ecf20Sopenharmony_ci		case ISDN_P_NONE:
12618c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPAC_TSAX, 0x1F);
12628c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPAC_TSAR, 0x1F);
12638c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPAC_MODEB, 0x84);
12648c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPAC_CCR1, 0x82);
12658c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPAC_MASKB, 0xFF);	/* ints off */
12668c2ecf20Sopenharmony_ci			test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags);
12678c2ecf20Sopenharmony_ci			test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
12688c2ecf20Sopenharmony_ci			break;
12698c2ecf20Sopenharmony_ci		case ISDN_P_B_RAW:
12708c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPAC_MODEB, 0xe4);	/* ex trans */
12718c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPAC_CCR1, 0x82);
12728c2ecf20Sopenharmony_ci			hscx_cmdr(hscx, 0x41);
12738c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPAC_MASKB, 0);
12748c2ecf20Sopenharmony_ci			test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
12758c2ecf20Sopenharmony_ci			break;
12768c2ecf20Sopenharmony_ci		case ISDN_P_B_HDLC:
12778c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPAC_MODEB, 0x8c);
12788c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPAC_CCR1, 0x8a);
12798c2ecf20Sopenharmony_ci			hscx_cmdr(hscx, 0x41);
12808c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPAC_MASKB, 0);
12818c2ecf20Sopenharmony_ci			test_and_set_bit(FLG_HDLC, &hscx->bch.Flags);
12828c2ecf20Sopenharmony_ci			break;
12838c2ecf20Sopenharmony_ci		default:
12848c2ecf20Sopenharmony_ci			pr_info("%s: protocol not known %x\n", hscx->ip->name,
12858c2ecf20Sopenharmony_ci				bprotocol);
12868c2ecf20Sopenharmony_ci			return -ENOPROTOOPT;
12878c2ecf20Sopenharmony_ci		}
12888c2ecf20Sopenharmony_ci	} else if (hscx->ip->type & IPAC_TYPE_HSCX) { /* HSCX */
12898c2ecf20Sopenharmony_ci		WriteHSCX(hscx, IPAC_CCR1, 0x85);
12908c2ecf20Sopenharmony_ci		WriteHSCX(hscx, IPAC_CCR2, 0x30);
12918c2ecf20Sopenharmony_ci		WriteHSCX(hscx, IPAC_XCCR, 0x07);
12928c2ecf20Sopenharmony_ci		WriteHSCX(hscx, IPAC_RCCR, 0x07);
12938c2ecf20Sopenharmony_ci		WriteHSCX(hscx, IPAC_TSAX, hscx->slot);
12948c2ecf20Sopenharmony_ci		WriteHSCX(hscx, IPAC_TSAR, hscx->slot);
12958c2ecf20Sopenharmony_ci		switch (bprotocol) {
12968c2ecf20Sopenharmony_ci		case ISDN_P_NONE:
12978c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPAC_TSAX, 0x1F);
12988c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPAC_TSAR, 0x1F);
12998c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPAC_MODEB, 0x84);
13008c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPAC_CCR1, 0x85);
13018c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPAC_MASKB, 0xFF);	/* ints off */
13028c2ecf20Sopenharmony_ci			test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags);
13038c2ecf20Sopenharmony_ci			test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
13048c2ecf20Sopenharmony_ci			break;
13058c2ecf20Sopenharmony_ci		case ISDN_P_B_RAW:
13068c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPAC_MODEB, 0xe4);	/* ex trans */
13078c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPAC_CCR1, 0x85);
13088c2ecf20Sopenharmony_ci			hscx_cmdr(hscx, 0x41);
13098c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPAC_MASKB, 0);
13108c2ecf20Sopenharmony_ci			test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
13118c2ecf20Sopenharmony_ci			break;
13128c2ecf20Sopenharmony_ci		case ISDN_P_B_HDLC:
13138c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPAC_MODEB, 0x8c);
13148c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPAC_CCR1, 0x8d);
13158c2ecf20Sopenharmony_ci			hscx_cmdr(hscx, 0x41);
13168c2ecf20Sopenharmony_ci			WriteHSCX(hscx, IPAC_MASKB, 0);
13178c2ecf20Sopenharmony_ci			test_and_set_bit(FLG_HDLC, &hscx->bch.Flags);
13188c2ecf20Sopenharmony_ci			break;
13198c2ecf20Sopenharmony_ci		default:
13208c2ecf20Sopenharmony_ci			pr_info("%s: protocol not known %x\n", hscx->ip->name,
13218c2ecf20Sopenharmony_ci				bprotocol);
13228c2ecf20Sopenharmony_ci			return -ENOPROTOOPT;
13238c2ecf20Sopenharmony_ci		}
13248c2ecf20Sopenharmony_ci	} else
13258c2ecf20Sopenharmony_ci		return -EINVAL;
13268c2ecf20Sopenharmony_ci	hscx->bch.state = bprotocol;
13278c2ecf20Sopenharmony_ci	return 0;
13288c2ecf20Sopenharmony_ci}
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_cistatic int
13318c2ecf20Sopenharmony_cihscx_l2l1(struct mISDNchannel *ch, struct sk_buff *skb)
13328c2ecf20Sopenharmony_ci{
13338c2ecf20Sopenharmony_ci	struct bchannel *bch = container_of(ch, struct bchannel, ch);
13348c2ecf20Sopenharmony_ci	struct hscx_hw	*hx = container_of(bch, struct hscx_hw, bch);
13358c2ecf20Sopenharmony_ci	int ret = -EINVAL;
13368c2ecf20Sopenharmony_ci	struct mISDNhead *hh = mISDN_HEAD_P(skb);
13378c2ecf20Sopenharmony_ci	unsigned long flags;
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci	switch (hh->prim) {
13408c2ecf20Sopenharmony_ci	case PH_DATA_REQ:
13418c2ecf20Sopenharmony_ci		spin_lock_irqsave(hx->ip->hwlock, flags);
13428c2ecf20Sopenharmony_ci		ret = bchannel_senddata(bch, skb);
13438c2ecf20Sopenharmony_ci		if (ret > 0) { /* direct TX */
13448c2ecf20Sopenharmony_ci			ret = 0;
13458c2ecf20Sopenharmony_ci			hscx_fill_fifo(hx);
13468c2ecf20Sopenharmony_ci		}
13478c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(hx->ip->hwlock, flags);
13488c2ecf20Sopenharmony_ci		return ret;
13498c2ecf20Sopenharmony_ci	case PH_ACTIVATE_REQ:
13508c2ecf20Sopenharmony_ci		spin_lock_irqsave(hx->ip->hwlock, flags);
13518c2ecf20Sopenharmony_ci		if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
13528c2ecf20Sopenharmony_ci			ret = hscx_mode(hx, ch->protocol);
13538c2ecf20Sopenharmony_ci		else
13548c2ecf20Sopenharmony_ci			ret = 0;
13558c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(hx->ip->hwlock, flags);
13568c2ecf20Sopenharmony_ci		if (!ret)
13578c2ecf20Sopenharmony_ci			_queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
13588c2ecf20Sopenharmony_ci				    NULL, GFP_KERNEL);
13598c2ecf20Sopenharmony_ci		break;
13608c2ecf20Sopenharmony_ci	case PH_DEACTIVATE_REQ:
13618c2ecf20Sopenharmony_ci		spin_lock_irqsave(hx->ip->hwlock, flags);
13628c2ecf20Sopenharmony_ci		mISDN_clear_bchannel(bch);
13638c2ecf20Sopenharmony_ci		hscx_mode(hx, ISDN_P_NONE);
13648c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(hx->ip->hwlock, flags);
13658c2ecf20Sopenharmony_ci		_queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
13668c2ecf20Sopenharmony_ci			    NULL, GFP_KERNEL);
13678c2ecf20Sopenharmony_ci		ret = 0;
13688c2ecf20Sopenharmony_ci		break;
13698c2ecf20Sopenharmony_ci	default:
13708c2ecf20Sopenharmony_ci		pr_info("%s: %s unknown prim(%x,%x)\n",
13718c2ecf20Sopenharmony_ci			hx->ip->name, __func__, hh->prim, hh->id);
13728c2ecf20Sopenharmony_ci		ret = -EINVAL;
13738c2ecf20Sopenharmony_ci	}
13748c2ecf20Sopenharmony_ci	if (!ret)
13758c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
13768c2ecf20Sopenharmony_ci	return ret;
13778c2ecf20Sopenharmony_ci}
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_cistatic int
13808c2ecf20Sopenharmony_cichannel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
13818c2ecf20Sopenharmony_ci{
13828c2ecf20Sopenharmony_ci	return mISDN_ctrl_bchannel(bch, cq);
13838c2ecf20Sopenharmony_ci}
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_cistatic int
13868c2ecf20Sopenharmony_cihscx_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
13878c2ecf20Sopenharmony_ci{
13888c2ecf20Sopenharmony_ci	struct bchannel *bch = container_of(ch, struct bchannel, ch);
13898c2ecf20Sopenharmony_ci	struct hscx_hw	*hx = container_of(bch, struct hscx_hw, bch);
13908c2ecf20Sopenharmony_ci	int ret = -EINVAL;
13918c2ecf20Sopenharmony_ci	u_long flags;
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci	pr_debug("%s: %s cmd:%x %p\n", hx->ip->name, __func__, cmd, arg);
13948c2ecf20Sopenharmony_ci	switch (cmd) {
13958c2ecf20Sopenharmony_ci	case CLOSE_CHANNEL:
13968c2ecf20Sopenharmony_ci		test_and_clear_bit(FLG_OPEN, &bch->Flags);
13978c2ecf20Sopenharmony_ci		cancel_work_sync(&bch->workq);
13988c2ecf20Sopenharmony_ci		spin_lock_irqsave(hx->ip->hwlock, flags);
13998c2ecf20Sopenharmony_ci		mISDN_clear_bchannel(bch);
14008c2ecf20Sopenharmony_ci		hscx_mode(hx, ISDN_P_NONE);
14018c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(hx->ip->hwlock, flags);
14028c2ecf20Sopenharmony_ci		ch->protocol = ISDN_P_NONE;
14038c2ecf20Sopenharmony_ci		ch->peer = NULL;
14048c2ecf20Sopenharmony_ci		module_put(hx->ip->owner);
14058c2ecf20Sopenharmony_ci		ret = 0;
14068c2ecf20Sopenharmony_ci		break;
14078c2ecf20Sopenharmony_ci	case CONTROL_CHANNEL:
14088c2ecf20Sopenharmony_ci		ret = channel_bctrl(bch, arg);
14098c2ecf20Sopenharmony_ci		break;
14108c2ecf20Sopenharmony_ci	default:
14118c2ecf20Sopenharmony_ci		pr_info("%s: %s unknown prim(%x)\n",
14128c2ecf20Sopenharmony_ci			hx->ip->name, __func__, cmd);
14138c2ecf20Sopenharmony_ci	}
14148c2ecf20Sopenharmony_ci	return ret;
14158c2ecf20Sopenharmony_ci}
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_cistatic void
14188c2ecf20Sopenharmony_cifree_ipac(struct ipac_hw *ipac)
14198c2ecf20Sopenharmony_ci{
14208c2ecf20Sopenharmony_ci	isac_release(&ipac->isac);
14218c2ecf20Sopenharmony_ci}
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_cistatic const char *HSCXVer[] =
14248c2ecf20Sopenharmony_ci{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7",
14258c2ecf20Sopenharmony_ci "?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"};
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_cistatic void
14308c2ecf20Sopenharmony_cihscx_init(struct hscx_hw *hx)
14318c2ecf20Sopenharmony_ci{
14328c2ecf20Sopenharmony_ci	u8 val;
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ci	WriteHSCX(hx, IPAC_RAH2, 0xFF);
14358c2ecf20Sopenharmony_ci	WriteHSCX(hx, IPAC_XBCH, 0x00);
14368c2ecf20Sopenharmony_ci	WriteHSCX(hx, IPAC_RLCR, 0x00);
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_ci	if (hx->ip->type & IPAC_TYPE_HSCX) {
14398c2ecf20Sopenharmony_ci		WriteHSCX(hx, IPAC_CCR1, 0x85);
14408c2ecf20Sopenharmony_ci		val = ReadHSCX(hx, HSCX_VSTR);
14418c2ecf20Sopenharmony_ci		pr_debug("%s: HSCX VSTR %02x\n", hx->ip->name, val);
14428c2ecf20Sopenharmony_ci		if (hx->bch.debug & DEBUG_HW)
14438c2ecf20Sopenharmony_ci			pr_notice("%s: HSCX version %s\n", hx->ip->name,
14448c2ecf20Sopenharmony_ci				  HSCXVer[val & 0x0f]);
14458c2ecf20Sopenharmony_ci	} else
14468c2ecf20Sopenharmony_ci		WriteHSCX(hx, IPAC_CCR1, 0x82);
14478c2ecf20Sopenharmony_ci	WriteHSCX(hx, IPAC_CCR2, 0x30);
14488c2ecf20Sopenharmony_ci	WriteHSCX(hx, IPAC_XCCR, 0x07);
14498c2ecf20Sopenharmony_ci	WriteHSCX(hx, IPAC_RCCR, 0x07);
14508c2ecf20Sopenharmony_ci}
14518c2ecf20Sopenharmony_ci
14528c2ecf20Sopenharmony_cistatic int
14538c2ecf20Sopenharmony_ciipac_init(struct ipac_hw *ipac)
14548c2ecf20Sopenharmony_ci{
14558c2ecf20Sopenharmony_ci	u8 val;
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_ci	if (ipac->type & IPAC_TYPE_HSCX) {
14588c2ecf20Sopenharmony_ci		hscx_init(&ipac->hscx[0]);
14598c2ecf20Sopenharmony_ci		hscx_init(&ipac->hscx[1]);
14608c2ecf20Sopenharmony_ci		val = ReadIPAC(ipac, IPAC_ID);
14618c2ecf20Sopenharmony_ci	} else if (ipac->type & IPAC_TYPE_IPAC) {
14628c2ecf20Sopenharmony_ci		hscx_init(&ipac->hscx[0]);
14638c2ecf20Sopenharmony_ci		hscx_init(&ipac->hscx[1]);
14648c2ecf20Sopenharmony_ci		WriteIPAC(ipac, IPAC_MASK, IPAC__ON);
14658c2ecf20Sopenharmony_ci		val = ReadIPAC(ipac, IPAC_CONF);
14668c2ecf20Sopenharmony_ci		/* conf is default 0, but can be overwritten by card setup */
14678c2ecf20Sopenharmony_ci		pr_debug("%s: IPAC CONF %02x/%02x\n", ipac->name,
14688c2ecf20Sopenharmony_ci			 val, ipac->conf);
14698c2ecf20Sopenharmony_ci		WriteIPAC(ipac, IPAC_CONF, ipac->conf);
14708c2ecf20Sopenharmony_ci		val = ReadIPAC(ipac, IPAC_ID);
14718c2ecf20Sopenharmony_ci		if (ipac->hscx[0].bch.debug & DEBUG_HW)
14728c2ecf20Sopenharmony_ci			pr_notice("%s: IPAC Design ID %02x\n", ipac->name, val);
14738c2ecf20Sopenharmony_ci	}
14748c2ecf20Sopenharmony_ci	/* nothing special for IPACX to do here */
14758c2ecf20Sopenharmony_ci	return isac_init(&ipac->isac);
14768c2ecf20Sopenharmony_ci}
14778c2ecf20Sopenharmony_ci
14788c2ecf20Sopenharmony_cistatic int
14798c2ecf20Sopenharmony_ciopen_bchannel(struct ipac_hw *ipac, struct channel_req *rq)
14808c2ecf20Sopenharmony_ci{
14818c2ecf20Sopenharmony_ci	struct bchannel		*bch;
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci	if (rq->adr.channel == 0 || rq->adr.channel > 2)
14848c2ecf20Sopenharmony_ci		return -EINVAL;
14858c2ecf20Sopenharmony_ci	if (rq->protocol == ISDN_P_NONE)
14868c2ecf20Sopenharmony_ci		return -EINVAL;
14878c2ecf20Sopenharmony_ci	bch = &ipac->hscx[rq->adr.channel - 1].bch;
14888c2ecf20Sopenharmony_ci	if (test_and_set_bit(FLG_OPEN, &bch->Flags))
14898c2ecf20Sopenharmony_ci		return -EBUSY; /* b-channel can be only open once */
14908c2ecf20Sopenharmony_ci	test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);
14918c2ecf20Sopenharmony_ci	bch->ch.protocol = rq->protocol;
14928c2ecf20Sopenharmony_ci	rq->ch = &bch->ch;
14938c2ecf20Sopenharmony_ci	return 0;
14948c2ecf20Sopenharmony_ci}
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_cistatic int
14978c2ecf20Sopenharmony_cichannel_ctrl(struct ipac_hw *ipac, struct mISDN_ctrl_req *cq)
14988c2ecf20Sopenharmony_ci{
14998c2ecf20Sopenharmony_ci	int	ret = 0;
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_ci	switch (cq->op) {
15028c2ecf20Sopenharmony_ci	case MISDN_CTRL_GETOP:
15038c2ecf20Sopenharmony_ci		cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_L1_TIMER3;
15048c2ecf20Sopenharmony_ci		break;
15058c2ecf20Sopenharmony_ci	case MISDN_CTRL_LOOP:
15068c2ecf20Sopenharmony_ci		/* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */
15078c2ecf20Sopenharmony_ci		if (cq->channel < 0 || cq->channel > 3) {
15088c2ecf20Sopenharmony_ci			ret = -EINVAL;
15098c2ecf20Sopenharmony_ci			break;
15108c2ecf20Sopenharmony_ci		}
15118c2ecf20Sopenharmony_ci		ret = ipac->ctrl(ipac, HW_TESTLOOP, cq->channel);
15128c2ecf20Sopenharmony_ci		break;
15138c2ecf20Sopenharmony_ci	case MISDN_CTRL_L1_TIMER3:
15148c2ecf20Sopenharmony_ci		ret = ipac->isac.ctrl(&ipac->isac, HW_TIMER3_VALUE, cq->p1);
15158c2ecf20Sopenharmony_ci		break;
15168c2ecf20Sopenharmony_ci	default:
15178c2ecf20Sopenharmony_ci		pr_info("%s: unknown CTRL OP %x\n", ipac->name, cq->op);
15188c2ecf20Sopenharmony_ci		ret = -EINVAL;
15198c2ecf20Sopenharmony_ci		break;
15208c2ecf20Sopenharmony_ci	}
15218c2ecf20Sopenharmony_ci	return ret;
15228c2ecf20Sopenharmony_ci}
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_cistatic int
15258c2ecf20Sopenharmony_ciipac_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
15268c2ecf20Sopenharmony_ci{
15278c2ecf20Sopenharmony_ci	struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D);
15288c2ecf20Sopenharmony_ci	struct dchannel *dch = container_of(dev, struct dchannel, dev);
15298c2ecf20Sopenharmony_ci	struct isac_hw *isac = container_of(dch, struct isac_hw, dch);
15308c2ecf20Sopenharmony_ci	struct ipac_hw *ipac = container_of(isac, struct ipac_hw, isac);
15318c2ecf20Sopenharmony_ci	struct channel_req *rq;
15328c2ecf20Sopenharmony_ci	int err = 0;
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_ci	pr_debug("%s: DCTRL: %x %p\n", ipac->name, cmd, arg);
15358c2ecf20Sopenharmony_ci	switch (cmd) {
15368c2ecf20Sopenharmony_ci	case OPEN_CHANNEL:
15378c2ecf20Sopenharmony_ci		rq = arg;
15388c2ecf20Sopenharmony_ci		if (rq->protocol == ISDN_P_TE_S0)
15398c2ecf20Sopenharmony_ci			err = open_dchannel_caller(isac, rq, __builtin_return_address(0));
15408c2ecf20Sopenharmony_ci		else
15418c2ecf20Sopenharmony_ci			err = open_bchannel(ipac, rq);
15428c2ecf20Sopenharmony_ci		if (err)
15438c2ecf20Sopenharmony_ci			break;
15448c2ecf20Sopenharmony_ci		if (!try_module_get(ipac->owner))
15458c2ecf20Sopenharmony_ci			pr_info("%s: cannot get module\n", ipac->name);
15468c2ecf20Sopenharmony_ci		break;
15478c2ecf20Sopenharmony_ci	case CLOSE_CHANNEL:
15488c2ecf20Sopenharmony_ci		pr_debug("%s: dev(%d) close from %p\n", ipac->name,
15498c2ecf20Sopenharmony_ci			 dch->dev.id, __builtin_return_address(0));
15508c2ecf20Sopenharmony_ci		module_put(ipac->owner);
15518c2ecf20Sopenharmony_ci		break;
15528c2ecf20Sopenharmony_ci	case CONTROL_CHANNEL:
15538c2ecf20Sopenharmony_ci		err = channel_ctrl(ipac, arg);
15548c2ecf20Sopenharmony_ci		break;
15558c2ecf20Sopenharmony_ci	default:
15568c2ecf20Sopenharmony_ci		pr_debug("%s: unknown DCTRL command %x\n", ipac->name, cmd);
15578c2ecf20Sopenharmony_ci		return -EINVAL;
15588c2ecf20Sopenharmony_ci	}
15598c2ecf20Sopenharmony_ci	return err;
15608c2ecf20Sopenharmony_ci}
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ciu32
15638c2ecf20Sopenharmony_cimISDNipac_init(struct ipac_hw *ipac, void *hw)
15648c2ecf20Sopenharmony_ci{
15658c2ecf20Sopenharmony_ci	u32 ret;
15668c2ecf20Sopenharmony_ci	u8 i;
15678c2ecf20Sopenharmony_ci
15688c2ecf20Sopenharmony_ci	ipac->hw = hw;
15698c2ecf20Sopenharmony_ci	if (ipac->isac.dch.debug & DEBUG_HW)
15708c2ecf20Sopenharmony_ci		pr_notice("%s: ipac type %x\n", ipac->name, ipac->type);
15718c2ecf20Sopenharmony_ci	if (ipac->type & IPAC_TYPE_HSCX) {
15728c2ecf20Sopenharmony_ci		ipac->isac.type = IPAC_TYPE_ISAC;
15738c2ecf20Sopenharmony_ci		ipac->hscx[0].off = 0;
15748c2ecf20Sopenharmony_ci		ipac->hscx[1].off = 0x40;
15758c2ecf20Sopenharmony_ci		ipac->hscx[0].fifo_size = 32;
15768c2ecf20Sopenharmony_ci		ipac->hscx[1].fifo_size = 32;
15778c2ecf20Sopenharmony_ci	} else if (ipac->type & IPAC_TYPE_IPAC) {
15788c2ecf20Sopenharmony_ci		ipac->isac.type = IPAC_TYPE_IPAC | IPAC_TYPE_ISAC;
15798c2ecf20Sopenharmony_ci		ipac->hscx[0].off = 0;
15808c2ecf20Sopenharmony_ci		ipac->hscx[1].off = 0x40;
15818c2ecf20Sopenharmony_ci		ipac->hscx[0].fifo_size = 64;
15828c2ecf20Sopenharmony_ci		ipac->hscx[1].fifo_size = 64;
15838c2ecf20Sopenharmony_ci	} else if (ipac->type & IPAC_TYPE_IPACX) {
15848c2ecf20Sopenharmony_ci		ipac->isac.type = IPAC_TYPE_IPACX | IPAC_TYPE_ISACX;
15858c2ecf20Sopenharmony_ci		ipac->hscx[0].off = IPACX_OFF_ICA;
15868c2ecf20Sopenharmony_ci		ipac->hscx[1].off = IPACX_OFF_ICB;
15878c2ecf20Sopenharmony_ci		ipac->hscx[0].fifo_size = 64;
15888c2ecf20Sopenharmony_ci		ipac->hscx[1].fifo_size = 64;
15898c2ecf20Sopenharmony_ci	} else
15908c2ecf20Sopenharmony_ci		return 0;
15918c2ecf20Sopenharmony_ci
15928c2ecf20Sopenharmony_ci	mISDNisac_init(&ipac->isac, hw);
15938c2ecf20Sopenharmony_ci
15948c2ecf20Sopenharmony_ci	ipac->isac.dch.dev.D.ctrl = ipac_dctrl;
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++) {
15978c2ecf20Sopenharmony_ci		ipac->hscx[i].bch.nr = i + 1;
15988c2ecf20Sopenharmony_ci		set_channelmap(i + 1, ipac->isac.dch.dev.channelmap);
15998c2ecf20Sopenharmony_ci		list_add(&ipac->hscx[i].bch.ch.list,
16008c2ecf20Sopenharmony_ci			 &ipac->isac.dch.dev.bchannels);
16018c2ecf20Sopenharmony_ci		mISDN_initbchannel(&ipac->hscx[i].bch, MAX_DATA_MEM,
16028c2ecf20Sopenharmony_ci				   ipac->hscx[i].fifo_size);
16038c2ecf20Sopenharmony_ci		ipac->hscx[i].bch.ch.nr = i + 1;
16048c2ecf20Sopenharmony_ci		ipac->hscx[i].bch.ch.send = &hscx_l2l1;
16058c2ecf20Sopenharmony_ci		ipac->hscx[i].bch.ch.ctrl = hscx_bctrl;
16068c2ecf20Sopenharmony_ci		ipac->hscx[i].bch.hw = hw;
16078c2ecf20Sopenharmony_ci		ipac->hscx[i].ip = ipac;
16088c2ecf20Sopenharmony_ci		/* default values for IOM time slots
16098c2ecf20Sopenharmony_ci		 * can be overwritten by card */
16108c2ecf20Sopenharmony_ci		ipac->hscx[i].slot = (i == 0) ? 0x2f : 0x03;
16118c2ecf20Sopenharmony_ci	}
16128c2ecf20Sopenharmony_ci
16138c2ecf20Sopenharmony_ci	ipac->init = ipac_init;
16148c2ecf20Sopenharmony_ci	ipac->release = free_ipac;
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_ci	ret =	(1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
16178c2ecf20Sopenharmony_ci		(1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
16188c2ecf20Sopenharmony_ci	return ret;
16198c2ecf20Sopenharmony_ci}
16208c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDNipac_init);
16218c2ecf20Sopenharmony_ci
16228c2ecf20Sopenharmony_cistatic int __init
16238c2ecf20Sopenharmony_ciisac_mod_init(void)
16248c2ecf20Sopenharmony_ci{
16258c2ecf20Sopenharmony_ci	pr_notice("mISDNipac module version %s\n", ISAC_REV);
16268c2ecf20Sopenharmony_ci	return 0;
16278c2ecf20Sopenharmony_ci}
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_cistatic void __exit
16308c2ecf20Sopenharmony_ciisac_mod_cleanup(void)
16318c2ecf20Sopenharmony_ci{
16328c2ecf20Sopenharmony_ci	pr_notice("mISDNipac module unloaded\n");
16338c2ecf20Sopenharmony_ci}
16348c2ecf20Sopenharmony_cimodule_init(isac_mod_init);
16358c2ecf20Sopenharmony_cimodule_exit(isac_mod_cleanup);
1636