📄 fec.c
字号:
/* 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 (!(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 ((bdp->cbd_sc & BD_ENET_RX_LAST) == 0) printk("FEC ENET: rcv is not +last\n");#endif /* Check for errors. */ if (bdp->cbd_sc & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | BD_ENET_RX_CR | BD_ENET_RX_OV)) { fep->stats.rx_errors++; if (bdp->cbd_sc & (BD_ENET_RX_LG | BD_ENET_RX_SH)) { /* Frame too long or too short. */ fep->stats.rx_length_errors++; } if (bdp->cbd_sc & BD_ENET_RX_NO) /* Frame alignment */ fep->stats.rx_frame_errors++; if (bdp->cbd_sc & BD_ENET_RX_CR) /* CRC Error */ fep->stats.rx_crc_errors++; if (bdp->cbd_sc & BD_ENET_RX_OV) /* FIFO overrun */ fep->stats.rx_crc_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 (bdp->cbd_sc & BD_ENET_RX_CL) { fep->stats.rx_errors++; fep->stats.rx_frame_errors++; goto rx_processing_done; } /* Process the incoming frame. */ fep->stats.rx_packets++; pkt_len = bdp->cbd_datlen; fep->stats.rx_bytes += pkt_len; data = (__u8*)__va(bdp->cbd_bufaddr);#ifdef CONFIG_FEC_PACKETHOOK /* Packet hook ... */ if (fep->ph_rxhandler) { if (((struct ethhdr *)data)->h_proto == fep->ph_proto) { switch (fep->ph_rxhandler(data, pkt_len, regval, fep->ph_priv)) { case 1: goto rx_processing_done; break; case 0: break; default: fep->stats.rx_errors++; goto rx_processing_done; } } } /* If it wasn't filtered - copy it to an sk buffer. */#endif /* 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); fep->stats.rx_dropped++; } else { skb->dev = dev; skb_put(skb,pkt_len-4); /* Make room */ eth_copy_and_sum(skb, (unsigned char *)__va(bdp->cbd_bufaddr), pkt_len-4, 0); skb->protocol=eth_type_trans(skb,dev); netif_rx(skb); } rx_processing_done: /* Clear the status flags for this buffer. */ bdp->cbd_sc &= ~BD_ENET_RX_STATS; /* Mark the buffer empty. */ bdp->cbd_sc |= BD_ENET_RX_EMPTY; /* Update BD pointer to next entry. */ if (bdp->cbd_sc & 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 = 0x01000000;#endif#ifdef CONFIG_FEC_PACKETHOOK /* Re-read register. Not exactly guaranteed to be correct, but... */ if (fep->ph_regaddr) regval = *fep->ph_regaddr;#endif } /* while (!(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 = 0x01000000;#endif}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 = (struct fec_enet_private *)dev->priv; ep = &(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec); mii_reg = ep->fec_mii_data; if ((mip = mii_head) == NULL) { printk("MII and no head!\n"); return; } 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;}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 = dev->priv; regval |= fep->phy_addr << 23; retval = 0; save_flags(flags); cli(); 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; (&(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec))->fec_mii_data = regval; } } else { retval = 1; } restore_flags(flags); return(retval);}static void mii_do_cmd(struct net_device *dev, const phy_cmd_t *c){ int k; if(!c) return; for(k = 0; (c+k)->mii_data != mk_mii_end; k++) mii_queue(dev, (c+k)->mii_data, (c+k)->funct);}static void mii_parse_sr(uint mii_reg, struct net_device *dev){ struct fec_enet_private *fep = dev->priv; volatile uint *s = &(fep->phy_status); *s &= ~(PHY_STAT_LINK | PHY_STAT_FAULT | PHY_STAT_ANC); if (mii_reg & 0x0004) *s |= PHY_STAT_LINK; if (mii_reg & 0x0010) *s |= PHY_STAT_FAULT; if (mii_reg & 0x0020) *s |= PHY_STAT_ANC;}static void mii_parse_cr(uint mii_reg, struct net_device *dev){ struct fec_enet_private *fep = dev->priv; volatile uint *s = &(fep->phy_status); *s &= ~(PHY_CONF_ANE | PHY_CONF_LOOP); if (mii_reg & 0x1000) *s |= PHY_CONF_ANE; if (mii_reg & 0x4000) *s |= PHY_CONF_LOOP;}static void mii_parse_anar(uint mii_reg, struct net_device *dev){ struct fec_enet_private *fep = dev->priv; volatile uint *s = &(fep->phy_status); *s &= ~(PHY_CONF_SPMASK); if (mii_reg & 0x0020) *s |= PHY_CONF_10HDX; if (mii_reg & 0x0040) *s |= PHY_CONF_10FDX; if (mii_reg & 0x0080) *s |= PHY_CONF_100HDX; if (mii_reg & 0x00100) *s |= PHY_CONF_100FDX;}#if 0static void mii_disp_reg(uint mii_reg, struct net_device *dev){ printk("reg %u = 0x%04x\n", (mii_reg >> 18) & 0x1f, mii_reg & 0xffff);}#endif/* ------------------------------------------------------------------------- *//* The Level one LXT970 is used by many boards */#ifdef CONFIG_FEC_LXT970#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 = dev->priv; volatile uint *s = &(fep->phy_status); *s &= ~(PHY_STAT_SPMASK); if (mii_reg & 0x0800) { if (mii_reg & 0x1000) *s |= PHY_STAT_100FDX; else *s |= PHY_STAT_100HDX; } else { if (mii_reg & 0x1000) *s |= PHY_STAT_10FDX; else *s |= PHY_STAT_10HDX; }}static phy_info_t phy_info_lxt970 = { 0x07810000, "LXT970", (const phy_cmd_t []) { /* config */#if 0// { mk_mii_write(MII_REG_ANAR, 0x0021), NULL }, /* Set default operation of 100-TX....for some reason * some of these bits are set on power up, which is wrong. */ { mk_mii_write(MII_LXT970_CONFIG, 0), NULL },#endif { mk_mii_read(MII_REG_CR), mii_parse_cr }, { mk_mii_read(MII_REG_ANAR), mii_parse_anar }, { mk_mii_end, } }, (const phy_cmd_t []) { /* startup - enable interrupts */ { mk_mii_write(MII_LXT970_IER, 0x0002), NULL }, { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */ { mk_mii_end, } }, (const phy_cmd_t []) { /* 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, } }, (const phy_cmd_t []) { /* shutdown - disable interrupts */ { mk_mii_write(MII_LXT970_IER, 0x0000), NULL }, { mk_mii_end, } },}; #endif /* CONFIG_FEC_LXT970 *//* ------------------------------------------------------------------------- *//* The Level one LXT971 is used on some of my custom boards */#ifdef CONFIG_FEC_LXT971/* 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 * wierd, 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 = dev->priv; volatile uint *s = &(fep->phy_status); *s &= ~(PHY_STAT_SPMASK); if (mii_reg & 0x4000) { if (mii_reg & 0x0200) *s |= PHY_STAT_100FDX; else *s |= PHY_STAT_100HDX; } else { if (mii_reg & 0x0200) *s |= PHY_STAT_10FDX; else *s |= PHY_STAT_10HDX; } if (mii_reg & 0x0008) *s |= PHY_STAT_FAULT;}static phy_info_t phy_info_lxt971 = { 0x0001378e, "LXT971", (const phy_cmd_t []) { /* config */ /* limit to 10MBit because my protorype board * doesn't work with 100. */ { mk_mii_write(MII_REG_ANAR, 0x061), NULL }, /* 10 MBit */ { mk_mii_read(MII_REG_CR), mii_parse_cr }, { mk_mii_read(MII_REG_ANAR), mii_parse_anar }, { mk_mii_end, } }, (const phy_cmd_t []) { /* startup - enable interrupts */ { mk_mii_write(MII_LXT971_IER, 0x00f2), NULL }, { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */ /* 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, } }, (const phy_cmd_t []) { /* ack_int */ /* find out the current status */ { mk_mii_read(MII_REG_SR), mii_parse_sr }, { mk_mii_read(MII_LXT971_SR2), mii_parse_lxt971_sr2 }, /* we only need to read ISR to acknowledge */ { mk_mii_read(MII_LXT971_ISR), NULL }, { mk_mii_end, } }, (const phy_cmd_t []) { /* shutdown - disable interrupts */ { mk_mii_write(MII_LXT971_IER, 0x0000), NULL }, { mk_mii_end, } },};#endif /* CONFIG_FEC_LXT970 *//* ------------------------------------------------------------------------- *//* The Quality Semiconductor QS6612 is used on the RPX CLLF */#ifdef CONFIG_FEC_QS6612/* 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 = dev->priv; volatile uint *s = &(fep->phy_status); *s &= ~(PHY_STAT_SPMASK); switch((mii_reg >> 2) & 7) { case 1: *s |= PHY_STAT_10HDX; break; case 2: *s |= PHY_STAT_100HDX; break; case 5: *s |= PHY_STAT_10FDX; break; case 6: *s |= PHY_STAT_100FDX; break; }}static phy_info_t phy_info_qs6612 = { 0x00181440, "QS6612", (const phy_cmd_t []) { /* config */ // { mk_mii_write(MII_REG_ANAR, 0x061), NULL }, /* 10 MBit */ /* The PHY powers up isolated on the RPX, * so send a command to allow operation. */ { mk_mii_write(MII_QS6612_PCR, 0x0dc0), NULL }, /* parse cr and anar to get some info */ { mk_mii_read(MII_REG_CR), mii_parse_cr }, { mk_mii_read(MII_REG_ANAR), mii_parse_anar }, { mk_mii_end, } }, (const phy_cmd_t []) { /* startup - enable interrupts */ { mk_mii_write(MII_QS6612_IMR, 0x003a), NULL }, { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */ { mk_mii_end, } }, (const phy_cmd_t []) { /* ack_int */ /* we need to read ISR, SR and ANER to acknowledge */ { mk_mii_read(MII_QS6612_ISR), NULL }, { mk_mii_read(MII_REG_SR), mii_parse_sr }, { mk_mii_read(MII_REG_ANER), NULL }, /* read pcr to get info */ { mk_mii_read(MII_QS6612_PCR), mii_parse_qs6612_pcr }, { mk_mii_end, } }, (const phy_cmd_t []) { /* shutdown - disable interrupts */ { mk_mii_write(MII_QS6612_IMR, 0x0000), NULL }, { mk_mii_end, } },};#endif /* CONFIG_FEC_QS6612 */static phy_info_t *phy_info[] = {#ifdef CONFIG_FEC_LXT970 &phy_info_lxt970,#endif /* CONFIG_FEC_LXT970 */#ifdef CONFIG_FEC_LXT971 &phy_info_lxt971,#endif /* CONFIG_FEC_LXT971 */#ifdef CONFIG_FEC_QS6612 &phy_info_qs6612,#endif /* CONFIG_FEC_LXT971 */ NULL};static void mii_display_status(struct net_device *dev){ struct fec_enet_private *fep = dev->priv; volatile uint *s = &(fep->phy_status); if (!fep->link && !fep->old_link) { /* Link is still down - don't print anything */ return; } printk("%s: status: ", dev->name); if (!fep->link) { printk("link down"); } else { printk("link up"); switch(*s & PHY_STAT_SPMASK) { case PHY_STAT_100FDX: printk(", 100MBit Full Duplex"); break; case PHY_STAT_100HDX: printk(", 100MBit Half Duplex"); break; case PHY_STAT_10FDX: printk(", 10MBit Full Duplex"); break; case PHY_STAT_10HDX: printk(", 10MBit Half Duplex"); break; default: printk(", Unknown speed/duplex"); } if (*s & PHY_STAT_ANC) printk(", auto-negotiation complete"); } if (*s & PHY_STAT_FAULT) printk(", remote fault"); printk(".\n");}static void mii_display_config(struct net_device *dev){ struct fec_enet_private *fep = dev->priv; volatile uint *s = &(fep->phy_status); printk("%s: config: auto-negotiation ", dev->name); if (*s & PHY_CONF_ANE) printk("on"); else printk("off"); if (*s & PHY_CONF_100FDX) printk(", 100FDX"); if (*s & PHY_CONF_100HDX) printk(", 100HDX"); if (*s & PHY_CONF_10FDX) printk(", 10FDX"); if (*s & PHY_CONF_10HDX) printk(", 10HDX"); if (!(*s & PHY_CONF_SPMASK)) printk(", No speed/duplex selected?"); if (*s & PHY_CONF_LOOP) printk(", loopback enabled"); printk(".\n"); fep->sequence_done = 1;}static void mii_relink(struct net_device *dev){ struct fec_enet_private *fep = dev->priv; int duplex; fep->link = (fep->phy_status & PHY_STAT_LINK) ? 1 : 0; mii_display_status(dev); fep->old_link = fep->link; if (fep->link) { duplex = 0; if (fep->phy_status & (PHY_STAT_100FDX | PHY_STAT_10FDX)) duplex = 1; fec_restart(dev, duplex);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -