summaryrefslogtreecommitdiff
path: root/xhfc/base.c
diff options
context:
space:
mode:
authorNoe Rubinstein <nrubinstein@proformatique.com>2010-12-17 14:42:39 +0100
committerNoe Rubinstein <nrubinstein@proformatique.com>2010-12-17 14:42:39 +0100
commitcb620d8ecc82128738d8f2781dd57d4c9873c80e (patch)
treee7ab6d6f621dd4290079bad9721ece4de6a102b2 /xhfc/base.c
parent433efd139c08c4a5b7ca726c5313c17909b4ac3c (diff)
move xhfc.c to base.c and rename module to xivoxhfc.c
Diffstat (limited to 'xhfc/base.c')
-rw-r--r--xhfc/base.c1202
1 files changed, 1202 insertions, 0 deletions
diff --git a/xhfc/base.c b/xhfc/base.c
new file mode 100644
index 0000000..c2c9a02
--- /dev/null
+++ b/xhfc/base.c
@@ -0,0 +1,1202 @@
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#ifdef USE_GPIO
+#include <gpio/gpio.h>
+#endif
+
+#include "xhfc24sucd.h"
+#include "xhfc.h"
+#include "xhfc_leb.h"
+
+static const char xhfc_rev[] = "$Revision: 1.22 $";
+static int card_cnt = 0;
+
+MODULE_LICENSE("GPL");
+
+uint debug = 0;
+uint dbg_spanfilter = 0xFFFFFFFF;
+int exit_after_reset = 0;
+
+uint ntte = 0x3;
+
+#ifdef USE_GPIO
+static uint reset_gpio = 21;
+#endif
+
+int softconf = 0;
+
+module_param(debug, uint, S_IRUGO | S_IWUSR);
+module_param(dbg_spanfilter, uint, S_IRUGO | S_IWUSR);
+module_param(softconf, bool, S_IRUGO);
+module_param(ntte, uint, S_IRUGO);
+module_param(exit_after_reset, bool, S_IRUGO);
+#ifdef USE_GPIO
+module_param(reset_gpio, uint, S_IRUGO);
+#endif
+
+MODULE_PARM_DESC(debug, "Debug bitfield:\n"
+ "\t0: general\n"
+ //"\t2: verbose register access, only if DEBUG is defined\n"
+ "\t3: file operations\n"
+ "\t4: (not used, echo canceller)\n"
+ "\t5: ST state\n"
+ "\t6: HDLC\n"
+ "\t7: HDLC (verbose)\n"
+ "\t8: timing\n"
+ "\t9: alarms\n");
+MODULE_PARM_DESC(dbg_spanfilter, "bitfield, filter debug info by span.");
+MODULE_PARM_DESC(ntte, "bitfield, configuration of the physical ports. ex: 0x3 = 0 NT, 1 NT, 2 TE, 3 TE.");
+MODULE_PARM_DESC(softconf, "Configure the S/T port line interface in software.");
+#ifdef USE_GPIO
+MODULE_PARM_DESC(reset_gpio, "Reset the XHFC using this GPIO");
+#endif
+MODULE_PARM_DESC(exit_after_reset, "Exit after hard reset");
+
+#ifdef AUDIO
+#define RX_HISTO_NB 16
+static int rx_histo[RX_HISTO_NB];
+#endif
+
+void xhfc_waitbusy(struct xhfc *xhfc)
+{
+ while (read_xhfc(xhfc, R_STATUS) & M_BUSY)
+ cpu_relax();
+}
+
+void xhfc_selfifo(struct xhfc *xhfc, int fifo, int receive)
+{
+ u8 r_fifo = 0x00;
+ SET_V_FIFO_NUM(r_fifo, fifo);
+ SET_V_FIFO_DIR(r_fifo, receive);
+ write_xhfc(xhfc, R_FIFO, r_fifo);
+ xhfc_waitbusy(xhfc);
+}
+
+void xhfc_inc_f(struct xhfc *xhfc)
+{
+ write_xhfc(xhfc, A_INC_RES_FIFO, M_INC_F);
+ xhfc_waitbusy(xhfc);
+}
+
+static inline void xhfc_resetfifo(struct xhfc *xhfc)
+{
+ write_xhfc(xhfc, A_INC_RES_FIFO, M_RES_FIFO | M_RES_FIFO_ERR);
+ xhfc_waitbusy(xhfc);
+}
+
+int xhfc_reset(struct xhfc *xhfc)
+{
+ int timeout = 0x2000;
+
+ /* software reset to enable R_FIFO_MD setting */
+ write_xhfc(xhfc, R_CIRM, M_SRES);
+ udelay(5);
+ write_xhfc(xhfc, R_CIRM, 0);
+
+ while ((read_xhfc(xhfc, R_STATUS) & (M_BUSY | M_PCM_INIT))
+ && (timeout)) {
+ timeout--;
+ cpu_relax();
+ }
+
+ if (!(timeout)) {
+ printk(KERN_ERR
+ "%s %s: initialization sequence could not finish\n",
+ xhfc->pi->name, __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+void xhfc_config_st(struct xhfc *x, int port, int nt)
+{
+ u8 r_su_sel, a_su_ctrl0, a_su_ctrl1, a_su_ctrl2, a_su_wr_sta, a_su_clk_dly;
+ r_su_sel = a_su_ctrl0 = a_su_ctrl1 = a_su_ctrl2 = a_su_wr_sta = a_su_clk_dly = 0x00;
+ SET_V_SU_SEL(r_su_sel, port);
+ SET_V_SU_MD(a_su_ctrl0, !!nt);
+ SET_V_B1_TX_EN(a_su_ctrl0, 1);
+ SET_V_B2_TX_EN(a_su_ctrl0, 1);
+ SET_V_ST_E_IGNO(a_su_ctrl1, nt ? 1 : 0);
+ SET_V_G2_G3_EN(a_su_ctrl1, 0); /* Disallowed in order to be able to
+ * guarantee that T1 is handled
+ * correctly (xref6) */
+ SET_V_B1_RX_EN(a_su_ctrl2, 1);
+ SET_V_B2_RX_EN(a_su_ctrl2, 1);
+ SET_V_SU_ACT(a_su_wr_sta, 3); /* activation */
+ SET_V_SU_CLK_DLY(a_su_clk_dly, nt ? 0xC : 0xE); /* normal operation
+ * WARNING: if the
+ * interface is
+ * softconfigured, this
+ * should always be
+ * 0xC XXX */
+ SET_V_ST_SMPL(a_su_clk_dly, nt ? 0x6 : 0x0); /* default value */
+
+ write_xhfc(x, R_SU_SEL, r_su_sel);
+ xhfc_waitbusy(x);
+ write_xhfc(x, A_SU_CTRL0, a_su_ctrl0);
+ write_xhfc(x, A_SU_CTRL1, a_su_ctrl1);
+ write_xhfc(x, A_SU_CTRL2, a_su_ctrl2);
+ write_xhfc(x, A_SU_CLK_DLY, a_su_clk_dly);
+ write_xhfc(x, A_SU_WR_STA, a_su_wr_sta);
+ xhfc_waitbusy(x);
+}
+
+/* 2 MBit/s (C4IO is 4.096 MHz, 32 time slots): */
+#define V_PCM_DR_2M 0x00
+
+/* 4 MBit/s (C4IO is 8.192 MHz, 64 time slots): */
+#define V_PCM_DR_4M 0x10
+
+/* 8 MBit/s (C4IO is 16.384 MHz, 128 time slots): */
+#define V_PCM_DR_8M 0x20
+
+/* configures PCM master mode
+*/
+int xhfc_config_pcm(struct xhfc *xhfc, int master_or_slave)
+{
+ u8 r_pcm_md0 = 0;
+
+ SET_V_F0_LEN(r_pcm_md0, 1);
+
+ write_xhfc(xhfc, R_PCM_MD0, SET_V_PCM_IDX(r_pcm_md0, 0x9));
+ /* use slow PCM clock adjust speed */
+ write_xhfc(xhfc, R_PCM_MD1, M_PLL_ADJ | V_PCM_DR_2M);
+ /* NOTE: open drain on the PCM: add bit M_PCM_OD in R_PCM_MD1
+ * We can't do that with the demo interface card for
+ * Megrez <-> XHFC EVB because Xavier did not managed to get
+ * SMD 1k R before soldering :P
+ */
+
+ if (master_or_slave == XHFC_PCM_MASTER) {
+ write_xhfc(xhfc, R_PCM_MD0,
+ SET_V_PCM_IDX(r_pcm_md0, 0xA));
+ /* enable PCM bit clk for C2O pin */
+ write_xhfc(xhfc, R_PCM_MD2, M_C2O_EN);
+ } else {
+ write_xhfc(xhfc, R_PCM_MD0,
+ SET_V_PCM_IDX(r_pcm_md0, 0xA));
+ write_xhfc(xhfc, R_PCM_MD2, M_C2I_EN);
+ }
+
+ /* this could maybe be done sooner, but right now I'm limiting
+ * the number of changes */
+ write_xhfc(xhfc, R_PCM_MD0, SET_V_PCM_IDX(r_pcm_md0, 0x0)
+ | M_PCM_MD | M_C4_POL);
+
+ return 0;
+}
+
+#define debug_fz(fifo, prefix, buf) \
+do { \
+sprintf(buf, "%s(fifo %d): flen=%d, " \
+ "zlen=%d", prefix, fifo, flen, \
+ zlen); \
+} while (0)
+
+#define DEFINE_GET_LEN(F_or_Z) \
+static inline int \
+get_ ## F_or_Z ## len(struct xhfc* x) { \
+u8 _1, _2; \
+int len; \
+ \
+_1 = read_xhfc(x, A_ ## F_or_Z ## 1); \
+_2 = read_xhfc(x, A_ ## F_or_Z ## 2); \
+len = _1 - _2; \
+ \
+if (len < 0) \
+ len += \
+ (XHFC_ ## F_or_Z ## MAX - \
+ XHFC_ ## F_or_Z ## MIN) + 1; \
+ \
+return len; \
+}
+
+DEFINE_GET_LEN(F)
+DEFINE_GET_LEN(Z)
+
+void xhfc_config_d_chan_on_fifo(struct xhfc *x, int fifo, int direction)
+{
+ u8 r_fifo, a_con_hdlc, a_channel, a_subch_cfg, a_inc_res_fifo;
+ r_fifo = a_con_hdlc = a_channel = a_subch_cfg = a_inc_res_fifo = 0x00;
+
+ SET_V_FIFO_NUM(r_fifo, fifo);
+
+ SET_V_IFF(a_con_hdlc, 1); /* inter frame fill with 1s */
+ SET_V_HDLC_TRP(a_con_hdlc, 0); /* HDLC mode */
+ SET_V_FIFO_IRQ(a_con_hdlc, 7); /* FIFO & IRQ enabled */
+ SET_V_CH_FNUM(a_channel, fifo); /* chan number */
+ SET_V_BIT_CNT(a_subch_cfg, 2); /* only two bits read or written */
+ SET_V_RES_FIFO(a_inc_res_fifo, 1); /* reset FIFO */
+ SET_V_RES_LOST(a_inc_res_fifo, 1); /* reset FIFO */
+ SET_V_RES_FIFO_ERR(a_inc_res_fifo, 1); /* reset FIFO */
+
+ SET_V_DATA_FLOW(a_con_hdlc, 0); /* FIFO <-> ST/Up */
+
+ SET_V_FIFO_DIR(r_fifo, direction);
+ SET_V_CH_FDIR(a_channel, direction);
+
+ write_xhfc(x, R_FIFO, r_fifo);
+ xhfc_waitbusy(x);
+ write_xhfc(x, A_CON_HDLC, a_con_hdlc);
+ write_xhfc(x, A_CHANNEL, a_channel);
+ write_xhfc(x, A_SUBCH_CFG, a_subch_cfg);
+ write_xhfc(x, A_INC_RES_FIFO, a_inc_res_fifo);
+ xhfc_waitbusy(x);
+}
+
+void xhfc_config_b_chan_on_fifo(struct xhfc *x, int fifo, int slot, int direction)
+{
+ u8 r_fifo, a_con_hdlc, r_slot, a_sl_cfg;
+ r_fifo = a_con_hdlc = r_slot = a_sl_cfg = 0x00;
+
+ SET_V_FIFO_DIR(r_fifo, direction);
+ SET_V_FIFO_NUM(r_fifo, fifo);
+ SET_V_REV(r_fifo, 1);
+
+ SET_V_IFF(a_con_hdlc, 0); /* Ox7E as interframe fill -- not really used */
+ SET_V_HDLC_TRP(a_con_hdlc, 1); /* transparent mode */
+ SET_V_FIFO_IRQ(a_con_hdlc, 7); /* enable data transmission */
+ SET_V_DATA_FLOW(a_con_hdlc, 6); /* '110': ST <-> PCM */
+
+ SET_V_SL_DIR(r_slot, direction);
+ SET_V_SL_NUM(r_slot, slot);
+
+ SET_V_CH_SDIR(a_sl_cfg, direction);
+ SET_V_CH_SNUM(a_sl_cfg, fifo);
+ SET_V_ROUT(a_sl_cfg, 3); /* '11': receive data from STIO1, output to STIO2 */
+
+ write_xhfc(x, R_FIFO, r_fifo);
+ xhfc_waitbusy(x);
+ write_xhfc(x, A_CON_HDLC, a_con_hdlc);
+ write_xhfc(x, R_SLOT, r_slot);
+ write_xhfc(x, A_SL_CFG, a_sl_cfg);
+}
+
+void xhfc_config_data_flow(struct xhfc* x)
+{
+ int i;
+ for (i = 0; i < SPANS_PER_CHIP; i++) {
+ /* HFC chan, PCM slot, direction */
+ xhfc_config_b_chan_on_fifo(x, i * 4 + 0, i * 2 + 0, TRANSMIT);
+ xhfc_config_b_chan_on_fifo(x, i * 4 + 0, i * 2 + 0, RECEIVE);
+ xhfc_config_b_chan_on_fifo(x, i * 4 + 1, i * 2 + 1, TRANSMIT);
+ xhfc_config_b_chan_on_fifo(x, i * 4 + 1, i * 2 + 1, RECEIVE);
+
+ xhfc_config_d_chan_on_fifo(x, i * 4 + 2, TRANSMIT);
+ xhfc_config_d_chan_on_fifo(x, i * 4 + 2, RECEIVE);
+ }
+}
+
+/**
+* hdlc_signal_complete() - Signal dahdi that we have a complete frame.
+*
+* @xhfc_span: The span which received the frame.
+* @stat: The frame status byte from the XHFC controller.
+*
+*/
+static void hdlc_signal_complete(struct xhfc_span *xhfc_span, u8 stat)
+{
+ if (stat != 0x00) {
+ if (DBG_HDLC && DBG_SPAN(xhfc_span)) {
+ printk(KERN_INFO DRIVER_NAME "(port %d) STAT=0x%02x indicates "
+ "frame problem: %s\n", portno(xhfc_span),
+ stat,
+ (0xff == stat) ? "HDLC Abort" : "Bad FCS");
+ }
+
+ dahdi_hdlc_abort(xhfc_span->sigchan, (0xff == stat) ?
+ DAHDI_EVENT_ABORT : DAHDI_EVENT_BADFCS);
+ } else {
+ if (DBG_HDLC && DBG_SPAN(xhfc_span)) {
+ printk(KERN_INFO DRIVER_NAME
+ "(port %d) Frame " /*"%d" */ "is good!\n",
+ portno(xhfc_span)
+ /*, xhfc_span->frames_in */ );
+ }
+ dahdi_hdlc_finish(xhfc_span->sigchan);
+ }
+}
+
+/*
+ * Inner loop for D-channel receive function. Retrieves HDLC data from the
+ * hardware. If the hardware indicates that the frame is complete, we check
+ * the HDLC engine's STAT byte and update DAHDI as needed.
+ *
+ * Returns the number of HDLC frames left in the FIFO. */
+static int hdlc_rx_frame(struct xhfc_span *xhfc_span)
+{
+ char debugbuf[MAX(256, HDLC_BUF_LEN * 3 + 1)];
+ int dbglen;
+
+ int fifo, i, j, k, zleft;
+ int zlen, flen;
+ unsigned char buf[HDLC_BUF_LEN];
+ struct xhfc *x = xhfc_span->xhfc;
+
+ fifo = dchan_fifo(xhfc_span);
+
+ if (DBG_VERBOSE_HDLC && DBG_SPAN(xhfc_span))
+ printk(KERN_INFO DRIVER_NAME ": %s(fifo %d): start\n",
+ __func__, fifo);
+
+ xhfc_selfifo(x, fifo, RECEIVE);
+
+ flen = get_Flen(x);
+ zlen = get_Zlen(x);
+
+ debug_fz(fifo, __func__, debugbuf);
+
+ if (!flen && !zlen) {
+ if (DBG_VERBOSE_HDLC && DBG_SPAN(xhfc_span))
+ printk(KERN_INFO DRIVER_NAME
+ ": %s, nothing to receive (%ld)\n",
+ debugbuf, xhfc_span->non_rx_cnt);
+ xhfc_span->non_rx_cnt++;
+ return 0;
+ }
+ xhfc_span->non_rx_cnt = 0;
+
+ if (DBG_VERBOSE_HDLC && DBG_SPAN(xhfc_span))
+ printk(KERN_INFO DRIVER_NAME ": %s\n", debugbuf);
+
+ /* if we have at least one complete frame, increment zleft to include
+ * status byte */
+ zleft = zlen;
+ if (flen)
+ zleft++;
+
+ do {
+ j = zleft > HDLC_BUF_LEN ? HDLC_BUF_LEN : zleft;
+
+ for (i = 0; i < j; i++)
+ buf[i] = read_xhfc(x, A_FIFO_DATA);
+
+ /* don't send STAT byte to DAHDI */
+ k = j;
+ if (xhfc_span->sigchan) {
+ if ((j != HDLC_BUF_LEN) && flen)
+ k--;
+ if (k)
+ dahdi_hdlc_putbuf(xhfc_span->sigchan, buf, k);
+ }
+
+ zleft -= j;
+
+ if (DBG_VERBOSE_HDLC && DBG_SPAN(xhfc_span))
+ printk(KERN_INFO DRIVER_NAME
+ ": transmitted %d bytes to dahdi, "
+ "zleft=%d\n", k, zleft);
+
+ if (DBG_HDLC && DBG_SPAN(xhfc_span)) {
+ dbglen = 0;
+ for (i = 0; i < j; i++)
+ dbglen += sprintf(debugbuf+dbglen, "%02x ", buf[i]);
+ debugbuf[dbglen] = '\0';
+
+ printk(KERN_INFO DRIVER_NAME ": %s(fifo %d): "
+ "zlen=%d, zleft=%d: %s\n", __func__,
+ fifo, zlen, zleft, debugbuf);
+ }
+ } while (zleft > 0);
+
+ /* Frame received, increment F2 */
+ if (flen)
+ xhfc_inc_f(x);
+
+ /* If this channel is not configured with a signalling span we don't
+ * need to notify the rest of dahdi about this frame. */
+ if (!xhfc_span->sigchan) {
+ if (DBG_HDLC && DBG_SPAN(xhfc_span))
+ printk(KERN_INFO DRIVER_NAME
+ ": %s(fifo %d): flen %d, early end\n",
+ __func__, fifo, flen);
+ return flen;
+ }
+
+ if (flen)
+ hdlc_signal_complete(xhfc_span, buf[i - 1]);
+
+ if (DBG_VERBOSE_HDLC && DBG_SPAN(xhfc_span))
+ printk(KERN_INFO DRIVER_NAME ": %s(fifo %d): flen=%d end\n",
+ __func__, fifo, flen);
+
+ return flen;
+}
+
+static uint frames_out;
+/*
+* Takes one blob of data from DAHDI and shoots it out to the hardware. The
+* blob may or may not be a complete HDLC frame. If it isn't, the rest will be
+* pulled during the next timer interrupt.
+* Returns nonzero if there is still data to send in the current HDLC frame.
+*/
+static int hdlc_tx_frame(struct xhfc_span *xhfc_span)
+{
+ struct xhfc *x = xhfc_span->xhfc;
+ struct dahdi_span *dahdi_span = &xhfc_span->span;
+ int res, i, fifo;
+ int zlen;
+ int flen = -1;
+ unsigned char buf[HDLC_BUF_LEN];
+ unsigned int size = ARRAY_SIZE(buf);
+ char debugbuf[MAX(256, HDLC_BUF_LEN * 3 + 1)];
+ int dbglen;
+
+ /* ignoring TE red alarms: if TE and we are in alarm, restart the
+ * S/T state machine... */
+ if (!xhfc_span->nt && (xhfc_span->span.alarms != 0)) {
+ activate_request(xhfc_span);
+ }
+
+ fifo = dchan_fifo(xhfc_span);
+ res = dahdi_hdlc_getbuf(xhfc_span->sigchan, buf, &size);
+
+ xhfc_selfifo(x, fifo, TRANSMIT);
+
+ zlen = get_Zlen(x);
+ debug_fz(fifo, __func__, debugbuf);
+
+ /* TODO: check zlen, etc. */
+ if ((XHFC_ZMAX - zlen) < size) {
+ static int arg;
+ printk(KERN_INFO DRIVER_NAME ": arg (%d), zlen (%d) < what we "
+ "grabbed from DAHDI (%d)!\n", ++arg, zlen, size);
+ size = zlen;
+ dahdi_hdlc_abort(xhfc_span->sigchan, DAHDI_EVENT_OVERRUN);
+ }
+
+ if (size > 0) {
+
+ for (i = 0; i < size; i++)
+ write_xhfc(x, A_FIFO_DATA, buf[i]);
+ /*
+ * If we got a full frame from DAHDI, increment F and
+ * decrement our HDLC pending counter. */
+ if (res != 0) {
+ frames_out++;
+ xhfc_inc_f(x);
+ atomic_dec(&xhfc_span->hdlc_pending);
+ }
+ }
+
+ if (DBG_HDLC && DBG_SPAN(xhfc_span)) {
+ printk(KERN_INFO DRIVER_NAME ": %s\n", debugbuf);
+
+ dbglen = 0;
+ for (i = 0; i < size; i++)
+ dbglen += sprintf(debugbuf+dbglen, "%02x ", buf[i]);
+ debugbuf[dbglen] = '\0';
+
+ printk(KERN_INFO DRIVER_NAME
+ ": hdlc_tx_frame(span %d): DAHDI gave %d "
+ "bytes for FIFO %d (res = %d): %s\n",
+ dahdi_span->spanno,
+ size, fifo, res, debugbuf);
+
+ if (size && res != 0)
+ printk(KERN_INFO DRIVER_NAME
+ ": Transmitted frame %d on span %d\n",
+ frames_out - 1, dahdi_span->spanno);
+ }
+
+ return !res;
+}
+
+/* DAHDI calls this when it has data it wants to send to the HDLC controller */
+void xhfc_hdlc_hard_xmit(struct dahdi_chan *chan)
+{
+ struct dahdi_span *dahdi_span = chan->span;
+ struct xhfc_span *xhfc_span =
+ container_of(dahdi_span, struct xhfc_span, span);
+
+ if ((DBG_FOPS || DBG_HDLC) && DBG_SPAN(xhfc_span))
+ printk(KERN_INFO DRIVER_NAME ": %s on chan %s (%i/%i), "
+ "span=%i (sigchan=%p, chan=%p): %d+1 frames\n",
+ __func__, chan->name, chan->channo,
+ chan->chanpos, dahdi_span->spanno,
+ xhfc_span->sigchan, chan,
+ atomic_read(&xhfc_span->hdlc_pending));
+
+ /* Increment the hdlc_pending counter and trigger hdlc_tx_frame */
+ if (xhfc_span->sigchan == chan)
+ atomic_inc(&xhfc_span->hdlc_pending);
+}
+
+/*****************************************************************************
+ * Interrupt handler, does all sort of stuff, some of which should prolly be *
+ * scheduled in a tasklet instead *
+ *****************************************************************************/
+
+#define fifo_test(reg, fifonum, dir) (reg[fifonum/4] & (1 << (((fifonum % 4) * 2) + dir)))
+
+irqreturn_t xhfc_interrupt(int irq, void *dev_id, struct pt_regs* ptregs)
+{
+
+ struct xhfc_pi *pi = dev_id;
+ struct xhfc *xhfc;
+
+ int i;
+ u8 r_su_irq;
+ u8 misc_irq;
+ u8 fifo_irq[SPANS_PER_CHIP];
+ u8 fifo_fill[SPANS_PER_CHIP];
+
+ (void) ptregs;
+
+ xhfc = &pi->xhfc;
+
+ /* IRQs for other HW with shared IRQ or spurious
+ * XXX for XiVO OpenHardware we should not share IRQ! */
+ if (!(read_xhfc(xhfc, R_IRQ_OVIEW)))
+ return IRQ_NONE;
+
+ /* reset IRQ source */
+ misc_irq = read_xhfc(xhfc, R_MISC_IRQ);
+
+ if (xhfc->running) {
+ /* check for Timer IRQ */
+ if (misc_irq & M_TI_IRQMSK)
+ xhfc->ticks++;
+
+ for(i = 0; i < SPANS_PER_CHIP; i++) {
+ fifo_irq[i] = read_xhfc(xhfc, R_FIFO_BL0_IRQ + i);
+ fifo_fill[i] = read_xhfc(xhfc, R_FILL_BL0 + i);
+ }
+
+ for (i = 0; i < SPANS_PER_CHIP; i++) {
+ struct xhfc_span* s = &xhfc->spans[i];
+
+ if ((s->span.flags & DAHDI_FLAG_RUNNING) && s->sigchan &&
+ s->span.alarms == DAHDI_ALARM_NONE) {
+
+ /* No need to loop, makes no sense to receive
+ * more than one HDLC frame per second.
+ */
+ if ( fifo_test(fifo_irq, dchan_fifo(s), RECEIVE) ||
+ fifo_test(fifo_fill, dchan_fifo(s), RECEIVE))
+
+ hdlc_rx_frame(s);
+
+ if (atomic_read(&s->hdlc_pending))
+ hdlc_tx_frame(s);
+#ifdef AUDIO
+ /* Put in readchunk chunk _previously_ retrieved
+ * from icp soft.
+ * We can't update it at icp level right now,
+ * because it has been written by trippy monkeys
+ * and it would probably be a very bad idea to
+ * try to call it from hardirq context.
+ * (look at ixOsalMutexLock ... :/ )
+ */
+ xivo_tdm_receive(pi->tdm_port, i*2 + 0,
+ s->chans[0]->readchunk);
+ xivo_tdm_receive(pi->tdm_port, i*2 + 1,
+ s->chans[1]->readchunk);
+
+ dahdi_receive(&s->span);
+ dahdi_transmit(&s->span);
+ dahdi_ec_span(&s->span);
+
+ xivo_tdm_transmit(pi->tdm_port, i*2 + 0,
+ s->chans[0]->writechunk);
+ xivo_tdm_transmit(pi->tdm_port, i*2 + 1,
+ s->chans[1]->writechunk);
+#endif
+ }
+ }
+
+#ifdef AUDIO
+ xivo_tdm_tick(pi->tdm_port);
+#endif
+
+ r_su_irq = read_xhfc(xhfc, R_SU_IRQ);
+
+ for (i = 0; i < SPANS_PER_CHIP; i++) {
+ handle_st_timers(&xhfc->spans[i]);
+ if (r_su_irq & (1 << i) || 2 == xhfc->ticks /* bootstrap XXX WTF*/)
+ handle_state_change(&xhfc->spans[i]);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+
+/*****************************************************/
+/* disable all interrupts by disabling M_GLOB_IRQ_EN */
+/*****************************************************/
+static void disable_interrupts(struct xhfc * xhfc)
+{
+ printk(KERN_INFO "%s %s\n", xhfc->name, __func__);
+
+ write_xhfc(xhfc, R_IRQ_CTRL, 0);
+ read_xhfc(xhfc, R_CHIP_ID);
+ mb();
+
+ set_mb(xhfc->running, 0);
+}
+
+
+/******************************************/
+/* start interrupt and set interrupt mask */
+/******************************************/
+static void enable_interrupts(struct xhfc * xhfc)
+{
+ u8 r_irq_ctrl = 0;
+ printk(KERN_INFO "%s %s\n", xhfc->name, __func__);
+
+ set_mb(xhfc->running, 1);
+
+ /* timer interrupt every 1 ms */
+ write_xhfc(xhfc, R_TI_WD, 0x02);
+ write_xhfc(xhfc, R_MISC_IRQMSK, M_TI_IRQMSK);
+
+ /* clear all pending interrupts bits */
+ read_xhfc(xhfc, R_MISC_IRQ);
+ read_xhfc(xhfc, R_SU_IRQ);
+ read_xhfc(xhfc, R_FIFO_BL0_IRQ);
+ read_xhfc(xhfc, R_FIFO_BL1_IRQ);
+ read_xhfc(xhfc, R_FIFO_BL2_IRQ);
+ read_xhfc(xhfc, R_FIFO_BL3_IRQ);
+
+ /* enable global interrupts */
+ SET_V_GLOB_IRQ_EN(r_irq_ctrl, 1);
+ SET_V_FIFO_IRQ_EN(r_irq_ctrl, 1);
+ write_xhfc(xhfc, R_IRQ_CTRL, r_irq_ctrl);
+}
+
+
+/***********************************/
+/* initialise the XHFC ISDN Chip */
+/* return 0 on success. */
+/***********************************/
+int xhfc_collect_chip_id(struct xhfc * xhfc)
+{
+ u8 chip_id = read_xhfc(xhfc, R_CHIP_ID);
+ switch (chip_id) {
+ case CHIP_ID_1SU:
+ case CHIP_ID_2SU:
+ case CHIP_ID_2S4U:
+ case CHIP_ID_4SU:
+ printk(KERN_INFO "%s ChipID: 0x%x\n",
+ xhfc->name, chip_id);
+ return 0;
+ }
+
+ printk(KERN_ERR "%s %s: unkown Chip ID 0x%x\n",
+ xhfc->name, __func__, chip_id);
+ return -EIO;
+}
+
+void xhfc_hard_reset(void)
+{
+#ifdef USE_GPIO
+ gpio_set_direction(reset_gpio, GPIO_OUTPUT);
+ gpio_set_to_gpio(reset_gpio);
+ gpio_set_level(reset_gpio, GPIO_LOW);
+ msleep(1);
+ gpio_set_level(reset_gpio, GPIO_HIGH);
+#endif
+}
+
+#if 0
+void configure_port(int port, int nt, int lineterm)
+{
+ gpio_set_direction(lineterm_gpio, GPIO_OUTPUT);
+ gpio_set_to_gpio(lineterm_gpio);
+ gpio_set_direction(ntte_gpio, GPIO_OUTPUT);
+ gpio_set_to_gpio(ntte_gpio);
+ gpio_set_level(lineterm_gpio, lineterm);
+ gpio_set_level(ntte_gpio, nt);
+}
+#endif
+
+void xhfc_init_and_configure(struct xhfc* x)
+{
+ u8 r_fifo_thres = 0;
+ /* In DAHDI's driver's this part is "stage 1" */
+ xhfc_reset(x);
+
+ /* Make sure interrupts disabled */
+ disable_interrupts(x);
+
+ /* "stage 2" */
+ xhfc_config_pcm(x, XHFC_PCM_MASTER);
+
+ /* Now we need to setup the flow controller and stuff */
+ xhfc_config_data_flow(x);
+
+ SET_V_THRES_RX(r_fifo_thres, 2);
+ SET_V_THRES_TX(r_fifo_thres, 2);
+ write_xhfc(x, R_FIFO_THRES, r_fifo_thres);
+}
+
+static void xhfc_span_set_ntte(struct xhfc_span* s, int ntte)
+{
+ s->nt = !!ntte;
+ s->span.spantype = ntte ? "NT" : "TE";
+}
+
+int xhfc_spanconfig(struct dahdi_span *span, struct dahdi_lineconfig *lc)
+{
+ struct xhfc_span *xhfc_span;
+ struct xhfc *xhfc;
+ int term;
+
+ xhfc_span = container_of(span, struct xhfc_span, span);
+ xhfc = xhfc_span->xhfc;
+
+ xhfc_span_set_ntte(xhfc_span, lc->lineconfig & DAHDI_CONFIG_NTTE); /* xref2 */
+
+ term = (lc->lineconfig & DAHDI_CONFIG_TERM) ? 1 : 0;
+
+ printk(KERN_INFO DRIVER_NAME ": Configuring port %d span %d in %s mode"
+ " with termination resistance %s\n",
+ portno(xhfc_span), span->spanno,
+ xhfc_span->nt ? "NT" : "TE",
+ term ? "ENABLED" : "DISABLED");
+
+ /* TODO conf hardware interface w/ GPIO */
+ xhfc_config_st(xhfc, xhfc_span->port, xhfc_span->nt);
+
+ printk(KERN_INFO DRIVER_NAME ": WARNING: This driver can't configure "
+ "the ports yet, expect problems if port %d isn't "
+ "configured in %s mode\n",
+ portno(xhfc_span), xhfc_span->nt ? "NT" : "TE");
+
+ if (lc->sync < 0) {
+ printk(KERN_INFO DRIVER_NAME
+ ": Span %d has invalid sync priority (%d), "
+ "removing from sync source list\n",
+ span->spanno, lc->sync);
+ lc->sync = 0;
+ }
+
+ if (xhfc_span->nt && lc->sync) {
+ printk(KERN_INFO DRIVER_NAME
+ ": NT Spans cannot be timing sources. "
+ "Span %d requested to be timing source of "
+ "priority %d. Changing priority to 0\n",
+ span->offset, lc->sync);
+ lc->sync = 0;
+ }
+
+ start_state_machine(xhfc_span);
+
+ return 0;
+}
+
+int xhfc_chanconfig(struct dahdi_chan *chan, int sigtype)
+{
+ int alreadyrunning;
+ struct xhfc_span *xhfc_span = container_of(chan->span, struct xhfc_span, span);
+ int res;
+
+ alreadyrunning = xhfc_span->span.flags & DAHDI_FLAG_RUNNING;
+
+ if (DBG_FOPS)
+ printk(KERN_INFO DRIVER_NAME
+ ": %s channel %d (%s) sigtype %08x\n",
+ alreadyrunning ? "Reconfigured" : "Configured",
+ chan->channo, chan->name, sigtype);
+
+ switch (sigtype) {
+ case DAHDI_SIG_HARDHDLC:
+ /* xref1 */
+ if (DBG_FOPS)
+ printk(KERN_INFO DRIVER_NAME
+ ": Configuring hardware HDLC on %s\n",
+ chan->name);
+ xhfc_span->sigchan = chan;
+ atomic_set(&xhfc_span->hdlc_pending, 0);
+ res = 0;
+ break;
+ case DAHDI_SIG_HDLCFCS:
+ printk(KERN_INFO DRIVER_NAME ": HDLCFCS not supported\n");
+ res = -ENOSYS;
+ break;
+ case DAHDI_SIG_HDLCNET:
+ printk(KERN_INFO DRIVER_NAME ": HDLCNET not supported\n");
+ res = -ENOSYS;
+ break;
+ case DAHDI_SIG_HDLCRAW:
+ printk(KERN_INFO DRIVER_NAME ": HDLCRAW not supported\n");
+ res = -ENOSYS;
+ break;
+ default:
+ res = 0;
+ break;
+ };
+
+ return res;
+}
+
+static int xhfc_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data)
+{
+ return -ENOTTY;
+}
+
+static int xhfc_span_startup(struct dahdi_span* s)
+{
+ struct xhfc_span *xhfc_span = container_of(s, struct xhfc_span, span);
+ activate_request(xhfc_span);
+ return 0;
+}
+
+static int xhfc_span_shutdown(struct dahdi_span* s)
+{
+ struct xhfc_span *xhfc_span = container_of(s, struct xhfc_span, span);
+ deactivate_request(xhfc_span);
+ return 0;
+}
+
+static const struct dahdi_span_ops xhfc_span_ops = {
+ .owner = THIS_MODULE,
+ .spanconfig = xhfc_spanconfig,
+ .chanconfig = xhfc_chanconfig,
+ .startup = xhfc_span_startup,
+ .shutdown = xhfc_span_shutdown,
+ .ioctl = xhfc_ioctl,
+ .hdlc_hard_xmit = xhfc_hdlc_hard_xmit,
+};
+
+/************************************************************************
+ * Responsible for initializing the xhfc_span and dahdi_span structures *
+ ************************************************************************/
+void init_spans(struct xhfc* x)
+{
+ int i, j;
+ struct xhfc_span *xhfc_span;
+ struct dahdi_span *dahdi_span;
+ struct dahdi_chan *chan;
+
+/* for each span on the card */
+ for (i = 0; i < SPANS_PER_CHIP; i++) {
+
+ xhfc_span = &x->spans[i];
+ dahdi_span = &xhfc_span->span;
+
+ xhfc_span->xhfc = x;
+ xhfc_span->port = i;
+
+ xhfc_span->sigchan = NULL; /* conf'd in chanconfig (xref1) */
+ xhfc_span_set_ntte(xhfc_span, ntte & (1 << i)); /* reconf'd in spanconfig (xref2) */
+
+ /* All the timer stuff is done in the timer and state
+ * functions, not here. */
+
+ dahdi_span->ops = &xhfc_span_ops;
+ dahdi_span->chans = xhfc_span->chans;
+ dahdi_span->irq = x->pi->pci_dev->irq;
+ dahdi_span->offset = i;
+ dahdi_span->channels = CHANS_PER_SPAN;
+ dahdi_span->flags = 0;
+ dahdi_span->deflaw = DAHDI_LAW_ALAW;
+ dahdi_span->linecompat =
+ DAHDI_CONFIG_TERM | DAHDI_CONFIG_NTTE |
+ DAHDI_CONFIG_AMI | DAHDI_CONFIG_CCS ;
+
+ sprintf(dahdi_span->name, "XHFC/%d", i+1);
+ sprintf(dahdi_span->desc, "XHFC port %d", i+1);
+ dahdi_span->manufacturer = "Avencall";
+ dahdi_copy_string(dahdi_span->devicetype, "XHFC-4SU",
+ sizeof(dahdi_span->devicetype));
+ sprintf(dahdi_span->location, "PCI Bus %02d Slot %02d",
+ x->pi->pci_dev->bus->number,
+ PCI_SLOT(x->pi->pci_dev->devfn) + 1);
+
+ dahdi_span->alarms = DAHDI_ALARM_RED;
+
+
+ init_waitqueue_head(&dahdi_span->maintq); /* kinda everyone does this */
+
+ /* now initialize each channel in the span */
+ for (j=0; j < CHANS_PER_SPAN; j++) {
+ xhfc_span->chans[j] = &xhfc_span->_chans[j];
+ chan = xhfc_span->chans[j];
+ chan->pvt = x;
+
+ sprintf(chan->name, "XHFC/%d/%d", i + 1, j + 1);
+
+ if (j == 2) {
+ chan->sigcap = DAHDI_SIG_HARDHDLC;
+ } else {
+ chan->sigcap = DAHDI_SIG_CLEAR | DAHDI_SIG_DACS;
+ }
+ chan->chanpos = j + 1;
+ chan->writechunk = (void *)(xhfc_span->writechunk + j * DAHDI_CHUNKSIZE);
+ chan->readchunk = (void *)(xhfc_span->readchunk + j * DAHDI_CHUNKSIZE);
+ }
+ }
+}
+
+void release_card_irq(struct xhfc_pi * pi)
+{
+ disable_interrupts(&pi->xhfc);
+ free_irq(pi->irq, pi);
+}
+
+static const struct pci_device_id tlp_leb_pci_tbl[] = {
+ { 0x8086, 0x503d, PCI_ANY_ID, PCI_ANY_ID, }, /* Intel Tolapai LEB controler */
+ { }
+};
+MODULE_DEVICE_TABLE(pci, tlp_leb_pci_tbl);
+
+static struct pci_driver xhfc_driver = {
+ .name = DRIVER_NAME,
+ .id_table = tlp_leb_pci_tbl,
+ .probe = xhfc_init_one,
+ .remove = __devexit_p(xhfc_remove_one),
+// .shutdown = xhfc_shutdown,
+};
+
+/* pci.txt: called from process context */
+int __devinit xhfc_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ int span;
+ struct xhfc_pi * pi;
+ unsigned long base, size;
+ int rc = -ENOMEM;
+
+ printk(KERN_DEBUG "entering xhfc_init_one\n");
+
+
+ /****************
+ * Memory alloc *
+ ****************/
+
+ pi = kzalloc(sizeof(struct xhfc_pi), GFP_KERNEL);
+ if (pi == NULL)
+ goto err_alloc;
+
+ sprintf(pi->name, "%s", DRIVER_NAME); /* ? */
+
+ printk(KERN_INFO "%s %s: adapter '%s' found on PCI bus %02x dev %02x\n",
+ pi->name, __func__, pi->name,
+ pdev->bus->number, pdev->devfn);
+
+
+ /************
+ * PCI init *
+ ************/
+
+ rc = pci_enable_device(pdev);
+
+ if (rc)
+ goto err_enable_device;
+
+ printk(KERN_DEBUG "pci_enable_device succeeded\n");
+
+ rc = pci_request_regions(pdev, DRIVER_NAME);
+ if (rc)
+ goto err_request_regions;
+
+ rc = -ENOMEM;
+
+ base = pci_resource_start(pdev, LEB_CSRBAR);
+ size = pci_resource_len(pdev, LEB_CSRBAR);
+ pi->regs = ioremap(base, size);
+ if (!pi->regs)
+ goto err_ioremap_csrbar;
+
+ base = pci_resource_start(pdev, LEB_MMBAR);
+ size = pci_resource_len(pdev, LEB_MMBAR);
+ pi->cs_n0 = ioremap(base, size);
+ if (!pi->cs_n0)
+ goto err_ioremap_mmbar;
+
+ pci_set_drvdata(pdev, pi);
+
+
+ /*************
+ * Hard init *
+ *************/
+
+ xhfc_hard_reset();
+
+ if (exit_after_reset) {
+ rc = -EINVAL;
+ goto exit_after_reset;
+ }
+
+ leb_init(pi);
+
+
+#ifdef AUDIO
+ /*************
+ * TDM alloc *
+ *************/
+
+ pi->tdm_port = xivo_tdm_get_port(XIVO_XHFC_TDM_PORT);
+ if (!pi->tdm_port) {
+ rc = -EBUSY;
+ goto err_tdm_get_port;
+ }
+#endif
+
+ /********************
+ * XHFC struct init *
+ ********************/
+
+ pi->pci_dev = pdev;
+ pi->xhfc.pi = pi;
+
+ /* only adress one xhfc per PI */
+ pi->xhfc.name = pi->name;
+
+ /* init interrupt engine */
+ rc = pi->irq = acpi_register_gsi(
+ IRQ_TLP_GPIO_24,
+ ACPI_LEVEL_SENSITIVE,
+ ACPI_ACTIVE_LOW);
+ if (rc < 0)
+ goto err_acpi_register;
+
+
+ /********
+ * test *
+ ********/
+
+ rc = xhfc_collect_chip_id(&pi->xhfc);
+ if (rc)
+ goto err_collect_chip_id;
+
+
+ /***************
+ * OK let's go *
+ ***************/
+
+ card_cnt++;
+
+ if ((rc = request_irq(pi->irq, &xhfc_interrupt, 0, "XHFC", pi))) {
+ printk(KERN_WARNING "%s %s: couldn't get interrupt %d\n",
+ pi->name, __func__, pi->irq);
+ goto err_request_irq;
+ }
+
+ init_spans(&pi->xhfc);
+
+ xhfc_init_and_configure(&pi->xhfc);
+#ifdef AUDIO
+ /* TDM started on the XHFC side, XHFC is MASTER */
+ /* Now it's possible to start the TDM bus on the EP80579 side, as SLAVE: */
+ if ((rc = xivo_tdm_config_port(pi->tdm_port, XHFC_MEGREZ_PROTO_XIVO_CONFIG)) < 0) {
+ printk(KERN_ERR "%s %s: xivo_tdm_config_port failed (err=%d)\n", pi->name, __func__, rc);
+ goto err_tdm_config_port;
+ }
+#endif
+
+ for (span = 0; span < SPANS_PER_CHIP; span++)
+ if ((rc = dahdi_register(&pi->xhfc.spans[span].span, /*prefmaster*/ 1)) < 0) {
+ printk(KERN_WARNING "%s %s: couldn't register spans\n", pi->name, __func__);
+ goto err_in_dahdi_register;
+ }
+
+ enable_interrupts(&pi->xhfc);
+
+#ifdef AUDIO
+ xivo_tdm_start_chans(pi->tdm_port,
+ /* timeslots 0->7 */ 0xFF,
+ NULL);
+#endif
+
+ return 0;
+
+err_in_dahdi_register:
+ for (span--; span >= 0; span--)
+ dahdi_unregister(&pi->xhfc.spans[span].span);
+#ifdef AUDIO
+err_tdm_config_port:
+#endif
+ free_irq(pi->irq, pi);
+err_request_irq:
+err_collect_chip_id:
+ //acpi_unregister_gsi(IRQ_TLP_GPIO_30); /* symbol doesn't exist??? */
+err_acpi_register:
+#ifdef AUDIO
+ xivo_tdm_put_port(pi->tdm_port);
+err_tdm_get_port:
+#endif
+exit_after_reset:
+ iounmap(pi->cs_n0);
+err_ioremap_mmbar:
+ iounmap(pi->regs);
+err_ioremap_csrbar:
+ pci_release_regions(pdev);
+err_request_regions:
+ pci_disable_device(pdev);
+err_enable_device:
+ kfree(pi);
+err_alloc:
+ return rc;
+}
+
+void __devexit xhfc_remove_one(struct pci_dev *pdev)
+{
+ int i;
+ struct xhfc_pi *pi;
+
+ pi = pci_get_drvdata(pdev);
+
+ if (DBG)
+ printk(KERN_INFO "%s %s: removing card\n",
+ pi->name, __func__);
+
+ for (i = 0; i < SPANS_PER_CHIP; i++)
+ dahdi_unregister(&pi->xhfc.spans[i].span);
+
+ release_card_irq(pi);
+
+ card_cnt--;
+
+ iounmap(pi->regs);
+ pi->regs = NULL;
+ iounmap(pi->cs_n0);
+ pi->cs_n0 = NULL;
+
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+ kfree(pi);
+
+ printk(KERN_DEBUG "%s: bye-bye\n", __func__);
+}
+
+/***************/
+/* Module init */
+/***************/
+static int __init xhfc_init(void)
+{
+ int err;
+
+ if (DBG)
+ printk(KERN_INFO DRIVER_NAME " driver Rev. %s\n", xhfc_rev);
+
+ err = dahdi_pci_module(&xhfc_driver);
+ if (err < 0)
+ goto out;
+
+ /* XXX fail with error if no cards detected */
+
+ return 0;
+
+ out:
+ return (err);
+}
+
+static void __exit xhfc_cleanup(void)
+{
+ pci_unregister_driver(&xhfc_driver);
+ if (DBG)
+ printk(KERN_INFO "%s: driver removed\n", __func__);
+}
+
+module_init(xhfc_init);
+module_exit(xhfc_cleanup);
+