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