Path: blob/master/drivers/isdn/hardware/mISDN/mISDNipac.c
15112 views
/*1* isac.c ISAC specific routines2*3* Author Karsten Keil <[email protected]>4*5* Copyright 2009 by Karsten Keil <[email protected]>6*7* This program is free software; you can redistribute it and/or modify8* it under the terms of the GNU General Public License version 2 as9* published by the Free Software Foundation.10*11* This program is distributed in the hope that it will be useful,12* but WITHOUT ANY WARRANTY; without even the implied warranty of13* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the14* GNU General Public License for more details.15*16* You should have received a copy of the GNU General Public License17* along with this program; if not, write to the Free Software18* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.19*20*/2122#include <linux/slab.h>23#include <linux/module.h>24#include <linux/mISDNhw.h>25#include "ipac.h"262728#define DBUSY_TIMER_VALUE 8029#define ARCOFI_USE 13031#define ISAC_REV "2.0"3233MODULE_AUTHOR("Karsten Keil");34MODULE_VERSION(ISAC_REV);35MODULE_LICENSE("GPL v2");3637#define ReadISAC(is, o) (is->read_reg(is->dch.hw, o + is->off))38#define WriteISAC(is, o, v) (is->write_reg(is->dch.hw, o + is->off, v))39#define ReadHSCX(h, o) (h->ip->read_reg(h->ip->hw, h->off + o))40#define WriteHSCX(h, o, v) (h->ip->write_reg(h->ip->hw, h->off + o, v))41#define ReadIPAC(ip, o) (ip->read_reg(ip->hw, o))42#define WriteIPAC(ip, o, v) (ip->write_reg(ip->hw, o, v))4344static inline void45ph_command(struct isac_hw *isac, u8 command)46{47pr_debug("%s: ph_command %x\n", isac->name, command);48if (isac->type & IPAC_TYPE_ISACX)49WriteISAC(isac, ISACX_CIX0, (command << 4) | 0xE);50else51WriteISAC(isac, ISAC_CIX0, (command << 2) | 3);52}5354static void55isac_ph_state_change(struct isac_hw *isac)56{57switch (isac->state) {58case (ISAC_IND_RS):59case (ISAC_IND_EI):60ph_command(isac, ISAC_CMD_DUI);61}62schedule_event(&isac->dch, FLG_PHCHANGE);63}6465static void66isac_ph_state_bh(struct dchannel *dch)67{68struct isac_hw *isac = container_of(dch, struct isac_hw, dch);6970switch (isac->state) {71case ISAC_IND_RS:72case ISAC_IND_EI:73dch->state = 0;74l1_event(dch->l1, HW_RESET_IND);75break;76case ISAC_IND_DID:77dch->state = 3;78l1_event(dch->l1, HW_DEACT_CNF);79break;80case ISAC_IND_DR:81dch->state = 3;82l1_event(dch->l1, HW_DEACT_IND);83break;84case ISAC_IND_PU:85dch->state = 4;86l1_event(dch->l1, HW_POWERUP_IND);87break;88case ISAC_IND_RSY:89if (dch->state <= 5) {90dch->state = 5;91l1_event(dch->l1, ANYSIGNAL);92} else {93dch->state = 8;94l1_event(dch->l1, LOSTFRAMING);95}96break;97case ISAC_IND_ARD:98dch->state = 6;99l1_event(dch->l1, INFO2);100break;101case ISAC_IND_AI8:102dch->state = 7;103l1_event(dch->l1, INFO4_P8);104break;105case ISAC_IND_AI10:106dch->state = 7;107l1_event(dch->l1, INFO4_P10);108break;109}110pr_debug("%s: TE newstate %x\n", isac->name, dch->state);111}112113void114isac_empty_fifo(struct isac_hw *isac, int count)115{116u8 *ptr;117118pr_debug("%s: %s %d\n", isac->name, __func__, count);119120if (!isac->dch.rx_skb) {121isac->dch.rx_skb = mI_alloc_skb(isac->dch.maxlen, GFP_ATOMIC);122if (!isac->dch.rx_skb) {123pr_info("%s: D receive out of memory\n", isac->name);124WriteISAC(isac, ISAC_CMDR, 0x80);125return;126}127}128if ((isac->dch.rx_skb->len + count) >= isac->dch.maxlen) {129pr_debug("%s: %s overrun %d\n", isac->name, __func__,130isac->dch.rx_skb->len + count);131WriteISAC(isac, ISAC_CMDR, 0x80);132return;133}134ptr = skb_put(isac->dch.rx_skb, count);135isac->read_fifo(isac->dch.hw, isac->off, ptr, count);136WriteISAC(isac, ISAC_CMDR, 0x80);137if (isac->dch.debug & DEBUG_HW_DFIFO) {138char pfx[MISDN_MAX_IDLEN + 16];139140snprintf(pfx, MISDN_MAX_IDLEN + 15, "D-recv %s %d ",141isac->name, count);142print_hex_dump_bytes(pfx, DUMP_PREFIX_OFFSET, ptr, count);143}144}145146static void147isac_fill_fifo(struct isac_hw *isac)148{149int count, more;150u8 *ptr;151152if (!isac->dch.tx_skb)153return;154count = isac->dch.tx_skb->len - isac->dch.tx_idx;155if (count <= 0)156return;157158more = 0;159if (count > 32) {160more = !0;161count = 32;162}163pr_debug("%s: %s %d\n", isac->name, __func__, count);164ptr = isac->dch.tx_skb->data + isac->dch.tx_idx;165isac->dch.tx_idx += count;166isac->write_fifo(isac->dch.hw, isac->off, ptr, count);167WriteISAC(isac, ISAC_CMDR, more ? 0x8 : 0xa);168if (test_and_set_bit(FLG_BUSY_TIMER, &isac->dch.Flags)) {169pr_debug("%s: %s dbusytimer running\n", isac->name, __func__);170del_timer(&isac->dch.timer);171}172init_timer(&isac->dch.timer);173isac->dch.timer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);174add_timer(&isac->dch.timer);175if (isac->dch.debug & DEBUG_HW_DFIFO) {176char pfx[MISDN_MAX_IDLEN + 16];177178snprintf(pfx, MISDN_MAX_IDLEN + 15, "D-send %s %d ",179isac->name, count);180print_hex_dump_bytes(pfx, DUMP_PREFIX_OFFSET, ptr, count);181}182}183184static void185isac_rme_irq(struct isac_hw *isac)186{187u8 val, count;188189val = ReadISAC(isac, ISAC_RSTA);190if ((val & 0x70) != 0x20) {191if (val & 0x40) {192pr_debug("%s: ISAC RDO\n", isac->name);193#ifdef ERROR_STATISTIC194isac->dch.err_rx++;195#endif196}197if (!(val & 0x20)) {198pr_debug("%s: ISAC CRC error\n", isac->name);199#ifdef ERROR_STATISTIC200isac->dch.err_crc++;201#endif202}203WriteISAC(isac, ISAC_CMDR, 0x80);204if (isac->dch.rx_skb)205dev_kfree_skb(isac->dch.rx_skb);206isac->dch.rx_skb = NULL;207} else {208count = ReadISAC(isac, ISAC_RBCL) & 0x1f;209if (count == 0)210count = 32;211isac_empty_fifo(isac, count);212recv_Dchannel(&isac->dch);213}214}215216static void217isac_xpr_irq(struct isac_hw *isac)218{219if (test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags))220del_timer(&isac->dch.timer);221if (isac->dch.tx_skb && isac->dch.tx_idx < isac->dch.tx_skb->len) {222isac_fill_fifo(isac);223} else {224if (isac->dch.tx_skb)225dev_kfree_skb(isac->dch.tx_skb);226if (get_next_dframe(&isac->dch))227isac_fill_fifo(isac);228}229}230231static void232isac_retransmit(struct isac_hw *isac)233{234if (test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags))235del_timer(&isac->dch.timer);236if (test_bit(FLG_TX_BUSY, &isac->dch.Flags)) {237/* Restart frame */238isac->dch.tx_idx = 0;239isac_fill_fifo(isac);240} else if (isac->dch.tx_skb) { /* should not happen */241pr_info("%s: tx_skb exist but not busy\n", isac->name);242test_and_set_bit(FLG_TX_BUSY, &isac->dch.Flags);243isac->dch.tx_idx = 0;244isac_fill_fifo(isac);245} else {246pr_info("%s: ISAC XDU no TX_BUSY\n", isac->name);247if (get_next_dframe(&isac->dch))248isac_fill_fifo(isac);249}250}251252static void253isac_mos_irq(struct isac_hw *isac)254{255u8 val;256int ret;257258val = ReadISAC(isac, ISAC_MOSR);259pr_debug("%s: ISAC MOSR %02x\n", isac->name, val);260#if ARCOFI_USE261if (val & 0x08) {262if (!isac->mon_rx) {263isac->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC);264if (!isac->mon_rx) {265pr_info("%s: ISAC MON RX out of memory!\n",266isac->name);267isac->mocr &= 0xf0;268isac->mocr |= 0x0a;269WriteISAC(isac, ISAC_MOCR, isac->mocr);270goto afterMONR0;271} else272isac->mon_rxp = 0;273}274if (isac->mon_rxp >= MAX_MON_FRAME) {275isac->mocr &= 0xf0;276isac->mocr |= 0x0a;277WriteISAC(isac, ISAC_MOCR, isac->mocr);278isac->mon_rxp = 0;279pr_debug("%s: ISAC MON RX overflow!\n", isac->name);280goto afterMONR0;281}282isac->mon_rx[isac->mon_rxp++] = ReadISAC(isac, ISAC_MOR0);283pr_debug("%s: ISAC MOR0 %02x\n", isac->name,284isac->mon_rx[isac->mon_rxp - 1]);285if (isac->mon_rxp == 1) {286isac->mocr |= 0x04;287WriteISAC(isac, ISAC_MOCR, isac->mocr);288}289}290afterMONR0:291if (val & 0x80) {292if (!isac->mon_rx) {293isac->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC);294if (!isac->mon_rx) {295pr_info("%s: ISAC MON RX out of memory!\n",296isac->name);297isac->mocr &= 0x0f;298isac->mocr |= 0xa0;299WriteISAC(isac, ISAC_MOCR, isac->mocr);300goto afterMONR1;301} else302isac->mon_rxp = 0;303}304if (isac->mon_rxp >= MAX_MON_FRAME) {305isac->mocr &= 0x0f;306isac->mocr |= 0xa0;307WriteISAC(isac, ISAC_MOCR, isac->mocr);308isac->mon_rxp = 0;309pr_debug("%s: ISAC MON RX overflow!\n", isac->name);310goto afterMONR1;311}312isac->mon_rx[isac->mon_rxp++] = ReadISAC(isac, ISAC_MOR1);313pr_debug("%s: ISAC MOR1 %02x\n", isac->name,314isac->mon_rx[isac->mon_rxp - 1]);315isac->mocr |= 0x40;316WriteISAC(isac, ISAC_MOCR, isac->mocr);317}318afterMONR1:319if (val & 0x04) {320isac->mocr &= 0xf0;321WriteISAC(isac, ISAC_MOCR, isac->mocr);322isac->mocr |= 0x0a;323WriteISAC(isac, ISAC_MOCR, isac->mocr);324if (isac->monitor) {325ret = isac->monitor(isac->dch.hw, MONITOR_RX_0,326isac->mon_rx, isac->mon_rxp);327if (ret)328kfree(isac->mon_rx);329} else {330pr_info("%s: MONITOR 0 received %d but no user\n",331isac->name, isac->mon_rxp);332kfree(isac->mon_rx);333}334isac->mon_rx = NULL;335isac->mon_rxp = 0;336}337if (val & 0x40) {338isac->mocr &= 0x0f;339WriteISAC(isac, ISAC_MOCR, isac->mocr);340isac->mocr |= 0xa0;341WriteISAC(isac, ISAC_MOCR, isac->mocr);342if (isac->monitor) {343ret = isac->monitor(isac->dch.hw, MONITOR_RX_1,344isac->mon_rx, isac->mon_rxp);345if (ret)346kfree(isac->mon_rx);347} else {348pr_info("%s: MONITOR 1 received %d but no user\n",349isac->name, isac->mon_rxp);350kfree(isac->mon_rx);351}352isac->mon_rx = NULL;353isac->mon_rxp = 0;354}355if (val & 0x02) {356if ((!isac->mon_tx) || (isac->mon_txc &&357(isac->mon_txp >= isac->mon_txc) && !(val & 0x08))) {358isac->mocr &= 0xf0;359WriteISAC(isac, ISAC_MOCR, isac->mocr);360isac->mocr |= 0x0a;361WriteISAC(isac, ISAC_MOCR, isac->mocr);362if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {363if (isac->monitor)364ret = isac->monitor(isac->dch.hw,365MONITOR_TX_0, NULL, 0);366}367kfree(isac->mon_tx);368isac->mon_tx = NULL;369isac->mon_txc = 0;370isac->mon_txp = 0;371goto AfterMOX0;372}373if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {374if (isac->monitor)375ret = isac->monitor(isac->dch.hw,376MONITOR_TX_0, NULL, 0);377kfree(isac->mon_tx);378isac->mon_tx = NULL;379isac->mon_txc = 0;380isac->mon_txp = 0;381goto AfterMOX0;382}383WriteISAC(isac, ISAC_MOX0, isac->mon_tx[isac->mon_txp++]);384pr_debug("%s: ISAC %02x -> MOX0\n", isac->name,385isac->mon_tx[isac->mon_txp - 1]);386}387AfterMOX0:388if (val & 0x20) {389if ((!isac->mon_tx) || (isac->mon_txc &&390(isac->mon_txp >= isac->mon_txc) && !(val & 0x80))) {391isac->mocr &= 0x0f;392WriteISAC(isac, ISAC_MOCR, isac->mocr);393isac->mocr |= 0xa0;394WriteISAC(isac, ISAC_MOCR, isac->mocr);395if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {396if (isac->monitor)397ret = isac->monitor(isac->dch.hw,398MONITOR_TX_1, NULL, 0);399}400kfree(isac->mon_tx);401isac->mon_tx = NULL;402isac->mon_txc = 0;403isac->mon_txp = 0;404goto AfterMOX1;405}406if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {407if (isac->monitor)408ret = isac->monitor(isac->dch.hw,409MONITOR_TX_1, NULL, 0);410kfree(isac->mon_tx);411isac->mon_tx = NULL;412isac->mon_txc = 0;413isac->mon_txp = 0;414goto AfterMOX1;415}416WriteISAC(isac, ISAC_MOX1, isac->mon_tx[isac->mon_txp++]);417pr_debug("%s: ISAC %02x -> MOX1\n", isac->name,418isac->mon_tx[isac->mon_txp - 1]);419}420AfterMOX1:421val = 0; /* dummy to avoid warning */422#endif423}424425static void426isac_cisq_irq(struct isac_hw *isac) {427u8 val;428429val = ReadISAC(isac, ISAC_CIR0);430pr_debug("%s: ISAC CIR0 %02X\n", isac->name, val);431if (val & 2) {432pr_debug("%s: ph_state change %x->%x\n", isac->name,433isac->state, (val >> 2) & 0xf);434isac->state = (val >> 2) & 0xf;435isac_ph_state_change(isac);436}437if (val & 1) {438val = ReadISAC(isac, ISAC_CIR1);439pr_debug("%s: ISAC CIR1 %02X\n", isac->name, val);440}441}442443static void444isacsx_cic_irq(struct isac_hw *isac)445{446u8 val;447448val = ReadISAC(isac, ISACX_CIR0);449pr_debug("%s: ISACX CIR0 %02X\n", isac->name, val);450if (val & ISACX_CIR0_CIC0) {451pr_debug("%s: ph_state change %x->%x\n", isac->name,452isac->state, val >> 4);453isac->state = val >> 4;454isac_ph_state_change(isac);455}456}457458static void459isacsx_rme_irq(struct isac_hw *isac)460{461int count;462u8 val;463464val = ReadISAC(isac, ISACX_RSTAD);465if ((val & (ISACX_RSTAD_VFR |466ISACX_RSTAD_RDO |467ISACX_RSTAD_CRC |468ISACX_RSTAD_RAB))469!= (ISACX_RSTAD_VFR | ISACX_RSTAD_CRC)) {470pr_debug("%s: RSTAD %#x, dropped\n", isac->name, val);471#ifdef ERROR_STATISTIC472if (val & ISACX_RSTAD_CRC)473isac->dch.err_rx++;474else475isac->dch.err_crc++;476#endif477WriteISAC(isac, ISACX_CMDRD, ISACX_CMDRD_RMC);478if (isac->dch.rx_skb)479dev_kfree_skb(isac->dch.rx_skb);480isac->dch.rx_skb = NULL;481} else {482count = ReadISAC(isac, ISACX_RBCLD) & 0x1f;483if (count == 0)484count = 32;485isac_empty_fifo(isac, count);486if (isac->dch.rx_skb) {487skb_trim(isac->dch.rx_skb, isac->dch.rx_skb->len - 1);488pr_debug("%s: dchannel received %d\n", isac->name,489isac->dch.rx_skb->len);490recv_Dchannel(&isac->dch);491}492}493}494495irqreturn_t496mISDNisac_irq(struct isac_hw *isac, u8 val)497{498if (unlikely(!val))499return IRQ_NONE;500pr_debug("%s: ISAC interrupt %02x\n", isac->name, val);501if (isac->type & IPAC_TYPE_ISACX) {502if (val & ISACX__CIC)503isacsx_cic_irq(isac);504if (val & ISACX__ICD) {505val = ReadISAC(isac, ISACX_ISTAD);506pr_debug("%s: ISTAD %02x\n", isac->name, val);507if (val & ISACX_D_XDU) {508pr_debug("%s: ISAC XDU\n", isac->name);509#ifdef ERROR_STATISTIC510isac->dch.err_tx++;511#endif512isac_retransmit(isac);513}514if (val & ISACX_D_XMR) {515pr_debug("%s: ISAC XMR\n", isac->name);516#ifdef ERROR_STATISTIC517isac->dch.err_tx++;518#endif519isac_retransmit(isac);520}521if (val & ISACX_D_XPR)522isac_xpr_irq(isac);523if (val & ISACX_D_RFO) {524pr_debug("%s: ISAC RFO\n", isac->name);525WriteISAC(isac, ISACX_CMDRD, ISACX_CMDRD_RMC);526}527if (val & ISACX_D_RME)528isacsx_rme_irq(isac);529if (val & ISACX_D_RPF)530isac_empty_fifo(isac, 0x20);531}532} else {533if (val & 0x80) /* RME */534isac_rme_irq(isac);535if (val & 0x40) /* RPF */536isac_empty_fifo(isac, 32);537if (val & 0x10) /* XPR */538isac_xpr_irq(isac);539if (val & 0x04) /* CISQ */540isac_cisq_irq(isac);541if (val & 0x20) /* RSC - never */542pr_debug("%s: ISAC RSC interrupt\n", isac->name);543if (val & 0x02) /* SIN - never */544pr_debug("%s: ISAC SIN interrupt\n", isac->name);545if (val & 0x01) { /* EXI */546val = ReadISAC(isac, ISAC_EXIR);547pr_debug("%s: ISAC EXIR %02x\n", isac->name, val);548if (val & 0x80) /* XMR */549pr_debug("%s: ISAC XMR\n", isac->name);550if (val & 0x40) { /* XDU */551pr_debug("%s: ISAC XDU\n", isac->name);552#ifdef ERROR_STATISTIC553isac->dch.err_tx++;554#endif555isac_retransmit(isac);556}557if (val & 0x04) /* MOS */558isac_mos_irq(isac);559}560}561return IRQ_HANDLED;562}563EXPORT_SYMBOL(mISDNisac_irq);564565static int566isac_l1hw(struct mISDNchannel *ch, struct sk_buff *skb)567{568struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D);569struct dchannel *dch = container_of(dev, struct dchannel, dev);570struct isac_hw *isac = container_of(dch, struct isac_hw, dch);571int ret = -EINVAL;572struct mISDNhead *hh = mISDN_HEAD_P(skb);573u32 id;574u_long flags;575576switch (hh->prim) {577case PH_DATA_REQ:578spin_lock_irqsave(isac->hwlock, flags);579ret = dchannel_senddata(dch, skb);580if (ret > 0) { /* direct TX */581id = hh->id; /* skb can be freed */582isac_fill_fifo(isac);583ret = 0;584spin_unlock_irqrestore(isac->hwlock, flags);585queue_ch_frame(ch, PH_DATA_CNF, id, NULL);586} else587spin_unlock_irqrestore(isac->hwlock, flags);588return ret;589case PH_ACTIVATE_REQ:590ret = l1_event(dch->l1, hh->prim);591break;592case PH_DEACTIVATE_REQ:593test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags);594ret = l1_event(dch->l1, hh->prim);595break;596}597598if (!ret)599dev_kfree_skb(skb);600return ret;601}602603static int604isac_ctrl(struct isac_hw *isac, u32 cmd, u_long para)605{606u8 tl = 0;607u_long flags;608609switch (cmd) {610case HW_TESTLOOP:611spin_lock_irqsave(isac->hwlock, flags);612if (!(isac->type & IPAC_TYPE_ISACX)) {613/* TODO: implement for IPAC_TYPE_ISACX */614if (para & 1) /* B1 */615tl |= 0x0c;616else if (para & 2) /* B2 */617tl |= 0x3;618/* we only support IOM2 mode */619WriteISAC(isac, ISAC_SPCR, tl);620if (tl)621WriteISAC(isac, ISAC_ADF1, 0x8);622else623WriteISAC(isac, ISAC_ADF1, 0x0);624}625spin_unlock_irqrestore(isac->hwlock, flags);626break;627default:628pr_debug("%s: %s unknown command %x %lx\n", isac->name,629__func__, cmd, para);630return -1;631}632return 0;633}634635static int636isac_l1cmd(struct dchannel *dch, u32 cmd)637{638struct isac_hw *isac = container_of(dch, struct isac_hw, dch);639u_long flags;640641pr_debug("%s: cmd(%x) state(%02x)\n", isac->name, cmd, isac->state);642switch (cmd) {643case INFO3_P8:644spin_lock_irqsave(isac->hwlock, flags);645ph_command(isac, ISAC_CMD_AR8);646spin_unlock_irqrestore(isac->hwlock, flags);647break;648case INFO3_P10:649spin_lock_irqsave(isac->hwlock, flags);650ph_command(isac, ISAC_CMD_AR10);651spin_unlock_irqrestore(isac->hwlock, flags);652break;653case HW_RESET_REQ:654spin_lock_irqsave(isac->hwlock, flags);655if ((isac->state == ISAC_IND_EI) ||656(isac->state == ISAC_IND_DR) ||657(isac->state == ISAC_IND_RS))658ph_command(isac, ISAC_CMD_TIM);659else660ph_command(isac, ISAC_CMD_RS);661spin_unlock_irqrestore(isac->hwlock, flags);662break;663case HW_DEACT_REQ:664skb_queue_purge(&dch->squeue);665if (dch->tx_skb) {666dev_kfree_skb(dch->tx_skb);667dch->tx_skb = NULL;668}669dch->tx_idx = 0;670if (dch->rx_skb) {671dev_kfree_skb(dch->rx_skb);672dch->rx_skb = NULL;673}674test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);675if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))676del_timer(&dch->timer);677break;678case HW_POWERUP_REQ:679spin_lock_irqsave(isac->hwlock, flags);680ph_command(isac, ISAC_CMD_TIM);681spin_unlock_irqrestore(isac->hwlock, flags);682break;683case PH_ACTIVATE_IND:684test_and_set_bit(FLG_ACTIVE, &dch->Flags);685_queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,686GFP_ATOMIC);687break;688case PH_DEACTIVATE_IND:689test_and_clear_bit(FLG_ACTIVE, &dch->Flags);690_queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,691GFP_ATOMIC);692break;693default:694pr_debug("%s: %s unknown command %x\n", isac->name,695__func__, cmd);696return -1;697}698return 0;699}700701static void702isac_release(struct isac_hw *isac)703{704if (isac->type & IPAC_TYPE_ISACX)705WriteISAC(isac, ISACX_MASK, 0xff);706else707WriteISAC(isac, ISAC_MASK, 0xff);708if (isac->dch.timer.function != NULL) {709del_timer(&isac->dch.timer);710isac->dch.timer.function = NULL;711}712kfree(isac->mon_rx);713isac->mon_rx = NULL;714kfree(isac->mon_tx);715isac->mon_tx = NULL;716if (isac->dch.l1)717l1_event(isac->dch.l1, CLOSE_CHANNEL);718mISDN_freedchannel(&isac->dch);719}720721static void722dbusy_timer_handler(struct isac_hw *isac)723{724int rbch, star;725u_long flags;726727if (test_bit(FLG_BUSY_TIMER, &isac->dch.Flags)) {728spin_lock_irqsave(isac->hwlock, flags);729rbch = ReadISAC(isac, ISAC_RBCH);730star = ReadISAC(isac, ISAC_STAR);731pr_debug("%s: D-Channel Busy RBCH %02x STAR %02x\n",732isac->name, rbch, star);733if (rbch & ISAC_RBCH_XAC) /* D-Channel Busy */734test_and_set_bit(FLG_L1_BUSY, &isac->dch.Flags);735else {736/* discard frame; reset transceiver */737test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags);738if (isac->dch.tx_idx)739isac->dch.tx_idx = 0;740else741pr_info("%s: ISAC D-Channel Busy no tx_idx\n",742isac->name);743/* Transmitter reset */744WriteISAC(isac, ISAC_CMDR, 0x01);745}746spin_unlock_irqrestore(isac->hwlock, flags);747}748}749750static int751open_dchannel(struct isac_hw *isac, struct channel_req *rq)752{753pr_debug("%s: %s dev(%d) open from %p\n", isac->name, __func__,754isac->dch.dev.id, __builtin_return_address(1));755if (rq->protocol != ISDN_P_TE_S0)756return -EINVAL;757if (rq->adr.channel == 1)758/* E-Channel not supported */759return -EINVAL;760rq->ch = &isac->dch.dev.D;761rq->ch->protocol = rq->protocol;762if (isac->dch.state == 7)763_queue_data(rq->ch, PH_ACTIVATE_IND, MISDN_ID_ANY,7640, NULL, GFP_KERNEL);765return 0;766}767768static const char *ISACVer[] =769{"2086/2186 V1.1", "2085 B1", "2085 B2",770"2085 V2.3"};771772static int773isac_init(struct isac_hw *isac)774{775u8 val;776int err = 0;777778if (!isac->dch.l1) {779err = create_l1(&isac->dch, isac_l1cmd);780if (err)781return err;782}783isac->mon_tx = NULL;784isac->mon_rx = NULL;785isac->dch.timer.function = (void *) dbusy_timer_handler;786isac->dch.timer.data = (long)isac;787init_timer(&isac->dch.timer);788isac->mocr = 0xaa;789if (isac->type & IPAC_TYPE_ISACX) {790/* Disable all IRQ */791WriteISAC(isac, ISACX_MASK, 0xff);792val = ReadISAC(isac, ISACX_STARD);793pr_debug("%s: ISACX STARD %x\n", isac->name, val);794val = ReadISAC(isac, ISACX_ISTAD);795pr_debug("%s: ISACX ISTAD %x\n", isac->name, val);796val = ReadISAC(isac, ISACX_ISTA);797pr_debug("%s: ISACX ISTA %x\n", isac->name, val);798/* clear LDD */799WriteISAC(isac, ISACX_TR_CONF0, 0x00);800/* enable transmitter */801WriteISAC(isac, ISACX_TR_CONF2, 0x00);802/* transparent mode 0, RAC, stop/go */803WriteISAC(isac, ISACX_MODED, 0xc9);804/* all HDLC IRQ unmasked */805val = ReadISAC(isac, ISACX_ID);806if (isac->dch.debug & DEBUG_HW)807pr_notice("%s: ISACX Design ID %x\n",808isac->name, val & 0x3f);809val = ReadISAC(isac, ISACX_CIR0);810pr_debug("%s: ISACX CIR0 %02X\n", isac->name, val);811isac->state = val >> 4;812isac_ph_state_change(isac);813ph_command(isac, ISAC_CMD_RS);814WriteISAC(isac, ISACX_MASK, IPACX__ON);815WriteISAC(isac, ISACX_MASKD, 0x00);816} else { /* old isac */817WriteISAC(isac, ISAC_MASK, 0xff);818val = ReadISAC(isac, ISAC_STAR);819pr_debug("%s: ISAC STAR %x\n", isac->name, val);820val = ReadISAC(isac, ISAC_MODE);821pr_debug("%s: ISAC MODE %x\n", isac->name, val);822val = ReadISAC(isac, ISAC_ADF2);823pr_debug("%s: ISAC ADF2 %x\n", isac->name, val);824val = ReadISAC(isac, ISAC_ISTA);825pr_debug("%s: ISAC ISTA %x\n", isac->name, val);826if (val & 0x01) {827val = ReadISAC(isac, ISAC_EXIR);828pr_debug("%s: ISAC EXIR %x\n", isac->name, val);829}830val = ReadISAC(isac, ISAC_RBCH);831if (isac->dch.debug & DEBUG_HW)832pr_notice("%s: ISAC version (%x): %s\n", isac->name,833val, ISACVer[(val >> 5) & 3]);834isac->type |= ((val >> 5) & 3);835if (!isac->adf2)836isac->adf2 = 0x80;837if (!(isac->adf2 & 0x80)) { /* only IOM 2 Mode */838pr_info("%s: only support IOM2 mode but adf2=%02x\n",839isac->name, isac->adf2);840isac_release(isac);841return -EINVAL;842}843WriteISAC(isac, ISAC_ADF2, isac->adf2);844WriteISAC(isac, ISAC_SQXR, 0x2f);845WriteISAC(isac, ISAC_SPCR, 0x00);846WriteISAC(isac, ISAC_STCR, 0x70);847WriteISAC(isac, ISAC_MODE, 0xc9);848WriteISAC(isac, ISAC_TIMR, 0x00);849WriteISAC(isac, ISAC_ADF1, 0x00);850val = ReadISAC(isac, ISAC_CIR0);851pr_debug("%s: ISAC CIR0 %x\n", isac->name, val);852isac->state = (val >> 2) & 0xf;853isac_ph_state_change(isac);854ph_command(isac, ISAC_CMD_RS);855WriteISAC(isac, ISAC_MASK, 0);856}857return err;858}859860int861mISDNisac_init(struct isac_hw *isac, void *hw)862{863mISDN_initdchannel(&isac->dch, MAX_DFRAME_LEN_L1, isac_ph_state_bh);864isac->dch.hw = hw;865isac->dch.dev.D.send = isac_l1hw;866isac->init = isac_init;867isac->release = isac_release;868isac->ctrl = isac_ctrl;869isac->open = open_dchannel;870isac->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0);871isac->dch.dev.nrbchan = 2;872return 0;873}874EXPORT_SYMBOL(mISDNisac_init);875876static void877waitforCEC(struct hscx_hw *hx)878{879u8 starb, to = 50;880881while (to) {882starb = ReadHSCX(hx, IPAC_STARB);883if (!(starb & 0x04))884break;885udelay(1);886to--;887}888if (to < 50)889pr_debug("%s: B%1d CEC %d us\n", hx->ip->name, hx->bch.nr,89050 - to);891if (!to)892pr_info("%s: B%1d CEC timeout\n", hx->ip->name, hx->bch.nr);893}894895896static void897waitforXFW(struct hscx_hw *hx)898{899u8 starb, to = 50;900901while (to) {902starb = ReadHSCX(hx, IPAC_STARB);903if ((starb & 0x44) == 0x40)904break;905udelay(1);906to--;907}908if (to < 50)909pr_debug("%s: B%1d XFW %d us\n", hx->ip->name, hx->bch.nr,91050 - to);911if (!to)912pr_info("%s: B%1d XFW timeout\n", hx->ip->name, hx->bch.nr);913}914915static void916hscx_cmdr(struct hscx_hw *hx, u8 cmd)917{918if (hx->ip->type & IPAC_TYPE_IPACX)919WriteHSCX(hx, IPACX_CMDRB, cmd);920else {921waitforCEC(hx);922WriteHSCX(hx, IPAC_CMDRB, cmd);923}924}925926static void927hscx_empty_fifo(struct hscx_hw *hscx, u8 count)928{929u8 *p;930931pr_debug("%s: B%1d %d\n", hscx->ip->name, hscx->bch.nr, count);932if (!hscx->bch.rx_skb) {933hscx->bch.rx_skb = mI_alloc_skb(hscx->bch.maxlen, GFP_ATOMIC);934if (!hscx->bch.rx_skb) {935pr_info("%s: B receive out of memory\n",936hscx->ip->name);937hscx_cmdr(hscx, 0x80); /* RMC */938return;939}940}941if ((hscx->bch.rx_skb->len + count) > hscx->bch.maxlen) {942pr_debug("%s: overrun %d\n", hscx->ip->name,943hscx->bch.rx_skb->len + count);944skb_trim(hscx->bch.rx_skb, 0);945hscx_cmdr(hscx, 0x80); /* RMC */946return;947}948p = skb_put(hscx->bch.rx_skb, count);949950if (hscx->ip->type & IPAC_TYPE_IPACX)951hscx->ip->read_fifo(hscx->ip->hw,952hscx->off + IPACX_RFIFOB, p, count);953else954hscx->ip->read_fifo(hscx->ip->hw,955hscx->off, p, count);956957hscx_cmdr(hscx, 0x80); /* RMC */958959if (hscx->bch.debug & DEBUG_HW_BFIFO) {960snprintf(hscx->log, 64, "B%1d-recv %s %d ",961hscx->bch.nr, hscx->ip->name, count);962print_hex_dump_bytes(hscx->log, DUMP_PREFIX_OFFSET, p, count);963}964}965966static void967hscx_fill_fifo(struct hscx_hw *hscx)968{969int count, more;970u8 *p;971972if (!hscx->bch.tx_skb)973return;974count = hscx->bch.tx_skb->len - hscx->bch.tx_idx;975if (count <= 0)976return;977p = hscx->bch.tx_skb->data + hscx->bch.tx_idx;978979more = test_bit(FLG_TRANSPARENT, &hscx->bch.Flags) ? 1 : 0;980if (count > hscx->fifo_size) {981count = hscx->fifo_size;982more = 1;983}984pr_debug("%s: B%1d %d/%d/%d\n", hscx->ip->name, hscx->bch.nr, count,985hscx->bch.tx_idx, hscx->bch.tx_skb->len);986hscx->bch.tx_idx += count;987988if (hscx->ip->type & IPAC_TYPE_IPACX)989hscx->ip->write_fifo(hscx->ip->hw,990hscx->off + IPACX_XFIFOB, p, count);991else {992waitforXFW(hscx);993hscx->ip->write_fifo(hscx->ip->hw,994hscx->off, p, count);995}996hscx_cmdr(hscx, more ? 0x08 : 0x0a);997998if (hscx->bch.debug & DEBUG_HW_BFIFO) {999snprintf(hscx->log, 64, "B%1d-send %s %d ",1000hscx->bch.nr, hscx->ip->name, count);1001print_hex_dump_bytes(hscx->log, DUMP_PREFIX_OFFSET, p, count);1002}1003}10041005static void1006hscx_xpr(struct hscx_hw *hx)1007{1008if (hx->bch.tx_skb && hx->bch.tx_idx < hx->bch.tx_skb->len)1009hscx_fill_fifo(hx);1010else {1011if (hx->bch.tx_skb) {1012/* send confirm, on trans, free on hdlc. */1013if (test_bit(FLG_TRANSPARENT, &hx->bch.Flags))1014confirm_Bsend(&hx->bch);1015dev_kfree_skb(hx->bch.tx_skb);1016}1017if (get_next_bframe(&hx->bch))1018hscx_fill_fifo(hx);1019}1020}10211022static void1023ipac_rme(struct hscx_hw *hx)1024{1025int count;1026u8 rstab;10271028if (hx->ip->type & IPAC_TYPE_IPACX)1029rstab = ReadHSCX(hx, IPACX_RSTAB);1030else1031rstab = ReadHSCX(hx, IPAC_RSTAB);1032pr_debug("%s: B%1d RSTAB %02x\n", hx->ip->name, hx->bch.nr, rstab);1033if ((rstab & 0xf0) != 0xa0) {1034/* !(VFR && !RDO && CRC && !RAB) */1035if (!(rstab & 0x80)) {1036if (hx->bch.debug & DEBUG_HW_BCHANNEL)1037pr_notice("%s: B%1d invalid frame\n",1038hx->ip->name, hx->bch.nr);1039}1040if (rstab & 0x40) {1041if (hx->bch.debug & DEBUG_HW_BCHANNEL)1042pr_notice("%s: B%1d RDO proto=%x\n",1043hx->ip->name, hx->bch.nr,1044hx->bch.state);1045}1046if (!(rstab & 0x20)) {1047if (hx->bch.debug & DEBUG_HW_BCHANNEL)1048pr_notice("%s: B%1d CRC error\n",1049hx->ip->name, hx->bch.nr);1050}1051hscx_cmdr(hx, 0x80); /* Do RMC */1052return;1053}1054if (hx->ip->type & IPAC_TYPE_IPACX)1055count = ReadHSCX(hx, IPACX_RBCLB);1056else1057count = ReadHSCX(hx, IPAC_RBCLB);1058count &= (hx->fifo_size - 1);1059if (count == 0)1060count = hx->fifo_size;1061hscx_empty_fifo(hx, count);1062if (!hx->bch.rx_skb)1063return;1064if (hx->bch.rx_skb->len < 2) {1065pr_debug("%s: B%1d frame to short %d\n",1066hx->ip->name, hx->bch.nr, hx->bch.rx_skb->len);1067skb_trim(hx->bch.rx_skb, 0);1068} else {1069skb_trim(hx->bch.rx_skb, hx->bch.rx_skb->len - 1);1070recv_Bchannel(&hx->bch, 0);1071}1072}10731074static void1075ipac_irq(struct hscx_hw *hx, u8 ista)1076{1077u8 istab, m, exirb = 0;10781079if (hx->ip->type & IPAC_TYPE_IPACX)1080istab = ReadHSCX(hx, IPACX_ISTAB);1081else if (hx->ip->type & IPAC_TYPE_IPAC) {1082istab = ReadHSCX(hx, IPAC_ISTAB);1083m = (hx->bch.nr & 1) ? IPAC__EXA : IPAC__EXB;1084if (m & ista) {1085exirb = ReadHSCX(hx, IPAC_EXIRB);1086pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name,1087hx->bch.nr, exirb);1088}1089} else if (hx->bch.nr & 2) { /* HSCX B */1090if (ista & (HSCX__EXA | HSCX__ICA))1091ipac_irq(&hx->ip->hscx[0], ista);1092if (ista & HSCX__EXB) {1093exirb = ReadHSCX(hx, IPAC_EXIRB);1094pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name,1095hx->bch.nr, exirb);1096}1097istab = ista & 0xF8;1098} else { /* HSCX A */1099istab = ReadHSCX(hx, IPAC_ISTAB);1100if (ista & HSCX__EXA) {1101exirb = ReadHSCX(hx, IPAC_EXIRB);1102pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name,1103hx->bch.nr, exirb);1104}1105istab = istab & 0xF8;1106}1107if (exirb & IPAC_B_XDU)1108istab |= IPACX_B_XDU;1109if (exirb & IPAC_B_RFO)1110istab |= IPACX_B_RFO;1111pr_debug("%s: B%1d ISTAB %02x\n", hx->ip->name, hx->bch.nr, istab);11121113if (!test_bit(FLG_ACTIVE, &hx->bch.Flags))1114return;11151116if (istab & IPACX_B_RME)1117ipac_rme(hx);11181119if (istab & IPACX_B_RPF) {1120hscx_empty_fifo(hx, hx->fifo_size);1121if (test_bit(FLG_TRANSPARENT, &hx->bch.Flags)) {1122/* receive transparent audio data */1123if (hx->bch.rx_skb)1124recv_Bchannel(&hx->bch, 0);1125}1126}11271128if (istab & IPACX_B_RFO) {1129pr_debug("%s: B%1d RFO error\n", hx->ip->name, hx->bch.nr);1130hscx_cmdr(hx, 0x40); /* RRES */1131}11321133if (istab & IPACX_B_XPR)1134hscx_xpr(hx);11351136if (istab & IPACX_B_XDU) {1137if (test_bit(FLG_TRANSPARENT, &hx->bch.Flags)) {1138hscx_fill_fifo(hx);1139return;1140}1141pr_debug("%s: B%1d XDU error at len %d\n", hx->ip->name,1142hx->bch.nr, hx->bch.tx_idx);1143hx->bch.tx_idx = 0;1144hscx_cmdr(hx, 0x01); /* XRES */1145}1146}11471148irqreturn_t1149mISDNipac_irq(struct ipac_hw *ipac, int maxloop)1150{1151int cnt = maxloop + 1;1152u8 ista, istad;1153struct isac_hw *isac = &ipac->isac;11541155if (ipac->type & IPAC_TYPE_IPACX) {1156ista = ReadIPAC(ipac, ISACX_ISTA);1157while (ista && cnt--) {1158pr_debug("%s: ISTA %02x\n", ipac->name, ista);1159if (ista & IPACX__ICA)1160ipac_irq(&ipac->hscx[0], ista);1161if (ista & IPACX__ICB)1162ipac_irq(&ipac->hscx[1], ista);1163if (ista & (ISACX__ICD | ISACX__CIC))1164mISDNisac_irq(&ipac->isac, ista);1165ista = ReadIPAC(ipac, ISACX_ISTA);1166}1167} else if (ipac->type & IPAC_TYPE_IPAC) {1168ista = ReadIPAC(ipac, IPAC_ISTA);1169while (ista && cnt--) {1170pr_debug("%s: ISTA %02x\n", ipac->name, ista);1171if (ista & (IPAC__ICD | IPAC__EXD)) {1172istad = ReadISAC(isac, ISAC_ISTA);1173pr_debug("%s: ISTAD %02x\n", ipac->name, istad);1174if (istad & IPAC_D_TIN2)1175pr_debug("%s TIN2 irq\n", ipac->name);1176if (ista & IPAC__EXD)1177istad |= 1; /* ISAC EXI */1178mISDNisac_irq(isac, istad);1179}1180if (ista & (IPAC__ICA | IPAC__EXA))1181ipac_irq(&ipac->hscx[0], ista);1182if (ista & (IPAC__ICB | IPAC__EXB))1183ipac_irq(&ipac->hscx[1], ista);1184ista = ReadIPAC(ipac, IPAC_ISTA);1185}1186} else if (ipac->type & IPAC_TYPE_HSCX) {1187while (cnt) {1188ista = ReadIPAC(ipac, IPAC_ISTAB + ipac->hscx[1].off);1189pr_debug("%s: B2 ISTA %02x\n", ipac->name, ista);1190if (ista)1191ipac_irq(&ipac->hscx[1], ista);1192istad = ReadISAC(isac, ISAC_ISTA);1193pr_debug("%s: ISTAD %02x\n", ipac->name, istad);1194if (istad)1195mISDNisac_irq(isac, istad);1196if (0 == (ista | istad))1197break;1198cnt--;1199}1200}1201if (cnt > maxloop) /* only for ISAC/HSCX without PCI IRQ test */1202return IRQ_NONE;1203if (cnt < maxloop)1204pr_debug("%s: %d irqloops cpu%d\n", ipac->name,1205maxloop - cnt, smp_processor_id());1206if (maxloop && !cnt)1207pr_notice("%s: %d IRQ LOOP cpu%d\n", ipac->name,1208maxloop, smp_processor_id());1209return IRQ_HANDLED;1210}1211EXPORT_SYMBOL(mISDNipac_irq);12121213static int1214hscx_mode(struct hscx_hw *hscx, u32 bprotocol)1215{1216pr_debug("%s: HSCX %c protocol %x-->%x ch %d\n", hscx->ip->name,1217'@' + hscx->bch.nr, hscx->bch.state, bprotocol, hscx->bch.nr);1218if (hscx->ip->type & IPAC_TYPE_IPACX) {1219if (hscx->bch.nr & 1) { /* B1 and ICA */1220WriteIPAC(hscx->ip, ISACX_BCHA_TSDP_BC1, 0x80);1221WriteIPAC(hscx->ip, ISACX_BCHA_CR, 0x88);1222} else { /* B2 and ICB */1223WriteIPAC(hscx->ip, ISACX_BCHB_TSDP_BC1, 0x81);1224WriteIPAC(hscx->ip, ISACX_BCHB_CR, 0x88);1225}1226switch (bprotocol) {1227case ISDN_P_NONE: /* init */1228WriteHSCX(hscx, IPACX_MODEB, 0xC0); /* rec off */1229WriteHSCX(hscx, IPACX_EXMB, 0x30); /* std adj. */1230WriteHSCX(hscx, IPACX_MASKB, 0xFF); /* ints off */1231hscx_cmdr(hscx, 0x41);1232test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags);1233test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags);1234break;1235case ISDN_P_B_RAW:1236WriteHSCX(hscx, IPACX_MODEB, 0x88); /* ex trans */1237WriteHSCX(hscx, IPACX_EXMB, 0x00); /* trans */1238hscx_cmdr(hscx, 0x41);1239WriteHSCX(hscx, IPACX_MASKB, IPACX_B_ON);1240test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags);1241break;1242case ISDN_P_B_HDLC:1243WriteHSCX(hscx, IPACX_MODEB, 0xC0); /* trans */1244WriteHSCX(hscx, IPACX_EXMB, 0x00); /* hdlc,crc */1245hscx_cmdr(hscx, 0x41);1246WriteHSCX(hscx, IPACX_MASKB, IPACX_B_ON);1247test_and_set_bit(FLG_HDLC, &hscx->bch.Flags);1248break;1249default:1250pr_info("%s: protocol not known %x\n", hscx->ip->name,1251bprotocol);1252return -ENOPROTOOPT;1253}1254} else if (hscx->ip->type & IPAC_TYPE_IPAC) { /* IPAC */1255WriteHSCX(hscx, IPAC_CCR1, 0x82);1256WriteHSCX(hscx, IPAC_CCR2, 0x30);1257WriteHSCX(hscx, IPAC_XCCR, 0x07);1258WriteHSCX(hscx, IPAC_RCCR, 0x07);1259WriteHSCX(hscx, IPAC_TSAX, hscx->slot);1260WriteHSCX(hscx, IPAC_TSAR, hscx->slot);1261switch (bprotocol) {1262case ISDN_P_NONE:1263WriteHSCX(hscx, IPAC_TSAX, 0x1F);1264WriteHSCX(hscx, IPAC_TSAR, 0x1F);1265WriteHSCX(hscx, IPAC_MODEB, 0x84);1266WriteHSCX(hscx, IPAC_CCR1, 0x82);1267WriteHSCX(hscx, IPAC_MASKB, 0xFF); /* ints off */1268test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags);1269test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags);1270break;1271case ISDN_P_B_RAW:1272WriteHSCX(hscx, IPAC_MODEB, 0xe4); /* ex trans */1273WriteHSCX(hscx, IPAC_CCR1, 0x82);1274hscx_cmdr(hscx, 0x41);1275WriteHSCX(hscx, IPAC_MASKB, 0);1276test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags);1277break;1278case ISDN_P_B_HDLC:1279WriteHSCX(hscx, IPAC_MODEB, 0x8c);1280WriteHSCX(hscx, IPAC_CCR1, 0x8a);1281hscx_cmdr(hscx, 0x41);1282WriteHSCX(hscx, IPAC_MASKB, 0);1283test_and_set_bit(FLG_HDLC, &hscx->bch.Flags);1284break;1285default:1286pr_info("%s: protocol not known %x\n", hscx->ip->name,1287bprotocol);1288return -ENOPROTOOPT;1289}1290} else if (hscx->ip->type & IPAC_TYPE_HSCX) { /* HSCX */1291WriteHSCX(hscx, IPAC_CCR1, 0x85);1292WriteHSCX(hscx, IPAC_CCR2, 0x30);1293WriteHSCX(hscx, IPAC_XCCR, 0x07);1294WriteHSCX(hscx, IPAC_RCCR, 0x07);1295WriteHSCX(hscx, IPAC_TSAX, hscx->slot);1296WriteHSCX(hscx, IPAC_TSAR, hscx->slot);1297switch (bprotocol) {1298case ISDN_P_NONE:1299WriteHSCX(hscx, IPAC_TSAX, 0x1F);1300WriteHSCX(hscx, IPAC_TSAR, 0x1F);1301WriteHSCX(hscx, IPAC_MODEB, 0x84);1302WriteHSCX(hscx, IPAC_CCR1, 0x85);1303WriteHSCX(hscx, IPAC_MASKB, 0xFF); /* ints off */1304test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags);1305test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags);1306break;1307case ISDN_P_B_RAW:1308WriteHSCX(hscx, IPAC_MODEB, 0xe4); /* ex trans */1309WriteHSCX(hscx, IPAC_CCR1, 0x85);1310hscx_cmdr(hscx, 0x41);1311WriteHSCX(hscx, IPAC_MASKB, 0);1312test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags);1313break;1314case ISDN_P_B_HDLC:1315WriteHSCX(hscx, IPAC_MODEB, 0x8c);1316WriteHSCX(hscx, IPAC_CCR1, 0x8d);1317hscx_cmdr(hscx, 0x41);1318WriteHSCX(hscx, IPAC_MASKB, 0);1319test_and_set_bit(FLG_HDLC, &hscx->bch.Flags);1320break;1321default:1322pr_info("%s: protocol not known %x\n", hscx->ip->name,1323bprotocol);1324return -ENOPROTOOPT;1325}1326} else1327return -EINVAL;1328hscx->bch.state = bprotocol;1329return 0;1330}13311332static int1333hscx_l2l1(struct mISDNchannel *ch, struct sk_buff *skb)1334{1335struct bchannel *bch = container_of(ch, struct bchannel, ch);1336struct hscx_hw *hx = container_of(bch, struct hscx_hw, bch);1337int ret = -EINVAL;1338struct mISDNhead *hh = mISDN_HEAD_P(skb);1339u32 id;1340u_long flags;13411342switch (hh->prim) {1343case PH_DATA_REQ:1344spin_lock_irqsave(hx->ip->hwlock, flags);1345ret = bchannel_senddata(bch, skb);1346if (ret > 0) { /* direct TX */1347id = hh->id; /* skb can be freed */1348ret = 0;1349hscx_fill_fifo(hx);1350spin_unlock_irqrestore(hx->ip->hwlock, flags);1351if (!test_bit(FLG_TRANSPARENT, &bch->Flags))1352queue_ch_frame(ch, PH_DATA_CNF, id, NULL);1353} else1354spin_unlock_irqrestore(hx->ip->hwlock, flags);1355return ret;1356case PH_ACTIVATE_REQ:1357spin_lock_irqsave(hx->ip->hwlock, flags);1358if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))1359ret = hscx_mode(hx, ch->protocol);1360else1361ret = 0;1362spin_unlock_irqrestore(hx->ip->hwlock, flags);1363if (!ret)1364_queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,1365NULL, GFP_KERNEL);1366break;1367case PH_DEACTIVATE_REQ:1368spin_lock_irqsave(hx->ip->hwlock, flags);1369mISDN_clear_bchannel(bch);1370hscx_mode(hx, ISDN_P_NONE);1371spin_unlock_irqrestore(hx->ip->hwlock, flags);1372_queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,1373NULL, GFP_KERNEL);1374ret = 0;1375break;1376default:1377pr_info("%s: %s unknown prim(%x,%x)\n",1378hx->ip->name, __func__, hh->prim, hh->id);1379ret = -EINVAL;1380}1381if (!ret)1382dev_kfree_skb(skb);1383return ret;1384}13851386static int1387channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)1388{1389int ret = 0;13901391switch (cq->op) {1392case MISDN_CTRL_GETOP:1393cq->op = 0;1394break;1395/* Nothing implemented yet */1396case MISDN_CTRL_FILL_EMPTY:1397default:1398pr_info("%s: unknown Op %x\n", __func__, cq->op);1399ret = -EINVAL;1400break;1401}1402return ret;1403}14041405static int1406hscx_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)1407{1408struct bchannel *bch = container_of(ch, struct bchannel, ch);1409struct hscx_hw *hx = container_of(bch, struct hscx_hw, bch);1410int ret = -EINVAL;1411u_long flags;14121413pr_debug("%s: %s cmd:%x %p\n", hx->ip->name, __func__, cmd, arg);1414switch (cmd) {1415case CLOSE_CHANNEL:1416test_and_clear_bit(FLG_OPEN, &bch->Flags);1417if (test_bit(FLG_ACTIVE, &bch->Flags)) {1418spin_lock_irqsave(hx->ip->hwlock, flags);1419mISDN_freebchannel(bch);1420hscx_mode(hx, ISDN_P_NONE);1421spin_unlock_irqrestore(hx->ip->hwlock, flags);1422} else {1423skb_queue_purge(&bch->rqueue);1424bch->rcount = 0;1425}1426ch->protocol = ISDN_P_NONE;1427ch->peer = NULL;1428module_put(hx->ip->owner);1429ret = 0;1430break;1431case CONTROL_CHANNEL:1432ret = channel_bctrl(bch, arg);1433break;1434default:1435pr_info("%s: %s unknown prim(%x)\n",1436hx->ip->name, __func__, cmd);1437}1438return ret;1439}14401441static void1442free_ipac(struct ipac_hw *ipac)1443{1444isac_release(&ipac->isac);1445}14461447static const char *HSCXVer[] =1448{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7",1449"?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"};1450145114521453static void1454hscx_init(struct hscx_hw *hx)1455{1456u8 val;14571458WriteHSCX(hx, IPAC_RAH2, 0xFF);1459WriteHSCX(hx, IPAC_XBCH, 0x00);1460WriteHSCX(hx, IPAC_RLCR, 0x00);14611462if (hx->ip->type & IPAC_TYPE_HSCX) {1463WriteHSCX(hx, IPAC_CCR1, 0x85);1464val = ReadHSCX(hx, HSCX_VSTR);1465pr_debug("%s: HSCX VSTR %02x\n", hx->ip->name, val);1466if (hx->bch.debug & DEBUG_HW)1467pr_notice("%s: HSCX version %s\n", hx->ip->name,1468HSCXVer[val & 0x0f]);1469} else1470WriteHSCX(hx, IPAC_CCR1, 0x82);1471WriteHSCX(hx, IPAC_CCR2, 0x30);1472WriteHSCX(hx, IPAC_XCCR, 0x07);1473WriteHSCX(hx, IPAC_RCCR, 0x07);1474}14751476static int1477ipac_init(struct ipac_hw *ipac)1478{1479u8 val;14801481if (ipac->type & IPAC_TYPE_HSCX) {1482hscx_init(&ipac->hscx[0]);1483hscx_init(&ipac->hscx[1]);1484val = ReadIPAC(ipac, IPAC_ID);1485} else if (ipac->type & IPAC_TYPE_IPAC) {1486hscx_init(&ipac->hscx[0]);1487hscx_init(&ipac->hscx[1]);1488WriteIPAC(ipac, IPAC_MASK, IPAC__ON);1489val = ReadIPAC(ipac, IPAC_CONF);1490/* conf is default 0, but can be overwritten by card setup */1491pr_debug("%s: IPAC CONF %02x/%02x\n", ipac->name,1492val, ipac->conf);1493WriteIPAC(ipac, IPAC_CONF, ipac->conf);1494val = ReadIPAC(ipac, IPAC_ID);1495if (ipac->hscx[0].bch.debug & DEBUG_HW)1496pr_notice("%s: IPAC Design ID %02x\n", ipac->name, val);1497}1498/* nothing special for IPACX to do here */1499return isac_init(&ipac->isac);1500}15011502static int1503open_bchannel(struct ipac_hw *ipac, struct channel_req *rq)1504{1505struct bchannel *bch;15061507if (rq->adr.channel > 2)1508return -EINVAL;1509if (rq->protocol == ISDN_P_NONE)1510return -EINVAL;1511bch = &ipac->hscx[rq->adr.channel - 1].bch;1512if (test_and_set_bit(FLG_OPEN, &bch->Flags))1513return -EBUSY; /* b-channel can be only open once */1514test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);1515bch->ch.protocol = rq->protocol;1516rq->ch = &bch->ch;1517return 0;1518}15191520static int1521channel_ctrl(struct ipac_hw *ipac, struct mISDN_ctrl_req *cq)1522{1523int ret = 0;15241525switch (cq->op) {1526case MISDN_CTRL_GETOP:1527cq->op = MISDN_CTRL_LOOP;1528break;1529case MISDN_CTRL_LOOP:1530/* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */1531if (cq->channel < 0 || cq->channel > 3) {1532ret = -EINVAL;1533break;1534}1535ret = ipac->ctrl(ipac, HW_TESTLOOP, cq->channel);1536break;1537default:1538pr_info("%s: unknown CTRL OP %x\n", ipac->name, cq->op);1539ret = -EINVAL;1540break;1541}1542return ret;1543}15441545static int1546ipac_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg)1547{1548struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D);1549struct dchannel *dch = container_of(dev, struct dchannel, dev);1550struct isac_hw *isac = container_of(dch, struct isac_hw, dch);1551struct ipac_hw *ipac = container_of(isac, struct ipac_hw, isac);1552struct channel_req *rq;1553int err = 0;15541555pr_debug("%s: DCTRL: %x %p\n", ipac->name, cmd, arg);1556switch (cmd) {1557case OPEN_CHANNEL:1558rq = arg;1559if (rq->protocol == ISDN_P_TE_S0)1560err = open_dchannel(isac, rq);1561else1562err = open_bchannel(ipac, rq);1563if (err)1564break;1565if (!try_module_get(ipac->owner))1566pr_info("%s: cannot get module\n", ipac->name);1567break;1568case CLOSE_CHANNEL:1569pr_debug("%s: dev(%d) close from %p\n", ipac->name,1570dch->dev.id, __builtin_return_address(0));1571module_put(ipac->owner);1572break;1573case CONTROL_CHANNEL:1574err = channel_ctrl(ipac, arg);1575break;1576default:1577pr_debug("%s: unknown DCTRL command %x\n", ipac->name, cmd);1578return -EINVAL;1579}1580return err;1581}15821583u321584mISDNipac_init(struct ipac_hw *ipac, void *hw)1585{1586u32 ret;1587u8 i;15881589ipac->hw = hw;1590if (ipac->isac.dch.debug & DEBUG_HW)1591pr_notice("%s: ipac type %x\n", ipac->name, ipac->type);1592if (ipac->type & IPAC_TYPE_HSCX) {1593ipac->isac.type = IPAC_TYPE_ISAC;1594ipac->hscx[0].off = 0;1595ipac->hscx[1].off = 0x40;1596ipac->hscx[0].fifo_size = 32;1597ipac->hscx[1].fifo_size = 32;1598} else if (ipac->type & IPAC_TYPE_IPAC) {1599ipac->isac.type = IPAC_TYPE_IPAC | IPAC_TYPE_ISAC;1600ipac->hscx[0].off = 0;1601ipac->hscx[1].off = 0x40;1602ipac->hscx[0].fifo_size = 64;1603ipac->hscx[1].fifo_size = 64;1604} else if (ipac->type & IPAC_TYPE_IPACX) {1605ipac->isac.type = IPAC_TYPE_IPACX | IPAC_TYPE_ISACX;1606ipac->hscx[0].off = IPACX_OFF_ICA;1607ipac->hscx[1].off = IPACX_OFF_ICB;1608ipac->hscx[0].fifo_size = 64;1609ipac->hscx[1].fifo_size = 64;1610} else1611return 0;16121613mISDNisac_init(&ipac->isac, hw);16141615ipac->isac.dch.dev.D.ctrl = ipac_dctrl;16161617for (i = 0; i < 2; i++) {1618ipac->hscx[i].bch.nr = i + 1;1619set_channelmap(i + 1, ipac->isac.dch.dev.channelmap);1620list_add(&ipac->hscx[i].bch.ch.list,1621&ipac->isac.dch.dev.bchannels);1622mISDN_initbchannel(&ipac->hscx[i].bch, MAX_DATA_MEM);1623ipac->hscx[i].bch.ch.nr = i + 1;1624ipac->hscx[i].bch.ch.send = &hscx_l2l1;1625ipac->hscx[i].bch.ch.ctrl = hscx_bctrl;1626ipac->hscx[i].bch.hw = hw;1627ipac->hscx[i].ip = ipac;1628/* default values for IOM time slots1629* can be overwriten by card */1630ipac->hscx[i].slot = (i == 0) ? 0x2f : 0x03;1631}16321633ipac->init = ipac_init;1634ipac->release = free_ipac;16351636ret = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |1637(1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));1638return ret;1639}1640EXPORT_SYMBOL(mISDNipac_init);16411642static int __init1643isac_mod_init(void)1644{1645pr_notice("mISDNipac module version %s\n", ISAC_REV);1646return 0;1647}16481649static void __exit1650isac_mod_cleanup(void)1651{1652pr_notice("mISDNipac module unloaded\n");1653}1654module_init(isac_mod_init);1655module_exit(isac_mod_cleanup);165616571658