fec.c
来自「linux 内核源代码」· C语言 代码 · 共 2,492 行 · 第 1/5 页
C
2,492 行
fep = netdev_priv(dev); spin_lock(&fep->lock); bdp = fep->dirty_tx; while (((status = bdp->cbd_sc) & BD_ENET_TX_READY) == 0) { if (bdp == fep->cur_tx && fep->tx_full == 0) break; skb = fep->tx_skbuff[fep->skb_dirty]; /* Check for errors. */ if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC | BD_ENET_TX_RL | BD_ENET_TX_UN | BD_ENET_TX_CSL)) { dev->stats.tx_errors++; if (status & BD_ENET_TX_HB) /* No heartbeat */ dev->stats.tx_heartbeat_errors++; if (status & BD_ENET_TX_LC) /* Late collision */ dev->stats.tx_window_errors++; if (status & BD_ENET_TX_RL) /* Retrans limit */ dev->stats.tx_aborted_errors++; if (status & BD_ENET_TX_UN) /* Underrun */ dev->stats.tx_fifo_errors++; if (status & BD_ENET_TX_CSL) /* Carrier lost */ dev->stats.tx_carrier_errors++; } else { dev->stats.tx_packets++; }#ifndef final_version if (status & BD_ENET_TX_READY) printk("HEY! Enet xmit interrupt and TX_READY.\n");#endif /* Deferred means some collisions occurred during transmit, * but we eventually sent the packet OK. */ if (status & BD_ENET_TX_DEF) dev->stats.collisions++; /* Free the sk buffer associated with this last transmit. */ dev_kfree_skb_any(skb); fep->tx_skbuff[fep->skb_dirty] = NULL; fep->skb_dirty = (fep->skb_dirty + 1) & TX_RING_MOD_MASK; /* Update pointer to next buffer descriptor to be transmitted. */ if (status & BD_ENET_TX_WRAP) bdp = fep->tx_bd_base; else bdp++; /* Since we have freed up a buffer, the ring is no longer * full. */ if (fep->tx_full) { fep->tx_full = 0; if (netif_queue_stopped(dev)) netif_wake_queue(dev); } } fep->dirty_tx = (cbd_t *)bdp; spin_unlock(&fep->lock);}/* During a receive, the cur_rx points to the current incoming buffer. * When we update through the ring, if the next incoming buffer has * not been given to the system, we just set the empty indicator, * effectively tossing the packet. */static voidfec_enet_rx(struct net_device *dev){ struct fec_enet_private *fep; volatile fec_t *fecp; volatile cbd_t *bdp; unsigned short status; struct sk_buff *skb; ushort pkt_len; __u8 *data;#ifdef CONFIG_M532x flush_cache_all();#endif fep = netdev_priv(dev); fecp = (volatile fec_t*)dev->base_addr; /* First, grab all of the stats for the incoming packet. * These get messed up if we get called due to a busy condition. */ bdp = fep->cur_rx;while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) {#ifndef final_version /* Since we have allocated space to hold a complete frame, * the last indicator should be set. */ if ((status & BD_ENET_RX_LAST) == 0) printk("FEC ENET: rcv is not +last\n");#endif if (!fep->opened) goto rx_processing_done; /* Check for errors. */ if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | BD_ENET_RX_CR | BD_ENET_RX_OV)) { dev->stats.rx_errors++; if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH)) { /* Frame too long or too short. */ dev->stats.rx_length_errors++; } if (status & BD_ENET_RX_NO) /* Frame alignment */ dev->stats.rx_frame_errors++; if (status & BD_ENET_RX_CR) /* CRC Error */ dev->stats.rx_crc_errors++; if (status & BD_ENET_RX_OV) /* FIFO overrun */ dev->stats.rx_fifo_errors++; } /* Report late collisions as a frame error. * On this error, the BD is closed, but we don't know what we * have in the buffer. So, just drop this frame on the floor. */ if (status & BD_ENET_RX_CL) { dev->stats.rx_errors++; dev->stats.rx_frame_errors++; goto rx_processing_done; } /* Process the incoming frame. */ dev->stats.rx_packets++; pkt_len = bdp->cbd_datlen; dev->stats.rx_bytes += pkt_len; data = (__u8*)__va(bdp->cbd_bufaddr); /* This does 16 byte alignment, exactly what we need. * The packet length includes FCS, but we don't want to * include that when passing upstream as it messes up * bridging applications. */ skb = dev_alloc_skb(pkt_len-4); if (skb == NULL) { printk("%s: Memory squeeze, dropping packet.\n", dev->name); dev->stats.rx_dropped++; } else { skb_put(skb,pkt_len-4); /* Make room */ skb_copy_to_linear_data(skb, data, pkt_len-4); skb->protocol=eth_type_trans(skb,dev); netif_rx(skb); } rx_processing_done: /* Clear the status flags for this buffer. */ status &= ~BD_ENET_RX_STATS; /* Mark the buffer empty. */ status |= BD_ENET_RX_EMPTY; bdp->cbd_sc = status; /* Update BD pointer to next entry. */ if (status & BD_ENET_RX_WRAP) bdp = fep->rx_bd_base; else bdp++;#if 1 /* Doing this here will keep the FEC running while we process * incoming frames. On a heavily loaded network, we should be * able to keep up at the expense of system resources. */ fecp->fec_r_des_active = 0;#endif } /* while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) */ fep->cur_rx = (cbd_t *)bdp;#if 0 /* Doing this here will allow us to process all frames in the * ring before the FEC is allowed to put more there. On a heavily * loaded network, some frames may be lost. Unfortunately, this * increases the interrupt overhead since we can potentially work * our way back to the interrupt return only to come right back * here. */ fecp->fec_r_des_active = 0;#endif}/* called from interrupt context */static voidfec_enet_mii(struct net_device *dev){ struct fec_enet_private *fep; volatile fec_t *ep; mii_list_t *mip; uint mii_reg; fep = netdev_priv(dev); ep = fep->hwp; mii_reg = ep->fec_mii_data; spin_lock(&fep->lock); if ((mip = mii_head) == NULL) { printk("MII and no head!\n"); goto unlock; } if (mip->mii_func != NULL) (*(mip->mii_func))(mii_reg, dev); mii_head = mip->mii_next; mip->mii_next = mii_free; mii_free = mip; if ((mip = mii_head) != NULL) ep->fec_mii_data = mip->mii_regval;unlock: spin_unlock(&fep->lock);}static intmii_queue(struct net_device *dev, int regval, void (*func)(uint, struct net_device *)){ struct fec_enet_private *fep; unsigned long flags; mii_list_t *mip; int retval; /* Add PHY address to register command. */ fep = netdev_priv(dev); regval |= fep->phy_addr << 23; retval = 0; spin_lock_irqsave(&fep->lock,flags); if ((mip = mii_free) != NULL) { mii_free = mip->mii_next; mip->mii_regval = regval; mip->mii_func = func; mip->mii_next = NULL; if (mii_head) { mii_tail->mii_next = mip; mii_tail = mip; } else { mii_head = mii_tail = mip; fep->hwp->fec_mii_data = regval; } } else { retval = 1; } spin_unlock_irqrestore(&fep->lock,flags); return(retval);}static void mii_do_cmd(struct net_device *dev, const phy_cmd_t *c){ if(!c) return; for (; c->mii_data != mk_mii_end; c++) mii_queue(dev, c->mii_data, c->funct);}static void mii_parse_sr(uint mii_reg, struct net_device *dev){ struct fec_enet_private *fep = netdev_priv(dev); volatile uint *s = &(fep->phy_status); uint status; status = *s & ~(PHY_STAT_LINK | PHY_STAT_FAULT | PHY_STAT_ANC); if (mii_reg & 0x0004) status |= PHY_STAT_LINK; if (mii_reg & 0x0010) status |= PHY_STAT_FAULT; if (mii_reg & 0x0020) status |= PHY_STAT_ANC; *s = status;}static void mii_parse_cr(uint mii_reg, struct net_device *dev){ struct fec_enet_private *fep = netdev_priv(dev); volatile uint *s = &(fep->phy_status); uint status; status = *s & ~(PHY_CONF_ANE | PHY_CONF_LOOP); if (mii_reg & 0x1000) status |= PHY_CONF_ANE; if (mii_reg & 0x4000) status |= PHY_CONF_LOOP; *s = status;}static void mii_parse_anar(uint mii_reg, struct net_device *dev){ struct fec_enet_private *fep = netdev_priv(dev); volatile uint *s = &(fep->phy_status); uint status; status = *s & ~(PHY_CONF_SPMASK); if (mii_reg & 0x0020) status |= PHY_CONF_10HDX; if (mii_reg & 0x0040) status |= PHY_CONF_10FDX; if (mii_reg & 0x0080) status |= PHY_CONF_100HDX; if (mii_reg & 0x00100) status |= PHY_CONF_100FDX; *s = status;}/* ------------------------------------------------------------------------- *//* The Level one LXT970 is used by many boards */#define MII_LXT970_MIRROR 16 /* Mirror register */#define MII_LXT970_IER 17 /* Interrupt Enable Register */#define MII_LXT970_ISR 18 /* Interrupt Status Register */#define MII_LXT970_CONFIG 19 /* Configuration Register */#define MII_LXT970_CSR 20 /* Chip Status Register */static void mii_parse_lxt970_csr(uint mii_reg, struct net_device *dev){ struct fec_enet_private *fep = netdev_priv(dev); volatile uint *s = &(fep->phy_status); uint status; status = *s & ~(PHY_STAT_SPMASK); if (mii_reg & 0x0800) { if (mii_reg & 0x1000) status |= PHY_STAT_100FDX; else status |= PHY_STAT_100HDX; } else { if (mii_reg & 0x1000) status |= PHY_STAT_10FDX; else status |= PHY_STAT_10HDX; } *s = status;}static phy_cmd_t const phy_cmd_lxt970_config[] = { { mk_mii_read(MII_REG_CR), mii_parse_cr }, { mk_mii_read(MII_REG_ANAR), mii_parse_anar }, { mk_mii_end, } };static phy_cmd_t const phy_cmd_lxt970_startup[] = { /* enable interrupts */ { mk_mii_write(MII_LXT970_IER, 0x0002), NULL }, { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */ { mk_mii_end, } };static phy_cmd_t const phy_cmd_lxt970_ack_int[] = { /* read SR and ISR to acknowledge */ { mk_mii_read(MII_REG_SR), mii_parse_sr }, { mk_mii_read(MII_LXT970_ISR), NULL }, /* find out the current status */ { mk_mii_read(MII_LXT970_CSR), mii_parse_lxt970_csr }, { mk_mii_end, } };static phy_cmd_t const phy_cmd_lxt970_shutdown[] = { /* disable interrupts */ { mk_mii_write(MII_LXT970_IER, 0x0000), NULL }, { mk_mii_end, } };static phy_info_t const phy_info_lxt970 = { .id = 0x07810000, .name = "LXT970", .config = phy_cmd_lxt970_config, .startup = phy_cmd_lxt970_startup, .ack_int = phy_cmd_lxt970_ack_int, .shutdown = phy_cmd_lxt970_shutdown};/* ------------------------------------------------------------------------- *//* The Level one LXT971 is used on some of my custom boards *//* register definitions for the 971 */#define MII_LXT971_PCR 16 /* Port Control Register */#define MII_LXT971_SR2 17 /* Status Register 2 */#define MII_LXT971_IER 18 /* Interrupt Enable Register */#define MII_LXT971_ISR 19 /* Interrupt Status Register */#define MII_LXT971_LCR 20 /* LED Control Register */#define MII_LXT971_TCR 30 /* Transmit Control Register *//* * I had some nice ideas of running the MDIO faster... * The 971 should support 8MHz and I tried it, but things acted really * weird, so 2.5 MHz ought to be enough for anyone... */static void mii_parse_lxt971_sr2(uint mii_reg, struct net_device *dev){ struct fec_enet_private *fep = netdev_priv(dev); volatile uint *s = &(fep->phy_status); uint status; status = *s & ~(PHY_STAT_SPMASK | PHY_STAT_LINK | PHY_STAT_ANC); if (mii_reg & 0x0400) { fep->link = 1; status |= PHY_STAT_LINK; } else { fep->link = 0; } if (mii_reg & 0x0080) status |= PHY_STAT_ANC; if (mii_reg & 0x4000) { if (mii_reg & 0x0200) status |= PHY_STAT_100FDX; else status |= PHY_STAT_100HDX; } else { if (mii_reg & 0x0200) status |= PHY_STAT_10FDX; else status |= PHY_STAT_10HDX; } if (mii_reg & 0x0008) status |= PHY_STAT_FAULT; *s = status;}static phy_cmd_t const phy_cmd_lxt971_config[] = { /* limit to 10MBit because my prototype board * doesn't work with 100. */ { mk_mii_read(MII_REG_CR), mii_parse_cr }, { mk_mii_read(MII_REG_ANAR), mii_parse_anar }, { mk_mii_read(MII_LXT971_SR2), mii_parse_lxt971_sr2 }, { mk_mii_end, } };static phy_cmd_t const phy_cmd_lxt971_startup[] = { /* enable interrupts */ { mk_mii_write(MII_LXT971_IER, 0x00f2), NULL }, { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */ { mk_mii_write(MII_LXT971_LCR, 0xd422), NULL }, /* LED config */ /* Somehow does the 971 tell me that the link is down * the first read after power-up. * read here to get a valid value in ack_int */ { mk_mii_read(MII_REG_SR), mii_parse_sr }, { mk_mii_end, } };static phy_cmd_t const phy_cmd_lxt971_ack_int[] = { /* acknowledge the int before reading status ! */ { mk_mii_read(MII_LXT971_ISR), NULL }, /* find out the current status */ { mk_mii_read(MII_REG_SR), mii_parse_sr }, { mk_mii_read(MII_LXT971_SR2), mii_parse_lxt971_sr2 }, { mk_mii_end, } };static phy_cmd_t const phy_cmd_lxt971_shutdown[] = { /* disable interrupts */ { mk_mii_write(MII_LXT971_IER, 0x0000), NULL }, { mk_mii_end, } };static phy_info_t const phy_info_lxt971 = { .id = 0x0001378e, .name = "LXT971", .config = phy_cmd_lxt971_config, .startup = phy_cmd_lxt971_startup, .ack_int = phy_cmd_lxt971_ack_int, .shutdown = phy_cmd_lxt971_shutdown};/* ------------------------------------------------------------------------- *//* The Quality Semiconductor QS6612 is used on the RPX CLLF *//* register definitions */#define MII_QS6612_MCR 17 /* Mode Control Register */#define MII_QS6612_FTR 27 /* Factory Test Register */#define MII_QS6612_MCO 28 /* Misc. Control Register */#define MII_QS6612_ISR 29 /* Interrupt Source Register */#define MII_QS6612_IMR 30 /* Interrupt Mask Register */#define MII_QS6612_PCR 31 /* 100BaseTx PHY Control Reg. */static void mii_parse_qs6612_pcr(uint mii_reg, struct net_device *dev){ struct fec_enet_private *fep = netdev_priv(dev); volatile uint *s = &(fep->phy_status); uint status; status = *s & ~(PHY_STAT_SPMASK);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?