sgiseeq.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 740 行 · 第 1/2 页
C
740 行
* adapter. The following code is only safe iff the HPCDMA * is not active! */ while ((td->tdma.cntinfo & (HPCDMA_XIU | HPCDMA_ETXD)) == (HPCDMA_XIU | HPCDMA_ETXD)) td = (struct sgiseeq_tx_desc *)(long) KSEG1ADDR(td->tdma.pnext); if (td->tdma.cntinfo & HPCDMA_XIU) { hregs->tx_ndptr = CPHYSADDR(td); hregs->tx_ctrl = HPC3_ETXCTRL_ACTIVE; }}static inline void sgiseeq_tx(struct net_device *dev, struct sgiseeq_private *sp, struct hpc3_ethregs *hregs, struct sgiseeq_regs *sregs){ struct sgiseeq_tx_desc *td; unsigned long status = hregs->tx_ctrl; int j; tx_maybe_reset_collisions(sp, sregs); if (!(status & (HPC3_ETXCTRL_ACTIVE | SEEQ_TSTAT_PTRANS))) { /* Oops, HPC detected some sort of error. */ if (status & SEEQ_TSTAT_R16) sp->stats.tx_aborted_errors++; if (status & SEEQ_TSTAT_UFLOW) sp->stats.tx_fifo_errors++; if (status & SEEQ_TSTAT_LCLS) sp->stats.collisions++; } /* Ack 'em... */ for (j = sp->tx_old; j != sp->tx_new; j = NEXT_TX(j)) { td = &sp->tx_desc[j]; if (!(td->tdma.cntinfo & (HPCDMA_XIU))) break; if (!(td->tdma.cntinfo & (HPCDMA_ETXD))) { if (!(status & HPC3_ETXCTRL_ACTIVE)) { hregs->tx_ndptr = CPHYSADDR(td); hregs->tx_ctrl = HPC3_ETXCTRL_ACTIVE; } break; } sp->stats.tx_packets++; sp->tx_old = NEXT_TX(sp->tx_old); td->tdma.cntinfo &= ~(HPCDMA_XIU | HPCDMA_XIE); td->tdma.cntinfo |= HPCDMA_EOX; }}static irqreturn_t sgiseeq_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct net_device *dev = (struct net_device *) dev_id; struct sgiseeq_private *sp = netdev_priv(dev); struct hpc3_ethregs *hregs = sp->hregs; struct sgiseeq_regs *sregs = sp->sregs; spin_lock(&sp->tx_lock); /* Ack the IRQ and set software state. */ hregs->rx_reset = HPC3_ERXRST_CLRIRQ; /* Always check for received packets. */ sgiseeq_rx(dev, sp, hregs, sregs); /* Only check for tx acks if we have something queued. */ if (sp->tx_old != sp->tx_new) sgiseeq_tx(dev, sp, hregs, sregs); if ((TX_BUFFS_AVAIL(sp) > 0) && netif_queue_stopped(dev)) { netif_wake_queue(dev); } spin_unlock(&sp->tx_lock); return IRQ_HANDLED;}static int sgiseeq_open(struct net_device *dev){ struct sgiseeq_private *sp = netdev_priv(dev); struct sgiseeq_regs *sregs = sp->sregs; unsigned int irq = dev->irq; int err; if (request_irq(irq, sgiseeq_interrupt, 0, sgiseeqstr, dev)) { printk(KERN_ERR "Seeq8003: Can't get irq %d\n", dev->irq); err = -EAGAIN; } err = init_seeq(dev, sp, sregs); if (err) goto out_free_irq; netif_start_queue(dev); return 0;out_free_irq: free_irq(irq, dev); return err;}static int sgiseeq_close(struct net_device *dev){ struct sgiseeq_private *sp = netdev_priv(dev); struct sgiseeq_regs *sregs = sp->sregs; netif_stop_queue(dev); /* Shutdown the Seeq. */ reset_hpc3_and_seeq(sp->hregs, sregs); return 0;}static inline int sgiseeq_reset(struct net_device *dev){ struct sgiseeq_private *sp = netdev_priv(dev); struct sgiseeq_regs *sregs = sp->sregs; int err; err = init_seeq(dev, sp, sregs); if (err) return err; dev->trans_start = jiffies; netif_wake_queue(dev); return 0;}void sgiseeq_my_reset(void){ printk("RESET!\n"); sgiseeq_reset(gdev);}static int sgiseeq_start_xmit(struct sk_buff *skb, struct net_device *dev){ struct sgiseeq_private *sp = netdev_priv(dev); struct hpc3_ethregs *hregs = sp->hregs; unsigned long flags; struct sgiseeq_tx_desc *td; int skblen, len, entry; spin_lock_irqsave(&sp->tx_lock, flags); /* Setup... */ skblen = skb->len; len = (skblen <= ETH_ZLEN) ? ETH_ZLEN : skblen; sp->stats.tx_bytes += len; entry = sp->tx_new; td = &sp->tx_desc[entry]; /* Create entry. There are so many races with adding a new * descriptor to the chain: * 1) Assume that the HPC is off processing a DMA chain while * we are changing all of the following. * 2) Do no allow the HPC to look at a new descriptor until * we have completely set up it's state. This means, do * not clear HPCDMA_EOX in the current last descritptor * until the one we are adding looks consistent and could * be processes right now. * 3) The tx interrupt code must notice when we've added a new * entry and the HPC got to the end of the chain before we * added this new entry and restarted it. */ memcpy((char *)(long)td->buf_vaddr, skb->data, skblen); if (len != skblen) memset((char *)(long)td->buf_vaddr + skb->len, 0, len-skblen); td->tdma.cntinfo = (len & HPCDMA_BCNT) | HPCDMA_XIU | HPCDMA_EOXP | HPCDMA_XIE | HPCDMA_EOX; if (sp->tx_old != sp->tx_new) { struct sgiseeq_tx_desc *backend; backend = &sp->tx_desc[PREV_TX(sp->tx_new)]; backend->tdma.cntinfo &= ~HPCDMA_EOX; } sp->tx_new = NEXT_TX(sp->tx_new); /* Advance. */ /* Maybe kick the HPC back into motion. */ if (!(hregs->tx_ctrl & HPC3_ETXCTRL_ACTIVE)) kick_tx(&sp->tx_desc[sp->tx_old], hregs); dev->trans_start = jiffies; dev_kfree_skb(skb); if (!TX_BUFFS_AVAIL(sp)) netif_stop_queue(dev); spin_unlock_irqrestore(&sp->tx_lock, flags); return 0;}static void timeout(struct net_device *dev){ printk(KERN_NOTICE "%s: transmit timed out, resetting\n", dev->name); sgiseeq_reset(dev); dev->trans_start = jiffies; netif_wake_queue(dev);}static struct net_device_stats *sgiseeq_get_stats(struct net_device *dev){ struct sgiseeq_private *sp = netdev_priv(dev); return &sp->stats;}static void sgiseeq_set_multicast(struct net_device *dev){}static inline void setup_tx_ring(struct sgiseeq_tx_desc *buf, int nbufs){ int i = 0; while (i < (nbufs - 1)) { buf[i].tdma.pnext = CPHYSADDR(buf + i + 1); buf[i].tdma.pbuf = 0; i++; } buf[i].tdma.pnext = CPHYSADDR(buf);}static inline void setup_rx_ring(struct sgiseeq_rx_desc *buf, int nbufs){ int i = 0; while (i < (nbufs - 1)) { buf[i].rdma.pnext = CPHYSADDR(buf + i + 1); buf[i].rdma.pbuf = 0; i++; } buf[i].rdma.pbuf = 0; buf[i].rdma.pnext = CPHYSADDR(buf);}#define ALIGNED(x) ((((unsigned long)(x)) + 0xf) & ~(0xf))static int sgiseeq_init(struct hpc3_regs* regs, int irq){ struct sgiseeq_init_block *sr; struct sgiseeq_private *sp; struct net_device *dev; int err, i; dev = alloc_etherdev(sizeof (struct sgiseeq_private)); if (!dev) { printk(KERN_ERR "Sgiseeq: Etherdev alloc failed, aborting.\n"); err = -ENOMEM; goto err_out; } sp = netdev_priv(dev); /* Make private data page aligned */ sr = (struct sgiseeq_init_block *) get_zeroed_page(GFP_KERNEL); if (!sr) { printk(KERN_ERR "Sgiseeq: Page alloc failed, aborting.\n"); err = -ENOMEM; goto err_out_free_dev; } sp->srings = sr;#define EADDR_NVOFS 250 for (i = 0; i < 3; i++) { unsigned short tmp = ip22_nvram_read(EADDR_NVOFS / 2 + i); dev->dev_addr[2 * i] = tmp >> 8; dev->dev_addr[2 * i + 1] = tmp & 0xff; }#ifdef DEBUG gpriv = sp; gdev = dev;#endif sp->sregs = (struct sgiseeq_regs *) &hpc3c0->eth_ext[0]; sp->hregs = &hpc3c0->ethregs; sp->name = sgiseeqstr; sp->rx_desc = (struct sgiseeq_rx_desc *) KSEG1ADDR(ALIGNED(&sp->srings->rxvector[0])); dma_cache_wback_inv((unsigned long)&sp->srings->rxvector, sizeof(sp->srings->rxvector)); sp->tx_desc = (struct sgiseeq_tx_desc *) KSEG1ADDR(ALIGNED(&sp->srings->txvector[0])); dma_cache_wback_inv((unsigned long)&sp->srings->txvector, sizeof(sp->srings->txvector)); /* A couple calculations now, saves many cycles later. */ setup_rx_ring(sp->rx_desc, SEEQ_RX_BUFFERS); setup_tx_ring(sp->tx_desc, SEEQ_TX_BUFFERS); /* Reset the chip. */ hpc3_eth_reset(sp->hregs); sp->is_edlc = !(sp->sregs->rw.rregs.collision_tx[0] & 0xff); if (sp->is_edlc) sp->control = SEEQ_CTRL_XCNT | SEEQ_CTRL_ACCNT | SEEQ_CTRL_SFLAG | SEEQ_CTRL_ESHORT | SEEQ_CTRL_ENCARR; dev->open = sgiseeq_open; dev->stop = sgiseeq_close; dev->hard_start_xmit = sgiseeq_start_xmit; dev->tx_timeout = timeout; dev->watchdog_timeo = (200 * HZ) / 1000; dev->get_stats = sgiseeq_get_stats; dev->set_multicast_list = sgiseeq_set_multicast; dev->irq = irq; if (register_netdev(dev)) { printk(KERN_ERR "Sgiseeq: Cannot register net device, " "aborting.\n"); err = -ENODEV; goto err_out_free_page; } printk(KERN_INFO "%s: SGI Seeq8003 ", dev->name); for (i = 0; i < 6; i++) printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':'); sp->next_module = root_sgiseeq_dev; root_sgiseeq_dev = dev; return 0;err_out_free_page: free_page((unsigned long) sp);err_out_free_dev: kfree(dev);err_out: return err;}static int __init sgiseeq_probe(void){ printk(version); /* On board adapter on 1st HPC is always present */ return sgiseeq_init(hpc3c0, SGI_ENET_IRQ);}static void __exit sgiseeq_exit(void){ struct net_device *next, *dev; struct sgiseeq_private *sp; int irq; for (dev = root_sgiseeq_dev; dev; dev = next) { sp = (struct sgiseeq_private *) netdev_priv(dev); next = sp->next_module; irq = dev->irq; unregister_netdev(dev); free_irq(irq, dev); free_page((unsigned long) sp); free_netdev(dev); }}module_init(sgiseeq_probe);module_exit(sgiseeq_exit);MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?