fec.c
来自「linux 内核源代码」· C语言 代码 · 共 2,492 行 · 第 1/5 页
C
2,492 行
struct fec_enet_private *fep = container_of(work, struct fec_enet_private, phy_task); struct net_device *dev = fep->netdev; int duplex; /* ** When we get here, phy_task is already removed from ** the workqueue. It is thus safe to allow to reuse it. */ fep->mii_phy_task_queued = 0; 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); } else fec_stop(dev);#if 0 enable_irq(fep->mii_irq);#endif}/* mii_queue_relink is called in interrupt context from mii_link_interrupt */static void mii_queue_relink(uint mii_reg, struct net_device *dev){ struct fec_enet_private *fep = netdev_priv(dev); /* ** We cannot queue phy_task twice in the workqueue. It ** would cause an endless loop in the workqueue. ** Fortunately, if the last mii_relink entry has not yet been ** executed now, it will do the job for the current interrupt, ** which is just what we want. */ if (fep->mii_phy_task_queued) return; fep->mii_phy_task_queued = 1; INIT_WORK(&fep->phy_task, mii_relink); schedule_work(&fep->phy_task);}/* mii_queue_config is called in interrupt context from fec_enet_mii */static void mii_queue_config(uint mii_reg, struct net_device *dev){ struct fec_enet_private *fep = netdev_priv(dev); if (fep->mii_phy_task_queued) return; fep->mii_phy_task_queued = 1; INIT_WORK(&fep->phy_task, mii_display_config); schedule_work(&fep->phy_task);}phy_cmd_t const phy_cmd_relink[] = { { mk_mii_read(MII_REG_CR), mii_queue_relink }, { mk_mii_end, } };phy_cmd_t const phy_cmd_config[] = { { mk_mii_read(MII_REG_CR), mii_queue_config }, { mk_mii_end, } };/* Read remainder of PHY ID.*/static voidmii_discover_phy3(uint mii_reg, struct net_device *dev){ struct fec_enet_private *fep; int i; fep = netdev_priv(dev); fep->phy_id |= (mii_reg & 0xffff); printk("fec: PHY @ 0x%x, ID 0x%08x", fep->phy_addr, fep->phy_id); for(i = 0; phy_info[i]; i++) { if(phy_info[i]->id == (fep->phy_id >> 4)) break; } if (phy_info[i]) printk(" -- %s\n", phy_info[i]->name); else printk(" -- unknown PHY!\n"); fep->phy = phy_info[i]; fep->phy_id_done = 1;}/* Scan all of the MII PHY addresses looking for someone to respond * with a valid ID. This usually happens quickly. */static voidmii_discover_phy(uint mii_reg, struct net_device *dev){ struct fec_enet_private *fep; volatile fec_t *fecp; uint phytype; fep = netdev_priv(dev); fecp = fep->hwp; if (fep->phy_addr < 32) { if ((phytype = (mii_reg & 0xffff)) != 0xffff && phytype != 0) { /* Got first part of ID, now get remainder. */ fep->phy_id = phytype << 16; mii_queue(dev, mk_mii_read(MII_REG_PHYIR2), mii_discover_phy3); } else { fep->phy_addr++; mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy); } } else { printk("FEC: No PHY device found.\n"); /* Disable external MII interface */ fecp->fec_mii_speed = fep->phy_speed = 0; fec_disable_phy_intr(); }}/* This interrupt occurs when the PHY detects a link change.*/#ifdef CONFIG_RPXCLASSICstatic voidmii_link_interrupt(void *dev_id)#elsestatic irqreturn_tmii_link_interrupt(int irq, void * dev_id)#endif{ struct net_device *dev = dev_id; struct fec_enet_private *fep = netdev_priv(dev); fec_phy_ack_intr();#if 0 disable_irq(fep->mii_irq); /* disable now, enable later */#endif mii_do_cmd(dev, fep->phy->ack_int); mii_do_cmd(dev, phy_cmd_relink); /* restart and display status */ return IRQ_HANDLED;}static intfec_enet_open(struct net_device *dev){ struct fec_enet_private *fep = netdev_priv(dev); /* I should reset the ring buffers here, but I don't yet know * a simple way to do that. */ fec_set_mac_address(dev); fep->sequence_done = 0; fep->link = 0; if (fep->phy) { mii_do_cmd(dev, fep->phy->ack_int); mii_do_cmd(dev, fep->phy->config); mii_do_cmd(dev, phy_cmd_config); /* display configuration */ /* Poll until the PHY tells us its configuration * (not link state). * Request is initiated by mii_do_cmd above, but answer * comes by interrupt. * This should take about 25 usec per register at 2.5 MHz, * and we read approximately 5 registers. */ while(!fep->sequence_done) schedule(); mii_do_cmd(dev, fep->phy->startup); /* Set the initial link state to true. A lot of hardware * based on this device does not implement a PHY interrupt, * so we are never notified of link change. */ fep->link = 1; } else { fep->link = 1; /* lets just try it and see */ /* no phy, go full duplex, it's most likely a hub chip */ fec_restart(dev, 1); } netif_start_queue(dev); fep->opened = 1; return 0; /* Success */}static intfec_enet_close(struct net_device *dev){ struct fec_enet_private *fep = netdev_priv(dev); /* Don't know what to do yet. */ fep->opened = 0; netif_stop_queue(dev); fec_stop(dev); return 0;}/* 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?). */#define HASH_BITS 6 /* #bits in hash */#define CRC32_POLY 0xEDB88320static void set_multicast_list(struct net_device *dev){ struct fec_enet_private *fep; volatile fec_t *ep; struct dev_mc_list *dmi; unsigned int i, j, bit, data, crc; unsigned char hash; fep = netdev_priv(dev); ep = fep->hwp; if (dev->flags&IFF_PROMISC) { ep->fec_r_cntrl |= 0x0008; } else { ep->fec_r_cntrl &= ~0x0008; if (dev->flags & IFF_ALLMULTI) { /* Catch all multicast addresses, so set the * filter to all 1's. */ ep->fec_hash_table_high = 0xffffffff; ep->fec_hash_table_low = 0xffffffff; } else { /* Clear filter and add the addresses in hash register. */ ep->fec_hash_table_high = 0; ep->fec_hash_table_low = 0; dmi = dev->mc_list; for (j = 0; j < dev->mc_count; j++, dmi = dmi->next) { /* Only support group multicast for now. */ if (!(dmi->dmi_addr[0] & 1)) continue; /* calculate crc32 value of mac address */ crc = 0xffffffff; for (i = 0; i < dmi->dmi_addrlen; i++) { data = dmi->dmi_addr[i]; for (bit = 0; bit < 8; bit++, data >>= 1) { crc = (crc >> 1) ^ (((crc ^ data) & 1) ? CRC32_POLY : 0); } } /* only upper 6 bits (HASH_BITS) are used which point to specific bit in he hash registers */ hash = (crc >> (32 - HASH_BITS)) & 0x3f; if (hash > 31) ep->fec_hash_table_high |= 1 << (hash - 32); else ep->fec_hash_table_low |= 1 << hash; } } }}/* Set a MAC change in hardware. */static voidfec_set_mac_address(struct net_device *dev){ volatile fec_t *fecp; fecp = ((struct fec_enet_private *)netdev_priv(dev))->hwp; /* Set station address. */ fecp->fec_addr_low = dev->dev_addr[3] | (dev->dev_addr[2] << 8) | (dev->dev_addr[1] << 16) | (dev->dev_addr[0] << 24); fecp->fec_addr_high = (dev->dev_addr[5] << 16) | (dev->dev_addr[4] << 24);}/* Initialize the FEC Ethernet on 860T (or ColdFire 5272). */ /* * XXX: We need to clean up on failure exits here. */int __init fec_enet_init(struct net_device *dev){ struct fec_enet_private *fep = netdev_priv(dev); unsigned long mem_addr; volatile cbd_t *bdp; cbd_t *cbd_base; volatile fec_t *fecp; int i, j; static int index = 0; /* Only allow us to be probed once. */ if (index >= FEC_MAX_PORTS) return -ENXIO; /* Allocate memory for buffer descriptors. */ mem_addr = __get_free_page(GFP_KERNEL); if (mem_addr == 0) { printk("FEC: allocate descriptor memory failed?\n"); return -ENOMEM; } /* Create an Ethernet device instance. */ fecp = (volatile fec_t *) fec_hw[index]; fep->index = index; fep->hwp = fecp; fep->netdev = dev; /* Whack a reset. We should wait for this. */ fecp->fec_ecntrl = 1; udelay(10); /* Set the Ethernet address. If using multiple Enets on the 8xx, * this needs some work to get unique addresses. * * This is our default MAC address unless the user changes * it via eth_mac_addr (our dev->set_mac_addr handler). */ fec_get_mac(dev); cbd_base = (cbd_t *)mem_addr; /* XXX: missing check for allocation failure */ fec_uncache(mem_addr); /* Set receive and transmit descriptor base. */ fep->rx_bd_base = cbd_base; fep->tx_bd_base = cbd_base + RX_RING_SIZE; fep->dirty_tx = fep->cur_tx = fep->tx_bd_base; fep->cur_rx = fep->rx_bd_base; fep->skb_cur = fep->skb_dirty = 0; /* Initialize the receive buffer descriptors. */ bdp = fep->rx_bd_base; for (i=0; i<FEC_ENET_RX_PAGES; i++) { /* Allocate a page. */ mem_addr = __get_free_page(GFP_KERNEL); /* XXX: missing check for allocation failure */ fec_uncache(mem_addr); /* Initialize the BD for every fragment in the page. */ for (j=0; j<FEC_ENET_RX_FRPPG; j++) { bdp->cbd_sc = BD_ENET_RX_EMPTY; bdp->cbd_bufaddr = __pa(mem_addr); mem_addr += FEC_ENET_RX_FRSIZE; bdp++; } } /* Set the last buffer to wrap. */ bdp--; bdp->cbd_sc |= BD_SC_WRAP; /* ...and the same for transmmit. */ bdp = fep->tx_bd_base; for (i=0, j=FEC_ENET_TX_FRPPG; i<TX_RING_SIZE; i++) { if (j >= FEC_ENET_TX_FRPPG) { mem_addr = __get_free_page(GFP_KERNEL); j = 1; } else { mem_addr += FEC_ENET_TX_FRSIZE; j++; } fep->tx_bounce[i] = (unsigned char *) mem_addr; /* Initialize the BD for every fragment in the page. */ bdp->cbd_sc = 0; bdp->cbd_bufaddr = 0; bdp++; } /* Set the last buffer to wrap. */ bdp--; bdp->cbd_sc |= BD_SC_WRAP; /* Set receive and transmit descriptor base. */ fecp->fec_r_des_start = __pa((uint)(fep->rx_bd_base)); fecp->fec_x_des_start = __pa((uint)(fep->tx_bd_base)); /* Install our interrupt handlers. This varies depending on * the architecture. */ fec_request_intrs(dev); fecp->fec_hash_table_high = 0; fecp->fec_hash_table_low = 0; fecp->fec_r_buff_size = PKT_MAXBLR_SIZE; fecp->fec_ecntrl = 2; fecp->fec_r_des_active = 0; dev->base_addr = (unsigned long)fecp; /* The FEC Ethernet specific entries in the device structure. */ dev->open = fec_enet_open; dev->hard_start_xmit = fec_enet_start_xmit; dev->tx_timeout = fec_timeout; dev->watchdog_timeo = TX_TIMEOUT; dev->stop = fec_enet_close; dev->set_multicast_list = set_multicast_list; for (i=0; i<NMII-1; i++) mii_cmds[i].mii_next = &mii_cmds[i+1]; mii_free = mii_cmds; /* setup MII interface */ fec_set_mii(dev, fep); /* Clear and enable interrupts */ fecp->fec_ievent = 0xffc00000; fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_TXB | FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII); /* Queue up command to detect the PHY and initialize the * remainder of the interface. */ fep->phy_id_done = 0; fep->phy_addr = 0; mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy); index++; return 0;}/* This function is called to start or restart the FEC during a link * change. This only happens when switching between half and full * duplex. */static voidfec_restart(struct net_device *dev, int duplex){ struct fec_enet_private *fep; volatile cbd_t *bdp; volatile fec_t *fecp; int i; fep = netdev_priv(dev); fecp = fep->hwp; /* Whack a reset. We should wait for this. */ fecp->fec_ecntrl = 1; udelay(10); /* Clear any outstanding inter
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?