Commit 126dbe74 authored by François Romieu's avatar François Romieu Committed by Linus Torvalds

[PATCH] 2.4.8 - dscc4 update 0/13

- description update;
- more #defines;
- SCC_REG_START() change (shorter code);
- some noise.
parent 0cf094f4
/* /*
* drivers/net/wan/dscc4/dscc4_main.c: a DSCC4 HDLC driver for Linux * 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 * This software may be used and distributed according to the terms of the
* GNU General Public License. * GNU General Public License.
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
* - Data Sheet "DSCC4, DMA Supported Serial Communication Controller with * - Data Sheet "DSCC4, DMA Supported Serial Communication Controller with
* 4 Channels, PEB 20534 Version 2.1, PEF 20534 Version 2.1"; * 4 Channels, PEB 20534 Version 2.1, PEF 20534 Version 2.1";
* - Application Hint "Management of DSCC4 on-chip FIFO resources". * - 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 * 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 * at http://www.afthd.tu-darmstadt.de/~dg1kjd/pciscc4 for a specific
* driver. * driver.
...@@ -37,15 +38,13 @@ ...@@ -37,15 +38,13 @@
* *
* III. Driver operation * III. Driver operation
* *
* The rx/tx operations are based on a linked list of descriptor. I haven't * The rx/tx operations are based on a linked list of descriptors. The driver
* tried the start/stop descriptor method as this one looks like the cheapest * doesn't use HOLD mode any more. HOLD mode is definitely buggy and the more
* in terms of PCI manipulation. * 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 * Tx direction
* Once the data section of the current descriptor processed, the next linked
* descriptor is loaded if the HOLD bit isn't set in the current descriptor.
* If HOLD is met, the transmission is stopped until the host unsets it and
* signals the change via TxPOLL.
* When the tx ring is full, the xmit routine issues a call to netdev_stop. * 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 * The device is supposed to be enabled again during an ALLS irq (we could
* use HI but as it's easy to loose events, it's fscked). * use HI but as it's easy to loose events, it's fscked).
...@@ -55,12 +54,6 @@ ...@@ -55,12 +54,6 @@
* I may implement it some day but it isn't the highest ranked item. * I may implement it some day but it isn't the highest ranked item.
* *
* IV. Notes * IV. Notes
* The chipset is buggy. Typically, under some specific load patterns (I
* wouldn't call them "high"), the irq queues and the descriptors look like
* some event has been lost. Even assuming some fancy PCI feature, it won't
* explain the reproductible missing "C" bit in the descriptors. Faking an
* irq in the periodic timer isn't really elegant but at least it seems
* reliable.
* The current error (XDU, RFO) recovery code is untested. * The current error (XDU, RFO) recovery code is untested.
* So far, RDO takes his RX channel down and the right sequence to enable it * 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 * again is still a mistery. If RDO happens, plan a reboot. More details
...@@ -69,7 +62,7 @@ ...@@ -69,7 +62,7 @@
* suggest it for DCE either but at least one can get some messages instead * suggest it for DCE either but at least one can get some messages instead
* of a complete instant freeze. * of a complete instant freeze.
* Tests are done on Rev. 20 of the silicium. The RDO handling changes with * Tests are done on Rev. 20 of the silicium. The RDO handling changes with
* the documentation/chipset releases. An on-line errata would be welcome. * the documentation/chipset releases.
* *
* TODO: * TODO:
* - test X25. * - test X25.
...@@ -115,7 +108,7 @@ ...@@ -115,7 +108,7 @@
#include <linux/hdlc.h> #include <linux/hdlc.h>
/* Version */ /* Version */
static const char version[] = "$Id: dscc4.c,v 1.158 2002/01/30 00:40:37 romieu Exp $\n"; static const char version[] = "$Id: dscc4.c,v 1.159 2002/04/10 22:05:17 romieu Exp $ for Linux\n";
static int debug; static int debug;
static int quartz; static int quartz;
...@@ -148,7 +141,7 @@ struct TxFD { ...@@ -148,7 +141,7 @@ struct TxFD {
u32 next; u32 next;
u32 data; u32 data;
u32 complete; u32 complete;
u32 jiffies; /* more hack to come :o) */ u32 jiffies; /* Allows sizeof(TxFD) == sizeof(RxFD) + extra hack */
}; };
struct RxFD { struct RxFD {
...@@ -159,23 +152,29 @@ struct RxFD { ...@@ -159,23 +152,29 @@ struct RxFD {
u32 end; u32 end;
}; };
#define DEBUG #define CONFIG_DSCC4_DEBUG
#define DEBUG_PARANOIA
#define DUMMY_SKB_SIZE 64
/*
* FIXME: TX_HIGH very different from TX_RING_SIZE doesn't make much sense
* for LxDA mode.
*/
#define TX_LOW 8
#define TX_HIGH 16
#define TX_RING_SIZE 32 #define TX_RING_SIZE 32
#define RX_RING_SIZE 32 #define RX_RING_SIZE 32
#define IRQ_RING_SIZE 64 /* Keep it A multiple of 32 */ #define IRQ_RING_SIZE 64 /* Keep it a multiple of 32 */
#define TX_TIMEOUT (HZ/10) #define TX_TIMEOUT (HZ/10)
#define DSCC4_HZ_MAX 33000000 #define DSCC4_HZ_MAX 33000000
#define BRR_DIVIDER_MAX 64*0x00008000 #define BRR_DIVIDER_MAX 64*0x00004000 /* Cf errata DS5 p.10 */
#define dev_per_card 4 #define dev_per_card 4
#define SCC_REGISTERS_MAX 23 /* Cf errata DS5 p.4 */
#define SOURCE_ID(flags) (((flags) >> 28 ) & 0x03) #define SOURCE_ID(flags) (((flags) >> 28) & 0x03)
#define TO_SIZE(state) (((state) >> 16) & 0x1fff) #define TO_SIZE(state) (((state) >> 16) & 0x1fff)
#define TO_STATE(len) cpu_to_le32(((len) & TxSizeMax) << 16) #define TO_STATE(len) cpu_to_le32(((len) & TxSizeMax) << 16)
#define RX_MAX(len) ((((len) >> 5) + 1)<< 5) #define RX_MAX(len) ((((len) >> 5) + 1) << 5)
#define SCC_REG_START(id) SCC_START+(id)*SCC_OFFSET #define SCC_REG_START(dpriv) (SCC_START+(dpriv->dev_id)*SCC_OFFSET)
#undef DEBUG
struct dscc4_pci_priv { struct dscc4_pci_priv {
u32 *iqcfg; u32 *iqcfg;
...@@ -197,21 +196,24 @@ struct dscc4_dev_priv { ...@@ -197,21 +196,24 @@ struct dscc4_dev_priv {
u32 *iqrx; u32 *iqrx;
u32 *iqtx; u32 *iqtx;
/* FIXME: check all the volatile are required */
volatile u32 tx_current;
u32 rx_current; u32 rx_current;
u32 tx_current;
u32 iqrx_current;
u32 iqtx_current; u32 iqtx_current;
u32 iqrx_current;
u32 tx_dirty; volatile u32 tx_dirty;
int bad_tx_frame; volatile u32 ltda;
int bad_rx_frame; u32 rx_dirty;
int rx_needs_refill; u32 lrda;
dma_addr_t tx_fd_dma; dma_addr_t tx_fd_dma;
dma_addr_t rx_fd_dma; dma_addr_t rx_fd_dma;
dma_addr_t iqtx_dma; dma_addr_t iqtx_dma;
dma_addr_t iqrx_dma; dma_addr_t iqrx_dma;
volatile u32 scc_regs[SCC_REGISTERS_MAX]; /* Cf errata DS5 p.4 */
struct timer_list timer; struct timer_list timer;
struct dscc4_pci_priv *pci_priv; struct dscc4_pci_priv *pci_priv;
...@@ -220,13 +222,12 @@ struct dscc4_dev_priv { ...@@ -220,13 +222,12 @@ struct dscc4_dev_priv {
int dev_id; int dev_id;
volatile u32 flags; volatile u32 flags;
u32 timer_help; u32 timer_help;
u32 hi_expected;
hdlc_device hdlc;
sync_serial_settings settings;
unsigned short encoding; unsigned short encoding;
unsigned short parity; unsigned short parity;
u32 pad __attribute__ ((aligned (4))); hdlc_device hdlc;
sync_serial_settings settings;
u32 __pad __attribute__ ((aligned (4)));
}; };
/* GLOBAL registers definitions */ /* GLOBAL registers definitions */
...@@ -245,6 +246,10 @@ struct dscc4_dev_priv { ...@@ -245,6 +246,10 @@ struct dscc4_dev_priv {
#define CH0CFG 0x50 #define CH0CFG 0x50
#define CH0BRDA 0x54 #define CH0BRDA 0x54
#define CH0BTDA 0x58 #define CH0BTDA 0x58
#define CH0FRDA 0x98
#define CH0FTDA 0xb0
#define CH0LRDA 0xc8
#define CH0LTDA 0xe0
/* SCC registers definitions */ /* SCC registers definitions */
#define SCC_START 0x0100 #define SCC_START 0x0100
...@@ -279,11 +284,13 @@ struct dscc4_dev_priv { ...@@ -279,11 +284,13 @@ struct dscc4_dev_priv {
#define Ccr0ClockMask 0x0000003f #define Ccr0ClockMask 0x0000003f
#define Ccr1LoopMask 0x00000200 #define Ccr1LoopMask 0x00000200
#define IsrMask 0x000fffff
#define BrrExpMask 0x00000f00 #define BrrExpMask 0x00000f00
#define BrrMultMask 0x0000003f #define BrrMultMask 0x0000003f
#define EncodingMask 0x00700000 #define EncodingMask 0x00700000
#define Hold 0x40000000 #define Hold 0x40000000
#define SccBusy 0x10000000 #define SccBusy 0x10000000
#define PowerUp 0x80000000
#define FrameOk (FrameVfr | FrameCrc) #define FrameOk (FrameVfr | FrameCrc)
#define FrameVfr 0x80 #define FrameVfr 0x80
#define FrameRdo 0x40 #define FrameRdo 0x40
...@@ -430,7 +437,6 @@ void inline try_get_rx_skb(struct dscc4_dev_priv *priv, int cur, struct net_devi ...@@ -430,7 +437,6 @@ void inline try_get_rx_skb(struct dscc4_dev_priv *priv, int cur, struct net_devi
if (!skb) { if (!skb) {
priv->rx_fd[cur--].data = (u32) NULL; priv->rx_fd[cur--].data = (u32) NULL;
priv->rx_fd[cur%RX_RING_SIZE].state1 |= Hold; priv->rx_fd[cur%RX_RING_SIZE].state1 |= Hold;
priv->rx_needs_refill++;
return; return;
} }
skb->dev = dev; skb->dev = dev;
...@@ -682,7 +688,7 @@ static int __init dscc4_init_one (struct pci_dev *pdev, ...@@ -682,7 +688,7 @@ static int __init dscc4_init_one (struct pci_dev *pdev,
*/ */
static void dscc4_init_registers(u32 base_addr, int dev_id) static void dscc4_init_registers(u32 base_addr, int dev_id)
{ {
u32 ioaddr = base_addr + SCC_REG_START(dev_id); u32 ioaddr = base_addr + SCC_REG_START(dpriv);
writel(0x80001000, ioaddr + CCR0); writel(0x80001000, ioaddr + CCR0);
...@@ -754,7 +760,7 @@ static int dscc4_found1(struct pci_dev *pdev, unsigned long ioaddr) ...@@ -754,7 +760,7 @@ static int dscc4_found1(struct pci_dev *pdev, unsigned long ioaddr)
} }
hdlc->proto = IF_PROTO_HDLC; hdlc->proto = IF_PROTO_HDLC;
SET_MODULE_OWNER(d); SET_MODULE_OWNER(d);
dscc4_init_registers(ioaddr, i); dscc4_init_registers(ioaddr, dpriv);
dpriv->parity = PARITY_CRC16_PR0_CCITT; dpriv->parity = PARITY_CRC16_PR0_CCITT;
dpriv->encoding = ENCODING_NRZ; dpriv->encoding = ENCODING_NRZ;
} }
...@@ -866,7 +872,7 @@ static int dscc4_open(struct net_device *dev) ...@@ -866,7 +872,7 @@ static int dscc4_open(struct net_device *dev)
if (dscc4_init_ring(dev)) if (dscc4_init_ring(dev))
goto err_out; goto err_out;
ioaddr = dev->base_addr + SCC_REG_START(dpriv->dev_id); ioaddr = dev->base_addr + SCC_REG_START(dpriv);
/* IDT+IDR during XPR */ /* IDT+IDR during XPR */
dpriv->flags = NeedIDR | NeedIDT; dpriv->flags = NeedIDR | NeedIDT;
...@@ -957,10 +963,8 @@ static int dscc4_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -957,10 +963,8 @@ static int dscc4_start_xmit(struct sk_buff *skb, struct net_device *dev)
* (especially the net_dev re-enabling ones) thus there is no * (especially the net_dev re-enabling ones) thus there is no
* reason to try and be smart. * reason to try and be smart.
*/ */
if ((dpriv->tx_dirty + 16) < dpriv->tx_current) { if ((dpriv->tx_dirty + 16) < dpriv->tx_current)
netif_stop_queue(dev); netif_stop_queue(dev);
dpriv->hi_expected = 2;
}
tx_fd = dpriv->tx_fd + cur; tx_fd = dpriv->tx_fd + cur;
tx_fd->state &= ~Hold; tx_fd->state &= ~Hold;
mb(); // FIXME: suppress ? mb(); // FIXME: suppress ?
...@@ -986,7 +990,7 @@ static int dscc4_close(struct net_device *dev) ...@@ -986,7 +990,7 @@ static int dscc4_close(struct net_device *dev)
dev_id = dpriv->dev_id; dev_id = dpriv->dev_id;
writel(0x00050000, ioaddr + SCC_REG_START(dev_id) + CCR2); writel(0x00050000, ioaddr + SCC_REG_START(dpriv) + CCR2);
writel(MTFi|Rdr|Rdt, ioaddr + CH0CFG + dev_id*0x0c); /* Reset Rx/Tx */ writel(MTFi|Rdr|Rdt, ioaddr + CH0CFG + dev_id*0x0c); /* Reset Rx/Tx */
writel(0x00000001, ioaddr + GCMDR); writel(0x00000001, ioaddr + GCMDR);
readl(ioaddr + GCMDR); readl(ioaddr + GCMDR);
...@@ -1059,7 +1063,7 @@ static int dscc4_set_clock(struct net_device *dev, u32 *bps, u32 *state) ...@@ -1059,7 +1063,7 @@ static int dscc4_set_clock(struct net_device *dev, u32 *bps, u32 *state)
*/ */
brr = 0; brr = 0;
} }
writel(brr, dev->base_addr + BRR + SCC_REG_START(dpriv->dev_id)); writel(brr, dev->base_addr + BRR + SCC_REG_START(dpriv));
return 0; return 0;
} }
...@@ -1135,7 +1139,7 @@ static int dscc4_clock_setting(struct net_device *dev) ...@@ -1135,7 +1139,7 @@ static int dscc4_clock_setting(struct net_device *dev)
u32 ioaddr; u32 ioaddr;
bps = settings->clock_rate; bps = settings->clock_rate;
ioaddr = dev->base_addr + CCR0 + SCC_REG_START(dpriv->dev_id); ioaddr = dev->base_addr + CCR0 + SCC_REG_START(dpriv);
state = readl(ioaddr); state = readl(ioaddr);
if(dscc4_set_clock(dev, &bps, &state) < 0) if(dscc4_set_clock(dev, &bps, &state) < 0)
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -1171,7 +1175,7 @@ static int dscc4_encoding_setting(struct net_device *dev) ...@@ -1171,7 +1175,7 @@ static int dscc4_encoding_setting(struct net_device *dev)
if (i >= 0) { if (i >= 0) {
u32 ioaddr; u32 ioaddr;
ioaddr = dev->base_addr + CCR0 + SCC_REG_START(dpriv->dev_id); ioaddr = dev->base_addr + CCR0 + SCC_REG_START(dpriv);
dscc4_patch_register(ioaddr, EncodingMask, encoding[i].bits); dscc4_patch_register(ioaddr, EncodingMask, encoding[i].bits);
} else } else
ret = -EOPNOTSUPP; ret = -EOPNOTSUPP;
...@@ -1184,7 +1188,7 @@ static int dscc4_loopback_setting(struct net_device *dev) ...@@ -1184,7 +1188,7 @@ static int dscc4_loopback_setting(struct net_device *dev)
sync_serial_settings *settings = &dpriv->settings; sync_serial_settings *settings = &dpriv->settings;
u32 ioaddr, state; u32 ioaddr, state;
ioaddr = dev->base_addr + CCR1 + SCC_REG_START(dpriv->dev_id); ioaddr = dev->base_addr + CCR1 + SCC_REG_START(dpriv);
state = readl(ioaddr); state = readl(ioaddr);
if (settings->loopback) { if (settings->loopback) {
printk(KERN_DEBUG "%s: loopback\n", dev->name); printk(KERN_DEBUG "%s: loopback\n", dev->name);
...@@ -1212,7 +1216,7 @@ static int dscc4_crc_setting(struct net_device *dev) ...@@ -1212,7 +1216,7 @@ static int dscc4_crc_setting(struct net_device *dev)
if (i >= 0) { if (i >= 0) {
u32 ioaddr; u32 ioaddr;
ioaddr = dev->base_addr + CCR1 + SCC_REG_START(dpriv->dev_id); ioaddr = dev->base_addr + CCR1 + SCC_REG_START(dpriv);
dscc4_patch_register(ioaddr, CrcMask, crc[i].bits); dscc4_patch_register(ioaddr, CrcMask, crc[i].bits);
} else } else
ret = -EOPNOTSUPP; ret = -EOPNOTSUPP;
...@@ -1341,7 +1345,7 @@ static inline void dscc4_tx_irq(struct dscc4_pci_priv *ppriv, struct dscc4_dev_p ...@@ -1341,7 +1345,7 @@ static inline void dscc4_tx_irq(struct dscc4_pci_priv *ppriv, struct dscc4_dev_p
u32 ioaddr, isr; u32 ioaddr, isr;
ioaddr = dev->base_addr + ioaddr = dev->base_addr +
SCC_REG_START(dpriv->dev_id) + ISR; SCC_REG_START(dpriv) + ISR;
isr = readl(ioaddr); isr = readl(ioaddr);
printk(KERN_DEBUG printk(KERN_DEBUG
"%s: DataComplete=0 cur=%d isr=%08x state=%08x\n", "%s: DataComplete=0 cur=%d isr=%08x state=%08x\n",
...@@ -1392,7 +1396,7 @@ static inline void dscc4_tx_irq(struct dscc4_pci_priv *ppriv, struct dscc4_dev_p ...@@ -1392,7 +1396,7 @@ static inline void dscc4_tx_irq(struct dscc4_pci_priv *ppriv, struct dscc4_dev_p
unsigned long scc_offset; unsigned long scc_offset;
u32 scc_addr; u32 scc_addr;
scc_offset = ioaddr + SCC_REG_START(dpriv->dev_id); scc_offset = ioaddr + SCC_REG_START(dpriv);
scc_addr = ioaddr + 0x0c*dpriv->dev_id; scc_addr = ioaddr + 0x0c*dpriv->dev_id;
if (readl(scc_offset + STAR) & SccBusy) if (readl(scc_offset + STAR) & SccBusy)
printk(KERN_DEBUG "%s busy. Fatal\n", printk(KERN_DEBUG "%s busy. Fatal\n",
...@@ -1482,21 +1486,21 @@ static inline void dscc4_rx_irq(struct dscc4_pci_priv *priv, ...@@ -1482,21 +1486,21 @@ static inline void dscc4_rx_irq(struct dscc4_pci_priv *priv,
* problem with latency. In this case, increasing * problem with latency. In this case, increasing
* RX_RING_SIZE may help. * RX_RING_SIZE may help.
*/ */
while (dpriv->rx_needs_refill) { //while (dpriv->rx_needs_refill) {
while(!(rx_fd->state1 & Hold)) { while(!(rx_fd->state1 & Hold)) {
rx_fd++; rx_fd++;
cur++; cur++;
if (!(cur = cur%RX_RING_SIZE)) if (!(cur = cur%RX_RING_SIZE))
rx_fd = dpriv->rx_fd; rx_fd = dpriv->rx_fd;
} }
dpriv->rx_needs_refill--; //dpriv->rx_needs_refill--;
try_get_rx_skb(dpriv, cur, dev); try_get_rx_skb(dpriv, cur, dev);
if (!rx_fd->data) if (!rx_fd->data)
goto try; goto try;
rx_fd->state1 &= ~Hold; rx_fd->state1 &= ~Hold;
rx_fd->state2 = 0x00000000; rx_fd->state2 = 0x00000000;
rx_fd->end = 0xbabeface; rx_fd->end = 0xbabeface;
} //}
goto try; goto try;
} }
if (state & Fi) { if (state & Fi) {
...@@ -1548,7 +1552,7 @@ static inline void dscc4_rx_irq(struct dscc4_pci_priv *priv, ...@@ -1548,7 +1552,7 @@ static inline void dscc4_rx_irq(struct dscc4_pci_priv *priv,
// dscc4_rx_dump(dpriv); // dscc4_rx_dump(dpriv);
ioaddr = dev->base_addr; ioaddr = dev->base_addr;
scc_addr = ioaddr + 0x0c*dpriv->dev_id; scc_addr = ioaddr + 0x0c*dpriv->dev_id;
scc_offset = ioaddr + SCC_REG_START(dpriv->dev_id); scc_offset = ioaddr + SCC_REG_START(dpriv);
writel(readl(scc_offset + CCR2) & ~RxActivate, writel(readl(scc_offset + CCR2) & ~RxActivate,
scc_offset + CCR2); scc_offset + CCR2);
......
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