Commit 8ac84dd1 authored by Kai Germaschewski's avatar Kai Germaschewski

Start new driver for HFC PCI based cards.

parent 0dc68d77
......@@ -84,6 +84,7 @@ if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then
dep_tristate 'ST5481 USB ISDN modem (EXPERIMENTAL)' CONFIG_HISAX_ST5481 $CONFIG_HISAX $CONFIG_USB $CONFIG_EXPERIMENTAL
dep_tristate 'AVM Fritz!Card PCI/PCIv2/PnP support (EXPERIMENTAL)' CONFIG_HISAX_FRITZ_PCIPNP $CONFIG_HISAX $CONFIG_EXPERIMENTAL
dep_tristate 'AVM Fritz!Card classic support (EXPERIMENTAL)' CONFIG_HISAX_FRITZ_CLASSIC $CONFIG_HISAX $CONFIG_EXPERIMENTAL
dep_tristate 'HFC PCI support (EXPERIMENTAL)' CONFIG_HISAX_HFCPCI $CONFIG_HISAX $CONFIG_EXPERIMENTAL
fi
endmenu
......
......@@ -64,6 +64,7 @@ obj-$(CONFIG_HISAX_ELSA_CS) += elsa_cs.o
obj-$(CONFIG_HISAX_ST5481) += hisax_st5481.o
obj-$(CONFIG_HISAX_FRITZ_PCIPNP) += hisax_isac.o hisax_fcpcipnp.o
obj-$(CONFIG_HISAX_FRITZ_CLASSIC) += hisax_isac.o hisax_hscx.o hisax_fcclassic.o
obj-$(CONFIG_HISAX_FRITZ_PCIPNP) += hisax_hfcpci.o
CERT := $(shell md5sum -c md5sums.asc >> /dev/null;echo $$?)
CFLAGS_cert.o := -DCERTIFICATION=$(CERT)
......
/*
* Driver for HFC PCI based cards
*
* Author Kai Germaschewski
* Copyright 2002 by Kai Germaschewski <kai.germaschewski@gmx.de>
* 2000 by Karsten Keil <keil@isdn4linux.de>
* 2000 by Werner Cornelius <werner@isdn4linux.de>
*
* based upon Werner Cornelius's original hfc_pci.c driver
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/isapnp.h>
#include <linux/kmod.h>
#include <linux/slab.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <asm/delay.h>
#include "hisax_hfcpci.h"
// debugging cruft
#define __debug_variable debug
#include "hisax_debug.h"
#ifdef CONFIG_HISAX_DEBUG
static int debug = 0;
MODULE_PARM(debug, "i");
#endif
MODULE_AUTHOR("Kai Germaschewski <kai.germaschewski@gmx.de>/Werner Cornelius <werner@isdn4linux.de>");
MODULE_DESCRIPTION("HFC PCI ISDN driver");
#define ID(ven, dev, name) \
{ vendor: PCI_VENDOR_ID_##ven, \
device: PCI_DEVICE_ID_##dev, \
subvendor: PCI_ANY_ID, \
subdevice: PCI_ANY_ID, \
class: 0, \
class_mask: 0, \
driver_data: (unsigned long) name }
static struct pci_device_id hfcpci_ids[] __devinitdata = {
ID(CCD, CCD_2BD0, "CCD/Billion/Asuscom 2BD0"),
ID(CCD, CCD_B000, "Billion B000"),
ID(CCD, CCD_B006, "Billion B006"),
ID(CCD, CCD_B007, "Billion B007"),
ID(CCD, CCD_B008, "Billion B008"),
ID(CCD, CCD_B009, "Billion B009"),
ID(CCD, CCD_B00A, "Billion B00A"),
ID(CCD, CCD_B00B, "Billion B00B"),
ID(CCD, CCD_B00C, "Billion B00C"),
ID(CCD, CCD_B100, "Seyeon"),
ID(ABOCOM, ABOCOM_2BD1, "Abocom/Magitek"),
ID(ASUSTEK, ASUSTEK_0675, "Asuscom/Askey"),
ID(BERKOM, BERKOM_T_CONCEPT, "German Telekom T-Concept"),
ID(BERKOM, BERKOM_A1T, "German Telekom A1T"),
ID(ANIGMA, ANIGMA_MC145575, "Motorola MC145575"),
ID(ZOLTRIX, ZOLTRIX_2BD0, "Zoltrix 2BD0"),
ID(DIGI, DIGI_DF_M_IOM2_E, "Digi DataFire Micro V IOM2 (Europe)"),
ID(DIGI, DIGI_DF_M_E, "Digi DataFire Micro V (Europe)"),
ID(DIGI, DIGI_DF_M_IOM2_A, "Digi DataFire Micro V IOM2 (America)"),
ID(DIGI, DIGI_DF_M_A, "Digi DataFire Micro V (America)"),
};
MODULE_DEVICE_TABLE(pci, hfcpci_ids);
#undef ID
static int protocol = 2; /* EURO-ISDN Default */
MODULE_PARM(protocol, "i");
// ----------------------------------------------------------------------
//
/* memory window base address offset (in config space) */
#define HFCPCI_MWBA 0x80
/* GCI/IOM bus monitor registers */
#define HCFPCI_C_I 0x08
#define HFCPCI_TRxR 0x0C
#define HFCPCI_MON1_D 0x28
#define HFCPCI_MON2_D 0x2C
/* GCI/IOM bus timeslot registers */
#define HFCPCI_B1_SSL 0x80
#define HFCPCI_B2_SSL 0x84
#define HFCPCI_AUX1_SSL 0x88
#define HFCPCI_AUX2_SSL 0x8C
#define HFCPCI_B1_RSL 0x90
#define HFCPCI_B2_RSL 0x94
#define HFCPCI_AUX1_RSL 0x98
#define HFCPCI_AUX2_RSL 0x9C
/* GCI/IOM bus data registers */
#define HFCPCI_B1_D 0xA0
#define HFCPCI_B2_D 0xA4
#define HFCPCI_AUX1_D 0xA8
#define HFCPCI_AUX2_D 0xAC
/* GCI/IOM bus configuration registers */
#define HFCPCI_MST_EMOD 0xB4
#define HFCPCI_MST_MODE 0xB8
#define HFCPCI_CONNECT 0xBC
/* Interrupt and status registers */
#define HFCPCI_FIFO_EN 0x44
#define HFCPCI_TRM 0x48
#define HFCPCI_B_MODE 0x4C
#define HFCPCI_CHIP_ID 0x58
#define HFCPCI_CIRM 0x60
#define HFCPCI_CTMT 0x64
#define HFCPCI_INT_M1 0x68
#define HFCPCI_INT_M2 0x6C
#define HFCPCI_INT_S1 0x78
#define HFCPCI_INT_S2 0x7C
#define HFCPCI_STATUS 0x70
/* S/T section registers */
#define HFCPCI_STATES 0xC0
#define HFCPCI_SCTRL 0xC4
#define HFCPCI_SCTRL_E 0xC8
#define HFCPCI_SCTRL_R 0xCC
#define HFCPCI_SQ 0xD0
#define HFCPCI_CLKDEL 0xDC
#define HFCPCI_B1_REC 0xF0
#define HFCPCI_B1_SEND 0xF0
#define HFCPCI_B2_REC 0xF4
#define HFCPCI_B2_SEND 0xF4
#define HFCPCI_D_REC 0xF8
#define HFCPCI_D_SEND 0xF8
#define HFCPCI_E_REC 0xFC
/* bits in status register (READ) */
#define HFCPCI_PCI_PROC 0x02
#define HFCPCI_NBUSY 0x04
#define HFCPCI_TIMER_ELAP 0x10
#define HFCPCI_STATINT 0x20
#define HFCPCI_FRAMEINT 0x40
#define HFCPCI_ANYINT 0x80
/* bits in CTMT (Write) */
#define HFCPCI_CLTIMER 0x80
#define HFCPCI_TIM3_125 0x04
#define HFCPCI_TIM25 0x10
#define HFCPCI_TIM50 0x14
#define HFCPCI_TIM400 0x18
#define HFCPCI_TIM800 0x1C
#define HFCPCI_AUTO_TIMER 0x20
#define HFCPCI_TRANSB2 0x02
#define HFCPCI_TRANSB1 0x01
/* bits in CIRM (Write) */
#define HFCPCI_AUX_MSK 0x07
#define HFCPCI_RESET 0x08
#define HFCPCI_B1_REV 0x40
#define HFCPCI_B2_REV 0x80
/* bits in INT_M1 and INT_S1 */
#define HFCPCI_INTS_B1TRANS 0x01
#define HFCPCI_INTS_B2TRANS 0x02
#define HFCPCI_INTS_DTRANS 0x04
#define HFCPCI_INTS_B1REC 0x08
#define HFCPCI_INTS_B2REC 0x10
#define HFCPCI_INTS_DREC 0x20
#define HFCPCI_INTS_L1STATE 0x40
#define HFCPCI_INTS_TIMER 0x80
/* bits in INT_M2 */
#define HFCPCI_PROC_TRANS 0x01
#define HFCPCI_GCI_I_CHG 0x02
#define HFCPCI_GCI_MON_REC 0x04
#define HFCPCI_IRQ_ENABLE 0x08
#define HFCPCI_PMESEL 0x80
/* bits in STATES */
#define HFCPCI_STATE_MSK 0x0F
#define HFCPCI_LOAD_STATE 0x10
#define HFCPCI_ACTIVATE 0x20
#define HFCPCI_DO_ACTION 0x40
#define HFCPCI_NT_G2_G3 0x80
/* bits in HFCD_MST_MODE */
#define HFCPCI_MASTER 0x01
#define HFCPCI_SLAVE 0x00
/* remaining bits are for codecs control */
/* bits in HFCD_SCTRL */
#define SCTRL_B1_ENA 0x01
#define SCTRL_B2_ENA 0x02
#define SCTRL_MODE_TE 0x00
#define SCTRL_MODE_NT 0x04
#define SCTRL_LOW_PRIO 0x08
#define SCTRL_SQ_ENA 0x10
#define SCTRL_TEST 0x20
#define SCTRL_NONE_CAP 0x40
#define SCTRL_PWR_DOWN 0x80
/* bits in SCTRL_E */
#define HFCPCI_AUTO_AWAKE 0x01
#define HFCPCI_DBIT_1 0x04
#define HFCPCI_IGNORE_COL 0x08
#define HFCPCI_CHG_B1_B2 0x80
/* bits in FIFO_EN register */
#define HFCPCI_FIFOEN_B1 0x03
#define HFCPCI_FIFOEN_B2 0x0C
#define HFCPCI_FIFOEN_DTX 0x10
#define HFCPCI_FIFOEN_DRX 0x20
#define HFCPCI_FIFOEN_B1TX 0x01
#define HFCPCI_FIFOEN_B1RX 0x02
#define HFCPCI_FIFOEN_B2TX 0x04
#define HFCPCI_FIFOEN_B2RX 0x08
/*
* thresholds for transparent B-channel mode
* change mask and threshold simultaneously
*/
#define HFCPCI_BTRANS_THRESHOLD 128
#define HFCPCI_BTRANS_THRESMASK 0x00
#define CLKDEL_TE 0x0e /* CLKDEL in TE mode */
#define CLKDEL_NT 0x6c /* CLKDEL in NT mode */
#define MAX_D_FRAMES 0x0f
#define MAX_B_FRAMES 0x1f
#define B_FIFO_SIZE (0x2000 - 0x200)
#define D_FIFO_SIZE 0x200
// ----------------------------------------------------------------------
//
static inline void D_L1L2(struct hfcpci_adapter *adapter, int pr, void *arg)
{
struct hisax_if *ifc = (struct hisax_if *) &adapter->d_if;
DBG(2, "pr %#x", pr);
ifc->l1l2(ifc, pr, arg);
}
static inline void B_L1L2(struct hfcpci_bcs *bcs, int pr, void *arg)
{
struct hisax_if *ifc = (struct hisax_if *) &bcs->b_if;
DBG(2, "pr %#x", pr);
ifc->l1l2(ifc, pr, arg);
}
// ----------------------------------------------------------------------
//
static inline void
hfcpci_writeb(struct hfcpci_adapter *adapter, u8 b, unsigned char offset)
{
writeb(b, adapter->mmio + offset);
}
static inline u8
hfcpci_readb(struct hfcpci_adapter *adapter, unsigned char offset)
{
return readb(adapter->mmio + offset);
}
#define DECL_B_F(r, f) \
static inline u8 \
get_b_##r##_##f (struct hfcpci_bcs *bcs) \
{ \
u16 off = bcs->channel ? OFF_B2_##r##_##f : OFF_B1_##r##_##f; \
\
return *(bcs->adapter->fifo + off); \
} \
\
static inline void \
set_b_##r##_##f (struct hfcpci_bcs *bcs, u8 f) \
{ \
u16 off = bcs->channel ? OFF_B2_##r##_##f : OFF_B1_##r##_##f; \
\
*(bcs->adapter->fifo + off) = f; \
}
#define OFF_B1_rx_f1 0x6080
#define OFF_B2_rx_f1 0x6180
#define OFF_B1_rx_f2 0x6081
#define OFF_B2_rx_f2 0x6181
#define OFF_B1_tx_f1 0x2080
#define OFF_B2_tx_f1 0x2180
#define OFF_B1_tx_f2 0x2081
#define OFF_B2_tx_f2 0x2181
DECL_B_F(rx, f1)
DECL_B_F(rx, f2)
DECL_B_F(tx, f1)
DECL_B_F(tx, f2)
#undef DECL_B_F
#define DECL_B_Z(r, z) \
static inline u8 \
get_b_##r##_##z (struct hfcpci_bcs *bcs, u8 f) \
{ \
u16 off = bcs->channel ? OFF_B2_##r##_##z : OFF_B1_##r##_##z; \
\
return le16_to_cpu(*((u16 *) bcs->adapter->fifo + off + f * 4)); \
} \
\
static inline void \
set_b_##r##_##z(struct hfcpci_bcs *bcs, u8 f, u16 z) \
{ \
u16 off = bcs->channel ? OFF_B2_##r##_##z : OFF_B1_##r##_##z; \
\
*((u16 *) (bcs->adapter->fifo + off + f * 4)) = cpu_to_le16(z); \
}
#define OFF_B1_rx_z1 0x6000
#define OFF_B2_rx_z1 0x6100
#define OFF_B1_rx_z2 0x6000
#define OFF_B2_rx_z2 0x6100
#define OFF_B1_tx_z1 0x2002
#define OFF_B2_tx_z1 0x2102
#define OFF_B1_tx_z2 0x2002
#define OFF_B2_tx_z2 0x2102
DECL_B_Z(rx, z1)
DECL_B_Z(rx, z2)
DECL_B_Z(tx, z1)
DECL_B_Z(tx, z2)
#undef DECL_B_Z
// ----------------------------------------------------------------------
//
static void hfcpci_clear_b_rx_fifo(struct hfcpci_bcs *bcs);
static void hfcpci_clear_b_tx_fifo(struct hfcpci_bcs *bcs);
static void
hfcpci_b_mode(struct hfcpci_bcs *bcs, int mode)
{
struct hfcpci_adapter *adapter = bcs->adapter;
DBG(0x40, "B%d mode %d --> %d",
bcs->channel + 1, bcs->mode, mode);
if (bcs->mode == mode)
return;
switch (mode) {
case L1_MODE_NULL:
if (bcs->channel == 0) {
adapter->sctrl &= ~SCTRL_B1_ENA;
adapter->sctrl_r &= ~SCTRL_B1_ENA;
adapter->fifo_en &= ~HFCPCI_FIFOEN_B1;
adapter->int_m1 &= ~(HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC);
} else {
adapter->sctrl &= ~SCTRL_B2_ENA;
adapter->sctrl_r &= ~SCTRL_B2_ENA;
adapter->fifo_en &= ~HFCPCI_FIFOEN_B2;
adapter->int_m1 &= ~(HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC);
}
break;
case L1_MODE_TRANS:
case L1_MODE_HDLC:
hfcpci_clear_b_rx_fifo(bcs);
hfcpci_clear_b_tx_fifo(bcs);
if (bcs->channel == 0) {
adapter->sctrl |= SCTRL_B1_ENA;
adapter->sctrl_r |= SCTRL_B1_ENA;
adapter->fifo_en |= HFCPCI_FIFOEN_B1;
adapter->int_m1 |= (HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC);
// adapter->conn &= ~0x03;
if (mode == L1_MODE_TRANS)
adapter->ctmt |= 1;
else
adapter->ctmt &= ~1;
} else {
adapter->sctrl |= SCTRL_B2_ENA;
adapter->sctrl_r |= SCTRL_B2_ENA;
adapter->fifo_en |= HFCPCI_FIFOEN_B2;
adapter->int_m1 |= (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC);
// adapter->conn &= ~0x18; // XXX
if (mode == L1_MODE_TRANS)
adapter->ctmt |= 2;
else
adapter->ctmt &= ~2;
}
break;
}
hfcpci_writeb(adapter, adapter->int_m1, HFCPCI_INT_M1);
hfcpci_writeb(adapter, adapter->fifo_en, HFCPCI_FIFO_EN);
hfcpci_writeb(adapter, adapter->sctrl, HFCPCI_SCTRL);
hfcpci_writeb(adapter, adapter->sctrl_r, HFCPCI_SCTRL_R);
hfcpci_writeb(adapter, adapter->ctmt, HFCPCI_CTMT);
hfcpci_writeb(adapter, adapter->conn, HFCPCI_CONNECT);
bcs->mode = mode;
}
static inline void
hfcpci_fill_b_fifo_trans(struct hfcpci_bcs *bcs)
{
int cnt;
char *fifo_adr = bcs->adapter->fifo + (bcs->channel ? 0x2000 : 0x0000);
struct sk_buff *skb = bcs->tx_skb;
u8 f1, f2;
u16 z1, z2;
f1 = get_b_tx_f1(bcs);
f2 = get_b_tx_f2(bcs);
if (f1 != f2)
BUG();
z1 = get_b_tx_z1(bcs, f1);
z2 = get_b_tx_z2(bcs, f1);
cnt = z2 - z1;
if (cnt <= 0)
cnt += B_FIFO_SIZE;
if (bcs->tx_skb->len > cnt)
BUG();
if (z1 + cnt <= 0x2000) {
memcpy(fifo_adr + z1, skb->data, cnt);
} else {
memcpy(fifo_adr + z1, skb->data, 0x2000 - z1);
memcpy(fifo_adr + 0x200, skb->data + (0x2000 - z1),
cnt - (0x2000 - z2));
}
z1 += cnt;
if (z1 >= 0x2000)
z1 -= B_FIFO_SIZE;
mb();
set_b_tx_z1(bcs, f1, z1);
}
static inline void
hfcpci_fill_b_fifo_hdlc(struct hfcpci_bcs *bcs)
{
#if 0
struct IsdnCardState *cs = bcs->cs;
unsigned long flags;
int maxlen, fcnt;
int count, new_z1;
bzfifo_type *bz;
u_char *bdata;
u_char new_f1, *src, *dst;
unsigned short *z1t, *z2t;
if (cs->debug & L1_DEB_HSCX)
debugl1(cs, "hfcpci_fill_fifo_hdlc %d f1(%d) f2(%d) z1(f1)(%x)",
bcs->channel, bz->f1, bz->f2,
bz->za[bz->f1].z1);
fcnt = bz->f1 - bz->f2; /* frame count actually buffered */
if (fcnt < 0)
fcnt += (MAX_B_FRAMES + 1); /* if wrap around */
if (fcnt > (MAX_B_FRAMES - 1)) {
if (cs->debug & L1_DEB_HSCX)
debugl1(cs, "hfcpci_fill_Bfifo more as 14 frames");
restore_flags(flags);
return;
}
/* now determine free bytes in FIFO buffer */
count = bz->za[bz->f1].z2 - bz->za[bz->f1].z1;
if (count <= 0)
count += B_FIFO_SIZE; /* count now contains available bytes */
if (cs->debug & L1_DEB_HSCX)
debugl1(cs, "hfcpci_fill_fifo %d count(%ld/%d),%lx",
bcs->channel, bcs->tx_skb->len,
count, current->state);
if (count < bcs->tx_skb->len) {
if (cs->debug & L1_DEB_HSCX)
debugl1(cs, "hfcpci_fill_fifo no fifo mem");
restore_flags(flags);
return;
}
count = bcs->tx_skb->len; /* get frame len */
new_z1 = bz->za[bz->f1].z1 + count; /* new buffer Position */
if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL))
new_z1 -= B_FIFO_SIZE; /* buffer wrap */
new_f1 = ((bz->f1 + 1) & MAX_B_FRAMES);
src = bcs->tx_skb->data; /* source pointer */
dst = bdata + (bz->za[bz->f1].z1 - B_SUB_VAL);
maxlen = (B_FIFO_SIZE + B_SUB_VAL) - bz->za[bz->f1].z1; /* end fifo */
if (maxlen > count)
maxlen = count; /* limit size */
memcpy(dst, src, maxlen); /* first copy */
count -= maxlen; /* remaining bytes */
if (count) {
dst = bdata; /* start of buffer */
src += maxlen; /* new position */
memcpy(dst, src, count);
}
bcs->tx_cnt -= bcs->tx_skb->len;
if (bcs->st->lli.l1writewakeup &&
(PACKET_NOACK != bcs->tx_skb->pkt_type))
bcs->st->lli.l1writewakeup(bcs->st, bcs->tx_skb->len);
cli();
bz->za[new_f1].z1 = new_z1; /* for next buffer */
bz->f1 = new_f1; /* next frame */
restore_flags(flags);
dev_kfree_skb_any(bcs->tx_skb);
bcs->tx_skb = NULL;
test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
return;
#endif
}
static inline void
hfcpci_fill_b_fifo(struct hfcpci_bcs *bcs)
{
if (!bcs->tx_skb) {
DBG(0x1, "?");
return;
}
switch (bcs->mode) {
case L1_MODE_TRANS:
hfcpci_fill_b_fifo_trans(bcs);
break;
case L1_MODE_HDLC:
hfcpci_fill_b_fifo_hdlc(bcs);
break;
default:
DBG(0x1, "?");
}
}
static void
hfcpci_d_l2l1(struct hisax_if *ifc, int pr, void *arg)
{
}
static void
hfcpci_b_l2l1(struct hisax_if *ifc, int pr, void *arg)
{
struct hfcpci_bcs *bcs = ifc->priv;
struct sk_buff *skb = arg;
int mode;
DBG(0x10, "pr %#x", pr);
switch (pr) {
case PH_DATA | REQUEST:
if (bcs->tx_skb)
BUG();
bcs->tx_skb = skb;
DBG_SKB(1, skb);
hfcpci_fill_b_fifo(bcs);
break;
case PH_ACTIVATE | REQUEST:
mode = (int) arg;
DBG(4,"B%d,PH_ACTIVATE_REQUEST %d", bcs->channel + 1, mode);
hfcpci_b_mode(bcs, mode);
B_L1L2(bcs, PH_ACTIVATE | INDICATION, NULL);
break;
case PH_DEACTIVATE | REQUEST:
DBG(4,"B%d,PH_DEACTIVATE_REQUEST", bcs->channel + 1);
hfcpci_b_mode(bcs, L1_MODE_NULL);
B_L1L2(bcs, PH_DEACTIVATE | INDICATION, NULL);
break;
}
}
// ----------------------------------------------------------------------
//
static inline void
hfcpci_state_irq(struct hfcpci_adapter *adapter)
{
u8 val;
val = hfcpci_readb(adapter, HFCPCI_STATES) & 0xf;
#if 0
DBG(0x1, "ph_state chg %d->%d", adapter->ph_state, val);
adapter->ph_state = val;
#endif
// XXX FsmEvent
}
static inline void
hfcpci_timer_irq(struct hfcpci_adapter *adapter)
{
hfcpci_writeb(adapter, adapter->ctmt | HFCPCI_CLTIMER, HFCPCI_CTMT);
}
static void
hfcpci_clear_b_rx_fifo(struct hfcpci_bcs *bcs)
{
struct hfcpci_adapter *adapter = bcs->adapter;
int nr = bcs->channel;
u8 fifo_state;
fifo_state = adapter->fifo_en &
(nr ? HFCPCI_FIFOEN_B2RX : HFCPCI_FIFOEN_B1RX);
if (fifo_state) { // enabled
adapter->fifo_en &= ~fifo_state;
hfcpci_writeb(adapter, adapter->fifo_en, HFCPCI_FIFO_EN);
}
bcs->last_fcnt = 0;
set_b_rx_z1(bcs, MAX_B_FRAMES, 0x1fff);
set_b_rx_z2(bcs, MAX_B_FRAMES, 0x1fff);
mb();
set_b_rx_f1(bcs, MAX_B_FRAMES);
set_b_rx_f2(bcs, MAX_B_FRAMES);
mb();
if (fifo_state) {
adapter->fifo_en |= fifo_state;
hfcpci_writeb(adapter, adapter->fifo_en, HFCPCI_FIFO_EN);
}
}
static void
hfcpci_clear_b_tx_fifo(struct hfcpci_bcs *bcs)
{
struct hfcpci_adapter *adapter = bcs->adapter;
int nr = bcs->channel;
u8 fifo_state;
fifo_state = adapter->fifo_en &
(nr ? HFCPCI_FIFOEN_B2TX : HFCPCI_FIFOEN_B1TX);
if (fifo_state) { // enabled
adapter->fifo_en &= ~fifo_state;
hfcpci_writeb(adapter, adapter->fifo_en, HFCPCI_FIFO_EN);
}
bcs->last_fcnt = 0;
set_b_rx_z1(bcs, MAX_B_FRAMES, 0x1fff);
set_b_rx_z2(bcs, MAX_B_FRAMES, 0x1fff);
mb();
set_b_rx_f1(bcs, MAX_B_FRAMES);
set_b_rx_f2(bcs, MAX_B_FRAMES);
mb();
if (fifo_state) {
adapter->fifo_en |= fifo_state;
hfcpci_writeb(adapter, adapter->fifo_en, HFCPCI_FIFO_EN);
}
}
static inline void
hfcpci_b_recv_hdlc_irq(struct hfcpci_adapter *adapter, int nr)
{
struct hfcpci_bcs *bcs = &adapter->bcs[nr];
struct sk_buff *skb;
char *fifo_adr = adapter->fifo + (nr ? 0x6000 : 0x4000);
char *p;
int cnt, fcnt;
int loop = 5;
u8 f1, f2;
u16 z1, z2;
while (loop-- > 0) {
f1 = get_b_rx_f1(bcs);
f2 = get_b_rx_f2(bcs);
DBG(0x10, "f1 %d f2 %d\n", f1, f2);
fcnt = f1 - f2;
if (fcnt < 0)
fcnt += MAX_B_FRAMES;
if (!fcnt)
return;
if (fcnt < bcs->last_fcnt)
/* overrun */
hfcpci_clear_b_rx_fifo(bcs);
// XXX init last_fcnt
z1 = get_b_rx_z1(bcs, f2);
z2 = get_b_rx_z2(bcs, f2);
cnt = z1 - z2;
if (cnt < 0)
cnt += B_FIFO_SIZE;
cnt++;
if (cnt < 4) {
DBG(0x01, "frame too short\n");
goto next;
}
if (fifo_adr[z1] != 0) {
DBG(0x01, "CRC error\n");
goto next;
}
cnt -= 3;
skb = dev_alloc_skb(cnt);
if (!skb) {
DBG(0x01, "no mem\n");
goto next;
}
p = skb_put(skb, cnt);
if (z2 + cnt <= 0x2000) {
memcpy(p, fifo_adr + z2, cnt);
} else {
memcpy(p, fifo_adr + z2, 0x2000 - z2);
p += 0x2000 - z2;
memcpy(p, fifo_adr + 0x200, cnt - (0x2000 - z2));
}
DBG_SKB(1, skb);
B_L1L2(bcs, PH_DATA | INDICATION, skb);
next:
if (++z1 >= 0x2000)
z1 -= B_FIFO_SIZE;
f2 = (f2 + 1) & MAX_B_FRAMES;
mb();
set_b_rx_z2(bcs, f2, z1);
mb();
set_b_rx_f2(bcs, f2);
bcs->last_fcnt = fcnt - 1;
}
}
static inline void
hfcpci_b_recv_trans_irq(struct hfcpci_adapter *adapter, int nr)
{
struct hfcpci_bcs *bcs = &adapter->bcs[nr];
struct sk_buff *skb;
char *fifo_adr = adapter->fifo + (nr ? 0x6000 : 0x4000);
char *p;
int cnt;
int loop = 5;
u8 f1, f2;
u16 z1, z2;
f1 = get_b_rx_f1(bcs);
f2 = get_b_rx_f2(bcs);
if (f1 != f2)
BUG();
while (loop-- > 0) {
z1 = get_b_rx_z1(bcs, f2);
z2 = get_b_rx_z2(bcs, f2);
cnt = z1 - z2;
if (!cnt)
/* no data available */
return;
if (cnt < 0)
cnt += B_FIFO_SIZE;
if (cnt > HFCPCI_BTRANS_THRESHOLD)
cnt = HFCPCI_BTRANS_THRESHOLD;
skb = dev_alloc_skb(cnt);
if (!skb) {
DBG(0x01, "no mem\n");
goto next;
}
p = skb_put(skb, cnt);
if (z2 + cnt <= 0x2000) {
memcpy(p, fifo_adr + z2, cnt);
} else {
memcpy(p, fifo_adr + z2, 0x2000 - z2);
p += 0x2000 - z2;
memcpy(p, fifo_adr + 0x200, cnt - (0x2000 - z2));
}
DBG_SKB(1, skb);
B_L1L2(bcs, PH_DATA | INDICATION, skb);
next:
z2 += cnt;
if (z2 >= 0x2000)
z2 -= B_FIFO_SIZE;
mb();
set_b_rx_z2(bcs, f2, z2);
// XXX always receive buffers of a given size
}
}
static inline void
hfcpci_b_recv_irq(struct hfcpci_adapter *adapter, int nr)
{
DBG(0x10, "");
switch (adapter->bcs[nr].mode) {
case L1_MODE_NULL:
DBG(0x10, "?");
break;
case L1_MODE_HDLC:
hfcpci_b_recv_hdlc_irq(adapter, nr);
break;
case L1_MODE_TRANS:
hfcpci_b_recv_trans_irq(adapter, nr);
break;
}
}
static void
hfcpci_clear_d_rx_fifo(struct hfcpci_adapter *adapter)
{
u8 fifo_state;
fifo_state = adapter->fifo_en & HFCPCI_FIFOEN_DRX;
if (fifo_state) { // enabled
// XXX locking
adapter->fifo_en &= ~fifo_state;
hfcpci_writeb(adapter, adapter->fifo_en, HFCPCI_FIFO_EN);
}
adapter->last_fcnt = 0;
*((u16 *) (adapter->fifo + 0x6080 + (0x10 | MAX_D_FRAMES) * 4 )) = cpu_to_le16(0x1ff); // Z1
*((u16 *) (adapter->fifo + 0x6080 + (0x10 | MAX_D_FRAMES) * 4 + 2)) = cpu_to_le16(0x1ff); // Z2
mb();
*(adapter->fifo + 0x60a0) = 0x10 | MAX_D_FRAMES; // F1
*(adapter->fifo + 0x60a0) = 0x10 | MAX_D_FRAMES; // F2
mb();
if (fifo_state) {
adapter->fifo_en |= fifo_state;
hfcpci_writeb(adapter, adapter->fifo_en, HFCPCI_FIFO_EN);
}
}
// XXX make xmit FIFO deeper than 1
static inline void
hfcpci_b_xmit_irq(struct hfcpci_adapter *adapter, int nr)
{
struct hfcpci_bcs *bcs = &adapter->bcs[nr];
struct sk_buff *skb;
DBG(0x10, "");
skb = bcs->tx_skb;
if (!skb) {
DBG(0x10, "?");
return;
}
bcs->tx_skb = NULL;
B_L1L2(bcs, PH_DATA | CONFIRM, (void *) skb->truesize);
dev_kfree_skb_irq(skb);
}
static inline void
hfcpci_d_recv_irq(struct hfcpci_adapter *adapter)
{
struct sk_buff *skb;
char *p;
int cnt, fcnt;
int loop = 5;
u8 f1, f2;
u16 z1, z2;
while (loop-- > 0) {
f1 = *(adapter->fifo + 0x60a0) & 0x0f; // F1
f2 = *(adapter->fifo + 0x60a1) & 0x0f; // F2
DBG(0x10, "f1 %d f2 %d\n", f1, f2);
fcnt = f1 - f2;
if (fcnt < 0)
fcnt += MAX_D_FRAMES;
if (!fcnt)
return;
if (fcnt < adapter->last_fcnt)
/* overrun */
hfcpci_clear_d_rx_fifo(adapter);
// XXX init last_fcnt
z1 = le16_to_cpu(*((u16 *) (adapter->fifo + 0x6080 + f2 * 4 )));
z2 = le16_to_cpu(*((u16 *) (adapter->fifo + 0x6080 + f2 * 4 + 2)));
cnt = z1 - z2;
if (cnt < 0)
cnt += D_FIFO_SIZE;
cnt++;
if (cnt < 4) {
DBG(0x01, "frame too short\n");
goto next;
}
if (*(adapter->fifo + 0x4000 + z1) != 0) {
DBG(0x01, "CRC error\n");
goto next;
}
cnt -= 3;
skb = dev_alloc_skb(cnt);
if (!skb) {
DBG(0x01, "no mem\n");
goto next;
}
p = skb_put(skb, cnt);
if (z2 + cnt <= 0x200) {
memcpy(p, adapter->fifo + 0x4000 + z2, cnt);
} else {
memcpy(p, adapter->fifo + 0x4000 + z2, 0x200 - z2);
p += 0x200 - z2;
memcpy(p, adapter->fifo + 0x4000, cnt - (0x200 - z2));
}
DBG_SKB(1, skb);
D_L1L2(adapter, PH_DATA | INDICATION, skb);
next:
z1 = (z1 + 1) & 0x1ff;
f2 = (f2 + 1) & MAX_B_FRAMES;
mb();
*((u16 *) (adapter->fifo + 0x6080 + f2 * 4 + 2)) = cpu_to_le16(z1); // Z2
mb();
*(adapter->fifo + 0x60a1) = f2; // F2
adapter->last_fcnt = fcnt - 1;
}
}
static inline void
hfcpci_d_xmit_irq(struct hfcpci_adapter *adapter)
{
struct sk_buff *skb;
DBG(0x10, "");
skb = adapter->tx_skb;
if (!skb) {
DBG(0x10, "?");
return;
}
adapter->tx_skb = NULL;
D_L1L2(adapter, PH_DATA | CONFIRM, (void *) skb->truesize);
dev_kfree_skb_irq(skb);
}
static void
hfcpci_irq(int intno, void *dev, struct pt_regs *regs)
{
struct hfcpci_adapter *adapter = dev;
int loop = 15;
u8 val, stat;
if (!(adapter->int_m2 & 0x08))
return; /* not initialised */ // XX
stat = hfcpci_readb(adapter, HFCPCI_STATUS);
if (!(stat & HFCPCI_ANYINT))
return;
spin_lock(&adapter->hw_lock);
while (loop-- > 0) {
val = hfcpci_readb(adapter, HFCPCI_INT_S1);
DBG(0x10, "stat %02x s1 %02x\n", stat, val);
val &= adapter->int_m1;
if (!val)
break;
if (val & 0x08)
hfcpci_b_recv_irq(adapter, 0);
if (val & 0x10)
hfcpci_b_recv_irq(adapter, 1);
if (val & 0x01)
hfcpci_b_xmit_irq(adapter, 0);
if (val & 0x02)
hfcpci_b_xmit_irq(adapter, 1);
if (val & 0x20)
hfcpci_d_recv_irq(adapter);
if (val & 0x04)
hfcpci_d_xmit_irq(adapter);
if (val & 0x40)
hfcpci_state_irq(adapter);
if (val & 0x80)
hfcpci_timer_irq(adapter);
}
spin_unlock(&adapter->hw_lock);
}
// ----------------------------------------------------------------------
static void
hfcpci_reset(struct hfcpci_adapter *adapter)
{
/* disable all interrupts */
adapter->int_m1 = 0;
adapter->int_m2 = 0;
hfcpci_writeb(adapter, adapter->int_m1, HFCPCI_INT_M1);
hfcpci_writeb(adapter, adapter->int_m2, HFCPCI_INT_M2);
/* reset */
hfcpci_writeb(adapter, HFCPCI_RESET, HFCPCI_CIRM);
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout((30 * HZ) / 1000);
hfcpci_writeb(adapter, 0, HFCPCI_CIRM);
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout((20 * HZ) / 1000);
if (hfcpci_readb(adapter, HFCPCI_STATUS) & 2) // XX
printk(KERN_WARNING "HFC-PCI init bit busy\n");
}
static void
hfcpci_hw_init(struct hfcpci_adapter *adapter)
{
adapter->fifo_en = 0x30; /* only D fifos enabled */ // XX
hfcpci_writeb(adapter, adapter->fifo_en, HFCPCI_FIFO_EN);
/* no echo connect , threshold */
adapter->trm = HFCPCI_BTRANS_THRESMASK;
hfcpci_writeb(adapter, adapter->trm, HFCPCI_TRM);
/* ST-Bit delay for TE-Mode */
hfcpci_writeb(adapter, CLKDEL_TE, HFCPCI_CLKDEL);
/* S/T Auto awake */
adapter->sctrl_e = HFCPCI_AUTO_AWAKE;
hfcpci_writeb(adapter, adapter->sctrl_e, HFCPCI_SCTRL_E);
/* no exchange */
adapter->bswapped = 0;
/* we are in TE mode */
adapter->nt_mode = 0;
adapter->ctmt = HFCPCI_TIM3_125 | HFCPCI_AUTO_TIMER;
hfcpci_writeb(adapter, adapter->ctmt, HFCPCI_CTMT);
adapter->int_m1 = HFCPCI_INTS_DTRANS | HFCPCI_INTS_DREC |
HFCPCI_INTS_L1STATE | HFCPCI_INTS_TIMER;
hfcpci_writeb(adapter, adapter->int_m1, HFCPCI_INT_M1);
/* clear already pending ints */
hfcpci_readb(adapter, HFCPCI_INT_S1);
hfcpci_writeb(adapter, HFCPCI_LOAD_STATE | 2, HFCPCI_STATES); // XX /* HFC ST 2 */
udelay(10);
hfcpci_writeb(adapter, 2, HFCPCI_STATES); /* HFC ST 2 */
/* HFC Master Mode */
adapter->mst_m = HFCPCI_MASTER;
hfcpci_writeb(adapter, adapter->mst_m, HFCPCI_MST_MODE);
/* set tx_lo mode, error in datasheet ! */
adapter->sctrl = 0x40;
hfcpci_writeb(adapter, adapter->sctrl, HFCPCI_SCTRL);
adapter->sctrl_r = 0;
hfcpci_writeb(adapter, adapter->sctrl_r, HFCPCI_SCTRL_R);
// XXX
/* Init GCI/IOM2 in master mode */
/* Slots 0 and 1 are set for B-chan 1 and 2 */
/* D- and monitor/CI channel are not enabled */
/* STIO1 is used as output for data, B1+B2 from ST->IOM+HFC */
/* STIO2 is used as data input, B1+B2 from IOM->ST */
/* ST B-channel send disabled -> continous 1s */
/* The IOM slots are always enabled */
adapter->conn = 0; /* set data flow directions */
hfcpci_writeb(adapter, adapter->conn, HFCPCI_CONNECT);
hfcpci_writeb(adapter, 0x80, HFCPCI_B1_SSL); /* B1-Slot 0 STIO1 out enabled */
hfcpci_writeb(adapter, 0x81, HFCPCI_B2_SSL); /* B2-Slot 1 STIO1 out enabled */
hfcpci_writeb(adapter, 0x80, HFCPCI_B1_RSL); /* B1-Slot 0 STIO2 in enabled */
hfcpci_writeb(adapter, 0x81, HFCPCI_B2_RSL); /* B2-Slot 1 STIO2 in enabled */
/* Finally enable IRQ output */
adapter->int_m2 = HFCPCI_IRQ_ENABLE;
hfcpci_writeb(adapter, adapter->int_m2, HFCPCI_INT_M2);
hfcpci_readb(adapter, HFCPCI_INT_S2);
}
// ----------------------------------------------------------------------
static struct hfcpci_adapter * __devinit
new_adapter(struct pci_dev *pdev)
{
struct hfcpci_adapter *adapter;
struct hisax_b_if *b_if[2];
int i;
adapter = kmalloc(sizeof(struct hfcpci_adapter), GFP_KERNEL);
if (!adapter)
return NULL;
memset(adapter, 0, sizeof(struct hfcpci_adapter));
SET_MODULE_OWNER(&adapter->d_if);
adapter->d_if.ifc.priv = adapter;
adapter->d_if.ifc.l2l1 = hfcpci_d_l2l1;
for (i = 0; i < 2; i++) {
adapter->bcs[i].adapter = adapter;
adapter->bcs[i].channel = i;
adapter->bcs[i].b_if.ifc.priv = &adapter->bcs[i];
adapter->bcs[i].b_if.ifc.l2l1 = hfcpci_b_l2l1;
}
pci_set_drvdata(pdev, adapter);
for (i = 0; i < 2; i++)
b_if[i] = &adapter->bcs[i].b_if;
hisax_register(&adapter->d_if, b_if, "hfcpci", protocol);
return adapter;
}
static void delete_adapter(struct hfcpci_adapter *adapter)
{
hisax_unregister(&adapter->d_if);
kfree(adapter);
}
static int __devinit hfcpci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct hfcpci_adapter *adapter;
int retval;
retval = -ENOMEM;
adapter = new_adapter(pdev);
if (!adapter)
goto err;
retval = pci_enable_device(pdev);
if (retval)
goto err_free;
adapter->irq = pdev->irq;
retval = request_irq(adapter->irq, hfcpci_irq, SA_SHIRQ,
"hfc_pci", adapter);
if (retval)
goto err_free;
retval = -EBUSY;
if (!request_region(pci_resource_start(pdev, 1), 256, "hfc_pci"))
goto err_free_irq;
adapter->mmio = ioremap(pci_resource_start(pdev, 1), 256); // XX pci_io
if (!adapter->mmio)
goto err_release_region;
/* Allocate 32K for FIFOs */
if (pci_set_dma_mask(pdev, 0xffffffff))
goto err_unmap;
adapter->fifo = pci_alloc_consistent(pdev, 32768, &adapter->fifo_dma);
if (!adapter->fifo)
goto err_unmap;
pci_write_config_dword(pdev, HFCPCI_MWBA, (u32) adapter->fifo_dma);
pci_set_master(pdev);
hfcpci_reset(adapter);
hfcpci_hw_init(adapter);
printk(KERN_INFO "hisax_hfcpci: found adapter %s at %s\n",
(char *) ent->driver_data, pdev->slot_name);
return 0;
err_unmap:
iounmap(adapter->mmio);
err_release_region:
release_region(pci_resource_start(pdev, 1), 256);
err_free_irq:
free_irq(adapter->irq, adapter);
err_free:
delete_adapter(adapter);
err:
return retval;
}
static void __devexit hfcpci_remove(struct pci_dev *pdev)
{
struct hfcpci_adapter *adapter = pci_get_drvdata(pdev);
hfcpci_reset(adapter);
// del_timer(&cs->hw.hfcpci.timer); XX
/* disable DMA */
pci_disable_device(pdev);
pci_write_config_dword(pdev, HFCPCI_MWBA, 0);
pci_free_consistent(pdev, 32768, adapter->fifo, adapter->fifo_dma);
iounmap(adapter->mmio);
release_region(pci_resource_start(pdev, 1), 256);
free_irq(adapter->irq, adapter);
delete_adapter(adapter);
}
static struct pci_driver hfcpci_driver = {
name: "hfcpci",
probe: hfcpci_probe,
remove: hfcpci_remove,
id_table: hfcpci_ids,
};
static int __init hisax_hfcpci_init(void)
{
printk(KERN_INFO "hisax_hfcpcipnp: HFC PCI ISDN driver v0.0.1\n");
return pci_module_init(&hfcpci_driver);
}
static void __exit hisax_hfcpci_exit(void)
{
pci_unregister_driver(&hfcpci_driver);
}
module_init(hisax_hfcpci_init);
module_exit(hisax_hfcpci_exit);
#include "hisax_if.h"
#include "hisax_isac.h"
#include <linux/pci.h>
struct hfcpci_bcs {
struct hisax_b_if b_if;
struct hfcpci_adapter *adapter;
int mode;
int channel;
int last_fcnt;
struct sk_buff *tx_skb;
};
struct hfcpci_adapter {
struct hisax_d_if d_if;
spinlock_t hw_lock;
unsigned int irq;
void *mmio;
char *fifo;
dma_addr_t fifo_dma;
struct sk_buff *tx_skb;
int last_fcnt;
u8 int_m1, int_m2;
u8 fifo_en;
u8 trm;
u8 sctrl, sctrl_r, sctrl_e;
u8 nt_mode;
u8 ctmt;
u8 mst_m;
u8 conn;
u8 bswapped;
struct hfcpci_bcs bcs[2];
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment