162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * sgiseeq.c: Seeq8003 ethernet driver for SGI machines. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1996 David S. Miller (davem@davemloft.net) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#undef DEBUG 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/errno.h> 1562306a36Sopenharmony_ci#include <linux/types.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/string.h> 1862306a36Sopenharmony_ci#include <linux/delay.h> 1962306a36Sopenharmony_ci#include <linux/netdevice.h> 2062306a36Sopenharmony_ci#include <linux/platform_device.h> 2162306a36Sopenharmony_ci#include <linux/etherdevice.h> 2262306a36Sopenharmony_ci#include <linux/skbuff.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <asm/sgi/hpc3.h> 2562306a36Sopenharmony_ci#include <asm/sgi/ip22.h> 2662306a36Sopenharmony_ci#include <asm/sgi/seeq.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "sgiseeq.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic char *sgiseeqstr = "SGI Seeq8003"; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* 3362306a36Sopenharmony_ci * If you want speed, you do something silly, it always has worked for me. So, 3462306a36Sopenharmony_ci * with that in mind, I've decided to make this driver look completely like a 3562306a36Sopenharmony_ci * stupid Lance from a driver architecture perspective. Only difference is that 3662306a36Sopenharmony_ci * here our "ring buffer" looks and acts like a real Lance one does but is 3762306a36Sopenharmony_ci * laid out like how the HPC DMA and the Seeq want it to. You'd be surprised 3862306a36Sopenharmony_ci * how a stupid idea like this can pay off in performance, not to mention 3962306a36Sopenharmony_ci * making this driver 2,000 times easier to write. ;-) 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* Tune these if we tend to run out often etc. */ 4362306a36Sopenharmony_ci#define SEEQ_RX_BUFFERS 16 4462306a36Sopenharmony_ci#define SEEQ_TX_BUFFERS 16 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define PKT_BUF_SZ 1584 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define NEXT_RX(i) (((i) + 1) & (SEEQ_RX_BUFFERS - 1)) 4962306a36Sopenharmony_ci#define NEXT_TX(i) (((i) + 1) & (SEEQ_TX_BUFFERS - 1)) 5062306a36Sopenharmony_ci#define PREV_RX(i) (((i) - 1) & (SEEQ_RX_BUFFERS - 1)) 5162306a36Sopenharmony_ci#define PREV_TX(i) (((i) - 1) & (SEEQ_TX_BUFFERS - 1)) 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define TX_BUFFS_AVAIL(sp) ((sp->tx_old <= sp->tx_new) ? \ 5462306a36Sopenharmony_ci sp->tx_old + (SEEQ_TX_BUFFERS - 1) - sp->tx_new : \ 5562306a36Sopenharmony_ci sp->tx_old - sp->tx_new - 1) 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define VIRT_TO_DMA(sp, v) ((sp)->srings_dma + \ 5862306a36Sopenharmony_ci (dma_addr_t)((unsigned long)(v) - \ 5962306a36Sopenharmony_ci (unsigned long)((sp)->rx_desc))) 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* Copy frames shorter than rx_copybreak, otherwise pass on up in 6262306a36Sopenharmony_ci * a full sized sk_buff. Value of 100 stolen from tulip.c (!alpha). 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_cistatic int rx_copybreak = 100; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define PAD_SIZE (128 - sizeof(struct hpc_dma_desc) - sizeof(void *)) 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistruct sgiseeq_rx_desc { 6962306a36Sopenharmony_ci volatile struct hpc_dma_desc rdma; 7062306a36Sopenharmony_ci u8 padding[PAD_SIZE]; 7162306a36Sopenharmony_ci struct sk_buff *skb; 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistruct sgiseeq_tx_desc { 7562306a36Sopenharmony_ci volatile struct hpc_dma_desc tdma; 7662306a36Sopenharmony_ci u8 padding[PAD_SIZE]; 7762306a36Sopenharmony_ci struct sk_buff *skb; 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/* 8162306a36Sopenharmony_ci * Warning: This structure is laid out in a certain way because HPC dma 8262306a36Sopenharmony_ci * descriptors must be 8-byte aligned. So don't touch this without 8362306a36Sopenharmony_ci * some care. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_cistruct sgiseeq_init_block { /* Note the name ;-) */ 8662306a36Sopenharmony_ci struct sgiseeq_rx_desc rxvector[SEEQ_RX_BUFFERS]; 8762306a36Sopenharmony_ci struct sgiseeq_tx_desc txvector[SEEQ_TX_BUFFERS]; 8862306a36Sopenharmony_ci}; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistruct sgiseeq_private { 9162306a36Sopenharmony_ci struct sgiseeq_init_block *srings; 9262306a36Sopenharmony_ci dma_addr_t srings_dma; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* Ptrs to the descriptors in uncached space. */ 9562306a36Sopenharmony_ci struct sgiseeq_rx_desc *rx_desc; 9662306a36Sopenharmony_ci struct sgiseeq_tx_desc *tx_desc; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci char *name; 9962306a36Sopenharmony_ci struct hpc3_ethregs *hregs; 10062306a36Sopenharmony_ci struct sgiseeq_regs *sregs; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* Ring entry counters. */ 10362306a36Sopenharmony_ci unsigned int rx_new, tx_new; 10462306a36Sopenharmony_ci unsigned int rx_old, tx_old; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci int is_edlc; 10762306a36Sopenharmony_ci unsigned char control; 10862306a36Sopenharmony_ci unsigned char mode; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci spinlock_t tx_lock; 11162306a36Sopenharmony_ci}; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic inline void dma_sync_desc_cpu(struct net_device *dev, void *addr) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct sgiseeq_private *sp = netdev_priv(dev); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci dma_sync_single_for_cpu(dev->dev.parent, VIRT_TO_DMA(sp, addr), 11862306a36Sopenharmony_ci sizeof(struct sgiseeq_rx_desc), DMA_BIDIRECTIONAL); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic inline void dma_sync_desc_dev(struct net_device *dev, void *addr) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct sgiseeq_private *sp = netdev_priv(dev); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci dma_sync_single_for_device(dev->dev.parent, VIRT_TO_DMA(sp, addr), 12662306a36Sopenharmony_ci sizeof(struct sgiseeq_rx_desc), DMA_BIDIRECTIONAL); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic inline void hpc3_eth_reset(struct hpc3_ethregs *hregs) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci hregs->reset = HPC3_ERST_CRESET | HPC3_ERST_CLRIRQ; 13262306a36Sopenharmony_ci udelay(20); 13362306a36Sopenharmony_ci hregs->reset = 0; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic inline void reset_hpc3_and_seeq(struct hpc3_ethregs *hregs, 13762306a36Sopenharmony_ci struct sgiseeq_regs *sregs) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci hregs->rx_ctrl = hregs->tx_ctrl = 0; 14062306a36Sopenharmony_ci hpc3_eth_reset(hregs); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci#define RSTAT_GO_BITS (SEEQ_RCMD_IGOOD | SEEQ_RCMD_IEOF | SEEQ_RCMD_ISHORT | \ 14462306a36Sopenharmony_ci SEEQ_RCMD_IDRIB | SEEQ_RCMD_ICRC) 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic inline void seeq_go(struct sgiseeq_private *sp, 14762306a36Sopenharmony_ci struct hpc3_ethregs *hregs, 14862306a36Sopenharmony_ci struct sgiseeq_regs *sregs) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci sregs->rstat = sp->mode | RSTAT_GO_BITS; 15162306a36Sopenharmony_ci hregs->rx_ctrl = HPC3_ERXCTRL_ACTIVE; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic inline void __sgiseeq_set_mac_address(struct net_device *dev) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct sgiseeq_private *sp = netdev_priv(dev); 15762306a36Sopenharmony_ci struct sgiseeq_regs *sregs = sp->sregs; 15862306a36Sopenharmony_ci int i; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci sregs->tstat = SEEQ_TCMD_RB0; 16162306a36Sopenharmony_ci for (i = 0; i < 6; i++) 16262306a36Sopenharmony_ci sregs->rw.eth_addr[i] = dev->dev_addr[i]; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic int sgiseeq_set_mac_address(struct net_device *dev, void *addr) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct sgiseeq_private *sp = netdev_priv(dev); 16862306a36Sopenharmony_ci struct sockaddr *sa = addr; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci eth_hw_addr_set(dev, sa->sa_data); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci spin_lock_irq(&sp->tx_lock); 17362306a36Sopenharmony_ci __sgiseeq_set_mac_address(dev); 17462306a36Sopenharmony_ci spin_unlock_irq(&sp->tx_lock); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci return 0; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci#define TCNTINFO_INIT (HPCDMA_EOX | HPCDMA_ETXD) 18062306a36Sopenharmony_ci#define RCNTCFG_INIT (HPCDMA_OWN | HPCDMA_EORP | HPCDMA_XIE) 18162306a36Sopenharmony_ci#define RCNTINFO_INIT (RCNTCFG_INIT | (PKT_BUF_SZ & HPCDMA_BCNT)) 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic int seeq_init_ring(struct net_device *dev) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct sgiseeq_private *sp = netdev_priv(dev); 18662306a36Sopenharmony_ci int i; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci netif_stop_queue(dev); 18962306a36Sopenharmony_ci sp->rx_new = sp->tx_new = 0; 19062306a36Sopenharmony_ci sp->rx_old = sp->tx_old = 0; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci __sgiseeq_set_mac_address(dev); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* Setup tx ring. */ 19562306a36Sopenharmony_ci for(i = 0; i < SEEQ_TX_BUFFERS; i++) { 19662306a36Sopenharmony_ci sp->tx_desc[i].tdma.cntinfo = TCNTINFO_INIT; 19762306a36Sopenharmony_ci dma_sync_desc_dev(dev, &sp->tx_desc[i]); 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* And now the rx ring. */ 20162306a36Sopenharmony_ci for (i = 0; i < SEEQ_RX_BUFFERS; i++) { 20262306a36Sopenharmony_ci if (!sp->rx_desc[i].skb) { 20362306a36Sopenharmony_ci dma_addr_t dma_addr; 20462306a36Sopenharmony_ci struct sk_buff *skb = netdev_alloc_skb(dev, PKT_BUF_SZ); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (skb == NULL) 20762306a36Sopenharmony_ci return -ENOMEM; 20862306a36Sopenharmony_ci skb_reserve(skb, 2); 20962306a36Sopenharmony_ci dma_addr = dma_map_single(dev->dev.parent, 21062306a36Sopenharmony_ci skb->data - 2, 21162306a36Sopenharmony_ci PKT_BUF_SZ, DMA_FROM_DEVICE); 21262306a36Sopenharmony_ci sp->rx_desc[i].skb = skb; 21362306a36Sopenharmony_ci sp->rx_desc[i].rdma.pbuf = dma_addr; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci sp->rx_desc[i].rdma.cntinfo = RCNTINFO_INIT; 21662306a36Sopenharmony_ci dma_sync_desc_dev(dev, &sp->rx_desc[i]); 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci sp->rx_desc[i - 1].rdma.cntinfo |= HPCDMA_EOR; 21962306a36Sopenharmony_ci dma_sync_desc_dev(dev, &sp->rx_desc[i - 1]); 22062306a36Sopenharmony_ci return 0; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic void seeq_purge_ring(struct net_device *dev) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct sgiseeq_private *sp = netdev_priv(dev); 22662306a36Sopenharmony_ci int i; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* clear tx ring. */ 22962306a36Sopenharmony_ci for (i = 0; i < SEEQ_TX_BUFFERS; i++) { 23062306a36Sopenharmony_ci if (sp->tx_desc[i].skb) { 23162306a36Sopenharmony_ci dev_kfree_skb(sp->tx_desc[i].skb); 23262306a36Sopenharmony_ci sp->tx_desc[i].skb = NULL; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* And now the rx ring. */ 23762306a36Sopenharmony_ci for (i = 0; i < SEEQ_RX_BUFFERS; i++) { 23862306a36Sopenharmony_ci if (sp->rx_desc[i].skb) { 23962306a36Sopenharmony_ci dev_kfree_skb(sp->rx_desc[i].skb); 24062306a36Sopenharmony_ci sp->rx_desc[i].skb = NULL; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci#ifdef DEBUG 24662306a36Sopenharmony_cistatic struct sgiseeq_private *gpriv; 24762306a36Sopenharmony_cistatic struct net_device *gdev; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic void sgiseeq_dump_rings(void) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci static int once; 25262306a36Sopenharmony_ci struct sgiseeq_rx_desc *r = gpriv->rx_desc; 25362306a36Sopenharmony_ci struct sgiseeq_tx_desc *t = gpriv->tx_desc; 25462306a36Sopenharmony_ci struct hpc3_ethregs *hregs = gpriv->hregs; 25562306a36Sopenharmony_ci int i; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (once) 25862306a36Sopenharmony_ci return; 25962306a36Sopenharmony_ci once++; 26062306a36Sopenharmony_ci printk("RING DUMP:\n"); 26162306a36Sopenharmony_ci for (i = 0; i < SEEQ_RX_BUFFERS; i++) { 26262306a36Sopenharmony_ci printk("RX [%d]: @(%p) [%08x,%08x,%08x] ", 26362306a36Sopenharmony_ci i, (&r[i]), r[i].rdma.pbuf, r[i].rdma.cntinfo, 26462306a36Sopenharmony_ci r[i].rdma.pnext); 26562306a36Sopenharmony_ci i += 1; 26662306a36Sopenharmony_ci printk("-- [%d]: @(%p) [%08x,%08x,%08x]\n", 26762306a36Sopenharmony_ci i, (&r[i]), r[i].rdma.pbuf, r[i].rdma.cntinfo, 26862306a36Sopenharmony_ci r[i].rdma.pnext); 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci for (i = 0; i < SEEQ_TX_BUFFERS; i++) { 27162306a36Sopenharmony_ci printk("TX [%d]: @(%p) [%08x,%08x,%08x] ", 27262306a36Sopenharmony_ci i, (&t[i]), t[i].tdma.pbuf, t[i].tdma.cntinfo, 27362306a36Sopenharmony_ci t[i].tdma.pnext); 27462306a36Sopenharmony_ci i += 1; 27562306a36Sopenharmony_ci printk("-- [%d]: @(%p) [%08x,%08x,%08x]\n", 27662306a36Sopenharmony_ci i, (&t[i]), t[i].tdma.pbuf, t[i].tdma.cntinfo, 27762306a36Sopenharmony_ci t[i].tdma.pnext); 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci printk("INFO: [rx_new = %d rx_old=%d] [tx_new = %d tx_old = %d]\n", 28062306a36Sopenharmony_ci gpriv->rx_new, gpriv->rx_old, gpriv->tx_new, gpriv->tx_old); 28162306a36Sopenharmony_ci printk("RREGS: rx_cbptr[%08x] rx_ndptr[%08x] rx_ctrl[%08x]\n", 28262306a36Sopenharmony_ci hregs->rx_cbptr, hregs->rx_ndptr, hregs->rx_ctrl); 28362306a36Sopenharmony_ci printk("TREGS: tx_cbptr[%08x] tx_ndptr[%08x] tx_ctrl[%08x]\n", 28462306a36Sopenharmony_ci hregs->tx_cbptr, hregs->tx_ndptr, hregs->tx_ctrl); 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci#endif 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci#define TSTAT_INIT_SEEQ (SEEQ_TCMD_IPT|SEEQ_TCMD_I16|SEEQ_TCMD_IC|SEEQ_TCMD_IUF) 28962306a36Sopenharmony_ci#define TSTAT_INIT_EDLC ((TSTAT_INIT_SEEQ) | SEEQ_TCMD_RB2) 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic int init_seeq(struct net_device *dev, struct sgiseeq_private *sp, 29262306a36Sopenharmony_ci struct sgiseeq_regs *sregs) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci struct hpc3_ethregs *hregs = sp->hregs; 29562306a36Sopenharmony_ci int err; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci reset_hpc3_and_seeq(hregs, sregs); 29862306a36Sopenharmony_ci err = seeq_init_ring(dev); 29962306a36Sopenharmony_ci if (err) 30062306a36Sopenharmony_ci return err; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* Setup to field the proper interrupt types. */ 30362306a36Sopenharmony_ci if (sp->is_edlc) { 30462306a36Sopenharmony_ci sregs->tstat = TSTAT_INIT_EDLC; 30562306a36Sopenharmony_ci sregs->rw.wregs.control = sp->control; 30662306a36Sopenharmony_ci sregs->rw.wregs.frame_gap = 0; 30762306a36Sopenharmony_ci } else { 30862306a36Sopenharmony_ci sregs->tstat = TSTAT_INIT_SEEQ; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci hregs->rx_ndptr = VIRT_TO_DMA(sp, sp->rx_desc); 31262306a36Sopenharmony_ci hregs->tx_ndptr = VIRT_TO_DMA(sp, sp->tx_desc); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci seeq_go(sp, hregs, sregs); 31562306a36Sopenharmony_ci return 0; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic void record_rx_errors(struct net_device *dev, unsigned char status) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci if (status & SEEQ_RSTAT_OVERF || 32162306a36Sopenharmony_ci status & SEEQ_RSTAT_SFRAME) 32262306a36Sopenharmony_ci dev->stats.rx_over_errors++; 32362306a36Sopenharmony_ci if (status & SEEQ_RSTAT_CERROR) 32462306a36Sopenharmony_ci dev->stats.rx_crc_errors++; 32562306a36Sopenharmony_ci if (status & SEEQ_RSTAT_DERROR) 32662306a36Sopenharmony_ci dev->stats.rx_frame_errors++; 32762306a36Sopenharmony_ci if (status & SEEQ_RSTAT_REOF) 32862306a36Sopenharmony_ci dev->stats.rx_errors++; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic inline void rx_maybe_restart(struct sgiseeq_private *sp, 33262306a36Sopenharmony_ci struct hpc3_ethregs *hregs, 33362306a36Sopenharmony_ci struct sgiseeq_regs *sregs) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci if (!(hregs->rx_ctrl & HPC3_ERXCTRL_ACTIVE)) { 33662306a36Sopenharmony_ci hregs->rx_ndptr = VIRT_TO_DMA(sp, sp->rx_desc + sp->rx_new); 33762306a36Sopenharmony_ci seeq_go(sp, hregs, sregs); 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic inline void sgiseeq_rx(struct net_device *dev, struct sgiseeq_private *sp, 34262306a36Sopenharmony_ci struct hpc3_ethregs *hregs, 34362306a36Sopenharmony_ci struct sgiseeq_regs *sregs) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci struct sgiseeq_rx_desc *rd; 34662306a36Sopenharmony_ci struct sk_buff *skb = NULL; 34762306a36Sopenharmony_ci struct sk_buff *newskb; 34862306a36Sopenharmony_ci unsigned char pkt_status; 34962306a36Sopenharmony_ci int len = 0; 35062306a36Sopenharmony_ci unsigned int orig_end = PREV_RX(sp->rx_new); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci /* Service every received packet. */ 35362306a36Sopenharmony_ci rd = &sp->rx_desc[sp->rx_new]; 35462306a36Sopenharmony_ci dma_sync_desc_cpu(dev, rd); 35562306a36Sopenharmony_ci while (!(rd->rdma.cntinfo & HPCDMA_OWN)) { 35662306a36Sopenharmony_ci len = PKT_BUF_SZ - (rd->rdma.cntinfo & HPCDMA_BCNT) - 3; 35762306a36Sopenharmony_ci dma_unmap_single(dev->dev.parent, rd->rdma.pbuf, 35862306a36Sopenharmony_ci PKT_BUF_SZ, DMA_FROM_DEVICE); 35962306a36Sopenharmony_ci pkt_status = rd->skb->data[len]; 36062306a36Sopenharmony_ci if (pkt_status & SEEQ_RSTAT_FIG) { 36162306a36Sopenharmony_ci /* Packet is OK. */ 36262306a36Sopenharmony_ci /* We don't want to receive our own packets */ 36362306a36Sopenharmony_ci if (!ether_addr_equal(rd->skb->data + 6, dev->dev_addr)) { 36462306a36Sopenharmony_ci if (len > rx_copybreak) { 36562306a36Sopenharmony_ci skb = rd->skb; 36662306a36Sopenharmony_ci newskb = netdev_alloc_skb(dev, PKT_BUF_SZ); 36762306a36Sopenharmony_ci if (!newskb) { 36862306a36Sopenharmony_ci newskb = skb; 36962306a36Sopenharmony_ci skb = NULL; 37062306a36Sopenharmony_ci goto memory_squeeze; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci skb_reserve(newskb, 2); 37362306a36Sopenharmony_ci } else { 37462306a36Sopenharmony_ci skb = netdev_alloc_skb_ip_align(dev, len); 37562306a36Sopenharmony_ci if (skb) 37662306a36Sopenharmony_ci skb_copy_to_linear_data(skb, rd->skb->data, len); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci newskb = rd->skb; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_cimemory_squeeze: 38162306a36Sopenharmony_ci if (skb) { 38262306a36Sopenharmony_ci skb_put(skb, len); 38362306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 38462306a36Sopenharmony_ci netif_rx(skb); 38562306a36Sopenharmony_ci dev->stats.rx_packets++; 38662306a36Sopenharmony_ci dev->stats.rx_bytes += len; 38762306a36Sopenharmony_ci } else { 38862306a36Sopenharmony_ci dev->stats.rx_dropped++; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci } else { 39162306a36Sopenharmony_ci /* Silently drop my own packets */ 39262306a36Sopenharmony_ci newskb = rd->skb; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci } else { 39562306a36Sopenharmony_ci record_rx_errors(dev, pkt_status); 39662306a36Sopenharmony_ci newskb = rd->skb; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci rd->skb = newskb; 39962306a36Sopenharmony_ci rd->rdma.pbuf = dma_map_single(dev->dev.parent, 40062306a36Sopenharmony_ci newskb->data - 2, 40162306a36Sopenharmony_ci PKT_BUF_SZ, DMA_FROM_DEVICE); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci /* Return the entry to the ring pool. */ 40462306a36Sopenharmony_ci rd->rdma.cntinfo = RCNTINFO_INIT; 40562306a36Sopenharmony_ci sp->rx_new = NEXT_RX(sp->rx_new); 40662306a36Sopenharmony_ci dma_sync_desc_dev(dev, rd); 40762306a36Sopenharmony_ci rd = &sp->rx_desc[sp->rx_new]; 40862306a36Sopenharmony_ci dma_sync_desc_cpu(dev, rd); 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci dma_sync_desc_dev(dev, rd); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci dma_sync_desc_cpu(dev, &sp->rx_desc[orig_end]); 41362306a36Sopenharmony_ci sp->rx_desc[orig_end].rdma.cntinfo &= ~(HPCDMA_EOR); 41462306a36Sopenharmony_ci dma_sync_desc_dev(dev, &sp->rx_desc[orig_end]); 41562306a36Sopenharmony_ci dma_sync_desc_cpu(dev, &sp->rx_desc[PREV_RX(sp->rx_new)]); 41662306a36Sopenharmony_ci sp->rx_desc[PREV_RX(sp->rx_new)].rdma.cntinfo |= HPCDMA_EOR; 41762306a36Sopenharmony_ci dma_sync_desc_dev(dev, &sp->rx_desc[PREV_RX(sp->rx_new)]); 41862306a36Sopenharmony_ci rx_maybe_restart(sp, hregs, sregs); 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic inline void tx_maybe_reset_collisions(struct sgiseeq_private *sp, 42262306a36Sopenharmony_ci struct sgiseeq_regs *sregs) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci if (sp->is_edlc) { 42562306a36Sopenharmony_ci sregs->rw.wregs.control = sp->control & ~(SEEQ_CTRL_XCNT); 42662306a36Sopenharmony_ci sregs->rw.wregs.control = sp->control; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic inline void kick_tx(struct net_device *dev, 43162306a36Sopenharmony_ci struct sgiseeq_private *sp, 43262306a36Sopenharmony_ci struct hpc3_ethregs *hregs) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci struct sgiseeq_tx_desc *td; 43562306a36Sopenharmony_ci int i = sp->tx_old; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* If the HPC aint doin nothin, and there are more packets 43862306a36Sopenharmony_ci * with ETXD cleared and XIU set we must make very certain 43962306a36Sopenharmony_ci * that we restart the HPC else we risk locking up the 44062306a36Sopenharmony_ci * adapter. The following code is only safe iff the HPCDMA 44162306a36Sopenharmony_ci * is not active! 44262306a36Sopenharmony_ci */ 44362306a36Sopenharmony_ci td = &sp->tx_desc[i]; 44462306a36Sopenharmony_ci dma_sync_desc_cpu(dev, td); 44562306a36Sopenharmony_ci while ((td->tdma.cntinfo & (HPCDMA_XIU | HPCDMA_ETXD)) == 44662306a36Sopenharmony_ci (HPCDMA_XIU | HPCDMA_ETXD)) { 44762306a36Sopenharmony_ci i = NEXT_TX(i); 44862306a36Sopenharmony_ci td = &sp->tx_desc[i]; 44962306a36Sopenharmony_ci dma_sync_desc_cpu(dev, td); 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci if (td->tdma.cntinfo & HPCDMA_XIU) { 45262306a36Sopenharmony_ci dma_sync_desc_dev(dev, td); 45362306a36Sopenharmony_ci hregs->tx_ndptr = VIRT_TO_DMA(sp, td); 45462306a36Sopenharmony_ci hregs->tx_ctrl = HPC3_ETXCTRL_ACTIVE; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic inline void sgiseeq_tx(struct net_device *dev, struct sgiseeq_private *sp, 45962306a36Sopenharmony_ci struct hpc3_ethregs *hregs, 46062306a36Sopenharmony_ci struct sgiseeq_regs *sregs) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci struct sgiseeq_tx_desc *td; 46362306a36Sopenharmony_ci unsigned long status = hregs->tx_ctrl; 46462306a36Sopenharmony_ci int j; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci tx_maybe_reset_collisions(sp, sregs); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (!(status & (HPC3_ETXCTRL_ACTIVE | SEEQ_TSTAT_PTRANS))) { 46962306a36Sopenharmony_ci /* Oops, HPC detected some sort of error. */ 47062306a36Sopenharmony_ci if (status & SEEQ_TSTAT_R16) 47162306a36Sopenharmony_ci dev->stats.tx_aborted_errors++; 47262306a36Sopenharmony_ci if (status & SEEQ_TSTAT_UFLOW) 47362306a36Sopenharmony_ci dev->stats.tx_fifo_errors++; 47462306a36Sopenharmony_ci if (status & SEEQ_TSTAT_LCLS) 47562306a36Sopenharmony_ci dev->stats.collisions++; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci /* Ack 'em... */ 47962306a36Sopenharmony_ci for (j = sp->tx_old; j != sp->tx_new; j = NEXT_TX(j)) { 48062306a36Sopenharmony_ci td = &sp->tx_desc[j]; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci dma_sync_desc_cpu(dev, td); 48362306a36Sopenharmony_ci if (!(td->tdma.cntinfo & (HPCDMA_XIU))) 48462306a36Sopenharmony_ci break; 48562306a36Sopenharmony_ci if (!(td->tdma.cntinfo & (HPCDMA_ETXD))) { 48662306a36Sopenharmony_ci dma_sync_desc_dev(dev, td); 48762306a36Sopenharmony_ci if (!(status & HPC3_ETXCTRL_ACTIVE)) { 48862306a36Sopenharmony_ci hregs->tx_ndptr = VIRT_TO_DMA(sp, td); 48962306a36Sopenharmony_ci hregs->tx_ctrl = HPC3_ETXCTRL_ACTIVE; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci break; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci dev->stats.tx_packets++; 49462306a36Sopenharmony_ci sp->tx_old = NEXT_TX(sp->tx_old); 49562306a36Sopenharmony_ci td->tdma.cntinfo &= ~(HPCDMA_XIU | HPCDMA_XIE); 49662306a36Sopenharmony_ci td->tdma.cntinfo |= HPCDMA_EOX; 49762306a36Sopenharmony_ci if (td->skb) { 49862306a36Sopenharmony_ci dev_kfree_skb_any(td->skb); 49962306a36Sopenharmony_ci td->skb = NULL; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci dma_sync_desc_dev(dev, td); 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic irqreturn_t sgiseeq_interrupt(int irq, void *dev_id) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci struct net_device *dev = (struct net_device *) dev_id; 50862306a36Sopenharmony_ci struct sgiseeq_private *sp = netdev_priv(dev); 50962306a36Sopenharmony_ci struct hpc3_ethregs *hregs = sp->hregs; 51062306a36Sopenharmony_ci struct sgiseeq_regs *sregs = sp->sregs; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci spin_lock(&sp->tx_lock); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* Ack the IRQ and set software state. */ 51562306a36Sopenharmony_ci hregs->reset = HPC3_ERST_CLRIRQ; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci /* Always check for received packets. */ 51862306a36Sopenharmony_ci sgiseeq_rx(dev, sp, hregs, sregs); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* Only check for tx acks if we have something queued. */ 52162306a36Sopenharmony_ci if (sp->tx_old != sp->tx_new) 52262306a36Sopenharmony_ci sgiseeq_tx(dev, sp, hregs, sregs); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if ((TX_BUFFS_AVAIL(sp) > 0) && netif_queue_stopped(dev)) { 52562306a36Sopenharmony_ci netif_wake_queue(dev); 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci spin_unlock(&sp->tx_lock); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci return IRQ_HANDLED; 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic int sgiseeq_open(struct net_device *dev) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci struct sgiseeq_private *sp = netdev_priv(dev); 53562306a36Sopenharmony_ci struct sgiseeq_regs *sregs = sp->sregs; 53662306a36Sopenharmony_ci unsigned int irq = dev->irq; 53762306a36Sopenharmony_ci int err; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (request_irq(irq, sgiseeq_interrupt, 0, sgiseeqstr, dev)) { 54062306a36Sopenharmony_ci printk(KERN_ERR "Seeq8003: Can't get irq %d\n", dev->irq); 54162306a36Sopenharmony_ci return -EAGAIN; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci err = init_seeq(dev, sp, sregs); 54562306a36Sopenharmony_ci if (err) 54662306a36Sopenharmony_ci goto out_free_irq; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci netif_start_queue(dev); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci return 0; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ciout_free_irq: 55362306a36Sopenharmony_ci free_irq(irq, dev); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci return err; 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic int sgiseeq_close(struct net_device *dev) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci struct sgiseeq_private *sp = netdev_priv(dev); 56162306a36Sopenharmony_ci struct sgiseeq_regs *sregs = sp->sregs; 56262306a36Sopenharmony_ci unsigned int irq = dev->irq; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci netif_stop_queue(dev); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* Shutdown the Seeq. */ 56762306a36Sopenharmony_ci reset_hpc3_and_seeq(sp->hregs, sregs); 56862306a36Sopenharmony_ci free_irq(irq, dev); 56962306a36Sopenharmony_ci seeq_purge_ring(dev); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci return 0; 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic inline int sgiseeq_reset(struct net_device *dev) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci struct sgiseeq_private *sp = netdev_priv(dev); 57762306a36Sopenharmony_ci struct sgiseeq_regs *sregs = sp->sregs; 57862306a36Sopenharmony_ci int err; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci err = init_seeq(dev, sp, sregs); 58162306a36Sopenharmony_ci if (err) 58262306a36Sopenharmony_ci return err; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci netif_trans_update(dev); /* prevent tx timeout */ 58562306a36Sopenharmony_ci netif_wake_queue(dev); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci return 0; 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic netdev_tx_t 59162306a36Sopenharmony_cisgiseeq_start_xmit(struct sk_buff *skb, struct net_device *dev) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci struct sgiseeq_private *sp = netdev_priv(dev); 59462306a36Sopenharmony_ci struct hpc3_ethregs *hregs = sp->hregs; 59562306a36Sopenharmony_ci unsigned long flags; 59662306a36Sopenharmony_ci struct sgiseeq_tx_desc *td; 59762306a36Sopenharmony_ci int len, entry; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci spin_lock_irqsave(&sp->tx_lock, flags); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci /* Setup... */ 60262306a36Sopenharmony_ci len = skb->len; 60362306a36Sopenharmony_ci if (len < ETH_ZLEN) { 60462306a36Sopenharmony_ci if (skb_padto(skb, ETH_ZLEN)) { 60562306a36Sopenharmony_ci spin_unlock_irqrestore(&sp->tx_lock, flags); 60662306a36Sopenharmony_ci return NETDEV_TX_OK; 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci len = ETH_ZLEN; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci dev->stats.tx_bytes += len; 61262306a36Sopenharmony_ci entry = sp->tx_new; 61362306a36Sopenharmony_ci td = &sp->tx_desc[entry]; 61462306a36Sopenharmony_ci dma_sync_desc_cpu(dev, td); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci /* Create entry. There are so many races with adding a new 61762306a36Sopenharmony_ci * descriptor to the chain: 61862306a36Sopenharmony_ci * 1) Assume that the HPC is off processing a DMA chain while 61962306a36Sopenharmony_ci * we are changing all of the following. 62062306a36Sopenharmony_ci * 2) Do no allow the HPC to look at a new descriptor until 62162306a36Sopenharmony_ci * we have completely set up it's state. This means, do 62262306a36Sopenharmony_ci * not clear HPCDMA_EOX in the current last descritptor 62362306a36Sopenharmony_ci * until the one we are adding looks consistent and could 62462306a36Sopenharmony_ci * be processes right now. 62562306a36Sopenharmony_ci * 3) The tx interrupt code must notice when we've added a new 62662306a36Sopenharmony_ci * entry and the HPC got to the end of the chain before we 62762306a36Sopenharmony_ci * added this new entry and restarted it. 62862306a36Sopenharmony_ci */ 62962306a36Sopenharmony_ci td->skb = skb; 63062306a36Sopenharmony_ci td->tdma.pbuf = dma_map_single(dev->dev.parent, skb->data, 63162306a36Sopenharmony_ci len, DMA_TO_DEVICE); 63262306a36Sopenharmony_ci td->tdma.cntinfo = (len & HPCDMA_BCNT) | 63362306a36Sopenharmony_ci HPCDMA_XIU | HPCDMA_EOXP | HPCDMA_XIE | HPCDMA_EOX; 63462306a36Sopenharmony_ci dma_sync_desc_dev(dev, td); 63562306a36Sopenharmony_ci if (sp->tx_old != sp->tx_new) { 63662306a36Sopenharmony_ci struct sgiseeq_tx_desc *backend; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci backend = &sp->tx_desc[PREV_TX(sp->tx_new)]; 63962306a36Sopenharmony_ci dma_sync_desc_cpu(dev, backend); 64062306a36Sopenharmony_ci backend->tdma.cntinfo &= ~HPCDMA_EOX; 64162306a36Sopenharmony_ci dma_sync_desc_dev(dev, backend); 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci sp->tx_new = NEXT_TX(sp->tx_new); /* Advance. */ 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci /* Maybe kick the HPC back into motion. */ 64662306a36Sopenharmony_ci if (!(hregs->tx_ctrl & HPC3_ETXCTRL_ACTIVE)) 64762306a36Sopenharmony_ci kick_tx(dev, sp, hregs); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci if (!TX_BUFFS_AVAIL(sp)) 65062306a36Sopenharmony_ci netif_stop_queue(dev); 65162306a36Sopenharmony_ci spin_unlock_irqrestore(&sp->tx_lock, flags); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci return NETDEV_TX_OK; 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic void timeout(struct net_device *dev, unsigned int txqueue) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci printk(KERN_NOTICE "%s: transmit timed out, resetting\n", dev->name); 65962306a36Sopenharmony_ci sgiseeq_reset(dev); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci netif_trans_update(dev); /* prevent tx timeout */ 66262306a36Sopenharmony_ci netif_wake_queue(dev); 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cistatic void sgiseeq_set_multicast(struct net_device *dev) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci struct sgiseeq_private *sp = netdev_priv(dev); 66862306a36Sopenharmony_ci unsigned char oldmode = sp->mode; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci if(dev->flags & IFF_PROMISC) 67162306a36Sopenharmony_ci sp->mode = SEEQ_RCMD_RANY; 67262306a36Sopenharmony_ci else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev)) 67362306a36Sopenharmony_ci sp->mode = SEEQ_RCMD_RBMCAST; 67462306a36Sopenharmony_ci else 67562306a36Sopenharmony_ci sp->mode = SEEQ_RCMD_RBCAST; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci /* XXX I know this sucks, but is there a better way to reprogram 67862306a36Sopenharmony_ci * XXX the receiver? At least, this shouldn't happen too often. 67962306a36Sopenharmony_ci */ 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (oldmode != sp->mode) 68262306a36Sopenharmony_ci sgiseeq_reset(dev); 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_cistatic inline void setup_tx_ring(struct net_device *dev, 68662306a36Sopenharmony_ci struct sgiseeq_tx_desc *buf, 68762306a36Sopenharmony_ci int nbufs) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci struct sgiseeq_private *sp = netdev_priv(dev); 69062306a36Sopenharmony_ci int i = 0; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci while (i < (nbufs - 1)) { 69362306a36Sopenharmony_ci buf[i].tdma.pnext = VIRT_TO_DMA(sp, buf + i + 1); 69462306a36Sopenharmony_ci buf[i].tdma.pbuf = 0; 69562306a36Sopenharmony_ci dma_sync_desc_dev(dev, &buf[i]); 69662306a36Sopenharmony_ci i++; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci buf[i].tdma.pnext = VIRT_TO_DMA(sp, buf); 69962306a36Sopenharmony_ci dma_sync_desc_dev(dev, &buf[i]); 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_cistatic inline void setup_rx_ring(struct net_device *dev, 70362306a36Sopenharmony_ci struct sgiseeq_rx_desc *buf, 70462306a36Sopenharmony_ci int nbufs) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci struct sgiseeq_private *sp = netdev_priv(dev); 70762306a36Sopenharmony_ci int i = 0; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci while (i < (nbufs - 1)) { 71062306a36Sopenharmony_ci buf[i].rdma.pnext = VIRT_TO_DMA(sp, buf + i + 1); 71162306a36Sopenharmony_ci buf[i].rdma.pbuf = 0; 71262306a36Sopenharmony_ci dma_sync_desc_dev(dev, &buf[i]); 71362306a36Sopenharmony_ci i++; 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci buf[i].rdma.pbuf = 0; 71662306a36Sopenharmony_ci buf[i].rdma.pnext = VIRT_TO_DMA(sp, buf); 71762306a36Sopenharmony_ci dma_sync_desc_dev(dev, &buf[i]); 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_cistatic const struct net_device_ops sgiseeq_netdev_ops = { 72162306a36Sopenharmony_ci .ndo_open = sgiseeq_open, 72262306a36Sopenharmony_ci .ndo_stop = sgiseeq_close, 72362306a36Sopenharmony_ci .ndo_start_xmit = sgiseeq_start_xmit, 72462306a36Sopenharmony_ci .ndo_tx_timeout = timeout, 72562306a36Sopenharmony_ci .ndo_set_rx_mode = sgiseeq_set_multicast, 72662306a36Sopenharmony_ci .ndo_set_mac_address = sgiseeq_set_mac_address, 72762306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 72862306a36Sopenharmony_ci}; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_cistatic int sgiseeq_probe(struct platform_device *pdev) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci struct sgiseeq_platform_data *pd = dev_get_platdata(&pdev->dev); 73362306a36Sopenharmony_ci struct hpc3_regs *hpcregs = pd->hpc; 73462306a36Sopenharmony_ci struct sgiseeq_init_block *sr; 73562306a36Sopenharmony_ci unsigned int irq = pd->irq; 73662306a36Sopenharmony_ci struct sgiseeq_private *sp; 73762306a36Sopenharmony_ci struct net_device *dev; 73862306a36Sopenharmony_ci int err; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci dev = alloc_etherdev(sizeof (struct sgiseeq_private)); 74162306a36Sopenharmony_ci if (!dev) { 74262306a36Sopenharmony_ci err = -ENOMEM; 74362306a36Sopenharmony_ci goto err_out; 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci platform_set_drvdata(pdev, dev); 74762306a36Sopenharmony_ci SET_NETDEV_DEV(dev, &pdev->dev); 74862306a36Sopenharmony_ci sp = netdev_priv(dev); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci /* Make private data page aligned */ 75162306a36Sopenharmony_ci sr = dma_alloc_noncoherent(&pdev->dev, sizeof(*sp->srings), 75262306a36Sopenharmony_ci &sp->srings_dma, DMA_BIDIRECTIONAL, GFP_KERNEL); 75362306a36Sopenharmony_ci if (!sr) { 75462306a36Sopenharmony_ci printk(KERN_ERR "Sgiseeq: Page alloc failed, aborting.\n"); 75562306a36Sopenharmony_ci err = -ENOMEM; 75662306a36Sopenharmony_ci goto err_out_free_dev; 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci sp->srings = sr; 75962306a36Sopenharmony_ci sp->rx_desc = sp->srings->rxvector; 76062306a36Sopenharmony_ci sp->tx_desc = sp->srings->txvector; 76162306a36Sopenharmony_ci spin_lock_init(&sp->tx_lock); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci /* A couple calculations now, saves many cycles later. */ 76462306a36Sopenharmony_ci setup_rx_ring(dev, sp->rx_desc, SEEQ_RX_BUFFERS); 76562306a36Sopenharmony_ci setup_tx_ring(dev, sp->tx_desc, SEEQ_TX_BUFFERS); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci eth_hw_addr_set(dev, pd->mac); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci#ifdef DEBUG 77062306a36Sopenharmony_ci gpriv = sp; 77162306a36Sopenharmony_ci gdev = dev; 77262306a36Sopenharmony_ci#endif 77362306a36Sopenharmony_ci sp->sregs = (struct sgiseeq_regs *) &hpcregs->eth_ext[0]; 77462306a36Sopenharmony_ci sp->hregs = &hpcregs->ethregs; 77562306a36Sopenharmony_ci sp->name = sgiseeqstr; 77662306a36Sopenharmony_ci sp->mode = SEEQ_RCMD_RBCAST; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci /* Setup PIO and DMA transfer timing */ 77962306a36Sopenharmony_ci sp->hregs->pconfig = 0x161; 78062306a36Sopenharmony_ci sp->hregs->dconfig = HPC3_EDCFG_FIRQ | HPC3_EDCFG_FEOP | 78162306a36Sopenharmony_ci HPC3_EDCFG_FRXDC | HPC3_EDCFG_PTO | 0x026; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci /* Setup PIO and DMA transfer timing */ 78462306a36Sopenharmony_ci sp->hregs->pconfig = 0x161; 78562306a36Sopenharmony_ci sp->hregs->dconfig = HPC3_EDCFG_FIRQ | HPC3_EDCFG_FEOP | 78662306a36Sopenharmony_ci HPC3_EDCFG_FRXDC | HPC3_EDCFG_PTO | 0x026; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci /* Reset the chip. */ 78962306a36Sopenharmony_ci hpc3_eth_reset(sp->hregs); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci sp->is_edlc = !(sp->sregs->rw.rregs.collision_tx[0] & 0xff); 79262306a36Sopenharmony_ci if (sp->is_edlc) 79362306a36Sopenharmony_ci sp->control = SEEQ_CTRL_XCNT | SEEQ_CTRL_ACCNT | 79462306a36Sopenharmony_ci SEEQ_CTRL_SFLAG | SEEQ_CTRL_ESHORT | 79562306a36Sopenharmony_ci SEEQ_CTRL_ENCARR; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci dev->netdev_ops = &sgiseeq_netdev_ops; 79862306a36Sopenharmony_ci dev->watchdog_timeo = (200 * HZ) / 1000; 79962306a36Sopenharmony_ci dev->irq = irq; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci if (register_netdev(dev)) { 80262306a36Sopenharmony_ci printk(KERN_ERR "Sgiseeq: Cannot register net device, " 80362306a36Sopenharmony_ci "aborting.\n"); 80462306a36Sopenharmony_ci err = -ENODEV; 80562306a36Sopenharmony_ci goto err_out_free_attrs; 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci printk(KERN_INFO "%s: %s %pM\n", dev->name, sgiseeqstr, dev->dev_addr); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci return 0; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_cierr_out_free_attrs: 81362306a36Sopenharmony_ci dma_free_noncoherent(&pdev->dev, sizeof(*sp->srings), sp->srings, 81462306a36Sopenharmony_ci sp->srings_dma, DMA_BIDIRECTIONAL); 81562306a36Sopenharmony_cierr_out_free_dev: 81662306a36Sopenharmony_ci free_netdev(dev); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_cierr_out: 81962306a36Sopenharmony_ci return err; 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_cistatic int sgiseeq_remove(struct platform_device *pdev) 82362306a36Sopenharmony_ci{ 82462306a36Sopenharmony_ci struct net_device *dev = platform_get_drvdata(pdev); 82562306a36Sopenharmony_ci struct sgiseeq_private *sp = netdev_priv(dev); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci unregister_netdev(dev); 82862306a36Sopenharmony_ci dma_free_noncoherent(&pdev->dev, sizeof(*sp->srings), sp->srings, 82962306a36Sopenharmony_ci sp->srings_dma, DMA_BIDIRECTIONAL); 83062306a36Sopenharmony_ci free_netdev(dev); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci return 0; 83362306a36Sopenharmony_ci} 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_cistatic struct platform_driver sgiseeq_driver = { 83662306a36Sopenharmony_ci .probe = sgiseeq_probe, 83762306a36Sopenharmony_ci .remove = sgiseeq_remove, 83862306a36Sopenharmony_ci .driver = { 83962306a36Sopenharmony_ci .name = "sgiseeq", 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci}; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cimodule_platform_driver(sgiseeq_driver); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ciMODULE_DESCRIPTION("SGI Seeq 8003 driver"); 84662306a36Sopenharmony_ciMODULE_AUTHOR("Linux/MIPS Mailing List <linux-mips@linux-mips.org>"); 84762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 84862306a36Sopenharmony_ciMODULE_ALIAS("platform:sgiseeq"); 849