/* * adapter.c * * Xilinx Ethernet Adapter component to interface XEmac component to Linux * * Author: MontaVista Software, Inc. * source@mvista.com * * 2002 (c) MontaVista, Software, Inc. This file is licensed under the terms * of the GNU General Public License version 2.1. This program is licensed * "as is" without any warranty of any kind, whether express or implied. */ /* * This driver is a bit unusual in that it is composed of two logical * parts where one part is the OS independent code and the other part is * the OS dependent code. Xilinx provides their drivers split in this * fashion. This file represents the Linux OS dependent part known as * the Linux adapter. The other files in this directory are the OS * independent files as provided by Xilinx with no changes made to them. * The names exported by those files begin with XEmac_. All functions * in this file that are called by Linux have names that begin with * xenet_. The functions in this file that have Handler in their name * are registered as callbacks with the underlying Xilinx OS independent * layer. Any other functions are static helper functions. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xemac.h" #include "xemac_i.h" #include "xipif_v1_23_b.h" #undef XEM_DFT_SEND_DESC #define XEM_DFT_SEND_DESC 64 #undef XEM_DFT_RECV_DESC #define XEM_DFT_RECV_DESC 256 #define DRIVER_NAME "Xilinx Eth MAC driver" #define DRIVER_VERSION "1.0" MODULE_AUTHOR("MontaVista Software, Inc. "); MODULE_DESCRIPTION(DRIVER_NAME); MODULE_LICENSE("GPL"); #define TX_TIMEOUT (60*HZ) /* Transmission timeout is 60 seconds. */ /* On the OPB, the 10/100 EMAC requires data to be aligned to 4 bytes. * On the PLB, the 10/100 EMAC requires data to be aligned to 8 bytes. * For simplicity, we always align to 8 bytes. */ #define ALIGNMENT 32 /* BUFFER_ALIGN(adr) calculates the number of bytes to the next alignment. */ #define BUFFER_ALIGN(adr) ((ALIGNMENT - ((u32) adr)) % ALIGNMENT) /* physical to virtual pointer conversion */ #define P_TO_V(InstancePtr, p) \ ((p) ? \ ((InstancePtr)->VirtPtr + ((u32)(p) - (u32)(InstancePtr)->PhyPtr)) : \ 0) int bh_entry = 0; /* * Our private per device data. When a net_device is allocated we will * ask for enough extra space for this. */ struct net_local { struct list_head rcv; XBufDescriptor * rcvBdPtr; int rcvBds; struct list_head xmit; XBufDescriptor * xmitBdPtr; int xmitBds; struct net_device_stats stats; /* Statistics for this device */ struct net_device *next_dev; /* The next device in dev_list */ struct net_device *dev; /* this device */ struct timer_list phy_timer; /* PHY monitoring timer */ u32 index; /* Which interface is this */ XInterruptHandler Isr; /* Pointer to the XEmac ISR routine */ u8 mii_addr; /* The MII address of the PHY */ /* * The underlying OS independent code needs space as well. A * pointer to the following XEmac structure will be passed to * any XEmac_ function that requires it. However, we treat the * data as an opaque object in this file (meaning that we never * reference any of the fields inside of the structure). */ XEmac Emac; void *desc_space; dma_addr_t desc_space_handle; int desc_space_size; u8 *ddrVirtPtr; u32 ddrOffset; u32 ddrSize; struct sk_buff* deferred_skb; atomic_t availSendBds; }; /* List of devices we're handling and a lock to give us atomic access. */ static struct net_device *dev_list = NULL; static spinlock_t dev_lock = SPIN_LOCK_UNLOCKED; /* for exclusion of all program flows (processes, ISRs and BHs) possible to share data with current one */ static spinlock_t reset_lock = SPIN_LOCK_UNLOCKED; /* Helper function to determine if a given XEmac error warrants a reset. */ extern inline int status_requires_reset(XStatus s) { return (s == XST_DMA_ERROR || s == XST_FIFO_ERROR || s == XST_RESET_ERROR || s == XST_DMA_SG_NO_LIST || s == XST_DMA_SG_LIST_EMPTY); } /* BH statics */ LIST_HEAD(receivedQueue); static spinlock_t rcvSpin = SPIN_LOCK_UNLOCKED; LIST_HEAD(sentQueue); static spinlock_t xmitSpin = SPIN_LOCK_UNLOCKED; /* SAATODO: This function will be moved into the Xilinx code. */ /*****************************************************************************/ /** * * Lookup the device configuration based on the emac instance. The table * EmacConfigTable contains the configuration info for each device in the system. * * @param Instance is the index of the emac being looked up. * * @return * * A pointer to the configuration table entry corresponding to the given * device ID, or NULL if no match is found. * * @note * * None. * ******************************************************************************/ XEmac_Config * XEmac_GetConfig(int Instance) { if (Instance < 0 || Instance >= XPAR_XEMAC_NUM_INSTANCES) { return NULL; } return &XEmac_ConfigTable[Instance]; } /* * The following are notes regarding the critical sections in this * driver and how they are protected. * * dev_list * There is a spinlock protecting the device list. It isn't really * necessary yet because the list is only manipulated at init and * cleanup, but it's there because it is basically free and if we start * doing hot add and removal of ethernet devices when the FPGA is * reprogrammed while the system is up, we'll need to protect the list. * * XEmac_Start, XEmac_Stop and XEmac_SetOptions are not thread safe. * These functions are called from xenet_open(), xenet_close(), reset(), * and xenet_set_multicast_list(). xenet_open() and xenet_close() * should be safe because when they do start and stop, they don't have * interrupts or timers enabled. The other side is that they won't be * called while a timer or interrupt is being handled. * * XEmac_PhyRead and XEmac_PhyWrite are not thread safe. * These functions are called from get_phy_status(), xenet_ioctl() and * probe(). probe() is only called from xenet_init() so it is not an * issue (nothing is really up and running yet). get_phy_status() is * called from both poll_mii() (a timer bottom half) and xenet_open(). * These shouldn't interfere with each other because xenet_open() is * what starts the poll_mii() timer. xenet_open() and xenet_ioctl() * should be safe as well because they will be sequential. That leaves * the interaction between poll_mii() and xenet_ioctl(). While the * timer bottom half is executing, a new ioctl won't come in so that is * taken care of. That leaves the one case of the poll_mii timer * popping while handling an ioctl. To take care of that case, the * timer is deleted when the ioctl comes in and then added back in after * the ioctl is finished. */ typedef enum DUPLEX { UNKNOWN_DUPLEX, HALF_DUPLEX, FULL_DUPLEX } DUPLEX; static void reset(struct net_device *dev, DUPLEX duplex) { struct net_local *lp = (struct net_local *) dev->priv; u32 Options; u8 IfgPart1; u8 IfgPart2; u8 SendThreshold; u32 SendWaitBound; u8 RecvThreshold; u32 RecvWaitBound; int dma_works; /* Shouldn't really be necessary, but shouldn't hurt. */ netif_stop_queue(dev); /* * XEmac_Reset puts the device back to the default state. We need * to save all the settings we don't already know, reset, restore * the settings, and then restart the emac. */ XEmac_GetInterframeGap(&lp->Emac, &IfgPart1, &IfgPart2); Options = XEmac_GetOptions(&lp->Emac); switch (duplex) { case HALF_DUPLEX: Options &= ~XEM_FDUPLEX_OPTION; break; case FULL_DUPLEX: Options |= XEM_FDUPLEX_OPTION; break; case UNKNOWN_DUPLEX: break; } if (XEmac_mIsSgDma(&lp->Emac)) { /* * The following four functions will return an error if we are * not doing scatter-gather DMA. We just checked that so we * can safely ignore the return values. We cast them to void * to make that explicit. */ dma_works = 1; (void) XEmac_GetPktThreshold(&lp->Emac, XEM_SEND, &SendThreshold); (void) XEmac_GetPktWaitBound(&lp->Emac, XEM_SEND, &SendWaitBound); (void) XEmac_GetPktThreshold(&lp->Emac, XEM_RECV, &RecvThreshold); (void) XEmac_GetPktWaitBound(&lp->Emac, XEM_RECV, &RecvWaitBound); } else dma_works = 0; XEmac_Reset(&lp->Emac); /* * The following three functions will return an error if the * EMAC is already started. We just stopped it by calling * XEmac_Reset() so we can safely ignore the return values. * We cast them to void to make that explicit. */ (void) XEmac_SetMacAddress(&lp->Emac, dev->dev_addr); (void) XEmac_SetInterframeGap(&lp->Emac, IfgPart1, IfgPart2); (void) XEmac_SetOptions(&lp->Emac, Options); if (XEmac_mIsSgDma(&lp->Emac)) { /* * The following four functions will return an error if * we are not doing scatter-gather DMA or if the EMAC is * already started. We just checked that we are indeed * doing scatter-gather and we just stopped the EMAC so * we can safely ignore the return values. We cast them * to void to make that explicit. */ (void) XEmac_SetPktThreshold(&lp->Emac, XEM_SEND, SendThreshold); (void) XEmac_SetPktWaitBound(&lp->Emac, XEM_SEND, SendWaitBound); (void) XEmac_SetPktThreshold(&lp->Emac, XEM_RECV, RecvThreshold); (void) XEmac_SetPktWaitBound(&lp->Emac, XEM_RECV, RecvWaitBound); } /* * XEmac_Start returns an error when: it is already started, the send * and receive handlers are not set, or a scatter-gather DMA list is * missing. None of these can happen at this point, so we cast the * return to void to make that explicit. */ if (dma_works) { int avail_plus = 0; while (!(XDmaChannel_IsSgListEmpty(&(lp->Emac.SendChannel)))) { /* list isn't empty, has to be cleared */ XStatus ret; XBufDescriptor* BdPtr; if ((ret = XDmaChannel_GetDescriptor (&(lp->Emac.SendChannel), &BdPtr)) != XST_SUCCESS) { printk (KERN_ERR "SgDma ring structure ERROR %d\n", ret); break; } avail_plus++; XBufDescriptor_Unlock(BdPtr); pci_unmap_single(NULL, (u32) XBufDescriptor_GetSrcAddress(BdPtr), XBufDescriptor_GetLength(BdPtr), PCI_DMA_TODEVICE); lp->stats.tx_errors++; } atomic_add(avail_plus,&lp->availSendBds); } else { if (lp->deferred_skb) { dev_kfree_skb(lp->deferred_skb); lp->deferred_skb = NULL; lp->stats.tx_errors++; } } dev->trans_start = 0xffffffff - TX_TIMEOUT - TX_TIMEOUT; /* to exclude tx timeout */ (void) XEmac_Start(&lp->Emac); /* We're all ready to go. Start the queue in case it was stopped. */ if (!bh_entry) netif_wake_queue(dev); } static int get_phy_status(struct net_device *dev, DUPLEX * duplex, int *linkup) { struct net_local *lp = (struct net_local *) dev->priv; u16 reg; XStatus xs; xs = XEmac_PhyRead(&lp->Emac, lp->mii_addr, MII_BMCR, ®); if (xs != XST_SUCCESS) { printk(KERN_ERR "%s: Could not read PHY control register; error %d\n", dev->name, xs); return -1; } if (!(reg & BMCR_ANENABLE)) { /* * Auto-negotiation is disabled so the full duplex bit in * the control register tells us if the PHY is running * half or full duplex. */ *duplex = (reg & BMCR_FULLDPLX) ? FULL_DUPLEX : HALF_DUPLEX; } else { /* * Auto-negotiation is enabled. Figure out what was * negotiated by looking for the best mode in the union * of what we and our partner advertise. */ u16 advertise, partner, negotiated; xs = XEmac_PhyRead(&lp->Emac, lp->mii_addr, MII_ADVERTISE, &advertise); if (xs != XST_SUCCESS) { printk(KERN_ERR "%s: Could not read PHY advertisement; error %d\n", dev->name, xs); return -1; } xs = XEmac_PhyRead(&lp->Emac, lp->mii_addr, MII_LPA, &partner); if (xs != XST_SUCCESS) { printk(KERN_ERR "%s: Could not read PHY LPA; error %d\n", dev->name, xs); return -1; } negotiated = advertise & partner & ADVERTISE_ALL; if (negotiated & ADVERTISE_100FULL) *duplex = FULL_DUPLEX; else if (negotiated & ADVERTISE_100HALF) *duplex = HALF_DUPLEX; else if (negotiated & ADVERTISE_10FULL) *duplex = FULL_DUPLEX; else *duplex = HALF_DUPLEX; } xs = XEmac_PhyRead(&lp->Emac, lp->mii_addr, MII_BMSR, ®); if (xs != XST_SUCCESS) { printk(KERN_ERR "%s: Could not read PHY status register; error %d\n", dev->name, xs); return -1; } *linkup = (reg & BMSR_LSTATUS) != 0; return 0; } /* * This routine is used for two purposes. The first is to keep the * EMAC's duplex setting in sync with the PHY's. The second is to keep * the system apprised of the state of the link. Note that this driver * does not configure the PHY. Either the PHY should be configured for * auto-negotiation or it should be handled by something like mii-tool. */ static void poll_mii(unsigned long data) { struct net_device *dev = (struct net_device *) data; struct net_local *lp = (struct net_local *) dev->priv; u32 Options; DUPLEX phy_duplex, mac_duplex; int phy_carrier, netif_carrier; unsigned long flags; /* First, find out what's going on with the PHY. */ if (get_phy_status(dev, &phy_duplex, &phy_carrier)) { printk(KERN_ERR "%s: Terminating link monitoring.\n", dev->name); return; } /* Second, figure out if we have the EMAC in half or full duplex. */ Options = XEmac_GetOptions(&lp->Emac); mac_duplex = (Options & XEM_FDUPLEX_OPTION) ? FULL_DUPLEX : HALF_DUPLEX; /* Now see if there is a mismatch. */ if (mac_duplex != phy_duplex) { /* * Make sure that no interrupts come in that could cause * reentrancy problems in reset. */ spin_lock_irqsave(reset_lock, flags); reset(dev, phy_duplex); /* the function sets Emac options to match the PHY */ spin_unlock_irqrestore(reset_lock, flags); if (mac_duplex == FULL_DUPLEX) printk(KERN_INFO "%s: Duplex has been changed: now %s\n", dev->name, "HALF_DUPLEX"); else printk(KERN_INFO "%s: Duplex has been changed: now %s\n", dev->name, "FULL_DUPLEX"); } netif_carrier = netif_carrier_ok(dev) != 0; if (phy_carrier != netif_carrier) { if (phy_carrier) { printk(KERN_INFO "%s: Link carrier restored.\n", dev->name); netif_carrier_on(dev); } else { printk(KERN_INFO "%s: Link carrier lost.\n", dev->name); netif_carrier_off(dev); } } /* Set up the timer so we'll get called again in 2 seconds. */ lp->phy_timer.expires = jiffies + 2 * HZ; add_timer(&lp->phy_timer); } /* * This routine is registered with the OS as the function to call when * the EMAC interrupts. It in turn, calls the Xilinx OS independent * interrupt function. There are different interrupt functions for FIFO * and scatter-gather so we just set a pointer (Isr) into our private * data so we don't have to figure it out here. The Xilinx OS * independent interrupt function will in turn call any callbacks that * we have registered for various conditions. */ static void xenet_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct net_device *dev = dev_id; struct net_local *lp = (struct net_local *) dev->priv; /* Call it. */ (*(lp->Isr)) (&lp->Emac); } static int xenet_open(struct net_device *dev) { struct net_local *lp = (struct net_local *) dev->priv; u32 Options; DUPLEX phy_duplex, mac_duplex; int phy_carrier; /* * Just to be safe, stop the device first. If the device is already * stopped, an error will be returned. In this case, we don't really * care, so cast it to void to make it explicit. */ (void) XEmac_Stop(&lp->Emac); /* Set the MAC address each time opened. */ if (XEmac_SetMacAddress(&lp->Emac, dev->dev_addr) != XST_SUCCESS) { printk(KERN_ERR "%s: Could not set MAC address.\n", dev->name); return -EIO; } /* * If the device is not configured for polled mode, connect to the * interrupt controller and enable interrupts. Currently, there * isn't any code to set polled mode, so this check is probably * superfluous. */ Options = XEmac_GetOptions(&lp->Emac); if ((Options & XEM_POLLED_OPTION) == 0) { int retval; /* Grab the IRQ */ retval = request_irq(dev->irq, &xenet_interrupt, 0, dev->name, dev); if (retval) { printk(KERN_ERR "%s: Could not allocate interrupt %d.\n", dev->name, dev->irq); return retval; } } /* Set the EMAC's duplex setting based upon what the PHY says. */ if (!get_phy_status(dev, &phy_duplex, &phy_carrier)) { /* We successfully got the PHY status. */ mac_duplex = ((Options & XEM_FDUPLEX_OPTION) ? FULL_DUPLEX : HALF_DUPLEX); if (mac_duplex != phy_duplex) { switch (phy_duplex) { case HALF_DUPLEX: Options &= ~XEM_FDUPLEX_OPTION; break; case FULL_DUPLEX: Options |= XEM_FDUPLEX_OPTION; break; case UNKNOWN_DUPLEX: break; } /* * The following function will return an error * if the EMAC is already started. We know it * isn't started so we can safely ignore the * return value. We cast it to void to make * that explicit. */ } } Options |= XEM_FLOW_CONTROL_OPTION; (void) XEmac_SetOptions(&lp->Emac, Options); INIT_LIST_HEAD(&(lp->rcv)); lp->rcvBds = 0; INIT_LIST_HEAD(&(lp->xmit)); lp->xmitBds = 0; if (XEmac_Start(&lp->Emac) != XST_SUCCESS) { printk(KERN_ERR "%s: Could not start device.\n", dev->name); free_irq(dev->irq, dev); return -EBUSY; } /* We're ready to go. */ MOD_INC_USE_COUNT; netif_start_queue(dev); /* Set up the PHY monitoring timer. */ lp->phy_timer.expires = jiffies + 2 * HZ; lp->phy_timer.data = (unsigned long) dev; lp->phy_timer.function = &poll_mii; add_timer(&lp->phy_timer); return 0; } static int xenet_close(struct net_device *dev) { struct net_local *lp = (struct net_local *) dev->priv; unsigned long flags; /* Shut down the PHY monitoring timer. */ del_timer_sync(&lp->phy_timer); netif_stop_queue(dev); /* * If not in polled mode, free the interrupt. Currently, there * isn't any code to set polled mode, so this check is probably * superfluous. */ if ((XEmac_GetOptions(&lp->Emac) & XEM_POLLED_OPTION) == 0) free_irq(dev->irq, dev); spin_lock_irqsave(rcvSpin, flags); list_del(&(lp->rcv)); spin_unlock_irqrestore(rcvSpin, flags); spin_lock_irqsave(xmitSpin, flags); list_del(&(lp->xmit)); spin_unlock_irqrestore(xmitSpin, flags); if (XEmac_Stop(&lp->Emac) != XST_SUCCESS) { printk(KERN_ERR "%s: Could not stop device.\n", dev->name); return -EBUSY; } MOD_DEC_USE_COUNT; return 0; } static struct net_device_stats * xenet_get_stats(struct net_device *dev) { struct net_local *lp = (struct net_local *) dev->priv; return &lp->stats; } static int xenet_FifoSend(struct sk_buff *orig_skb, struct net_device *dev) { struct net_local *lp = (struct net_local *) dev->priv; struct sk_buff *new_skb; unsigned int len, align; unsigned long flags; len = orig_skb->len; /* PR FIXME: what follows can be removed if the asserts in the Xilinx * independent drivers change. There is really no need to align the * buffers in FIFO mode. The story is different for simple DMA. */ /* * The packet FIFO requires the buffers to be 32/64 bit aligned. * The sk_buff data is not 32/64 bit aligned, so we have to do this * copy. As you probably well know, this is not optimal. */ if (!(new_skb = alloc_skb(len + ALIGNMENT, GFP_ATOMIC))) { /* We couldn't get another skb. */ dev_kfree_skb(orig_skb); lp->stats.tx_dropped++; printk(KERN_ERR "%s: Could not allocate transmit buffer.\n", dev->name); netif_wake_queue(dev); return -EBUSY; } /* * A new skb should have the data word aligned, but this code is * here just in case that isn't true... Calculate how many * bytes we should reserve to get the data to start on a word * boundary. */ align = BUFFER_ALIGN(new_skb->data); if (align) skb_reserve(new_skb, align); /* Copy the data from the original skb to the new one. */ skb_put(new_skb, len); memcpy(new_skb->data, orig_skb->data, len); /* Get rid of the original skb. */ dev_kfree_skb(orig_skb); spin_lock_irqsave(reset_lock, flags); if (XEmac_FifoSend(&lp->Emac, (u8 *) new_skb->data, len) != XST_SUCCESS) { netif_stop_queue(dev); lp->deferred_skb = new_skb; spin_unlock_irqrestore(reset_lock, flags); return 0; } spin_unlock_irqrestore(reset_lock, flags); lp->stats.tx_bytes += len; dev_kfree_skb(new_skb); dev->trans_start = jiffies; return 0; } /* The callback function for completed frames sent in FIFO mode. */ static void FifoSendHandler(void *CallbackRef) { struct net_device *dev = (struct net_device *) CallbackRef; struct net_local *lp = (struct net_local *) dev->priv; if (lp->deferred_skb) { if (XEmac_FifoSend(&lp->Emac, (u8 *) lp->deferred_skb->data, lp->deferred_skb->len) != XST_SUCCESS) { return; } else { dev_kfree_skb(lp->deferred_skb); lp->deferred_skb = NULL; netif_wake_queue(dev); } } lp->stats.tx_packets++; } /* The send function for frames sent in DMA mode. */ static int xenet_SgSend(struct sk_buff *skb, struct net_device *dev) { struct net_local *lp = (struct net_local *) dev->priv; unsigned int len; XBufDescriptor bd; int result; u32 physAddr; u8 *virtAddr; unsigned long flags; len = skb->len; virtAddr = lp->ddrVirtPtr + lp->ddrOffset; if (skb->ip_summed == CHECKSUM_NONE) /*cacheable_*/memcpy(virtAddr, skb->data, len); else skb_copy_and_csum_dev(skb, virtAddr); dev_kfree_skb(skb); physAddr = (u32) pci_map_single(NULL, virtAddr, len, PCI_DMA_TODEVICE); /* * lock the buffer descriptor to prevent lower layers from reusing * it before the adapter has a chance to deallocate the buffer * attached to it. The adapter will unlock it in the callback function * that handles confirmation of transmits */ XBufDescriptor_Initialize(&bd); XBufDescriptor_Lock(&bd); XBufDescriptor_SetSrcAddress(&bd, physAddr); XBufDescriptor_SetLength(&bd, len); XBufDescriptor_SetLast(&bd); lp->ddrOffset += len + BUFFER_ALIGN(len); if (lp->ddrOffset + XEM_MAX_FRAME_SIZE > lp->ddrSize) lp->ddrOffset = 0; spin_lock_irqsave(reset_lock, flags); result = XEmac_SgSend(&lp->Emac, &bd, XEM_SGDMA_NODELAY); if (result != XST_SUCCESS) { lp->stats.tx_dropped++; printk(KERN_ERR "%s: ERROR, could not send transmit buffer (%d).\n", dev->name, result); /* we should never get here in the first place, but * for some reason the kernel doesn't like -EBUSY here, * so just return 0 and let the stack handle dropped packets. */ /* return -EBUSY; */ spin_unlock_irqrestore(reset_lock, flags); return 0; } if (atomic_dec_and_test(&lp->availSendBds)) { netif_stop_queue(dev); } dev->trans_start = jiffies; spin_unlock_irqrestore(reset_lock, flags); return 0; } /* The callback function for completed frames sent in DMA mode. */ static void SgSendHandlerBH (unsigned long p); static void SgRecvHandlerBH (unsigned long p); DECLARE_TASKLET (SgSendBH, SgSendHandlerBH, 0); DECLARE_TASKLET (SgRecvBH, SgRecvHandlerBH, 0); static void SgSendHandlerBH (unsigned long p) { struct net_device *dev; struct net_local *lp; XBufDescriptor * BdPtr; u32 NumBds; u32 len; XBufDescriptor *curbd; unsigned long flags; while (1) { spin_lock_irqsave(xmitSpin,flags); if (list_empty(&sentQueue)) { spin_unlock_irqrestore(xmitSpin,flags); break; } lp = list_entry(sentQueue.next, struct net_local, xmit); list_del_init(&(lp->xmit)); NumBds = lp->xmitBds; BdPtr = lp->xmitBdPtr; dev = lp->dev; atomic_add(NumBds, &lp->availSendBds); while(NumBds != 0) { NumBds--; len = XBufDescriptor_GetLength(BdPtr); pci_unmap_single(NULL, (u32) XBufDescriptor_GetSrcAddress(BdPtr), len, PCI_DMA_TODEVICE); lp->stats.tx_bytes += len; lp->stats.tx_packets++; curbd = BdPtr; BdPtr = P_TO_V(&lp->Emac.SendChannel, XBufDescriptor_GetNextPtr(BdPtr)); XBufDescriptor_Unlock(curbd); } spin_unlock_irqrestore(xmitSpin,flags); netif_wake_queue(dev); } } static void SgSendHandler(void *CallBackRef, XBufDescriptor * BdPtr, u32 NumBds) { struct net_device *dev = (struct net_device *) CallBackRef; struct net_local *lp = (struct net_local *) dev->priv; struct list_head* cur_lp = NULL; spin_lock(xmitSpin); list_for_each (cur_lp, &sentQueue) { if (cur_lp == &(lp->xmit)) { lp->xmitBds += NumBds; break; } } if (cur_lp != &(lp->xmit)) { lp->xmitBds = NumBds; lp->xmitBdPtr = BdPtr; list_add_tail(&lp->xmit,&sentQueue); bh_entry++; tasklet_schedule (&SgSendBH); } spin_unlock(xmitSpin); } static void SgRecvHandlerBH (unsigned long p) { struct net_device *dev; struct net_local *lp; XBufDescriptor* BdPtr; int NumBds; struct sk_buff *skb, *new_skb; u32 len, new_skb_vaddr; dma_addr_t skb_vaddr; u32 align; XStatus result; XBufDescriptor *curbd; unsigned long flags; while (1) { spin_lock_irqsave(rcvSpin,flags); if (list_empty(&receivedQueue)) { spin_unlock_irqrestore(rcvSpin,flags); break; } lp = list_entry(receivedQueue.next, struct net_local, rcv); list_del_init(&(lp->rcv)); NumBds = lp->rcvBds; BdPtr = lp->rcvBdPtr; dev = lp->dev; spin_unlock_irqrestore(rcvSpin,flags); while (NumBds != 0) { NumBds--; /* get ptr to skb */ skb = (struct sk_buff *) XBufDescriptor_GetId(BdPtr); len = XBufDescriptor_GetLength(BdPtr); /* we have all the information we need - move on */ curbd = BdPtr; BdPtr = P_TO_V(&lp->Emac.RecvChannel, XBufDescriptor_GetNextPtr(curbd)); skb_vaddr = (dma_addr_t)XBufDescriptor_GetDestAddress(curbd); pci_unmap_single(NULL, skb_vaddr, len, PCI_DMA_FROMDEVICE); /* replace skb with a new one */ new_skb = alloc_skb(XEM_MAX_FRAME_SIZE + ALIGNMENT, GFP_ATOMIC); if (new_skb == 0) { printk("SgRecvHandler: no mem for new_skb\n"); return; } /* make sure we're long-word aligned */ align = BUFFER_ALIGN(new_skb->data); if (align) { skb_reserve(new_skb, align); } new_skb_vaddr = (u32) pci_map_single(NULL, new_skb->data, XEM_MAX_FRAME_SIZE, PCI_DMA_FROMDEVICE); XBufDescriptor_SetDestAddress(curbd, new_skb_vaddr); XBufDescriptor_SetLength(curbd, XEM_MAX_FRAME_SIZE); XBufDescriptor_SetId(curbd, new_skb); XBufDescriptor_Unlock(curbd); /* give the descriptor back to the driver */ result = XEmac_SgRecv(&lp->Emac, curbd); if (result != XST_SUCCESS) { printk("SgRecvHandler: SgRecv unsuccessful\n"); return; } /* back to the original skb */ skb->len = len; skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); skb->ip_summed = CHECKSUM_NONE; lp->stats.rx_packets++; lp->stats.rx_bytes += len; netif_rx(skb); /* Send the packet upstream. */ } } } static void SgRecvHandler(void *CallBackRef, XBufDescriptor * BdPtr, u32 NumBds) { struct net_device *dev = (struct net_device *) CallBackRef; struct net_local *lp = (struct net_local *) dev->priv; struct list_head* cur_lp = NULL; spin_lock(rcvSpin); list_for_each (cur_lp, &receivedQueue) { if (cur_lp == &(lp->rcv)) { lp->rcvBds += NumBds; break; } } if (cur_lp != &(lp->rcv)) { lp->rcvBds = NumBds; lp->rcvBdPtr = BdPtr; list_add_tail(&lp->rcv, &receivedQueue); tasklet_schedule (&SgRecvBH); } spin_unlock(rcvSpin); } static void xenet_tx_timeout(struct net_device *dev) { struct net_local *lp = (struct net_local *) dev->priv; unsigned long flags; printk("%s: Exceeded transmit timeout of %lu ms.\n", dev->name, TX_TIMEOUT * 1000UL / HZ); lp->stats.tx_errors++; spin_lock_irqsave(reset_lock, flags); reset(dev, UNKNOWN_DUPLEX); spin_unlock_irqrestore(reset_lock, flags); } /* The callback function for frames received when in FIFO mode. */ static void FifoRecvHandler(void *CallbackRef) { struct net_device *dev = (struct net_device *) CallbackRef; struct net_local *lp = (struct net_local *) dev->priv; struct sk_buff *skb; unsigned int align; u32 len; XStatus Result; /* * The OS independent Xilinx EMAC code does not provide a * function to get the length of an incoming packet and a * separate call to actually get the packet data. It does this * because they didn't add any code to keep the hardware's * receive length and data FIFOs in sync. Instead, they require * that you send a maximal length buffer so that they can read * the length and data FIFOs in a single chunk of code so that * they can't get out of sync. So, we need to allocate an skb * that can hold a maximal sized packet. The OS independent * code needs to see the data 32/64-bit aligned, so we tack on an * extra four just in case we need to do an skb_reserve to get * it that way. */ len = XEM_MAX_FRAME_SIZE; if (!(skb = alloc_skb(len + ALIGNMENT, GFP_ATOMIC))) { /* Couldn't get memory. */ lp->stats.rx_dropped++; printk(KERN_ERR "%s: Could not allocate receive buffer.\n", dev->name); return; } /* * A new skb should have the data word aligned, but this code is * here just in case that isn't true... Calculate how many * bytes we should reserve to get the data to start on a word * boundary. */ align = BUFFER_ALIGN(skb->data); if (align) skb_reserve(skb, align); Result = XEmac_FifoRecv(&lp->Emac, (u8 *) skb->data, &len); if (Result != XST_SUCCESS) { int need_reset = status_requires_reset(Result); lp->stats.rx_errors++; dev_kfree_skb(skb); printk(KERN_ERR "%s: Could not receive buffer, error=%d%s.\n", dev->name, Result, need_reset ? ", resetting device." : ""); if (need_reset) { spin_lock(reset_lock); reset(dev, UNKNOWN_DUPLEX); spin_unlock(reset_lock); } return; } skb_put(skb, len); /* Tell the skb how much data we got. */ skb->dev = dev; /* Fill out required meta-data. */ skb->protocol = eth_type_trans(skb, dev); skb->ip_summed = CHECKSUM_NONE; lp->stats.rx_packets++; lp->stats.rx_bytes += len; netif_rx(skb); /* Send the packet upstream. */ } /* The callback function for errors. */ static void ErrorHandler(void *CallbackRef, XStatus Code) { struct net_device *dev = (struct net_device *) CallbackRef; int need_reset = status_requires_reset(Code); unsigned long flags; /* ignore some errors */ if (Code == XST_DMA_ERROR) return; printk(KERN_ERR "%s: device error %d%s\n", dev->name, Code,need_reset ? ", resetting device." : ""); if (need_reset) { spin_lock_irqsave(reset_lock, flags); reset(dev, UNKNOWN_DUPLEX); spin_unlock_irqrestore(reset_lock, flags); } } static int descriptor_init(struct net_device *dev) { struct net_local *lp = (struct net_local *) dev->priv; int i, recvsize, sendsize; int dftsize; u32 *recvpoolptr, *sendpoolptr; void *recvpoolphy, *sendpoolphy; /* calc size of descriptor space pool; alloc from non-cached memory */ dftsize = (XEM_DFT_RECV_DESC + XEM_DFT_SEND_DESC) * sizeof (XBufDescriptor); lp->desc_space = consistent_alloc(GFP_ATOMIC, dftsize, &lp->desc_space_handle); if (lp->desc_space == 0) { return -1; } lp->desc_space_size = dftsize; lp->ddrSize = XEM_DFT_SEND_DESC * (XEM_MAX_FRAME_SIZE + ALIGNMENT); lp->ddrOffset = 0; lp->ddrVirtPtr = kmalloc(lp->ddrSize, GFP_ATOMIC); if (lp->ddrVirtPtr == 0) return -1; atomic_set(&lp->availSendBds, XEM_DFT_SEND_DESC); /* calc size of send and recv descriptor space */ recvsize = XEM_DFT_RECV_DESC * sizeof (XBufDescriptor); sendsize = XEM_DFT_SEND_DESC * sizeof (XBufDescriptor); recvpoolptr = lp->desc_space; sendpoolptr = (void *) ((u32) lp->desc_space + recvsize); recvpoolphy = (void *) lp->desc_space_handle; sendpoolphy = (void *) ((u32) lp->desc_space_handle + recvsize); /* add ptr to descriptor space to the driver */ XEmac_SetSgRecvSpace(&lp->Emac, recvpoolptr, recvsize, recvpoolphy); XEmac_SetSgSendSpace(&lp->Emac, sendpoolptr, sendsize, sendpoolphy); /* allocate skb's and give them to the dma engine */ for (i = 0; i < XEM_DFT_RECV_DESC; i++) { struct sk_buff *skb; XBufDescriptor bd; int result; u32 skb_vaddr, align; skb = alloc_skb(XEM_MAX_FRAME_SIZE + ALIGNMENT, GFP_ATOMIC); if (skb == 0) { return -1; } align = BUFFER_ALIGN(skb->data); if (align) skb_reserve(skb, align); skb_vaddr = (u32) pci_map_single(NULL, skb->data, XEM_MAX_FRAME_SIZE, PCI_DMA_FROMDEVICE); /* * initialize descriptors and set buffer address * buffer length gets max frame size */ XBufDescriptor_Initialize(&bd); XBufDescriptor_Lock(&bd); XBufDescriptor_SetDestAddress(&bd, skb_vaddr); XBufDescriptor_SetLength(&bd, XEM_MAX_FRAME_SIZE); XBufDescriptor_SetId(&bd, skb); /* * descriptor with attached buffer to the driver and * let it make it ready for frame reception */ result = XEmac_SgRecv(&lp->Emac, &bd); if (result != XST_SUCCESS) { return -1; } } return 0; } void free_descriptor_skb (struct net_device *dev) { struct net_local *lp = (struct net_local *) dev->priv; int i; XBufDescriptor* BdPtr; struct sk_buff* skb; BdPtr = (XBufDescriptor*)lp->Emac.RecvChannel.VirtPtr; for (i=0; idata), XBufDescriptor_GetLength(BdPtr), PCI_DMA_FROMDEVICE); dev_kfree_skb(skb); BdPtr = P_TO_V(&lp->Emac.RecvChannel,XBufDescriptor_GetNextPtr(BdPtr)); } } static void xenet_set_multicast_list(struct net_device *dev) { struct net_local *lp = (struct net_local *) dev->priv; u32 Options; int ret = 0; unsigned long flags; /* * XEmac_Start, XEmac_Stop and XEmac_SetOptions are supposed to * be protected by a semaphore. We do have one area in which * this is a problem. * * xenet_set_multicast_list() is called while the link is up and * interrupts are enabled, so at any point in time we could get * an error that causes our reset() to be called. reset() calls * the aforementioned functions, and we need to call them from * here as well. * * The solution is to make sure that we don't get interrupts or * timers popping while we are in this function. */ spin_lock_irqsave(reset_lock, flags); if ((ret = XEmac_Stop(&lp->Emac)) == XST_SUCCESS) { Options = XEmac_GetOptions(&lp->Emac); /* Clear out the bits we may set. */ Options &= ~(XEM_PROMISC_OPTION | XEM_MULTICAST_OPTION); if (dev->flags & IFF_PROMISC) Options |= XEM_PROMISC_OPTION; #if 0 else { /* * SAATODO: Xilinx is going to add multicast support to their * VxWorks adapter and OS independent layer. After that is * done, this skeleton code should be fleshed out. Note that * IFF_MULTICAST is being masked out from dev->flags in probe, * so that will need to be removed to actually do multidrop. */ if ((dev->flags & IFF_ALLMULTI) || dev->mc_count > MAX_MULTICAST ? ? ?) { xemac_get_all_multicast ? ? ? (); Options |= XEM_MULTICAST_OPTION; } else if (dev->mc_count != 0) { struct dev_mc_list *mc; XEmac_MulticastClear(&lp->Emac); for (mc = dev->mc_list; mc; mc = mc->next) XEmac_MulticastAdd(&lp->Emac, mc->dmi_addr); Options |= XEM_MULTICAST_OPTION; } } #endif /* * The following function will return an error if the EMAC is already * started. We know it isn't started so we can safely ignore the * return value. We cast it to void to make that explicit. */ (void) XEmac_SetOptions(&lp->Emac, Options); /* * XEmac_Start returns an error when: it is already started, the send * and receive handlers are not set, or a scatter-gather DMA list is * missing. None of these can happen at this point, so we cast the * return to void to make that explicit. */ (void) XEmac_Start(&lp->Emac); } /* All done, get those interrupts and timers going again. */ spin_unlock_irqrestore(reset_lock, flags); } static int xenet_ethtool_get_settings (struct net_device *dev, struct ethtool_cmd* ecmd) { int ret; struct net_local *lp = (struct net_local *) dev->priv; u32 mac_options; u8 threshold; u16 mii_cmd; u16 mii_status; u16 mii_advControl; XStatus xs; memset (ecmd, 0, sizeof(struct ethtool_cmd)); mac_options = XEmac_GetOptions (&(lp->Emac)); xs = XEmac_PhyRead(&lp->Emac, lp->mii_addr, MII_BMCR, &mii_cmd); if (xs != XST_SUCCESS) { printk(KERN_ERR "%s: Could not read mii command register; error %d\n", dev->name, xs); return -1; } xs = XEmac_PhyRead(&lp->Emac, lp->mii_addr, MII_BMSR, &mii_status); if (xs != XST_SUCCESS) { printk(KERN_ERR "%s: Could not read mii status register; error %d\n", dev->name, xs); return -1; } xs = XEmac_PhyRead(&lp->Emac, lp->mii_addr, MII_ADVERTISE, &mii_advControl); if (xs != XST_SUCCESS) { printk(KERN_ERR "%s: Could not read mii advertisement control register; error %d\n", dev->name, xs); return -1; } if (mac_options & XEM_FDUPLEX_OPTION) ecmd->duplex = DUPLEX_FULL; else ecmd->duplex = DUPLEX_HALF; if (mii_status & BMSR_100FULL) ecmd->supported |= SUPPORTED_100baseT_Full; if (mii_status & BMSR_100HALF) ecmd->supported |= SUPPORTED_100baseT_Half; if (mii_status & BMSR_10FULL) ecmd->supported |= SUPPORTED_10baseT_Full; if (mii_status & BMSR_10HALF) ecmd->supported |= SUPPORTED_10baseT_Half; if (XEmac_mHasMii(&(lp->Emac))) ecmd->supported |= SUPPORTED_MII; else ecmd->supported &= (~SUPPORTED_MII); if (mii_status & BMSR_ANEGCAPABLE) ecmd->supported |= SUPPORTED_Autoneg; if (mii_status & BMSR_ANEGCOMPLETE) { ecmd->autoneg = AUTONEG_ENABLE; ecmd->advertising |= ADVERTISED_Autoneg; if ((mii_advControl & ADVERTISE_100FULL) || (mii_advControl & ADVERTISE_100HALF)) ecmd->speed = SPEED_100; else ecmd->speed = SPEED_10; } else { ecmd->autoneg = AUTONEG_DISABLE; if (mii_cmd & BMCR_SPEED100) ecmd->speed = SPEED_100; else ecmd->speed = SPEED_10; } if (mii_advControl & ADVERTISE_10FULL) ecmd->advertising |= ADVERTISED_10baseT_Full; if (mii_advControl & ADVERTISE_10HALF) ecmd->advertising |= ADVERTISED_10baseT_Half; if (mii_advControl & ADVERTISE_100FULL) ecmd->advertising |= ADVERTISED_100baseT_Full; if (mii_advControl & ADVERTISE_100HALF) ecmd->advertising |= ADVERTISED_100baseT_Half; ecmd->advertising |= ADVERTISED_MII; ecmd->port = PORT_MII; ecmd->phy_address = lp->Emac.PhysAddress; ecmd->transceiver = XCVR_INTERNAL; if (XEmac_mIsSgDma(&lp->Emac)) { if ((ret = XEmac_GetPktThreshold(&lp->Emac, XEM_SEND, &threshold)) == XST_SUCCESS) { ecmd->maxtxpkt = threshold; } else return -EIO; if ((ret = XEmac_GetPktThreshold(&lp->Emac, XEM_RECV, &threshold)) == XST_SUCCESS) { ecmd->maxrxpkt = threshold; } else return -EIO; } return 0; } static int xenet_ethtool_get_coalesce (struct net_device *dev, struct ethtool_coalesce* ec) { int ret; struct net_local *lp = (struct net_local *) dev->priv; u8 threshold; memset (ec, 0, sizeof(struct ethtool_coalesce)); if ((ret = XEmac_GetPktThreshold(&lp->Emac, XEM_RECV, &threshold)) != XST_SUCCESS) { printk(KERN_INFO "XEmac_GetPktThreshold error %d\n", ret); return -EIO; } ec->rx_max_coalesced_frames = threshold; if ((ret = XEmac_GetPktWaitBound (&lp->Emac, XEM_RECV, &(ec->rx_coalesce_usecs))) != XST_SUCCESS) { printk (KERN_INFO "XEmac_GetPktWaitBound error %d\n", ret); return -EIO; } if ((ret = XEmac_GetPktThreshold(&lp->Emac, XEM_SEND, &threshold)) != XST_SUCCESS) { printk (KERN_INFO "XEmac_GetPktThreshold send error %d\n", ret); return -EIO; } ec->tx_max_coalesced_frames = threshold; if ((ret = XEmac_GetPktWaitBound (&lp->Emac, XEM_SEND, &(ec->tx_coalesce_usecs))) != XST_SUCCESS) { printk (KERN_INFO "XEmac_GetPktWaitBound send error %d\n", ret); return -EIO; } return 0; } static int xenet_ethtool_set_coalesce (struct net_device *dev, struct ethtool_coalesce* ec) { int ret; struct net_local *lp = (struct net_local *) dev->priv; unsigned long flags; spin_lock_irqsave(reset_lock, flags); if ((ret = XEmac_Stop(&lp->Emac)) != XST_SUCCESS) return -EIO; if ((ret = XEmac_SetPktThreshold(&lp->Emac, XEM_RECV, ec->rx_max_coalesced_frames)) != XST_SUCCESS) { printk (KERN_INFO "XEmac_SetPktThreshold error %d\n", ret); return -EIO; } if ((ret = XEmac_SetPktWaitBound (&lp->Emac, XEM_RECV, ec->rx_coalesce_usecs)) != XST_SUCCESS) { printk (KERN_INFO "XEmac_SetPktWaitBound error %d\n", ret); return -EIO; } if ((ret = XEmac_SetPktThreshold(&lp->Emac, XEM_SEND, ec->tx_max_coalesced_frames)) != XST_SUCCESS) { printk (KERN_INFO "XEmac_SetPktThreshold send error %d\n", ret); return -EIO; } if ((ret = XEmac_SetPktWaitBound (&lp->Emac, XEM_SEND, ec->tx_coalesce_usecs)) != XST_SUCCESS) { printk (KERN_INFO "XEmac_SetPktWaitBound send error %d\n", ret); return -EIO; } if ((ret = XEmac_Start(&lp->Emac)) != XST_SUCCESS) return -EIO; spin_unlock_irqrestore(reset_lock, flags); return 0; } static int xenet_ethtool_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo* ed) { memset (ed, 0, sizeof(struct ethtool_drvinfo)); strcpy (ed->driver, DRIVER_NAME); strcpy (ed->version, DRIVER_VERSION); return 0; } static int xenet_ethtool_get_ringparam (struct net_device *dev, struct ethtool_ringparam* erp) { memset (erp, 0, sizeof(struct ethtool_ringparam)); erp->rx_max_pending = XEM_DFT_RECV_DESC; erp->tx_max_pending = XEM_DFT_SEND_DESC; erp->rx_pending = XEM_DFT_RECV_DESC; erp->tx_pending = XEM_DFT_SEND_DESC; return 0; } #define EMAG_REGS_N 32 struct mac_regsDump { struct ethtool_regs hd; u16 data[EMAG_REGS_N]; }; static void xenet_ethtool_get_regs (struct net_device *dev, struct ethtool_regs* regs, void* ret) { struct net_local *lp = (struct net_local *) dev->priv; struct mac_regsDump* dump = (struct mac_regsDump*)regs; int i; XStatus r; dump->hd.version = 0; dump->hd.len = EMAG_REGS_N * sizeof(dump->data); for (i=0; iEmac), lp->mii_addr, i, &(dump->data[i]))) != XST_SUCCESS) { printk (KERN_INFO "PhyRead ERROR %d\n", r); *(int*)ret = -EIO; return; } } *(int*)ret = 0; } static int xenet_do_ethtool_ioctl (struct net_device *dev, struct ifreq *rq) { struct net_local *lp = (struct net_local *) dev->priv; struct ethtool_cmd ecmd; struct ethtool_coalesce eco; struct ethtool_drvinfo edrv; struct ethtool_ringparam erp; struct ethtool_pauseparam epp; struct mac_regsDump regs; int ret = -EOPNOTSUPP; XStatus result; u32 Options; u16 mii_reg_sset; u16 mii_reg_spause; u16 mii_reg_autoneg; u32 flags; if (copy_from_user(&ecmd, rq->ifr_data, sizeof (ecmd.cmd))) return -EFAULT; switch (ecmd.cmd) { case ETHTOOL_GSET: ret = xenet_ethtool_get_settings(dev, &ecmd); if (ret >= 0) { if (copy_to_user(rq->ifr_data, &ecmd, sizeof (ecmd))) ret = -EFAULT; } break; case ETHTOOL_SSET: if (copy_from_user(&ecmd, rq->ifr_data, sizeof (struct ethtool_cmd))) return -EFAULT; mii_reg_sset = 0; if (ecmd.speed == SPEED_100) mii_reg_sset |= BMCR_SPEED100; if (ecmd.duplex == DUPLEX_FULL) mii_reg_sset |= BMCR_FULLDPLX; if (ecmd.autoneg == AUTONEG_ENABLE) { mii_reg_sset |= (BMCR_ANENABLE | BMCR_ANRESTART); spin_lock_irqsave(reset_lock, flags); result = XEmac_PhyWrite(&lp->Emac, lp->mii_addr, MII_BMCR, mii_reg_sset); if (result != XST_SUCCESS) { spin_unlock_irqrestore(reset_lock, flags); ret = -EIO; break; } result = XEmac_PhyRead(&lp->Emac, lp->mii_addr, MII_ADVERTISE, &mii_reg_sset); if (result != XST_SUCCESS) { spin_unlock_irqrestore(reset_lock, flags); ret = -EIO; break; } if (ecmd.speed == SPEED_100) { if (ecmd.duplex == DUPLEX_FULL) { mii_reg_sset |= (ADVERTISE_10FULL | ADVERTISE_100FULL | ADVERTISE_10HALF | ADVERTISE_100HALF); } else { mii_reg_sset |= (ADVERTISE_10HALF | ADVERTISE_100HALF); mii_reg_sset &= ~(ADVERTISE_10FULL | ADVERTISE_100FULL); } } else { if (ecmd.duplex == DUPLEX_FULL) { mii_reg_sset |= (ADVERTISE_10FULL | ADVERTISE_10HALF); mii_reg_sset &= ~(ADVERTISE_100FULL| ADVERTISE_100HALF); } else { mii_reg_sset |= (ADVERTISE_10HALF); mii_reg_sset &= ~(ADVERTISE_100FULL| ADVERTISE_100HALF | ADVERTISE_10FULL); } } result = XEmac_PhyWrite(&lp->Emac, lp->mii_addr, MII_ADVERTISE, mii_reg_sset); spin_unlock_irqrestore(reset_lock, flags); if (result != XST_SUCCESS) { ret = -EIO; break; } } else { mii_reg_sset &= ~(BMCR_ANENABLE | BMCR_ANRESTART); if (ecmd.duplex == DUPLEX_FULL) { mii_reg_sset |= BMCR_FULLDPLX; } else { mii_reg_sset &= ~BMCR_FULLDPLX; } if (ecmd.speed == SPEED_100) { mii_reg_sset |= BMCR_SPEED100; } else { mii_reg_sset &= ~BMCR_SPEED100; } spin_lock_irqsave(reset_lock, flags); result = XEmac_PhyWrite(&lp->Emac, lp->mii_addr, MII_BMCR, mii_reg_sset); spin_unlock_irqrestore(reset_lock, flags); if (result != XST_SUCCESS) { ret = -EIO; break; } } ret = 0; break; case ETHTOOL_GPAUSEPARAM: ret = xenet_ethtool_get_settings(dev, &ecmd); if (ret < 0) { break; } epp.cmd = ecmd.cmd; epp.autoneg = ecmd.autoneg; Options = XEmac_GetOptions(&lp->Emac); if (Options & XEM_INSERT_PAD_OPTION) { epp.rx_pause = 1; epp.tx_pause = 1; } else { epp.rx_pause = 0; epp.tx_pause = 0; } if (copy_to_user(rq->ifr_data, &epp, sizeof(struct ethtool_pauseparam))) ret = -EFAULT; else ret = 0; break; case ETHTOOL_SPAUSEPARAM: if (copy_from_user(&epp, rq->ifr_data, sizeof (struct ethtool_pauseparam))) return -EFAULT; ret = xenet_ethtool_get_settings(dev, &ecmd); if (ret < 0) { break; } epp.cmd = ecmd.cmd; mii_reg_spause = 0; if (epp.autoneg == AUTONEG_ENABLE) { mii_reg_spause |= (BMCR_ANENABLE | BMCR_ANRESTART); } else { if (ecmd.speed == SPEED_100) mii_reg_spause |= BMCR_SPEED100; if (ecmd.duplex == DUPLEX_FULL) mii_reg_spause |= BMCR_FULLDPLX; } spin_lock_irqsave(reset_lock, flags); result = XEmac_PhyWrite(&lp->Emac, lp->mii_addr, MII_BMCR, mii_reg_spause); spin_unlock_irqrestore(reset_lock, flags); if (result != XST_SUCCESS) { ret = -EIO; break; } if (epp.rx_pause != epp.tx_pause) { ret = 0; break; } else { spin_lock_irqsave(reset_lock, flags); (void)XEmac_Stop(&(lp->Emac)); Options = XEmac_GetOptions(&lp->Emac); if (epp.rx_pause) Options |= XEM_INSERT_PAD_OPTION; else Options &= ~XEM_INSERT_PAD_OPTION; (void)XEmac_SetOptions(&lp->Emac,Options); (void)XEmac_Start(&(lp->Emac)); spin_unlock_irqrestore(reset_lock, flags); } ret = 0; break; case ETHTOOL_GCOALESCE: eco.cmd = ecmd.cmd; ret = xenet_ethtool_get_coalesce(dev, &eco); if (ret >= 0) { if (copy_to_user(rq->ifr_data, &eco, sizeof (struct ethtool_coalesce))) ret = -EFAULT; } break; case ETHTOOL_SCOALESCE: if (copy_from_user(&eco, rq->ifr_data, sizeof (struct ethtool_coalesce))) return -EFAULT; ret = xenet_ethtool_set_coalesce(dev, &eco); break; case ETHTOOL_GDRVINFO: edrv.cmd = edrv.cmd; ret = xenet_ethtool_get_drvinfo(dev, &edrv); if (ret >= 0) { if (copy_to_user(rq->ifr_data, &edrv, sizeof (struct ethtool_drvinfo))) ret = -EFAULT; } break; case ETHTOOL_GREGS: regs.hd.cmd = edrv.cmd; xenet_ethtool_get_regs (dev, &(regs.hd), &ret); if (ret >= 0) { if (copy_to_user(rq->ifr_data, ®s, sizeof (struct mac_regsDump))) ret = -EFAULT; } break; case ETHTOOL_GRINGPARAM: erp.cmd = edrv.cmd; ret = xenet_ethtool_get_ringparam (dev, &(erp)); if (ret >= 0) { if (copy_to_user(rq->ifr_data, &erp, sizeof (struct ethtool_ringparam))) ret = -EFAULT; } break; case ETHTOOL_NWAY_RST: epp.cmd = ecmd.cmd; mii_reg_autoneg = 0; mii_reg_autoneg |= (BMCR_ANENABLE | BMCR_ANRESTART); spin_lock_irqsave(reset_lock, flags); result = XEmac_PhyWrite(&lp->Emac, lp->mii_addr, MII_BMCR, mii_reg_autoneg); spin_unlock_irqrestore(reset_lock, flags); if (result != XST_SUCCESS) { ret = -EIO; break; } ret = 0; break; default: break; } return ret; } static int xenet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { struct net_local *lp = (struct net_local *) dev->priv; /* mii_ioctl_data has 4 u16 fields: phy_id, reg_num, val_in & val_out */ struct mii_ioctl_data *data = (struct mii_ioctl_data *) &rq->ifr_data; struct { __u8 threshold; __u32 direction; } thr_arg; struct { __u32 waitbound; __u32 direction; } wbnd_arg ; XStatus ret; unsigned long flags; XStatus Result; switch (cmd) { case SIOCETHTOOL: return xenet_do_ethtool_ioctl(dev, rq); case SIOCGMIIPHY: /* Get address of MII PHY in use. */ case SIOCDEVPRIVATE: /* for binary compat, remove in 2.5 */ data->phy_id = lp->mii_addr; /* Fall Through */ case SIOCGMIIREG: /* Read MII PHY register. */ case SIOCDEVPRIVATE + 1: /* for binary compat, remove in 2.5 */ if (data->phy_id > 31 || data->reg_num > 31) return -ENXIO; /* Stop the PHY timer to prevent reentrancy. */ del_timer_sync(&lp->phy_timer); spin_lock_irqsave(reset_lock, flags); Result = XEmac_PhyRead(&lp->Emac, data->phy_id, data->reg_num, &data->val_out); /* Start the PHY timer up again. */ spin_unlock_irqrestore(reset_lock, flags); lp->phy_timer.expires = jiffies + 2 * HZ; add_timer(&lp->phy_timer); if (Result != XST_SUCCESS) { printk(KERN_ERR "%s: Could not read from PHY, error=%d.\n", dev->name, Result); return (Result == XST_EMAC_MII_BUSY) ? -EBUSY : -EIO; } return 0; case SIOCSMIIREG: /* Write MII PHY register. */ case SIOCDEVPRIVATE + 2: /* for binary compat, remove in 2.5 */ if (!capable(CAP_NET_ADMIN)) return -EPERM; if (data->phy_id > 31 || data->reg_num > 31) return -ENXIO; /* Stop the PHY timer to prevent reentrancy. */ del_timer_sync(&lp->phy_timer); spin_lock_irqsave(reset_lock, flags); Result = XEmac_PhyWrite(&lp->Emac, data->phy_id, data->reg_num, data->val_in); spin_unlock_irqrestore(reset_lock, flags); /* Start the PHY timer up again. */ lp->phy_timer.expires = jiffies + 2 * HZ; add_timer(&lp->phy_timer); if (Result != XST_SUCCESS) { printk(KERN_ERR "%s: Could not write to PHY, error=%d.\n", dev->name, Result); return (Result == XST_EMAC_MII_BUSY) ? -EBUSY : -EIO; } return 0; case SIOCDEVPRIVATE + 3: /* set THRESHOLD */ if (copy_from_user(&thr_arg, rq->ifr_data, sizeof(thr_arg))) { return -EFAULT; } spin_lock_irqsave(reset_lock, flags); if ((ret = XEmac_Stop(&lp->Emac)) != XST_SUCCESS) { return -EIO; } if ((ret = XEmac_SetPktThreshold(&lp->Emac, thr_arg.direction, thr_arg.threshold)) != XST_SUCCESS) { return -EIO; } if ((ret = XEmac_Start(&lp->Emac)) != XST_SUCCESS) { return -EIO; } spin_unlock_irqrestore(reset_lock, flags); return 0; case SIOCDEVPRIVATE + 4: /* set WAITBOUND */ if (copy_from_user(&wbnd_arg, rq->ifr_data, sizeof(wbnd_arg))) { return -EFAULT; } spin_lock_irqsave(reset_lock, flags); if ((ret = XEmac_Stop(&lp->Emac)) != XST_SUCCESS) { return -EIO; } if ((ret = XEmac_SetPktWaitBound(&lp->Emac, wbnd_arg.direction, wbnd_arg.waitbound)) != XST_SUCCESS) { return -EIO; } if ((ret = XEmac_Start(&lp->Emac)) != XST_SUCCESS) { return -EIO; } spin_unlock_irqrestore(reset_lock, flags); return 0; case SIOCDEVPRIVATE + 5: /* get THRESHOLD */ if (copy_from_user(&thr_arg, rq->ifr_data, sizeof(thr_arg))) { return -EFAULT; } if ((ret = XEmac_GetPktThreshold(&lp->Emac, thr_arg.direction, &(thr_arg.threshold))) != XST_SUCCESS) { return -EIO; } if (copy_to_user(rq->ifr_data, &thr_arg, sizeof(thr_arg))) { return -EFAULT; } return 0; case SIOCDEVPRIVATE + 6: /* get WAITBOUND */ if (copy_from_user(&wbnd_arg, rq->ifr_data, sizeof(wbnd_arg))) { return -EFAULT; } if ((ret = XEmac_GetPktWaitBound(&lp->Emac, wbnd_arg.direction, &(wbnd_arg.waitbound))) != XST_SUCCESS) { return -EIO; } if (copy_to_user(rq->ifr_data, &wbnd_arg, sizeof(wbnd_arg))) { return -EFAULT; } return 0; default: return -EOPNOTSUPP; } } static void remove_head_dev(void) { struct net_local *lp; struct net_device *dev; XEmac_Config *cfg; /* Pull the head off of dev_list. */ spin_lock(&dev_lock); dev = dev_list; lp = (struct net_local *) dev->priv; dev_list = lp->next_dev; spin_unlock(&dev_lock); /* Put the physical address back */ cfg = XEmac_GetConfig(lp->index); iounmap((void *) cfg->BaseAddress); cfg->BaseAddress = cfg->PhysAddress; /* Free up the memory. */ if (lp->desc_space) { free_descriptor_skb(dev); consistent_free(lp->desc_space); } if (lp->ddrVirtPtr) { kfree (lp->ddrVirtPtr); } unregister_netdev(dev); kfree(dev); } static int __init probe(int index) { static const unsigned long remap_size = XPAR_EMAC_0_HIGHADDR - XPAR_EMAC_0_BASEADDR + 1; struct net_device *dev; struct net_local *lp; XEmac_Config *cfg; unsigned int irq; u32 maddr; switch (index) { #if defined(XPAR_INTC_0_EMAC_0_VEC_ID) case 0: irq = 31 - XPAR_INTC_0_EMAC_0_VEC_ID; break; #if defined(XPAR_INTC_0_EMAC_1_VEC_ID) case 1: irq = 31 - XPAR_INTC_0_EMAC_1_VEC_ID; break; #if defined(XPAR_INTC_0_EMAC_2_VEC_ID) case 2: irq = 31 - XPAR_INTC_0_EMAC_2_VEC_ID; break; #if defined(XPAR_INTC_0_EMAC_3_VEC_ID) #error Edit this file to add more devices. #endif /* 3 */ #endif /* 2 */ #endif /* 1 */ #endif /* 0 */ default: return -ENODEV; } /* Find the config for our device. */ cfg = XEmac_GetConfig(index); if (!cfg) return -ENODEV; dev = init_etherdev(0, sizeof (struct net_local)); if (!dev) { printk(KERN_ERR "Could not allocate Xilinx enet device %d.\n", index); return -ENOMEM; } SET_MODULE_OWNER(dev); ether_setup(dev); dev->irq = irq; /* Initialize our private data. */ lp = (struct net_local *) dev->priv; memset(lp, 0, sizeof (struct net_local)); lp->index = index; lp->dev = dev; /* Make it the head of dev_list. */ spin_lock(&dev_lock); lp->next_dev = dev_list; dev_list = dev; spin_unlock(&dev_lock); /* Change the addresses to be virtual */ cfg->PhysAddress = cfg->BaseAddress; cfg->BaseAddress = (u32) ioremap(cfg->PhysAddress, remap_size); if (XEmac_Initialize(&lp->Emac, cfg->DeviceId) != XST_SUCCESS) { printk(KERN_ERR "%s: Could not initialize device.\n", dev->name); remove_head_dev(); return -ENODEV; } memcpy(dev->dev_addr, ((bd_t *) __res)->bi_enetaddr, 6); if (XEmac_SetMacAddress(&lp->Emac, dev->dev_addr) != XST_SUCCESS) { /* should not fail right after an initialize */ printk(KERN_ERR "%s: Could not set MAC address.\n", dev->name); remove_head_dev(); return -EIO; } if (XEmac_mIsSgDma(&lp->Emac)) { int result; printk(KERN_ERR "%s: using sgDMA mode.\n", dev->name); XEmac_SetSgRecvHandler(&lp->Emac, dev, SgRecvHandler); XEmac_SetSgSendHandler(&lp->Emac, dev, SgSendHandler); dev->hard_start_xmit = xenet_SgSend; lp->Isr = XEmac_IntrHandlerDma; result = descriptor_init(dev); if (result) { remove_head_dev(); return -EIO; } /* set the packet threshold and waitbound*/ XEmac_SetPktThreshold(&lp->Emac, XEM_SEND, 31); XEmac_SetPktThreshold(&lp->Emac, XEM_RECV, 31); (void) XEmac_SetPktWaitBound(&lp->Emac, XEM_SEND, 5); (void) XEmac_SetPktWaitBound(&lp->Emac, XEM_RECV, 5); /* disable SGEND interrupt */ XEmac_SetOptions(&lp->Emac, XEmac_GetOptions(&lp->Emac) | XEM_NO_SGEND_INT_OPTION); } else { printk(KERN_ERR "%s: using fifo mode.\n", dev->name); XEmac_SetFifoRecvHandler(&lp->Emac, dev, FifoRecvHandler); XEmac_SetFifoSendHandler(&lp->Emac, dev, FifoSendHandler); dev->hard_start_xmit = xenet_FifoSend; lp->Isr = XEmac_IntrHandlerFifo; } XEmac_SetErrorHandler(&lp->Emac, dev, ErrorHandler); /* Scan to find the PHY. */ lp->mii_addr = 0xFF; for (maddr = 0; maddr < 31; maddr++) { XStatus Result; u16 reg; Result = XEmac_PhyRead(&lp->Emac, maddr, MII_BMCR, ®); /* * XEmac_PhyRead is currently returning XST_SUCCESS even * when reading from non-existent addresses. Work * around this by doing a primitive validation on the * control word we get back. */ if (Result == XST_SUCCESS && (reg & BMCR_RESV) == 0) { lp->mii_addr = maddr; break; } } if (lp->mii_addr == 0xFF) { lp->mii_addr = 0; printk(KERN_WARNING "%s: No PHY detected. Assuming a PHY at address %d.\n", dev->name, lp->mii_addr); } dev->open = xenet_open; dev->stop = xenet_close; dev->get_stats = xenet_get_stats; dev->flags &= ~IFF_MULTICAST; dev->set_multicast_list = xenet_set_multicast_list; dev->do_ioctl = xenet_ioctl; dev->tx_timeout = xenet_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HW_CSUM; printk(KERN_INFO "%s: Xilinx EMAC #%d at 0x%08X mapped to 0x%08X, irq=%d\n", dev->name, index, cfg->PhysAddress, cfg->BaseAddress, dev->irq); /* print h/w id */ { u32 id = XIo_In32(cfg->BaseAddress + XIIF_V123B_RESETR_OFFSET); printk("%s: id %d.%d%c; block id %d, type %d\n", dev->name, (id >> 28) & 0xf, (id >> 21) & 0x7f, ((id >> 16) & 0x1f) + 'a', (id >> 16) & 0xff, (id >> 0) & 0xff); } return 0; } static int __init xenet_init(void) { int index = 0; while (probe(index++) == 0) ; /* If we found at least one, report success. */ return (index > 1) ? 0 : -ENODEV; } static void __exit xenet_cleanup(void) { while (dev_list) remove_head_dev(); } EXPORT_NO_SYMBOLS; module_init(xenet_init); module_exit(xenet_cleanup);