📄 dscc4.c
字号:
/* * drivers/net/wan/dscc4/dscc4.c: a DSCC4 HDLC driver for Linux * * This software may be used and distributed according to the terms of the * GNU General Public License. * * The author may be reached as romieu@cogenit.fr. * Specific bug reports/asian food will be welcome. * * Special thanks to the nice people at CS-Telecom for the hardware and the * access to the test/measure tools. * * * Theory of Operation * * I. Board Compatibility * * This device driver is designed for the Siemens PEB20534 4 ports serial * controller as found on Etinc PCISYNC cards. The documentation for the * chipset is available at http://www.infineon.com: * - Data Sheet "DSCC4, DMA Supported Serial Communication Controller with * 4 Channels, PEB 20534 Version 2.1, PEF 20534 Version 2.1"; * - Application Hint "Management of DSCC4 on-chip FIFO resources". * - Errata sheet DS5 (courtesy of Michael Skerritt). * Jens David has built an adapter based on the same chipset. Take a look * at http://www.afthd.tu-darmstadt.de/~dg1kjd/pciscc4 for a specific * driver. * Sample code (2 revisions) is available at Infineon. * * II. Board-specific settings * * Pcisync can transmit some clock signal to the outside world on the * *first two* ports provided you put a quartz and a line driver on it and * remove the jumpers. The operation is described on Etinc web site. If you * go DCE on these ports, don't forget to use an adequate cable. * * Sharing of the PCI interrupt line for this board is possible. * * III. Driver operation * * The rx/tx operations are based on a linked list of descriptors. The driver * doesn't use HOLD mode any more. HOLD mode is definitely buggy and the more * I tried to fix it, the more it started to look like (convoluted) software * mutation of LxDA method. Errata sheet DS5 suggests to use LxDA: consider * this a rfc2119 MUST. * * Tx direction * When the tx ring is full, the xmit routine issues a call to netdev_stop. * The device is supposed to be enabled again during an ALLS irq (we could * use HI but as it's easy to lose events, it's fscked). * * Rx direction * The received frames aren't supposed to span over multiple receiving areas. * I may implement it some day but it isn't the highest ranked item. * * IV. Notes * The current error (XDU, RFO) recovery code is untested. * So far, RDO takes his RX channel down and the right sequence to enable it * again is still a mistery. If RDO happens, plan a reboot. More details * in the code (NB: as this happens, TX still works). * Don't mess the cables during operation, especially on DTE ports. I don't * suggest it for DCE either but at least one can get some messages instead * of a complete instant freeze. * Tests are done on Rev. 20 of the silicium. The RDO handling changes with * the documentation/chipset releases. * * TODO: * - test X25. * - use polling at high irq/s, * - performance analysis, * - endianness. * * 2001/12/10 Daniela Squassoni <daniela@cyclades.com> * - Contribution to support the new generic HDLC layer. * * 2002/01 Ueimor * - old style interface removal * - dscc4_release_ring fix (related to DMA mapping) * - hard_start_xmit fix (hint: TxSizeMax) * - misc crapectomy. */#include <linux/module.h>#include <linux/types.h>#include <linux/errno.h>#include <linux/list.h>#include <linux/ioport.h>#include <linux/pci.h>#include <linux/kernel.h>#include <linux/mm.h>#include <asm/system.h>#include <asm/cache.h>#include <asm/byteorder.h>#include <asm/uaccess.h>#include <asm/io.h>#include <asm/irq.h>#include <linux/init.h>#include <linux/string.h>#include <linux/if_arp.h>#include <linux/netdevice.h>#include <linux/skbuff.h>#include <linux/delay.h>#include <net/syncppp.h>#include <linux/hdlc.h>/* Version */static const char version[] = "$Id: dscc4.c,v 1.173 2003/09/20 23:55:34 romieu Exp $ for Linux\n";static int debug;static int quartz;#ifdef CONFIG_DSCC4_PCI_RSTstatic DECLARE_MUTEX(dscc4_sem);static u32 dscc4_pci_config_store[16];#endif#define DRV_NAME "dscc4"#undef DSCC4_POLLING/* Module parameters */MODULE_AUTHOR("Maintainer: Francois Romieu <romieu@cogenit.fr>");MODULE_DESCRIPTION("Siemens PEB20534 PCI Controler");MODULE_LICENSE("GPL");module_param(debug, int, 0);MODULE_PARM_DESC(debug,"Enable/disable extra messages");module_param(quartz, int, 0);MODULE_PARM_DESC(quartz,"If present, on-board quartz frequency (Hz)");/* Structures */struct thingie { int define; u32 bits;};struct TxFD { u32 state; u32 next; u32 data; u32 complete; u32 jiffies; /* Allows sizeof(TxFD) == sizeof(RxFD) + extra hack */};struct RxFD { u32 state1; u32 next; u32 data; u32 state2; u32 end;};#define DUMMY_SKB_SIZE 64#define TX_LOW 8#define TX_RING_SIZE 32#define RX_RING_SIZE 32#define TX_TOTAL_SIZE TX_RING_SIZE*sizeof(struct TxFD)#define RX_TOTAL_SIZE RX_RING_SIZE*sizeof(struct RxFD)#define IRQ_RING_SIZE 64 /* Keep it a multiple of 32 */#define TX_TIMEOUT (HZ/10)#define DSCC4_HZ_MAX 33000000#define BRR_DIVIDER_MAX 64*0x00004000 /* Cf errata DS5 p.10 */#define dev_per_card 4#define SCC_REGISTERS_MAX 23 /* Cf errata DS5 p.4 */#define SOURCE_ID(flags) (((flags) >> 28) & 0x03)#define TO_SIZE(state) (((state) >> 16) & 0x1fff)/* * Given the operating range of Linux HDLC, the 2 defines below could be * made simpler. However they are a fine reminder for the limitations of * the driver: it's better to stay < TxSizeMax and < RxSizeMax. */#define TO_STATE_TX(len) cpu_to_le32(((len) & TxSizeMax) << 16)#define TO_STATE_RX(len) cpu_to_le32((RX_MAX(len) % RxSizeMax) << 16)#define RX_MAX(len) ((((len) >> 5) + 1) << 5) /* Cf RLCR */#define SCC_REG_START(dpriv) (SCC_START+(dpriv->dev_id)*SCC_OFFSET)struct dscc4_pci_priv { u32 *iqcfg; int cfg_cur; spinlock_t lock; struct pci_dev *pdev; struct dscc4_dev_priv *root; dma_addr_t iqcfg_dma; u32 xtal_hz;};struct dscc4_dev_priv { struct sk_buff *rx_skbuff[RX_RING_SIZE]; struct sk_buff *tx_skbuff[TX_RING_SIZE]; struct RxFD *rx_fd; struct TxFD *tx_fd; u32 *iqrx; u32 *iqtx; /* FIXME: check all the volatile are required */ volatile u32 tx_current; u32 rx_current; u32 iqtx_current; u32 iqrx_current; volatile u32 tx_dirty; volatile u32 ltda; u32 rx_dirty; u32 lrda; dma_addr_t tx_fd_dma; dma_addr_t rx_fd_dma; dma_addr_t iqtx_dma; dma_addr_t iqrx_dma; u32 scc_regs[SCC_REGISTERS_MAX]; /* Cf errata DS5 p.4 */ struct timer_list timer; struct dscc4_pci_priv *pci_priv; spinlock_t lock; int dev_id; volatile u32 flags; u32 timer_help; unsigned short encoding; unsigned short parity; struct net_device *dev; sync_serial_settings settings; void __iomem *base_addr; u32 __pad __attribute__ ((aligned (4)));};/* GLOBAL registers definitions */#define GCMDR 0x00#define GSTAR 0x04#define GMODE 0x08#define IQLENR0 0x0C#define IQLENR1 0x10#define IQRX0 0x14#define IQTX0 0x24#define IQCFG 0x3c#define FIFOCR1 0x44#define FIFOCR2 0x48#define FIFOCR3 0x4c#define FIFOCR4 0x34#define CH0CFG 0x50#define CH0BRDA 0x54#define CH0BTDA 0x58#define CH0FRDA 0x98#define CH0FTDA 0xb0#define CH0LRDA 0xc8#define CH0LTDA 0xe0/* SCC registers definitions */#define SCC_START 0x0100#define SCC_OFFSET 0x80#define CMDR 0x00#define STAR 0x04#define CCR0 0x08#define CCR1 0x0c#define CCR2 0x10#define BRR 0x2C#define RLCR 0x40#define IMR 0x54#define ISR 0x58#define GPDIR 0x0400#define GPDATA 0x0404#define GPIM 0x0408/* Bit masks */#define EncodingMask 0x00700000#define CrcMask 0x00000003#define IntRxScc0 0x10000000#define IntTxScc0 0x01000000#define TxPollCmd 0x00000400#define RxActivate 0x08000000#define MTFi 0x04000000#define Rdr 0x00400000#define Rdt 0x00200000#define Idr 0x00100000#define Idt 0x00080000#define TxSccRes 0x01000000#define RxSccRes 0x00010000#define TxSizeMax 0x1fff /* Datasheet DS1 - 11.1.1.1 */#define RxSizeMax 0x1ffc /* Datasheet DS1 - 11.1.2.1 */#define Ccr0ClockMask 0x0000003f#define Ccr1LoopMask 0x00000200#define IsrMask 0x000fffff#define BrrExpMask 0x00000f00#define BrrMultMask 0x0000003f#define EncodingMask 0x00700000#define Hold 0x40000000#define SccBusy 0x10000000#define PowerUp 0x80000000#define Vis 0x00001000#define FrameOk (FrameVfr | FrameCrc)#define FrameVfr 0x80#define FrameRdo 0x40#define FrameCrc 0x20#define FrameRab 0x10#define FrameAborted 0x00000200#define FrameEnd 0x80000000#define DataComplete 0x40000000#define LengthCheck 0x00008000#define SccEvt 0x02000000#define NoAck 0x00000200#define Action 0x00000001#define HiDesc 0x20000000/* SCC events */#define RxEvt 0xf0000000#define TxEvt 0x0f000000#define Alls 0x00040000#define Xdu 0x00010000#define Cts 0x00004000#define Xmr 0x00002000#define Xpr 0x00001000#define Rdo 0x00000080#define Rfs 0x00000040#define Cd 0x00000004#define Rfo 0x00000002#define Flex 0x00000001/* DMA core events */#define Cfg 0x00200000#define Hi 0x00040000#define Fi 0x00020000#define Err 0x00010000#define Arf 0x00000002#define ArAck 0x00000001/* State flags */#define Ready 0x00000000#define NeedIDR 0x00000001#define NeedIDT 0x00000002#define RdoSet 0x00000004#define FakeReset 0x00000008/* Don't mask RDO. Ever. */#ifdef DSCC4_POLLING#define EventsMask 0xfffeef7f#else#define EventsMask 0xfffa8f7a#endif/* Functions prototypes */static void dscc4_rx_irq(struct dscc4_pci_priv *, struct dscc4_dev_priv *);static void dscc4_tx_irq(struct dscc4_pci_priv *, struct dscc4_dev_priv *);static int dscc4_found1(struct pci_dev *, void __iomem *ioaddr);static int dscc4_init_one(struct pci_dev *, const struct pci_device_id *ent);static int dscc4_open(struct net_device *);static int dscc4_start_xmit(struct sk_buff *, struct net_device *);static int dscc4_close(struct net_device *);static int dscc4_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);static int dscc4_init_ring(struct net_device *);static void dscc4_release_ring(struct dscc4_dev_priv *);static void dscc4_timer(unsigned long);static void dscc4_tx_timeout(struct net_device *);static irqreturn_t dscc4_irq(int irq, void *dev_id, struct pt_regs *ptregs);static int dscc4_hdlc_attach(struct net_device *, unsigned short, unsigned short);static int dscc4_set_iface(struct dscc4_dev_priv *, struct net_device *);#ifdef DSCC4_POLLINGstatic int dscc4_tx_poll(struct dscc4_dev_priv *, struct net_device *);#endifstatic inline struct dscc4_dev_priv *dscc4_priv(struct net_device *dev){ return dev_to_hdlc(dev)->priv;}static inline struct net_device *dscc4_to_dev(struct dscc4_dev_priv *p){ return p->dev;}static void scc_patchl(u32 mask, u32 value, struct dscc4_dev_priv *dpriv, struct net_device *dev, int offset){ u32 state; /* Cf scc_writel for concern regarding thread-safety */ state = dpriv->scc_regs[offset >> 2]; state &= ~mask; state |= value; dpriv->scc_regs[offset >> 2] = state; writel(state, dpriv->base_addr + SCC_REG_START(dpriv) + offset);}static void scc_writel(u32 bits, struct dscc4_dev_priv *dpriv, struct net_device *dev, int offset){ /* * Thread-UNsafe. * As of 2002/02/16, there are no thread racing for access. */ dpriv->scc_regs[offset >> 2] = bits; writel(bits, dpriv->base_addr + SCC_REG_START(dpriv) + offset);}static inline u32 scc_readl(struct dscc4_dev_priv *dpriv, int offset){ return dpriv->scc_regs[offset >> 2];}static u32 scc_readl_star(struct dscc4_dev_priv *dpriv, struct net_device *dev){ /* Cf errata DS5 p.4 */ readl(dpriv->base_addr + SCC_REG_START(dpriv) + STAR); return readl(dpriv->base_addr + SCC_REG_START(dpriv) + STAR);}static inline void dscc4_do_tx(struct dscc4_dev_priv *dpriv, struct net_device *dev){ dpriv->ltda = dpriv->tx_fd_dma + ((dpriv->tx_current-1)%TX_RING_SIZE)*sizeof(struct TxFD); writel(dpriv->ltda, dpriv->base_addr + CH0LTDA + dpriv->dev_id*4); /* Flush posted writes *NOW* */ readl(dpriv->base_addr + CH0LTDA + dpriv->dev_id*4);}static inline void dscc4_rx_update(struct dscc4_dev_priv *dpriv, struct net_device *dev){ dpriv->lrda = dpriv->rx_fd_dma + ((dpriv->rx_dirty - 1)%RX_RING_SIZE)*sizeof(struct RxFD); writel(dpriv->lrda, dpriv->base_addr + CH0LRDA + dpriv->dev_id*4);}static inline unsigned int dscc4_tx_done(struct dscc4_dev_priv *dpriv){ return dpriv->tx_current == dpriv->tx_dirty;}static inline unsigned int dscc4_tx_quiescent(struct dscc4_dev_priv *dpriv, struct net_device *dev){ return readl(dpriv->base_addr + CH0FTDA + dpriv->dev_id*4) == dpriv->ltda;}static int state_check(u32 state, struct dscc4_dev_priv *dpriv, struct net_device *dev, const char *msg){ int ret = 0; if (debug > 1) { if (SOURCE_ID(state) != dpriv->dev_id) { printk(KERN_DEBUG "%s (%s): Source Id=%d, state=%08x\n", dev->name, msg, SOURCE_ID(state), state ); ret = -1; } if (state & 0x0df80c00) { printk(KERN_DEBUG "%s (%s): state=%08x (UFO alert)\n", dev->name, msg, state); ret = -1; } } return ret;}static void dscc4_tx_print(struct net_device *dev, struct dscc4_dev_priv *dpriv, char *msg){ printk(KERN_DEBUG "%s: tx_current=%02d tx_dirty=%02d (%s)\n", dev->name, dpriv->tx_current, dpriv->tx_dirty, msg);}static void dscc4_release_ring(struct dscc4_dev_priv *dpriv){ struct pci_dev *pdev = dpriv->pci_priv->pdev; struct TxFD *tx_fd = dpriv->tx_fd; struct RxFD *rx_fd = dpriv->rx_fd; struct sk_buff **skbuff; int i; pci_free_consistent(pdev, TX_TOTAL_SIZE, tx_fd, dpriv->tx_fd_dma); pci_free_consistent(pdev, RX_TOTAL_SIZE, rx_fd, dpriv->rx_fd_dma); skbuff = dpriv->tx_skbuff; for (i = 0; i < TX_RING_SIZE; i++) { if (*skbuff) { pci_unmap_single(pdev, tx_fd->data, (*skbuff)->len, PCI_DMA_TODEVICE); dev_kfree_skb(*skbuff); } skbuff++; tx_fd++; } skbuff = dpriv->rx_skbuff; for (i = 0; i < RX_RING_SIZE; i++) { if (*skbuff) { pci_unmap_single(pdev, rx_fd->data, RX_MAX(HDLC_MAX_MRU), PCI_DMA_FROMDEVICE); dev_kfree_skb(*skbuff); } skbuff++; rx_fd++; }}static inline int try_get_rx_skb(struct dscc4_dev_priv *dpriv, struct net_device *dev){ unsigned int dirty = dpriv->rx_dirty%RX_RING_SIZE; struct RxFD *rx_fd = dpriv->rx_fd + dirty; const int len = RX_MAX(HDLC_MAX_MRU); struct sk_buff *skb; int ret = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -