diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/net/wan/pc300_drv.c |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/net/wan/pc300_drv.c')
-rw-r--r-- | drivers/net/wan/pc300_drv.c | 3692 |
1 files changed, 3692 insertions, 0 deletions
diff --git a/drivers/net/wan/pc300_drv.c b/drivers/net/wan/pc300_drv.c new file mode 100644 index 000000000000..d67be2587d4d --- /dev/null +++ b/drivers/net/wan/pc300_drv.c @@ -0,0 +1,3692 @@ +#define USE_PCI_CLOCK +static char rcsid[] = +"Revision: 3.4.5 Date: 2002/03/07 "; + +/* + * pc300.c Cyclades-PC300(tm) Driver. + * + * Author: Ivan Passos <ivan@cyclades.com> + * Maintainer: PC300 Maintainer <pc300@cyclades.com> + * + * Copyright: (c) 1999-2003 Cyclades Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Using tabstop = 4. + * + * $Log: pc300_drv.c,v $ + * Revision 3.23 2002/03/20 13:58:40 henrique + * Fixed ortographic mistakes + * + * Revision 3.22 2002/03/13 16:56:56 henrique + * Take out the debug messages + * + * Revision 3.21 2002/03/07 14:17:09 henrique + * License data fixed + * + * Revision 3.20 2002/01/17 17:58:52 ivan + * Support for PC300-TE/M (PMC). + * + * Revision 3.19 2002/01/03 17:08:47 daniela + * Enables DMA reception when the SCA-II disables it improperly. + * + * Revision 3.18 2001/12/03 18:47:50 daniela + * Esthetic changes. + * + * Revision 3.17 2001/10/19 16:50:13 henrique + * Patch to kernel 2.4.12 and new generic hdlc. + * + * Revision 3.16 2001/10/16 15:12:31 regina + * clear statistics + * + * Revision 3.11 to 3.15 2001/10/11 20:26:04 daniela + * More DMA fixes for noisy lines. + * Return the size of bad frames in dma_get_rx_frame_size, so that the Rx buffer + * descriptors can be cleaned by dma_buf_read (called in cpc_net_rx). + * Renamed dma_start routine to rx_dma_start. Improved Rx statistics. + * Fixed BOF interrupt treatment. Created dma_start routine. + * Changed min and max to cpc_min and cpc_max. + * + * Revision 3.10 2001/08/06 12:01:51 regina + * Fixed problem in DSR_DE bit. + * + * Revision 3.9 2001/07/18 19:27:26 daniela + * Added some history comments. + * + * Revision 3.8 2001/07/12 13:11:19 regina + * bug fix - DCD-OFF in pc300 tty driver + * + * Revision 3.3 to 3.7 2001/07/06 15:00:20 daniela + * Removing kernel 2.4.3 and previous support. + * DMA transmission bug fix. + * MTU check in cpc_net_rx fixed. + * Boot messages reviewed. + * New configuration parameters (line code, CRC calculation and clock). + * + * Revision 3.2 2001/06/22 13:13:02 regina + * MLPPP implementation. Changed the header of message trace to include + * the device name. New format : "hdlcX[R/T]: ". + * Default configuration changed. + * + * Revision 3.1 2001/06/15 regina + * in cpc_queue_xmit, netif_stop_queue is called if don't have free descriptor + * upping major version number + * + * Revision 1.1.1.1 2001/06/13 20:25:04 daniela + * PC300 initial CVS version (3.4.0-pre1) + * + * Revision 3.0.1.2 2001/06/08 daniela + * Did some changes in the DMA programming implementation to avoid the + * occurrence of a SCA-II bug when CDA is accessed during a DMA transfer. + * + * Revision 3.0.1.1 2001/05/02 daniela + * Added kernel 2.4.3 support. + * + * Revision 3.0.1.0 2001/03/13 daniela, henrique + * Added Frame Relay Support. + * Driver now uses HDLC generic driver to provide protocol support. + * + * Revision 3.0.0.8 2001/03/02 daniela + * Fixed ram size detection. + * Changed SIOCGPC300CONF ioctl, to give hw information to pc300util. + * + * Revision 3.0.0.7 2001/02/23 daniela + * netif_stop_queue called before the SCA-II transmition commands in + * cpc_queue_xmit, and with interrupts disabled to avoid race conditions with + * transmition interrupts. + * Fixed falc_check_status for Unframed E1. + * + * Revision 3.0.0.6 2000/12/13 daniela + * Implemented pc300util support: trace, statistics, status and loopback + * tests for the PC300 TE boards. + * + * Revision 3.0.0.5 2000/12/12 ivan + * Added support for Unframed E1. + * Implemented monitor mode. + * Fixed DCD sensitivity on the second channel. + * Driver now complies with new PCI kernel architecture. + * + * Revision 3.0.0.4 2000/09/28 ivan + * Implemented DCD sensitivity. + * Moved hardware-specific open to the end of cpc_open, to avoid race + * conditions with early reception interrupts. + * Included code for [request|release]_mem_region(). + * Changed location of pc300.h . + * Minor code revision (contrib. of Jeff Garzik). + * + * Revision 3.0.0.3 2000/07/03 ivan + * Previous bugfix for the framing errors with external clock made X21 + * boards stop working. This version fixes it. + * + * Revision 3.0.0.2 2000/06/23 ivan + * Revisited cpc_queue_xmit to prevent race conditions on Tx DMA buffer + * handling when Tx timeouts occur. + * Revisited Rx statistics. + * Fixed a bug in the SCA-II programming that would cause framing errors + * when external clock was configured. + * + * Revision 3.0.0.1 2000/05/26 ivan + * Added logic in the SCA interrupt handler so that no board can monopolize + * the driver. + * Request PLX I/O region, although driver doesn't use it, to avoid + * problems with other drivers accessing it. + * + * Revision 3.0.0.0 2000/05/15 ivan + * Did some changes in the DMA programming implementation to avoid the + * occurrence of a SCA-II bug in the second channel. + * Implemented workaround for PLX9050 bug that would cause a system lockup + * in certain systems, depending on the MMIO addresses allocated to the + * board. + * Fixed the FALC chip programming to avoid synchronization problems in the + * second channel (TE only). + * Implemented a cleaner and faster Tx DMA descriptor cleanup procedure in + * cpc_queue_xmit(). + * Changed the built-in driver implementation so that the driver can use the + * general 'hdlcN' naming convention instead of proprietary device names. + * Driver load messages are now device-centric, instead of board-centric. + * Dynamic allocation of net_device structures. + * Code is now compliant with the new module interface (module_[init|exit]). + * Make use of the PCI helper functions to access PCI resources. + * + * Revision 2.0.0.0 2000/04/15 ivan + * Added support for the PC300/TE boards (T1/FT1/E1/FE1). + * + * Revision 1.1.0.0 2000/02/28 ivan + * Major changes in the driver architecture. + * Softnet compliancy implemented. + * Driver now reports physical instead of virtual memory addresses. + * Added cpc_change_mtu function. + * + * Revision 1.0.0.0 1999/12/16 ivan + * First official release. + * Support for 1- and 2-channel boards (which use distinct PCI Device ID's). + * Support for monolythic installation (i.e., drv built into the kernel). + * X.25 additional checking when lapb_[dis]connect_request returns an error. + * SCA programming now covers X.21 as well. + * + * Revision 0.3.1.0 1999/11/18 ivan + * Made X.25 support configuration-dependent (as it depends on external + * modules to work). + * Changed X.25-specific function names to comply with adopted convention. + * Fixed typos in X.25 functions that would cause compile errors (Daniela). + * Fixed bug in ch_config that would disable interrupts on a previously + * enabled channel if the other channel on the same board was enabled later. + * + * Revision 0.3.0.0 1999/11/16 daniela + * X.25 support. + * + * Revision 0.2.3.0 1999/11/15 ivan + * Function cpc_ch_status now provides more detailed information. + * Added support for X.21 clock configuration. + * Changed TNR1 setting in order to prevent Tx FIFO overaccesses by the SCA. + * Now using PCI clock instead of internal oscillator clock for the SCA. + * + * Revision 0.2.2.0 1999/11/10 ivan + * Changed the *_dma_buf_check functions so that they would print only + * the useful info instead of the whole buffer descriptor bank. + * Fixed bug in cpc_queue_xmit that would eventually crash the system + * in case of a packet drop. + * Implemented TX underrun handling. + * Improved SCA fine tuning to boost up its performance. + * + * Revision 0.2.1.0 1999/11/03 ivan + * Added functions *dma_buf_pt_init to allow independent initialization + * of the next-descr. and DMA buffer pointers on the DMA descriptors. + * Kernel buffer release and tbusy clearing is now done in the interrupt + * handler. + * Fixed bug in cpc_open that would cause an interface reopen to fail. + * Added a protocol-specific code section in cpc_net_rx. + * Removed printk level defs (they might be added back after the beta phase). + * + * Revision 0.2.0.0 1999/10/28 ivan + * Revisited the code so that new protocols can be easily added / supported. + * + * Revision 0.1.0.1 1999/10/20 ivan + * Mostly "esthetic" changes. + * + * Revision 0.1.0.0 1999/10/11 ivan + * Initial version. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/ioport.h> +#include <linux/pci.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/net.h> +#include <linux/skbuff.h> +#include <linux/if_arp.h> +#include <linux/netdevice.h> +#include <linux/spinlock.h> +#include <linux/if.h> + +#include <net/syncppp.h> +#include <net/arp.h> + +#include <asm/io.h> +#include <asm/uaccess.h> + +#include "pc300.h" + +#define CPC_LOCK(card,flags) \ + do { \ + spin_lock_irqsave(&card->card_lock, flags); \ + } while (0) + +#define CPC_UNLOCK(card,flags) \ + do { \ + spin_unlock_irqrestore(&card->card_lock, flags); \ + } while (0) + +#undef PC300_DEBUG_PCI +#undef PC300_DEBUG_INTR +#undef PC300_DEBUG_TX +#undef PC300_DEBUG_RX +#undef PC300_DEBUG_OTHER + +static struct pci_device_id cpc_pci_dev_id[] __devinitdata = { + /* PC300/RSV or PC300/X21, 2 chan */ + {0x120e, 0x300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x300}, + /* PC300/RSV or PC300/X21, 1 chan */ + {0x120e, 0x301, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x301}, + /* PC300/TE, 2 chan */ + {0x120e, 0x310, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x310}, + /* PC300/TE, 1 chan */ + {0x120e, 0x311, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x311}, + /* PC300/TE-M, 2 chan */ + {0x120e, 0x320, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x320}, + /* PC300/TE-M, 1 chan */ + {0x120e, 0x321, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x321}, + /* End of table */ + {0,}, +}; +MODULE_DEVICE_TABLE(pci, cpc_pci_dev_id); + +#ifndef cpc_min +#define cpc_min(a,b) (((a)<(b))?(a):(b)) +#endif +#ifndef cpc_max +#define cpc_max(a,b) (((a)>(b))?(a):(b)) +#endif + +/* prototypes */ +static void tx_dma_buf_pt_init(pc300_t *, int); +static void tx_dma_buf_init(pc300_t *, int); +static void rx_dma_buf_pt_init(pc300_t *, int); +static void rx_dma_buf_init(pc300_t *, int); +static void tx_dma_buf_check(pc300_t *, int); +static void rx_dma_buf_check(pc300_t *, int); +static irqreturn_t cpc_intr(int, void *, struct pt_regs *); +static struct net_device_stats *cpc_get_stats(struct net_device *); +static int clock_rate_calc(uclong, uclong, int *); +static uclong detect_ram(pc300_t *); +static void plx_init(pc300_t *); +static void cpc_trace(struct net_device *, struct sk_buff *, char); +static int cpc_attach(struct net_device *, unsigned short, unsigned short); + +#ifdef CONFIG_PC300_MLPPP +void cpc_tty_init(pc300dev_t * dev); +void cpc_tty_unregister_service(pc300dev_t * pc300dev); +void cpc_tty_receive(pc300dev_t * pc300dev); +void cpc_tty_trigger_poll(pc300dev_t * pc300dev); +void cpc_tty_reset_var(void); +#endif + +/************************/ +/*** DMA Routines ***/ +/************************/ +static void tx_dma_buf_pt_init(pc300_t * card, int ch) +{ + int i; + int ch_factor = ch * N_DMA_TX_BUF; + volatile pcsca_bd_t __iomem *ptdescr = (card->hw.rambase + + DMA_TX_BD_BASE + ch_factor * sizeof(pcsca_bd_t)); + + for (i = 0; i < N_DMA_TX_BUF; i++, ptdescr++) { + cpc_writel(&ptdescr->next, (uclong) (DMA_TX_BD_BASE + + (ch_factor + ((i + 1) & (N_DMA_TX_BUF - 1))) * sizeof(pcsca_bd_t))); + cpc_writel(&ptdescr->ptbuf, + (uclong) (DMA_TX_BASE + (ch_factor + i) * BD_DEF_LEN)); + } +} + +static void tx_dma_buf_init(pc300_t * card, int ch) +{ + int i; + int ch_factor = ch * N_DMA_TX_BUF; + volatile pcsca_bd_t __iomem *ptdescr = (card->hw.rambase + + DMA_TX_BD_BASE + ch_factor * sizeof(pcsca_bd_t)); + + for (i = 0; i < N_DMA_TX_BUF; i++, ptdescr++) { + memset_io(ptdescr, 0, sizeof(pcsca_bd_t)); + cpc_writew(&ptdescr->len, 0); + cpc_writeb(&ptdescr->status, DST_OSB); + } + tx_dma_buf_pt_init(card, ch); +} + +static void rx_dma_buf_pt_init(pc300_t * card, int ch) +{ + int i; + int ch_factor = ch * N_DMA_RX_BUF; + volatile pcsca_bd_t __iomem *ptdescr = (card->hw.rambase + + DMA_RX_BD_BASE + ch_factor * sizeof(pcsca_bd_t)); + + for (i = 0; i < N_DMA_RX_BUF; i++, ptdescr++) { + cpc_writel(&ptdescr->next, (uclong) (DMA_RX_BD_BASE + + (ch_factor + ((i + 1) & (N_DMA_RX_BUF - 1))) * sizeof(pcsca_bd_t))); + cpc_writel(&ptdescr->ptbuf, + (uclong) (DMA_RX_BASE + (ch_factor + i) * BD_DEF_LEN)); + } +} + +static void rx_dma_buf_init(pc300_t * card, int ch) +{ + int i; + int ch_factor = ch * N_DMA_RX_BUF; + volatile pcsca_bd_t __iomem *ptdescr = (card->hw.rambase + + DMA_RX_BD_BASE + ch_factor * sizeof(pcsca_bd_t)); + + for (i = 0; i < N_DMA_RX_BUF; i++, ptdescr++) { + memset_io(ptdescr, 0, sizeof(pcsca_bd_t)); + cpc_writew(&ptdescr->len, 0); + cpc_writeb(&ptdescr->status, 0); + } + rx_dma_buf_pt_init(card, ch); +} + +static void tx_dma_buf_check(pc300_t * card, int ch) +{ + volatile pcsca_bd_t __iomem *ptdescr; + int i; + ucshort first_bd = card->chan[ch].tx_first_bd; + ucshort next_bd = card->chan[ch].tx_next_bd; + + printk("#CH%d: f_bd = %d(0x%08zx), n_bd = %d(0x%08zx)\n", ch, + first_bd, TX_BD_ADDR(ch, first_bd), + next_bd, TX_BD_ADDR(ch, next_bd)); + for (i = first_bd, + ptdescr = (card->hw.rambase + TX_BD_ADDR(ch, first_bd)); + i != ((next_bd + 1) & (N_DMA_TX_BUF - 1)); + i = (i + 1) & (N_DMA_TX_BUF - 1), + ptdescr = (card->hw.rambase + TX_BD_ADDR(ch, i))) { + printk("\n CH%d TX%d: next=0x%x, ptbuf=0x%x, ST=0x%x, len=%d", + ch, i, cpc_readl(&ptdescr->next), + cpc_readl(&ptdescr->ptbuf), + cpc_readb(&ptdescr->status), cpc_readw(&ptdescr->len)); + } + printk("\n"); +} + +#ifdef PC300_DEBUG_OTHER +/* Show all TX buffer descriptors */ +static void tx1_dma_buf_check(pc300_t * card, int ch) +{ + volatile pcsca_bd_t __iomem *ptdescr; + int i; + ucshort first_bd = card->chan[ch].tx_first_bd; + ucshort next_bd = card->chan[ch].tx_next_bd; + uclong scabase = card->hw.scabase; + + printk ("\nnfree_tx_bd = %d \n", card->chan[ch].nfree_tx_bd); + printk("#CH%d: f_bd = %d(0x%08x), n_bd = %d(0x%08x)\n", ch, + first_bd, TX_BD_ADDR(ch, first_bd), + next_bd, TX_BD_ADDR(ch, next_bd)); + printk("TX_CDA=0x%08x, TX_EDA=0x%08x\n", + cpc_readl(scabase + DTX_REG(CDAL, ch)), + cpc_readl(scabase + DTX_REG(EDAL, ch))); + for (i = 0; i < N_DMA_TX_BUF; i++) { + ptdescr = (card->hw.rambase + TX_BD_ADDR(ch, i)); + printk("\n CH%d TX%d: next=0x%x, ptbuf=0x%x, ST=0x%x, len=%d", + ch, i, cpc_readl(&ptdescr->next), + cpc_readl(&ptdescr->ptbuf), + cpc_readb(&ptdescr->status), cpc_readw(&ptdescr->len)); + } + printk("\n"); +} +#endif + +static void rx_dma_buf_check(pc300_t * card, int ch) +{ + volatile pcsca_bd_t __iomem *ptdescr; + int i; + ucshort first_bd = card->chan[ch].rx_first_bd; + ucshort last_bd = card->chan[ch].rx_last_bd; + int ch_factor; + + ch_factor = ch * N_DMA_RX_BUF; + printk("#CH%d: f_bd = %d, l_bd = %d\n", ch, first_bd, last_bd); + for (i = 0, ptdescr = (card->hw.rambase + + DMA_RX_BD_BASE + ch_factor * sizeof(pcsca_bd_t)); + i < N_DMA_RX_BUF; i++, ptdescr++) { + if (cpc_readb(&ptdescr->status) & DST_OSB) + printk ("\n CH%d RX%d: next=0x%x, ptbuf=0x%x, ST=0x%x, len=%d", + ch, i, cpc_readl(&ptdescr->next), + cpc_readl(&ptdescr->ptbuf), + cpc_readb(&ptdescr->status), + cpc_readw(&ptdescr->len)); + } + printk("\n"); +} + +int dma_get_rx_frame_size(pc300_t * card, int ch) +{ + volatile pcsca_bd_t __iomem *ptdescr; + ucshort first_bd = card->chan[ch].rx_first_bd; + int rcvd = 0; + volatile ucchar status; + + ptdescr = (card->hw.rambase + RX_BD_ADDR(ch, first_bd)); + while ((status = cpc_readb(&ptdescr->status)) & DST_OSB) { + rcvd += cpc_readw(&ptdescr->len); + first_bd = (first_bd + 1) & (N_DMA_RX_BUF - 1); + if ((status & DST_EOM) || (first_bd == card->chan[ch].rx_last_bd)) { + /* Return the size of a good frame or incomplete bad frame + * (dma_buf_read will clean the buffer descriptors in this case). */ + return (rcvd); + } + ptdescr = (card->hw.rambase + cpc_readl(&ptdescr->next)); + } + return (-1); +} + +/* + * dma_buf_write: writes a frame to the Tx DMA buffers + * NOTE: this function writes one frame at a time. + */ +int dma_buf_write(pc300_t * card, int ch, ucchar * ptdata, int len) +{ + int i, nchar; + volatile pcsca_bd_t __iomem *ptdescr; + int tosend = len; + ucchar nbuf = ((len - 1) / BD_DEF_LEN) + 1; + + if (nbuf >= card->chan[ch].nfree_tx_bd) { + return -ENOMEM; + } + + for (i = 0; i < nbuf; i++) { + ptdescr = (card->hw.rambase + + TX_BD_ADDR(ch, card->chan[ch].tx_next_bd)); + nchar = cpc_min(BD_DEF_LEN, tosend); + if (cpc_readb(&ptdescr->status) & DST_OSB) { + memcpy_toio((card->hw.rambase + cpc_readl(&ptdescr->ptbuf)), + &ptdata[len - tosend], nchar); + cpc_writew(&ptdescr->len, nchar); + card->chan[ch].nfree_tx_bd--; + if ((i + 1) == nbuf) { + /* This must be the last BD to be used */ + cpc_writeb(&ptdescr->status, DST_EOM); + } else { + cpc_writeb(&ptdescr->status, 0); + } + } else { + return -ENOMEM; + } + tosend -= nchar; + card->chan[ch].tx_next_bd = + (card->chan[ch].tx_next_bd + 1) & (N_DMA_TX_BUF - 1); + } + /* If it gets to here, it means we have sent the whole frame */ + return 0; +} + +/* + * dma_buf_read: reads a frame from the Rx DMA buffers + * NOTE: this function reads one frame at a time. + */ +int dma_buf_read(pc300_t * card, int ch, struct sk_buff *skb) +{ + int nchar; + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + volatile pcsca_bd_t __iomem *ptdescr; + int rcvd = 0; + volatile ucchar status; + + ptdescr = (card->hw.rambase + + RX_BD_ADDR(ch, chan->rx_first_bd)); + while ((status = cpc_readb(&ptdescr->status)) & DST_OSB) { + nchar = cpc_readw(&ptdescr->len); + if ((status & (DST_OVR | DST_CRC | DST_RBIT | DST_SHRT | DST_ABT)) + || (nchar > BD_DEF_LEN)) { + + if (nchar > BD_DEF_LEN) + status |= DST_RBIT; + rcvd = -status; + /* Discard remaining descriptors used by the bad frame */ + while (chan->rx_first_bd != chan->rx_last_bd) { + cpc_writeb(&ptdescr->status, 0); + chan->rx_first_bd = (chan->rx_first_bd+1) & (N_DMA_RX_BUF-1); + if (status & DST_EOM) + break; + ptdescr = (card->hw.rambase + + cpc_readl(&ptdescr->next)); + status = cpc_readb(&ptdescr->status); + } + break; + } + if (nchar != 0) { + if (skb) { + memcpy_fromio(skb_put(skb, nchar), + (card->hw.rambase+cpc_readl(&ptdescr->ptbuf)),nchar); + } + rcvd += nchar; + } + cpc_writeb(&ptdescr->status, 0); + cpc_writeb(&ptdescr->len, 0); + chan->rx_first_bd = (chan->rx_first_bd + 1) & (N_DMA_RX_BUF - 1); + + if (status & DST_EOM) + break; + + ptdescr = (card->hw.rambase + cpc_readl(&ptdescr->next)); + } + + if (rcvd != 0) { + /* Update pointer */ + chan->rx_last_bd = (chan->rx_first_bd - 1) & (N_DMA_RX_BUF - 1); + /* Update EDA */ + cpc_writel(card->hw.scabase + DRX_REG(EDAL, ch), + RX_BD_ADDR(ch, chan->rx_last_bd)); + } + return (rcvd); +} + +void tx_dma_stop(pc300_t * card, int ch) +{ + void __iomem *scabase = card->hw.scabase; + ucchar drr_ena_bit = 1 << (5 + 2 * ch); + ucchar drr_rst_bit = 1 << (1 + 2 * ch); + + /* Disable DMA */ + cpc_writeb(scabase + DRR, drr_ena_bit); + cpc_writeb(scabase + DRR, drr_rst_bit & ~drr_ena_bit); +} + +void rx_dma_stop(pc300_t * card, int ch) +{ + void __iomem *scabase = card->hw.scabase; + ucchar drr_ena_bit = 1 << (4 + 2 * ch); + ucchar drr_rst_bit = 1 << (2 * ch); + + /* Disable DMA */ + cpc_writeb(scabase + DRR, drr_ena_bit); + cpc_writeb(scabase + DRR, drr_rst_bit & ~drr_ena_bit); +} + +void rx_dma_start(pc300_t * card, int ch) +{ + void __iomem *scabase = card->hw.scabase; + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + + /* Start DMA */ + cpc_writel(scabase + DRX_REG(CDAL, ch), + RX_BD_ADDR(ch, chan->rx_first_bd)); + if (cpc_readl(scabase + DRX_REG(CDAL,ch)) != + RX_BD_ADDR(ch, chan->rx_first_bd)) { + cpc_writel(scabase + DRX_REG(CDAL, ch), + RX_BD_ADDR(ch, chan->rx_first_bd)); + } + cpc_writel(scabase + DRX_REG(EDAL, ch), + RX_BD_ADDR(ch, chan->rx_last_bd)); + cpc_writew(scabase + DRX_REG(BFLL, ch), BD_DEF_LEN); + cpc_writeb(scabase + DSR_RX(ch), DSR_DE); + if (!(cpc_readb(scabase + DSR_RX(ch)) & DSR_DE)) { + cpc_writeb(scabase + DSR_RX(ch), DSR_DE); + } +} + +/*************************/ +/*** FALC Routines ***/ +/*************************/ +void falc_issue_cmd(pc300_t * card, int ch, ucchar cmd) +{ + void __iomem *falcbase = card->hw.falcbase; + unsigned long i = 0; + + while (cpc_readb(falcbase + F_REG(SIS, ch)) & SIS_CEC) { + if (i++ >= PC300_FALC_MAXLOOP) { + printk("%s: FALC command locked(cmd=0x%x).\n", + card->chan[ch].d.name, cmd); + break; + } + } + cpc_writeb(falcbase + F_REG(CMDR, ch), cmd); +} + +void falc_intr_enable(pc300_t * card, int ch) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; + falc_t *pfalc = (falc_t *) & chan->falc; + void __iomem *falcbase = card->hw.falcbase; + + /* Interrupt pins are open-drain */ + cpc_writeb(falcbase + F_REG(IPC, ch), + cpc_readb(falcbase + F_REG(IPC, ch)) & ~IPC_IC0); + /* Conters updated each second */ + cpc_writeb(falcbase + F_REG(FMR1, ch), + cpc_readb(falcbase + F_REG(FMR1, ch)) | FMR1_ECM); + /* Enable SEC and ES interrupts */ + cpc_writeb(falcbase + F_REG(IMR3, ch), + cpc_readb(falcbase + F_REG(IMR3, ch)) & ~(IMR3_SEC | IMR3_ES)); + if (conf->fr_mode == PC300_FR_UNFRAMED) { + cpc_writeb(falcbase + F_REG(IMR4, ch), + cpc_readb(falcbase + F_REG(IMR4, ch)) & ~(IMR4_LOS)); + } else { + cpc_writeb(falcbase + F_REG(IMR4, ch), + cpc_readb(falcbase + F_REG(IMR4, ch)) & + ~(IMR4_LFA | IMR4_AIS | IMR4_LOS | IMR4_SLIP)); + } + if (conf->media == IF_IFACE_T1) { + cpc_writeb(falcbase + F_REG(IMR3, ch), + cpc_readb(falcbase + F_REG(IMR3, ch)) & ~IMR3_LLBSC); + } else { + cpc_writeb(falcbase + F_REG(IPC, ch), + cpc_readb(falcbase + F_REG(IPC, ch)) | IPC_SCI); + if (conf->fr_mode == PC300_FR_UNFRAMED) { + cpc_writeb(falcbase + F_REG(IMR2, ch), + cpc_readb(falcbase + F_REG(IMR2, ch)) & ~(IMR2_LOS)); + } else { + cpc_writeb(falcbase + F_REG(IMR2, ch), + cpc_readb(falcbase + F_REG(IMR2, ch)) & + ~(IMR2_FAR | IMR2_LFA | IMR2_AIS | IMR2_LOS)); + if (pfalc->multiframe_mode) { + cpc_writeb(falcbase + F_REG(IMR2, ch), + cpc_readb(falcbase + F_REG(IMR2, ch)) & + ~(IMR2_T400MS | IMR2_MFAR)); + } else { + cpc_writeb(falcbase + F_REG(IMR2, ch), + cpc_readb(falcbase + F_REG(IMR2, ch)) | + IMR2_T400MS | IMR2_MFAR); + } + } + } +} + +void falc_open_timeslot(pc300_t * card, int ch, int timeslot) +{ + void __iomem *falcbase = card->hw.falcbase; + ucchar tshf = card->chan[ch].falc.offset; + + cpc_writeb(falcbase + F_REG((ICB1 + (timeslot - tshf) / 8), ch), + cpc_readb(falcbase + F_REG((ICB1 + (timeslot - tshf) / 8), ch)) & + ~(0x80 >> ((timeslot - tshf) & 0x07))); + cpc_writeb(falcbase + F_REG((TTR1 + timeslot / 8), ch), + cpc_readb(falcbase + F_REG((TTR1 + timeslot / 8), ch)) | + (0x80 >> (timeslot & 0x07))); + cpc_writeb(falcbase + F_REG((RTR1 + timeslot / 8), ch), + cpc_readb(falcbase + F_REG((RTR1 + timeslot / 8), ch)) | + (0x80 >> (timeslot & 0x07))); +} + +void falc_close_timeslot(pc300_t * card, int ch, int timeslot) +{ + void __iomem *falcbase = card->hw.falcbase; + ucchar tshf = card->chan[ch].falc.offset; + + cpc_writeb(falcbase + F_REG((ICB1 + (timeslot - tshf) / 8), ch), + cpc_readb(falcbase + F_REG((ICB1 + (timeslot - tshf) / 8), ch)) | + (0x80 >> ((timeslot - tshf) & 0x07))); + cpc_writeb(falcbase + F_REG((TTR1 + timeslot / 8), ch), + cpc_readb(falcbase + F_REG((TTR1 + timeslot / 8), ch)) & + ~(0x80 >> (timeslot & 0x07))); + cpc_writeb(falcbase + F_REG((RTR1 + timeslot / 8), ch), + cpc_readb(falcbase + F_REG((RTR1 + timeslot / 8), ch)) & + ~(0x80 >> (timeslot & 0x07))); +} + +void falc_close_all_timeslots(pc300_t * card, int ch) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; + void __iomem *falcbase = card->hw.falcbase; + + cpc_writeb(falcbase + F_REG(ICB1, ch), 0xff); + cpc_writeb(falcbase + F_REG(TTR1, ch), 0); + cpc_writeb(falcbase + F_REG(RTR1, ch), 0); + cpc_writeb(falcbase + F_REG(ICB2, ch), 0xff); + cpc_writeb(falcbase + F_REG(TTR2, ch), 0); + cpc_writeb(falcbase + F_REG(RTR2, ch), 0); + cpc_writeb(falcbase + F_REG(ICB3, ch), 0xff); + cpc_writeb(falcbase + F_REG(TTR3, ch), 0); + cpc_writeb(falcbase + F_REG(RTR3, ch), 0); + if (conf->media == IF_IFACE_E1) { + cpc_writeb(falcbase + F_REG(ICB4, ch), 0xff); + cpc_writeb(falcbase + F_REG(TTR4, ch), 0); + cpc_writeb(falcbase + F_REG(RTR4, ch), 0); + } +} + +void falc_open_all_timeslots(pc300_t * card, int ch) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; + void __iomem *falcbase = card->hw.falcbase; + + cpc_writeb(falcbase + F_REG(ICB1, ch), 0); + if (conf->fr_mode == PC300_FR_UNFRAMED) { + cpc_writeb(falcbase + F_REG(TTR1, ch), 0xff); + cpc_writeb(falcbase + F_REG(RTR1, ch), 0xff); + } else { + /* Timeslot 0 is never enabled */ + cpc_writeb(falcbase + F_REG(TTR1, ch), 0x7f); + cpc_writeb(falcbase + F_REG(RTR1, ch), 0x7f); + } + cpc_writeb(falcbase + F_REG(ICB2, ch), 0); + cpc_writeb(falcbase + F_REG(TTR2, ch), 0xff); + cpc_writeb(falcbase + F_REG(RTR2, ch), 0xff); + cpc_writeb(falcbase + F_REG(ICB3, ch), 0); + cpc_writeb(falcbase + F_REG(TTR3, ch), 0xff); + cpc_writeb(falcbase + F_REG(RTR3, ch), 0xff); + if (conf->media == IF_IFACE_E1) { + cpc_writeb(falcbase + F_REG(ICB4, ch), 0); + cpc_writeb(falcbase + F_REG(TTR4, ch), 0xff); + cpc_writeb(falcbase + F_REG(RTR4, ch), 0xff); + } else { + cpc_writeb(falcbase + F_REG(ICB4, ch), 0xff); + cpc_writeb(falcbase + F_REG(TTR4, ch), 0x80); + cpc_writeb(falcbase + F_REG(RTR4, ch), 0x80); + } +} + +void falc_init_timeslot(pc300_t * card, int ch) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; + falc_t *pfalc = (falc_t *) & chan->falc; + int tslot; + + for (tslot = 0; tslot < pfalc->num_channels; tslot++) { + if (conf->tslot_bitmap & (1 << tslot)) { + // Channel enabled + falc_open_timeslot(card, ch, tslot + 1); + } else { + // Channel disabled + falc_close_timeslot(card, ch, tslot + 1); + } + } +} + +void falc_enable_comm(pc300_t * card, int ch) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + falc_t *pfalc = (falc_t *) & chan->falc; + + if (pfalc->full_bandwidth) { + falc_open_all_timeslots(card, ch); + } else { + falc_init_timeslot(card, ch); + } + // CTS/DCD ON + cpc_writeb(card->hw.falcbase + card->hw.cpld_reg1, + cpc_readb(card->hw.falcbase + card->hw.cpld_reg1) & + ~((CPLD_REG1_FALC_DCD | CPLD_REG1_FALC_CTS) << (2 * ch))); +} + +void falc_disable_comm(pc300_t * card, int ch) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + falc_t *pfalc = (falc_t *) & chan->falc; + + if (pfalc->loop_active != 2) { + falc_close_all_timeslots(card, ch); + } + // CTS/DCD OFF + cpc_writeb(card->hw.falcbase + card->hw.cpld_reg1, + cpc_readb(card->hw.falcbase + card->hw.cpld_reg1) | + ((CPLD_REG1_FALC_DCD | CPLD_REG1_FALC_CTS) << (2 * ch))); +} + +void falc_init_t1(pc300_t * card, int ch) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; + falc_t *pfalc = (falc_t *) & chan->falc; + void __iomem *falcbase = card->hw.falcbase; + ucchar dja = (ch ? (LIM2_DJA2 | LIM2_DJA1) : 0); + + /* Switch to T1 mode (PCM 24) */ + cpc_writeb(falcbase + F_REG(FMR1, ch), FMR1_PMOD); + + /* Wait 20 us for setup */ + udelay(20); + + /* Transmit Buffer Size (1 frame) */ + cpc_writeb(falcbase + F_REG(SIC1, ch), SIC1_XBS0); + + /* Clock mode */ + if (conf->phys_settings.clock_type == CLOCK_INT) { /* Master mode */ + cpc_writeb(falcbase + F_REG(LIM0, ch), + cpc_readb(falcbase + F_REG(LIM0, ch)) | LIM0_MAS); + } else { /* Slave mode */ + cpc_writeb(falcbase + F_REG(LIM0, ch), + cpc_readb(falcbase + F_REG(LIM0, ch)) & ~LIM0_MAS); + cpc_writeb(falcbase + F_REG(LOOP, ch), + cpc_readb(falcbase + F_REG(LOOP, ch)) & ~LOOP_RTM); + } + + cpc_writeb(falcbase + F_REG(IPC, ch), IPC_SCI); + cpc_writeb(falcbase + F_REG(FMR0, ch), + cpc_readb(falcbase + F_REG(FMR0, ch)) & + ~(FMR0_XC0 | FMR0_XC1 | FMR0_RC0 | FMR0_RC1)); + + switch (conf->lcode) { + case PC300_LC_AMI: + cpc_writeb(falcbase + F_REG(FMR0, ch), + cpc_readb(falcbase + F_REG(FMR0, ch)) | + FMR0_XC1 | FMR0_RC1); + /* Clear Channel register to ON for all channels */ + cpc_writeb(falcbase + F_REG(CCB1, ch), 0xff); + cpc_writeb(falcbase + F_REG(CCB2, ch), 0xff); + cpc_writeb(falcbase + F_REG(CCB3, ch), 0xff); + break; + + case PC300_LC_B8ZS: + cpc_writeb(falcbase + F_REG(FMR0, ch), + cpc_readb(falcbase + F_REG(FMR0, ch)) | + FMR0_XC0 | FMR0_XC1 | FMR0_RC0 | FMR0_RC1); + break; + + case PC300_LC_NRZ: + cpc_writeb(falcbase + F_REG(FMR0, ch), + cpc_readb(falcbase + F_REG(FMR0, ch)) | 0x00); + break; + } + + cpc_writeb(falcbase + F_REG(LIM0, ch), + cpc_readb(falcbase + F_REG(LIM0, ch)) | LIM0_ELOS); + cpc_writeb(falcbase + F_REG(LIM0, ch), + cpc_readb(falcbase + F_REG(LIM0, ch)) & ~(LIM0_SCL1 | LIM0_SCL0)); + /* Set interface mode to 2 MBPS */ + cpc_writeb(falcbase + F_REG(FMR1, ch), + cpc_readb(falcbase + F_REG(FMR1, ch)) | FMR1_IMOD); + + switch (conf->fr_mode) { + case PC300_FR_ESF: + pfalc->multiframe_mode = 0; + cpc_writeb(falcbase + F_REG(FMR4, ch), + cpc_readb(falcbase + F_REG(FMR4, ch)) | FMR4_FM1); + cpc_writeb(falcbase + F_REG(FMR1, ch), + cpc_readb(falcbase + F_REG(FMR1, ch)) | + FMR1_CRC | FMR1_EDL); + cpc_writeb(falcbase + F_REG(XDL1, ch), 0); + cpc_writeb(falcbase + F_REG(XDL2, ch), 0); + cpc_writeb(falcbase + F_REG(XDL3, ch), 0); + cpc_writeb(falcbase + F_REG(FMR0, ch), + cpc_readb(falcbase + F_REG(FMR0, ch)) & ~FMR0_SRAF); + cpc_writeb(falcbase + F_REG(FMR2, ch), + cpc_readb(falcbase + F_REG(FMR2,ch)) | FMR2_MCSP | FMR2_SSP); + break; + + case PC300_FR_D4: + pfalc->multiframe_mode = 1; + cpc_writeb(falcbase + F_REG(FMR4, ch), + cpc_readb(falcbase + F_REG(FMR4, ch)) & + ~(FMR4_FM1 | FMR4_FM0)); + cpc_writeb(falcbase + F_REG(FMR0, ch), + cpc_readb(falcbase + F_REG(FMR0, ch)) | FMR0_SRAF); + cpc_writeb(falcbase + F_REG(FMR2, ch), + cpc_readb(falcbase + F_REG(FMR2, ch)) & ~FMR2_SSP); + break; + } + + /* Enable Automatic Resynchronization */ + cpc_writeb(falcbase + F_REG(FMR4, ch), + cpc_readb(falcbase + F_REG(FMR4, ch)) | FMR4_AUTO); + + /* Transmit Automatic Remote Alarm */ + cpc_writeb(falcbase + F_REG(FMR2, ch), + cpc_readb(falcbase + F_REG(FMR2, ch)) | FMR2_AXRA); + + /* Channel translation mode 1 : one to one */ + cpc_writeb(falcbase + F_REG(FMR1, ch), + cpc_readb(falcbase + F_REG(FMR1, ch)) | FMR1_CTM); + + /* No signaling */ + cpc_writeb(falcbase + F_REG(FMR1, ch), + cpc_readb(falcbase + F_REG(FMR1, ch)) & ~FMR1_SIGM); + cpc_writeb(falcbase + F_REG(FMR5, ch), + cpc_readb(falcbase + F_REG(FMR5, ch)) & + ~(FMR5_EIBR | FMR5_SRS)); + cpc_writeb(falcbase + F_REG(CCR1, ch), 0); + + cpc_writeb(falcbase + F_REG(LIM1, ch), + cpc_readb(falcbase + F_REG(LIM1, ch)) | LIM1_RIL0 | LIM1_RIL1); + + switch (conf->lbo) { + /* Provides proper Line Build Out */ + case PC300_LBO_0_DB: + cpc_writeb(falcbase + F_REG(LIM2, ch), (LIM2_LOS1 | dja)); + cpc_writeb(falcbase + F_REG(XPM0, ch), 0x5a); + cpc_writeb(falcbase + F_REG(XPM1, ch), 0x8f); + cpc_writeb(falcbase + F_REG(XPM2, ch), 0x20); + break; + case PC300_LBO_7_5_DB: + cpc_writeb(falcbase + F_REG(LIM2, ch), (0x40 | LIM2_LOS1 | dja)); + cpc_writeb(falcbase + F_REG(XPM0, ch), 0x11); + cpc_writeb(falcbase + F_REG(XPM1, ch), 0x02); + cpc_writeb(falcbase + F_REG(XPM2, ch), 0x20); + break; + case PC300_LBO_15_DB: + cpc_writeb(falcbase + F_REG(LIM2, ch), (0x80 | LIM2_LOS1 | dja)); + cpc_writeb(falcbase + F_REG(XPM0, ch), 0x8e); + cpc_writeb(falcbase + F_REG(XPM1, ch), 0x01); + cpc_writeb(falcbase + F_REG(XPM2, ch), 0x20); + break; + case PC300_LBO_22_5_DB: + cpc_writeb(falcbase + F_REG(LIM2, ch), (0xc0 | LIM2_LOS1 | dja)); + cpc_writeb(falcbase + F_REG(XPM0, ch), 0x09); + cpc_writeb(falcbase + F_REG(XPM1, ch), 0x01); + cpc_writeb(falcbase + F_REG(XPM2, ch), 0x20); + break; + } + + /* Transmit Clock-Slot Offset */ + cpc_writeb(falcbase + F_REG(XC0, ch), + cpc_readb(falcbase + F_REG(XC0, ch)) | 0x01); + /* Transmit Time-slot Offset */ + cpc_writeb(falcbase + F_REG(XC1, ch), 0x3e); + /* Receive Clock-Slot offset */ + cpc_writeb(falcbase + F_REG(RC0, ch), 0x05); + /* Receive Time-slot offset */ + cpc_writeb(falcbase + F_REG(RC1, ch), 0x00); + + /* LOS Detection after 176 consecutive 0s */ + cpc_writeb(falcbase + F_REG(PCDR, ch), 0x0a); + /* LOS Recovery after 22 ones in the time window of PCD */ + cpc_writeb(falcbase + F_REG(PCRR, ch), 0x15); + + cpc_writeb(falcbase + F_REG(IDLE, ch), 0x7f); + + if (conf->fr_mode == PC300_FR_ESF_JAPAN) { + cpc_writeb(falcbase + F_REG(RC1, ch), + cpc_readb(falcbase + F_REG(RC1, ch)) | 0x80); + } + + falc_close_all_timeslots(card, ch); +} + +void falc_init_e1(pc300_t * card, int ch) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; + falc_t *pfalc = (falc_t *) & chan->falc; + void __iomem *falcbase = card->hw.falcbase; + ucchar dja = (ch ? (LIM2_DJA2 | LIM2_DJA1) : 0); + + /* Switch to E1 mode (PCM 30) */ + cpc_writeb(falcbase + F_REG(FMR1, ch), + cpc_readb(falcbase + F_REG(FMR1, ch)) & ~FMR1_PMOD); + + /* Clock mode */ + if (conf->phys_settings.clock_type == CLOCK_INT) { /* Master mode */ + cpc_writeb(falcbase + F_REG(LIM0, ch), + cpc_readb(falcbase + F_REG(LIM0, ch)) | LIM0_MAS); + } else { /* Slave mode */ + cpc_writeb(falcbase + F_REG(LIM0, ch), + cpc_readb(falcbase + F_REG(LIM0, ch)) & ~LIM0_MAS); + } + cpc_writeb(falcbase + F_REG(LOOP, ch), + cpc_readb(falcbase + F_REG(LOOP, ch)) & ~LOOP_SFM); + + cpc_writeb(falcbase + F_REG(IPC, ch), IPC_SCI); + cpc_writeb(falcbase + F_REG(FMR0, ch), + cpc_readb(falcbase + F_REG(FMR0, ch)) & + ~(FMR0_XC0 | FMR0_XC1 | FMR0_RC0 | FMR0_RC1)); + + switch (conf->lcode) { + case PC300_LC_AMI: + cpc_writeb(falcbase + F_REG(FMR0, ch), + cpc_readb(falcbase + F_REG(FMR0, ch)) | + FMR0_XC1 | FMR0_RC1); + break; + + case PC300_LC_HDB3: + cpc_writeb(falcbase + F_REG(FMR0, |