📄 fcc_enet.c
字号:
* effectively tossing the packet. */static intfcc_enet_rx(struct net_device *dev){ struct fcc_enet_private *cep; volatile cbd_t *bdp; struct sk_buff *skb; ushort pkt_len; cep = (struct fcc_enet_private *)dev->priv; /* First, grab all of the stats for the incoming packet. * These get messed up if we get called due to a busy condition. */ bdp = cep->cur_rx;for (;;) { if (bdp->cbd_sc & BD_ENET_RX_EMPTY) break; #ifndef final_version /* Since we have allocated space to hold a complete frame, both * the first and last indicators should be set. */ if ((bdp->cbd_sc & (BD_ENET_RX_FIRST | BD_ENET_RX_LAST)) != (BD_ENET_RX_FIRST | BD_ENET_RX_LAST)) printk("CPM ENET: rcv is not first+last\n");#endif /* Frame too long or too short. */ if (bdp->cbd_sc & (BD_ENET_RX_LG | BD_ENET_RX_SH)) cep->stats.rx_length_errors++; if (bdp->cbd_sc & BD_ENET_RX_NO) /* Frame alignment */ cep->stats.rx_frame_errors++; if (bdp->cbd_sc & BD_ENET_RX_CR) /* CRC Error */ cep->stats.rx_crc_errors++; if (bdp->cbd_sc & BD_ENET_RX_OV) /* FIFO overrun */ cep->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) { cep->stats.rx_frame_errors++; } else { /* Process the incoming frame. */ cep->stats.rx_packets++; pkt_len = bdp->cbd_datlen; cep->stats.rx_bytes += pkt_len; /* This does 16 byte alignment, much more than 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); cep->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); } } /* 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 = cep->rx_bd_base; else bdp++; } cep->cur_rx = (cbd_t *)bdp; return 0;}static intfcc_enet_close(struct net_device *dev){ /* Don't know what to do yet. */ netif_stop_queue(dev); return 0;}static struct net_device_stats *fcc_enet_get_stats(struct net_device *dev){ struct fcc_enet_private *cep = (struct fcc_enet_private *)dev->priv; return &cep->stats;}/* The MII is simulated from the 8xx FEC implementation. The FCC * is not responsible for the MII control/status interface. */static voidfcc_enet_mii(struct net_device *dev){ struct fcc_enet_private *fep; mii_list_t *mip; uint mii_reg; fep = (struct fcc_enet_private *)dev->priv;#if 0 ep = &(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec); mii_reg = ep->fec_mii_data;#endif 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 0 if ((mip = mii_head) != NULL) ep->fec_mii_data = mip->mii_regval;#endif}static intmii_queue(int regval, void (*func)(uint, struct net_device *)){ unsigned long flags; mii_list_t *mip; int retval; 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;#if 0 (&(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec))->fec_mii_data = regval;#endif } } else { retval = 1; } restore_flags(flags); return(retval);}static volatile uint full_duplex;static voidmii_status(uint mii_reg, struct net_device *dev){ volatile uint prev_duplex; if (((mii_reg >> 18) & 0x1f) == 1) { /* status register. */ printk("fec: "); if (mii_reg & 0x0004) printk("link up"); else printk("link down"); if (mii_reg & 0x0010) printk(",remote fault"); if (mii_reg & 0x0020) printk(",auto complete"); printk("\n"); } if (((mii_reg >> 18) & 0x1f) == 0x14) { /* Extended chip status register. */ prev_duplex = full_duplex; printk("fec: "); if (mii_reg & 0x0800) printk("100 Mbps"); else printk("10 Mbps"); if (mii_reg & 0x1000) { printk(", Full-Duplex\n"); full_duplex = 1; } else { printk(", Half-Duplex\n"); full_duplex = 0; }#if 0 if (prev_duplex != full_duplex) restart_fec(dev);#endif } if (((mii_reg >> 18) & 0x1f) == 31) { /* QS6612 PHY Control/Status. * OK, now we have it all, so figure out what is going on. */ prev_duplex = full_duplex; printk("fec: "); mii_reg = (mii_reg >> 2) & 7; if (mii_reg & 1) printk("10 Mbps"); else printk("100 Mbps"); if (mii_reg > 4) { printk(", Full-Duplex\n"); full_duplex = 1; } else { printk(", Half-Duplex\n"); full_duplex = 0; }#if 0 if (prev_duplex != full_duplex) restart_fec(dev);#endif }}static uint phyno;static voidmii_discover_phy3(uint mii_reg, struct net_device *dev){ phytype <<= 16; phytype |= (mii_reg & 0xffff); printk("fec: Phy @ 0x%x, type 0x%08x\n", phyno, phytype); mii_startup_cmds();}static voidmii_discover_phy(uint mii_reg, struct net_device *dev){ if (phyno < 32) { if ((phytype = (mii_reg & 0xffff)) != 0xffff) { phyaddr = phyno; mii_queue(mk_mii_read(3), mii_discover_phy3); } else { phyno++; mii_queue(mk_mii_phyaddr(phyno), mii_discover_phy); } } else { printk("FEC: No PHY device found.\n"); }}static voidmii_discover_phy_poll(fcc_info_t *fip){ uint rv; int i; for (i=0; i<32; i++) { rv = mii_send_receive(fip, mk_mii_phyaddr(i)); if ((phytype = (rv & 0xffff)) != 0xffff) { phyaddr = i; rv = mii_send_receive(fip, mk_mii_read(3)); phytype <<= 16; phytype |= (rv & 0xffff); printk("fec: Phy @ 0x%x, type 0x%08x\n", phyaddr, phytype); } }}static voidmii_startup_cmds(void){#if 1 /* Level One PHY. */ /* Read status registers to clear any pending interrupt. */ mii_queue(mk_mii_read(1), mii_status); mii_queue(mk_mii_read(18), mii_status); /* Read extended chip status register. */ mii_queue(mk_mii_read(0x14), mii_status); /* Set default operation of 100-TX....for some reason * some of these bits are set on power up, which is wrong. */ mii_queue(mk_mii_write(0x13, 0), NULL); /* Enable Link status change interrupts. */ mii_queue(mk_mii_write(0x11, 0x0002), NULL); /* Don't advertize Full duplex. mii_queue(mk_mii_write(0x04, 0x0021), NULL); */#endif}/* This supports the mii_link interrupt below. * We should get called three times. Once for register 1, once for * register 18, and once for register 20. */static uint mii_saved_reg1;static voidmii_relink(uint mii_reg, struct net_device *dev){ volatile uint prev_duplex; unsigned long flags; if (((mii_reg >> 18) & 0x1f) == 1) { /* Just save the status register and get out. */ mii_saved_reg1 = mii_reg; return; } if (((mii_reg >> 18) & 0x1f) == 18) { /* Not much here, but has to be read to clear the * interrupt condition. */ if ((mii_reg & 0x8000) == 0) printk("fec: re-link and no IRQ?\n"); if ((mii_reg & 0x4000) == 0) printk("fec: no PHY power?\n"); } if (((mii_reg >> 18) & 0x1f) == 20) { /* Extended chip status register. * OK, now we have it all, so figure out what is going on. */ prev_duplex = full_duplex; printk("fec: "); if (mii_saved_reg1 & 0x0004) printk("link up"); else printk("link down"); if (mii_saved_reg1 & 0x0010) printk(", remote fault"); if (mii_saved_reg1 & 0x0020) printk(", auto complete"); if (mii_reg & 0x0800) printk(", 100 Mbps"); else printk(", 10 Mbps"); if (mii_reg & 0x1000) { printk(", Full-Duplex\n"); full_duplex = 1; } else { printk(", Half-Duplex\n"); full_duplex = 0; } if (prev_duplex != full_duplex) { save_flags(flags); cli();#if 0 restart_fec(dev);#endif restore_flags(flags); } } if (((mii_reg >> 18) & 0x1f) == 31) { /* QS6612 PHY Control/Status. * OK, now we have it all, so figure out what is going on. */ prev_duplex = full_duplex; printk("fec: "); if (mii_saved_reg1 & 0x0004) printk("link up"); else printk("link down"); if (mii_saved_reg1 & 0x0010) printk(", remote fault"); if (mii_saved_reg1 & 0x0020) printk(", auto complete"); mii_reg = (mii_reg >> 2) & 7; if (mii_reg & 1) printk(", 10 Mbps"); else printk(", 100 Mbps"); if (mii_reg > 4) { printk(", Full-Duplex\n"); full_duplex = 1; } else { printk(", Half-Duplex\n"); full_duplex = 0; }#if 0 if (prev_duplex != full_duplex) { save_flags(flags); cli(); restart_fec(dev); restore_flags(flags); }#endif }}/* Set or clear the multicast filter for this adaptor. * Skeleton taken from sunlance driver. * The CPM Ethernet implementation allows Multicast as well as individual * MAC address filtering. Some of the drivers check to make sure it is * a group multicast address, and discard those that are not. I guess I * will do the same for now, but just remove the test if you want * individual filtering as well (do the upper net layers want or support * this kind of feature?). */static voidset_multicast_list(struct net_device *dev){ struct fcc_enet_private *cep; struct dev_mc_list *dmi; u_char *mcptr, *tdptr; volatile fcc_enet_t *ep; int i, j; cep = (struct fcc_enet_private *)dev->priv;return; /* Get pointer to FCC area in parameter RAM. */ ep = (fcc_enet_t *)dev->base_addr; if (dev->flags&IFF_PROMISC) { /* Log any net taps. */ printk("%s: Promiscuous mode enabled.\n", dev->name); cep->fccp->fcc_fpsmr |= FCC_PSMR_PRO; } else { cep->fccp->fcc_fpsmr &= ~FCC_PSMR_PRO; if (dev->flags & IFF_ALLMULTI) { /* Catch all multicast addresses, so set the * filter to all 1's. */ ep->fen_gaddrh = 0xffffffff; ep->fen_gaddrl = 0xffffffff; } else { /* Clear filter and add the addresses in the list. */ ep->fen_gaddrh = 0; ep->fen_gaddrl = 0; dmi = dev->mc_list; for (i=0; i<dev->mc_count; i++) { /* Only support group multicast for now. */ if (!(dmi->dmi_addr[0] & 1)) continue; /* The address in dmi_addr is LSB first, * and taddr is MSB first. We have to * copy bytes MSB first from dmi_addr. */ mcptr = (u_char *)dmi->dmi_addr + 5; tdptr = (u_char *)&ep->fen_taddrh; for (j=0; j<6; j++) *tdptr++ = *mcptr--; /* Ask CPM to run CRC and set bit in * filter mask. */ cpmp->cp_cpcr = mk_cr_cmd(cep->fip->fc_cpmpage, cep->fip->fc_cpmblock, 0x0c, CPM_CR_SET_GADDR) | CPM_CR_FLG; udelay(10); while (cpmp->cp_cpcr & CPM_CR_FLG); } } }}/* Initialize the CPM Ethernet on FCC. */int __init fec_enet_init(void){ struct net_device *dev; struct fcc_enet_private *cep; fcc_info_t *fip; int i, np;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -