📄 sunbmac.c
字号:
udelay(20); } if(timeout == 0) printk("%s: PHY reset failed.\n", bp->dev->name); bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR); /* Now we try 10baseT. */ bp->sw_bmcr &= ~(BMCR_SPEED100); bigmac_tcvr_write(bp, tregs, BIGMAC_BMCR, bp->sw_bmcr); return 0; } /* We've tried them all. */ return -1;}static void bigmac_timer(unsigned long data){ struct bigmac *bp = (struct bigmac *) data; struct bmac_tcvr *tregs = bp->tregs; int restart_timer = 0; bp->timer_ticks++; if(bp->timer_state == ltrywait) { bp->sw_bmsr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMSR); bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR); if(bp->sw_bmsr & BMSR_LSTATUS) { printk("%s: Link is now up at %s.\n", bp->dev->name, (bp->sw_bmcr & BMCR_SPEED100) ? "100baseT" : "10baseT"); bp->timer_state = asleep; restart_timer = 0; } else { if(bp->timer_ticks >= 4) { int ret; ret = try_next_permutation(bp, tregs); if(ret == -1) { printk("%s: Link down, cable problem?\n", bp->dev->name); ret = bigmac_init(bp, 0); if(ret) { printk("%s: Error, cannot re-init the " "BigMAC.\n", bp->dev->name); } return; } bp->timer_ticks = 0; restart_timer = 1; } else { restart_timer = 1; } } } else { /* Can't happens.... */ printk("%s: Aieee, link timer is asleep but we got one anyways!\n", bp->dev->name); restart_timer = 0; bp->timer_ticks = 0; bp->timer_state = asleep; /* foo on you */ } if(restart_timer != 0) { bp->bigmac_timer.expires = jiffies + ((12 * HZ)/10); /* 1.2 sec. */ add_timer(&bp->bigmac_timer); }}/* Well, really we just force the chip into 100baseT then * 10baseT, each time checking for a link status. */static void bigmac_begin_auto_negotiation(struct bigmac *bp){ struct bmac_tcvr *tregs = bp->tregs; int timeout; /* Grab new software copies of PHY registers. */ bp->sw_bmsr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMSR); bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR); /* Reset the PHY. */ bp->sw_bmcr = (BMCR_ISOLATE | BMCR_PDOWN | BMCR_LOOPBACK); bigmac_tcvr_write(bp, tregs, BIGMAC_BMCR, bp->sw_bmcr); bp->sw_bmcr = (BMCR_RESET); bigmac_tcvr_write(bp, tregs, BIGMAC_BMCR, bp->sw_bmcr); timeout = 64; while(--timeout) { bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR); if((bp->sw_bmcr & BMCR_RESET) == 0) break; udelay(20); } if(timeout == 0) printk("%s: PHY reset failed.\n", bp->dev->name); bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR); /* First we try 100baseT. */ bp->sw_bmcr |= BMCR_SPEED100; bigmac_tcvr_write(bp, tregs, BIGMAC_BMCR, bp->sw_bmcr); bp->timer_state = ltrywait; bp->timer_ticks = 0; bp->bigmac_timer.expires = jiffies + (12 * HZ) / 10; bp->bigmac_timer.data = (unsigned long) bp; bp->bigmac_timer.function = &bigmac_timer; add_timer(&bp->bigmac_timer);}static int bigmac_init(struct bigmac *bp, int from_irq){ struct qe_globreg *gregs = bp->gregs; struct qe_creg *cregs = bp->creg; struct BIG_MAC_regs *bregs = bp->bregs; unsigned char *e = &bp->dev->dev_addr[0]; /* Latch current counters into statistics. */ bigmac_get_counters(bp, bregs); /* Reset QEC. */ qec_global_reset(gregs); /* Init QEC. */ qec_init(bp); /* Alloc and reset the tx/rx descriptor chains. */#ifndef __sparc_v9__ if(sparc_cpu_model == sun4c) sun4c_bigmac_init_rings(bp); else#endif bigmac_init_rings(bp, from_irq); /* Initialize the PHY. */ bigmac_tcvr_init(bp); /* Stop transmitter and receiver. */ bigmac_stop(bp); /* Set hardware ethernet address. */ bregs->mac_addr2 = ((e[4] << 8) | e[5]); bregs->mac_addr1 = ((e[2] << 8) | e[3]); bregs->mac_addr0 = ((e[0] << 8) | e[1]); /* Clear the hash table until mc upload occurs. */ bregs->htable3 = 0; bregs->htable2 = 0; bregs->htable1 = 0; bregs->htable0 = 0; /* Enable Big Mac hash table filter. */ bregs->rx_cfg = (BIGMAC_RXCFG_HENABLE | BIGMAC_RXCFG_FIFO); udelay(20); /* Ok, configure the Big Mac transmitter. */ bregs->tx_cfg = BIGMAC_TXCFG_FIFO; /* The HME docs recommend to use the 10LSB of our MAC here. */ bregs->rand_seed = ((e[5] | e[4] << 8) & 0x3ff); /* Enable the output drivers no matter what. */ bregs->xif_cfg = (BIGMAC_XCFG_ODENABLE | BIGMAC_XCFG_RESV); /* Tell the QEC where the ring descriptors are. */ cregs->rxds = bp->bblock_dvma + bib_offset(be_rxd, 0); cregs->txds = bp->bblock_dvma + bib_offset(be_txd, 0); /* Setup the FIFO pointers into QEC local memory. */ cregs->rxwbufptr = cregs->rxrbufptr = 0; cregs->txwbufptr = cregs->txrbufptr = gregs->rsize; /* Tell bigmac what interrupts we don't want to hear about. */ bregs->imask = (BIGMAC_IMASK_GOTFRAME | BIGMAC_IMASK_SENTFRAME); /* Enable the various other irq's. */ cregs->rimask = 0; cregs->timask = 0; cregs->qmask = 0; cregs->bmask = 0; /* Set jam size to a reasonable default. */ bregs->jsize = DEFAULT_JAMSIZE; /* Clear collision counter. */ cregs->ccnt = 0; /* Enable transmitter and receiver. */ bregs->tx_cfg |= BIGMAC_TXCFG_ENABLE; bregs->rx_cfg |= BIGMAC_RXCFG_ENABLE; /* Ok, start detecting link speed/duplex. */ bigmac_begin_auto_negotiation(bp); /* Success. */ return 0;}/* Error interrupts get sent here. */static void bigmac_is_medium_rare(struct bigmac *bp, unsigned int qec_status, unsigned int bmac_status){ printk("bigmac_is_medium_rare: "); if(qec_status & (GLOB_STAT_ER | GLOB_STAT_BM)) { if(qec_status & GLOB_STAT_ER) printk("QEC_ERROR, "); if(qec_status & GLOB_STAT_BM) printk("QEC_BMAC_ERROR, "); } if(bmac_status & CREG_STAT_ERRORS) { if(bmac_status & CREG_STAT_BERROR) printk("BMAC_ERROR, "); if(bmac_status & CREG_STAT_TXDERROR) printk("TXD_ERROR, "); if(bmac_status & CREG_STAT_TXLERR) printk("TX_LATE_ERROR, "); if(bmac_status & CREG_STAT_TXPERR) printk("TX_PARITY_ERROR, "); if(bmac_status & CREG_STAT_TXSERR) printk("TX_SBUS_ERROR, "); if(bmac_status & CREG_STAT_RXDROP) printk("RX_DROP_ERROR, "); if(bmac_status & CREG_STAT_RXSMALL) printk("RX_SMALL_ERROR, "); if(bmac_status & CREG_STAT_RXLERR) printk("RX_LATE_ERROR, "); if(bmac_status & CREG_STAT_RXPERR) printk("RX_PARITY_ERROR, "); if(bmac_status & CREG_STAT_RXSERR) printk("RX_SBUS_ERROR, "); } printk(" RESET\n"); bigmac_init(bp, 1);}/* BigMAC transmit complete service routines. */static inline void bigmac_tx(struct bigmac *bp){ struct be_txd *txbase = &bp->bmac_block->be_txd[0]; struct be_txd *this; int elem = bp->tx_old; DTX(("bigmac_tx: tx_old[%d] ", elem)); while(elem != bp->tx_new) { struct sk_buff *skb; this = &txbase[elem]; DTX(("this(%p) [flags(%08x)addr(%08x)]", this, this->tx_flags, this->tx_addr)); if(this->tx_flags & TXD_OWN) break; skb = bp->tx_skbs[elem]; DTX(("skb(%p) ", skb)); bp->tx_skbs[elem] = NULL; dev_kfree_skb(skb); bp->enet_stats.tx_packets++; elem = NEXT_TX(elem); } DTX((" DONE, tx_old=%d\n", elem)); bp->tx_old = elem;}#ifndef __sparc_v9__static inline void sun4c_bigmac_tx(struct bigmac *bp){ struct be_txd *txbase = &bp->bmac_block->be_txd[0]; struct be_txd *this; int elem = bp->tx_old; while(elem != bp->tx_new) { this = &txbase[elem]; if(this->tx_flags & TXD_OWN) break; bp->enet_stats.tx_packets++; elem = NEXT_TX(elem); } bp->tx_old = elem;}#endif/* BigMAC receive complete service routines. */static inline void bigmac_rx(struct bigmac *bp){ struct be_rxd *rxbase = &bp->bmac_block->be_rxd[0]; struct be_rxd *this; int elem = bp->rx_new, drops = 0; this = &rxbase[elem]; while(!(this->rx_flags & RXD_OWN)) { struct sk_buff *skb; unsigned int flags = this->rx_flags; int len = (flags & RXD_LENGTH); /* FCS not included */ /* Check for errors. */ if(len < ETH_ZLEN) { bp->enet_stats.rx_errors++; bp->enet_stats.rx_length_errors++; drop_it: /* Return it to the BigMAC. */ bp->enet_stats.rx_dropped++; this->rx_addr = sbus_dvma_addr(bp->rx_skbs[elem]->data); this->rx_flags = (RXD_OWN | (RX_BUF_ALLOC_SIZE & RXD_LENGTH)); goto next; } skb = bp->rx_skbs[elem];#ifdef NEED_DMA_SYNCHRONIZATION#ifdef __sparc_v9__ if ((unsigned long) (skb->data + skb->len) >= MAX_DMA_ADDRESS) { printk("sunbmac: Bogus DMA buffer address " "[%016lx]\n", ((unsigned long) skb->data)); panic("DMA address too large, tell DaveM"); }#endif mmu_sync_dma(sbus_dvma_addr(skb->data), skb->len, bp->bigmac_sbus_dev->my_bus);#endif if(len > RX_COPY_THRESHOLD) { struct sk_buff *new_skb; /* Now refill the entry, if we can. */ new_skb = big_mac_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC); if(!new_skb) { drops++; goto drop_it; } bp->rx_skbs[elem] = new_skb; new_skb->dev = bp->dev; skb_put(new_skb, ETH_FRAME_LEN); skb_reserve(new_skb, 34); rxbase[elem].rx_addr = sbus_dvma_addr(new_skb->data); rxbase[elem].rx_flags = (RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH)); /* Trim the original skb for the netif. */ skb_trim(skb, len); } else { struct sk_buff *copy_skb = dev_alloc_skb(len + 2); if(!copy_skb) { drops++; goto drop_it; } copy_skb->dev = bp->dev; skb_reserve(copy_skb, 2); skb_put(copy_skb, len); eth_copy_and_sum(copy_skb, (unsigned char *)skb->data, len, 0); /* Reuse otiginal ring buffer. */ rxbase[elem].rx_addr = sbus_dvma_addr(skb->data); rxbase[elem].rx_flags = (RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH)); skb = copy_skb; } /* No checksums done by the BigMAC ;-( */ skb->protocol = eth_type_trans(skb, bp->dev); netif_rx(skb); bp->enet_stats.rx_packets++; next: elem = NEXT_RX(elem); this = &rxbase[elem]; } bp->rx_new = elem; if(drops) printk("%s: Memory squeeze, deferring packet.\n", bp->dev->name);}#ifndef __sparc_v9__static inline void sun4c_bigmac_rx(struct bigmac *bp){ struct be_rxd *rxbase = &bp->bmac_block->be_rxd[0]; struct be_rxd *this; struct bigmac_buffers *bbufs = bp->sun4c_buffers; __u32 bbufs_dvma = bp->s4c_buf_dvma; int elem = bp->rx_new, drops = 0; this = &rxbase[elem]; while(!(this->rx_flags & RXD_OWN)) { struct sk_buff *skb; unsigned char *this_bbuf = bbufs->rx_buf[elem & (SUN4C_RX_RING_SIZE - 1)]; __u32 this_bbuf_dvma = bbufs_dvma + bbuf_offset(rx_buf, (elem & (SUN4C_RX_RING_SIZE - 1))); struct be_rxd *end_rxd = &rxbase[(elem+SUN4C_RX_RING_SIZE)&(RX_RING_SIZE-1)]; unsigned int flags = this->rx_flags; int len = (flags & RXD_LENGTH) - 4; /* FCS not included */ /* Check for errors. */ if(len < ETH_ZLEN) { bp->enet_stats.rx_errors++; bp->enet_stats.rx_length_errors++; bp->enet_stats.rx_dropped++; } else { skb = dev_alloc_skb(len + 2); if(skb == 0) { drops++; bp->enet_stats.rx_dropped++; } else { skb->dev = bp->dev; skb_reserve(skb, 2); skb_put(skb, len); eth_copy_and_sum(skb, (unsigned char *)this_bbuf, len, 0); skb->protocol = eth_type_trans(skb, bp->dev); netif_rx(skb); bp->enet_stats.rx_packets++; } } end_rxd->rx_addr = this_bbuf_dvma; end_rxd->rx_flags = (RXD_OWN | (SUN4C_RX_BUFF_SIZE & RXD_LENGTH)); elem = NEXT_RX(elem); this = &rxbase[elem]; } bp->rx_new = elem; if(drops) printk("%s: Memory squeeze, deferring packet.\n", bp->dev->name);}#endifstatic void bigmac_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct bigmac *bp = (struct bigmac *) dev_id; unsigned int qec_status, bmac_status; DIRQ(("bigmac_interrupt: ")); /* Latch status registers now. */ bmac_status = bp->creg->stat; qec_status = bp->gregs->stat; bp->dev->interrupt = 1; DIRQ(("qec_status=%08x bmac_status=%08x\n", qec_status, bmac_status)); if((qec_status & (GLOB_STAT_ER | GLOB_STAT_BM)) || (bmac_status & CREG_STAT_ERRORS)) bigmac_is_medium_rare(bp, qec_status, bmac_status); if(bmac_status & CREG_STAT_TXIRQ) bigmac_tx(bp); if(bmac_status & CREG_STAT_RXIRQ) bigmac_rx(bp); if(bp->dev->tbusy && (TX_BUFFS_AVAIL(bp) >= 0)) { bp->dev->tbusy = 0; mark_bh(NET_BH); } bp->dev->interrupt = 0;}#ifndef __sparc_v9__static void sun4c_bigmac_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct bigmac *bp = (struct bigmac *) dev_id; unsigned int qec_status, bmac_status; /* Latch status registers now. */ bmac_status = bp->creg->stat; qec_status = bp->gregs->stat; bp->dev->interrupt = 1; if(qec_status & (GLOB_STAT_ER | GLOB_STAT_BM) || (bmac_status & CREG_STAT_ERRORS)) bigmac_is_medium_rare(bp, qec_status, bmac_status); if(bmac_status & CREG_STAT_TXIRQ) sun4c_bigmac_tx(bp); if(bmac_status & CREG_STAT_RXIRQ) sun4c_bigmac_rx(bp); if(bp->dev->tbusy && (SUN4C_TX_BUFFS_AVAIL(bp) >= 0)) { bp->dev->tbusy = 0; mark_bh(NET_BH); } bp->dev->interrupt = 0;}#endifstatic int bigmac_open(struct device *dev){ struct bigmac *bp = (struct bigmac *) dev->priv; int res;#ifndef __sparc_v9__ if(sparc_cpu_model == sun4c) { if(request_irq(dev->irq, &sun4c_bigmac_interrupt, SA_SHIRQ, "BIG MAC", (void *) bp)) { printk("BIGMAC: Can't order irq %d to go.\n", dev->irq); return -EAGAIN; } } else#endif if(request_irq(dev->irq, &bigmac_interrupt, SA_SHIRQ, "BIG MAC", (void *) bp)) { printk("BIGMAC: Can't order irq %d to go.\n", dev->irq); return -EAGAIN; } init_timer(&bp->bigmac_timer); res = bigmac_init(bp, 0); if(!res) { MOD_INC_USE_COUNT;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -