📄 fec.c
字号:
} else fec_stop(dev);#if 0 enable_irq(fep->mii_irq);#endif}static void mii_queue_relink(uint mii_reg, struct net_device *dev){ struct fec_enet_private *fep = dev->priv; fep->phy_task.routine = (void *)mii_relink; fep->phy_task.data = dev; schedule_task(&fep->phy_task);}static void mii_queue_config(uint mii_reg, struct net_device *dev){ struct fec_enet_private *fep = dev->priv; fep->phy_task.routine = (void *)mii_display_config; fep->phy_task.data = dev; schedule_task(&fep->phy_task);}phy_cmd_t phy_cmd_relink[] = { { mk_mii_read(MII_REG_CR), mii_queue_relink }, { mk_mii_end, } };phy_cmd_t 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 = dev->priv; fep->phy_id |= (mii_reg & 0xffff); printk("fec: Phy @ 0x%x, type 0x%08x\n", 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]) panic("%s: PHY id 0x%08x is not supported!\n", dev->name, fep->phy_id); 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; uint phytype; fep = dev->priv; if (fep->phy_addr < 32) { if ((phytype = (mii_reg & 0xffff)) != 0xffff) { /* 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"); }}/* This interrupt occurs when the PHY detects a link change.*/static void#ifdef CONFIG_RPXCLASSICmii_link_interrupt(void *dev_id)#elsemii_link_interrupt(int irq, void * dev_id, struct pt_regs * regs)#endif{ struct net_device *dev = dev_id; struct fec_enet_private *fep = dev->priv;#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 */}static intfec_enet_open(struct net_device *dev){ struct fec_enet_private *fep = dev->priv; /* I should reset the ring buffers here, but I don't yet know * a simple way to do that. */ 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 */ while(!fep->sequence_done) schedule(); mii_do_cmd(dev, fep->phy->startup); netif_start_queue(dev); return 0; /* Success */ } return -ENODEV; /* No PHY we understand */}static intfec_enet_close(struct net_device *dev){ /* Don't know what to do yet. */ netif_stop_queue(dev); fec_stop(dev); return 0;}static struct net_device_stats *fec_enet_get_stats(struct net_device *dev){ struct fec_enet_private *fep = (struct fec_enet_private *)dev->priv; return &fep->stats;}/* 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 void set_multicast_list(struct net_device *dev){ struct fec_enet_private *fep; volatile fec_t *ep; fep = (struct fec_enet_private *)dev->priv; ep = &(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec); if (dev->flags&IFF_PROMISC) { /* Log any net taps. */ printk("%s: Promiscuous mode enabled.\n", dev->name); 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; }#if 0 else { /* Clear filter and add the addresses in the list. */ ep->sen_gaddr1 = 0; ep->sen_gaddr2 = 0; ep->sen_gaddr3 = 0; ep->sen_gaddr4 = 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->sen_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(CPM_CR_CH_SCC1, CPM_CR_SET_GADDR) | CPM_CR_FLG; /* this delay is necessary here -- Cort */ udelay(10); while (cpmp->cp_cpcr & CPM_CR_FLG); } }#endif }}/* Initialize the FEC Ethernet on 860T. */int __init fec_enet_init(void){ struct net_device *dev; struct fec_enet_private *fep; int i, j; unsigned char *eap, *iap; unsigned long mem_addr; pte_t *pte; volatile cbd_t *bdp; cbd_t *cbd_base; volatile immap_t *immap; volatile fec_t *fecp; bd_t *bd; extern uint _get_IMMR(void);#ifdef CONFIG_RPXCLASSIC unsigned char tmpaddr[6];#endif immap = (immap_t *)IMAP_ADDR; /* pointer to internal registers */ bd = (bd_t *)__res; /* Allocate some private information. */ fep = (struct fec_enet_private *)kmalloc(sizeof(*fep), GFP_KERNEL); __clear_user(fep,sizeof(*fep)); /* Create an Ethernet device instance. */ dev = init_etherdev(0, 0); fecp = &(immap->im_cpm.cp_fec); /* 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. */ eap = (unsigned char *)my_enet_addr; iap = bd->bi_enetaddr;#ifdef CONFIG_RPXCLASSIC /* The Embedded Planet boards have only one MAC address in * the EEPROM, but can have two Ethernet ports. For the * FEC port, we create another address by setting one of * the address bits above something that would have (up to * now) been allocated. */ for (i=0; i<6; i++) tmpaddr[i] = *iap++; tmpaddr[3] |= 0x80; iap = tmpaddr;#endif for (i=0; i<6; i++) dev->dev_addr[i] = *eap++ = *iap++; /* Allocate memory for buffer descriptors. */ if (((RX_RING_SIZE + TX_RING_SIZE) * sizeof(cbd_t)) > PAGE_SIZE) { printk("FEC init error. Need more space.\n"); printk("FEC initialization failed.\n"); return 1; } mem_addr = __get_free_page(GFP_KERNEL); cbd_base = (cbd_t *)mem_addr; /* Make it uncached. */ pte = va_to_pte(mem_addr); pte_val(*pte) |= _PAGE_NO_CACHE; flush_tlb_page(init_mm.mmap, 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); /* Make it uncached. */ pte = va_to_pte(mem_addr); pte_val(*pte) |= _PAGE_NO_CACHE; flush_tlb_page(init_mm.mmap, 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;#ifdef CONFIG_FEC_PACKETHOOK fep->ph_lock = 0; fep->ph_rxhandler = fep->ph_txhandler = NULL; fep->ph_proto = 0; fep->ph_regaddr = NULL; fep->ph_priv = NULL;#endif /* Install our interrupt handler. */ if (request_8xxirq(FEC_INTERRUPT, fec_enet_interrupt, 0, "fec", dev) != 0) panic("Could not allocate FEC IRQ!");#ifdef CONFIG_RPXCLASSIC /* Make Port C, bit 15 an input that causes interrupts. */ immap->im_ioport.iop_pcpar &= ~0x0001; immap->im_ioport.iop_pcdir &= ~0x0001; immap->im_ioport.iop_pcso &= ~0x0001; immap->im_ioport.iop_pcint |= 0x0001; cpm_install_handler(CPMVEC_PIO_PC15, mii_link_interrupt, dev); /* Make LEDS reflect Link status. */ *((uint *) RPX_CSR_ADDR) &= ~BCSR2_FETHLEDMODE;#endif#ifdef CONFIG_FADS if (request_8xxirq(SIU_IRQ2, mii_link_interrupt, 0, "mii", dev) != 0) panic("Could not allocate MII IRQ!");#endif dev->base_addr = (unsigned long)fecp; dev->priv = fep; /* 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->get_stats = fec_enet_get_stats; 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; /* Configure all of port D for MII. */ immap->im_ioport.iop_pdpar = 0x1fff; /* Bits moved from Rev. D onward. */ if ((_get_IMMR() & 0xffff) < 0x0501) immap->im_ioport.iop_pddir = 0x1c58; /* Pre rev. D */ else immap->im_ioport.iop_pddir = 0x1fff; /* Rev. D and later */ /* Set MII speed to 2.5 MHz */ fecp->fec_mii_speed = fep->phy_speed = ((bd->bi_busfreq * 1000000) / 2500000) & 0x7e; printk("%s: FEC ENET Version 0.2, ", dev->name); for (i=0; i<5; i++) printk("%02x:", dev->dev_addr[i]); printk("%02x\n", dev->dev_addr[5]); /* 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); 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; int i; unsigned char *eap; volatile cbd_t *bdp; volatile immap_t *immap; volatile fec_t *fecp; immap = (immap_t *)IMAP_ADDR; /* pointer to internal registers */ fecp = &(immap->im_cpm.cp_fec); fep = dev->priv; /* Whack a reset. We should wait for this. */ fecp->fec_ecntrl = 1; udelay(10); /* Enable interrupts we wish to service. */ fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_TXB | FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII); /* Clear any outstanding interrupt. */ fecp->fec_ievent = 0xffc0; fecp->fec_ivec = (FEC_INTERRUPT/2) << 29; /* Set station address. */ fecp->fec_addr_low = (my_enet_addr[0] << 16) | my_enet_addr[1]; fecp->fec_addr_high = my_enet_addr[2]; eap = (unsigned char *)&my_enet_addr[0]; for (i=0; i<6; i++) dev->dev_addr[i] = *eap++; /* Reset all multicast. */ fecp->fec_hash_table_high = 0; fecp->fec_hash_table_low = 0; /* Set maximum receive buffer size. */ fecp->fec_r_buff_size = PKT_MAXBLR_SIZE; fecp->fec_r_hash = PKT_MAXBUF_SIZE; /* 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)); fep->dirty_tx = fep->cur_tx = fep->tx_bd_base; fep->cur_rx = fep->rx_bd_base; /* Reset SKB transmit buffers. */ fep->skb_cur = fep->skb_dirty = 0; for (i=0; i<=TX_RING_MOD_MASK; i++) { if (fep->tx_skbuff[i] != NULL) { dev_kfree_skb(fep->tx_skbuff[i]); fep->tx_skbuff[i] = NULL; } } /* Initialize the receive buffer descriptors. */ bdp = fep->rx_bd_base; for (i=0; i<RX_RING_SIZE; i++) { /* Initialize the BD for every fragment in the page. */ bdp->cbd_sc = BD_ENET_RX_EMPTY; 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; i<TX_RING_SIZE; i++) { /* 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; /* Enable MII mode. */ if (duplex) { fecp->fec_r_cntrl = 0x04; /* MII enable */ fecp->fec_x_cntrl = 0x04; /* FD enable */ } else { fecp->fec_r_cntrl = 0x06; /* MII enable|No Rcv on Xmit */ fecp->fec_x_cntrl = 0x00; } fep->full_duplex = duplex; /* Enable big endian and don't care about SDMA FC. */ fecp->fec_fun_code = 0x78000000; /* Set MII speed. */ fecp->fec_mii_speed = fep->phy_speed; /* And last, enable the transmit and receive processing. */ fecp->fec_ecntrl = 6; fecp->fec_r_des_active = 0x01000000;}static voidfec_stop(struct net_device *dev){ volatile immap_t *immap; volatile fec_t *fecp; struct fec_enet_private *fep; immap = (immap_t *)IMAP_ADDR; /* pointer to internal registers */ fecp = &(immap->im_cpm.cp_fec); fep = dev->priv; fecp->fec_x_cntrl = 0x01; /* Graceful transmit stop */ while(!(fecp->fec_ievent & 0x10000000)); /* Whack a reset. We should wait for this. */ fecp->fec_ecntrl = 1; udelay(10); /* Clear outstanding MII command interrupts. */ fecp->fec_ievent = FEC_ENET_MII; /* Enable MII command finihed interrupt */ fecp->fec_ivec = (FEC_INTERRUPT/2) << 29; fecp->fec_imask = FEC_ENET_MII; /* Set MII speed. */ fecp->fec_mii_speed = fep->phy_speed;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -