/* $Id: xemacps.c,v 1.1.2.3 2011/05/17 12:00:33 anirudh Exp $ */ /****************************************************************************** * * Copyright (C) 2010 - 2014 Xilinx, Inc. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * Use of the Software is limited solely to applications: * (a) running on a Xilinx device, or * (b) that interact with a Xilinx device through a bus or interconnect. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of the Xilinx shall not be used * in advertising or otherwise to promote the sale, use or other dealings in * this Software without prior written authorization from Xilinx. * ******************************************************************************/ /*****************************************************************************/ /** * * @file xemacps.c * * The XEmacPs driver. Functions in this file are the minimum required functions * for this driver. See xemacps.h for a detailed description of the driver. * *
* MODIFICATION HISTORY: * * Ver Who Date Changes * ----- ---- -------- ------------------------------------------------------- * 1.00a wsy 01/10/10 First release * 2.1 srt 07/15/14 Add support for Zynq Ultrascale Mp GEM specification and * 64-bit changes. * *******************************************************************************/ /***************************** Include Files *********************************/ #include "xemacps.h" /************************** Constant Definitions *****************************/ /**************************** Type Definitions *******************************/ /***************** Macros (Inline Functions) Definitions *********************/ /************************** Function Prototypes ******************************/ void XEmacPs_StubHandler(void); /* Default handler routine */ /************************** Variable Definitions *****************************/ /*****************************************************************************/ /** * Initialize a specific XEmacPs instance/driver. The initialization entails: * - Initialize fields of the XEmacPs instance structure * - Reset hardware and apply default options * - Configure the DMA channels * * The PHY is setup independently from the device. Use the MII or whatever other * interface may be present for setup. * * @param InstancePtr is a pointer to the instance to be worked on. * @param CfgPtr is the device configuration structure containing required * hardware build data. * @param EffectiveAddress is the base address of the device. If address * translation is not utilized, this parameter can be passed in using * CfgPtr->Config.BaseAddress to specify the physical base address. * * @return * - XST_SUCCESS if initialization was successful * ******************************************************************************/ LONG XEmacPs_CfgInitialize(XEmacPs *InstancePtr, XEmacPs_Config * CfgPtr, UINTPTR EffectiveAddress) { /* Verify arguments */ Xil_AssertNonvoid(InstancePtr != NULL); Xil_AssertNonvoid(CfgPtr != NULL); /* Set device base address and ID */ InstancePtr->Config.DeviceId = CfgPtr->DeviceId; InstancePtr->Config.BaseAddress = EffectiveAddress; /* Set callbacks to an initial stub routine */ InstancePtr->SendHandler = ((XEmacPs_Handler)((void*)XEmacPs_StubHandler)); InstancePtr->RecvHandler = ((XEmacPs_Handler)(void*)XEmacPs_StubHandler); InstancePtr->ErrorHandler = ((XEmacPs_ErrHandler)(void*)XEmacPs_StubHandler); /* Reset the hardware and set default options */ InstancePtr->IsReady = XIL_COMPONENT_IS_READY; XEmacPs_Reset(InstancePtr); return (LONG)(XST_SUCCESS); } /*****************************************************************************/ /** * Start the Ethernet controller as follows: * - Enable transmitter if XTE_TRANSMIT_ENABLE_OPTION is set * - Enable receiver if XTE_RECEIVER_ENABLE_OPTION is set * - Start the SG DMA send and receive channels and enable the device * interrupt * * @param InstancePtr is a pointer to the instance to be worked on. * * @return N/A * * @note * Hardware is configured with scatter-gather DMA, the driver expects to start * the scatter-gather channels and expects that the user has previously set up * the buffer descriptor lists. * * This function makes use of internal resources that are shared between the * Start, Stop, and Set/ClearOptions functions. So if one task might be setting * device options while another is trying to start the device, the user is * required to provide protection of this shared data (typically using a * semaphore). * * This function must not be preempted by an interrupt that may service the * device. * ******************************************************************************/ void XEmacPs_Start(XEmacPs *InstancePtr) { u32 Reg; /* Assert bad arguments and conditions */ Xil_AssertVoid(InstancePtr != NULL); Xil_AssertVoid(InstancePtr->IsReady == (u32)XIL_COMPONENT_IS_READY); /* Start DMA */ /* When starting the DMA channels, both transmit and receive sides * need an initialized BD list. */ if (InstancePtr->Version == 2) { Xil_AssertVoid(InstancePtr->RxBdRing.BaseBdAddr != 0); Xil_AssertVoid(InstancePtr->TxBdRing.BaseBdAddr != 0); XEmacPs_WriteReg(InstancePtr->Config.BaseAddress, XEMACPS_RXQBASE_OFFSET, InstancePtr->RxBdRing.BaseBdAddr); XEmacPs_WriteReg(InstancePtr->Config.BaseAddress, XEMACPS_TXQBASE_OFFSET, InstancePtr->TxBdRing.BaseBdAddr); } /* clear any existed int status */ XEmacPs_WriteReg(InstancePtr->Config.BaseAddress, XEMACPS_ISR_OFFSET, XEMACPS_IXR_ALL_MASK); /* Enable transmitter if not already enabled */ if ((InstancePtr->Options & (u32)XEMACPS_TRANSMITTER_ENABLE_OPTION)!=0x00000000U) { Reg = XEmacPs_ReadReg(InstancePtr->Config.BaseAddress, XEMACPS_NWCTRL_OFFSET); if ((!(Reg & XEMACPS_NWCTRL_TXEN_MASK))==TRUE) { XEmacPs_WriteReg(InstancePtr->Config.BaseAddress, XEMACPS_NWCTRL_OFFSET, Reg | (u32)XEMACPS_NWCTRL_TXEN_MASK); } } /* Enable receiver if not already enabled */ if ((InstancePtr->Options & XEMACPS_RECEIVER_ENABLE_OPTION) != 0x00000000U) { Reg = XEmacPs_ReadReg(InstancePtr->Config.BaseAddress, XEMACPS_NWCTRL_OFFSET); if ((!(Reg & XEMACPS_NWCTRL_RXEN_MASK))==TRUE) { XEmacPs_WriteReg(InstancePtr->Config.BaseAddress, XEMACPS_NWCTRL_OFFSET, Reg | (u32)XEMACPS_NWCTRL_RXEN_MASK); } } /* Enable TX and RX interrupts */ XEmacPs_IntEnable(InstancePtr, (XEMACPS_IXR_TX_ERR_MASK | XEMACPS_IXR_RX_ERR_MASK | (u32)XEMACPS_IXR_FRAMERX_MASK | (u32)XEMACPS_IXR_TXCOMPL_MASK)); /* Enable TX Q1 Interrupts */ if (InstancePtr->Version > 2) XEmacPs_IntQ1Enable(InstancePtr, XEMACPS_INTQ1_IXR_ALL_MASK); /* Mark as started */ InstancePtr->IsStarted = XIL_COMPONENT_IS_STARTED; return; } /*****************************************************************************/ /** * Gracefully stop the Ethernet MAC as follows: * - Disable all interrupts from this device * - Stop DMA channels * - Disable the tansmitter and receiver * * Device options currently in effect are not changed. * * This function will disable all interrupts. Default interrupts settings that * had been enabled will be restored when XEmacPs_Start() is called. * * @param InstancePtr is a pointer to the instance to be worked on. * * @note * This function makes use of internal resources that are shared between the * Start, Stop, SetOptions, and ClearOptions functions. So if one task might be * setting device options while another is trying to start the device, the user * is required to provide protection of this shared data (typically using a * semaphore). * * Stopping the DMA channels causes this function to block until the DMA * operation is complete. * ******************************************************************************/ void XEmacPs_Stop(XEmacPs *InstancePtr) { u32 Reg; Xil_AssertVoid(InstancePtr != NULL); Xil_AssertVoid(InstancePtr->IsReady == (u32)XIL_COMPONENT_IS_READY); /* Disable all interrupts */ XEmacPs_WriteReg(InstancePtr->Config.BaseAddress, XEMACPS_IDR_OFFSET, XEMACPS_IXR_ALL_MASK); /* Disable the receiver & transmitter */ Reg = XEmacPs_ReadReg(InstancePtr->Config.BaseAddress, XEMACPS_NWCTRL_OFFSET); Reg &= (u32)(~XEMACPS_NWCTRL_RXEN_MASK); Reg &= (u32)(~XEMACPS_NWCTRL_TXEN_MASK); XEmacPs_WriteReg(InstancePtr->Config.BaseAddress, XEMACPS_NWCTRL_OFFSET, Reg); /* Mark as stopped */ InstancePtr->IsStarted = 0U; } /*****************************************************************************/ /** * Perform a graceful reset of the Ethernet MAC. Resets the DMA channels, the * transmitter, and the receiver. * * Steps to reset * - Stops transmit and receive channels * - Stops DMA * - Configure transmit and receive buffer size to default * - Clear transmit and receive status register and counters * - Clear all interrupt sources * - Clear phy (if there is any previously detected) address * - Clear MAC addresses (1-4) as well as Type IDs and hash value * * All options are placed in their default state. Any frames in the * descriptor lists will remain in the lists. The side effect of doing * this is that after a reset and following a restart of the device, frames * were in the list before the reset may be transmitted or received. * * The upper layer software is responsible for re-configuring (if necessary) * and restarting the MAC after the reset. Note also that driver statistics * are not cleared on reset. It is up to the upper layer software to clear the * statistics if needed. * * When a reset is required, the driver notifies the upper layer software of * this need through the ErrorHandler callback and specific status codes. * The upper layer software is responsible for calling this Reset function * and then re-configuring the device. * * @param InstancePtr is a pointer to the instance to be worked on. * ******************************************************************************/ void XEmacPs_Reset(XEmacPs *InstancePtr) { u32 Reg; u8 i; s8 EmacPs_zero_MAC[6] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; Xil_AssertVoid(InstancePtr != NULL); Xil_AssertVoid(InstancePtr->IsReady == (u32)XIL_COMPONENT_IS_READY); /* Stop the device and reset hardware */ XEmacPs_Stop(InstancePtr); InstancePtr->Options = XEMACPS_DEFAULT_OPTIONS; InstancePtr->Version = XEmacPs_ReadReg(InstancePtr->Config.BaseAddress, 0xFC); InstancePtr->Version = (InstancePtr->Version >> 16) & 0xFFF; /* Setup hardware with default values */ XEmacPs_WriteReg(InstancePtr->Config.BaseAddress, XEMACPS_NWCTRL_OFFSET, (XEMACPS_NWCTRL_STATCLR_MASK | XEMACPS_NWCTRL_MDEN_MASK) & (u32)(~XEMACPS_NWCTRL_LOOPEN_MASK)); XEmacPs_WriteReg(InstancePtr->Config.BaseAddress, XEMACPS_NWCFG_OFFSET, ((u32)XEMACPS_NWCFG_100_MASK | (u32)XEMACPS_NWCFG_FDEN_MASK | (u32)XEMACPS_NWCFG_UCASTHASHEN_MASK)); if (InstancePtr->Version > 2) { XEmacPs_WriteReg(InstancePtr->Config.BaseAddress, XEMACPS_NWCFG_OFFSET, (XEmacPs_ReadReg(InstancePtr->Config.BaseAddress, XEMACPS_NWCFG_OFFSET) | XEMACPS_NWCFG_DWIDTH_64_MASK)); } XEmacPs_WriteReg(InstancePtr->Config.BaseAddress, XEMACPS_DMACR_OFFSET, (((((u32)XEMACPS_RX_BUF_SIZE / (u32)XEMACPS_RX_BUF_UNIT) + (((((u32)XEMACPS_RX_BUF_SIZE % (u32)XEMACPS_RX_BUF_UNIT))!=(u32)0) ? 1U : 0U)) << (u32)(XEMACPS_DMACR_RXBUF_SHIFT)) & (u32)(XEMACPS_DMACR_RXBUF_MASK)) | (u32)XEMACPS_DMACR_RXSIZE_MASK | (u32)XEMACPS_DMACR_TXSIZE_MASK); /* Single bursts */ /* FIXME: Why Single bursts? */ if (InstancePtr->Version > 2) { XEmacPs_WriteReg(InstancePtr->Config.BaseAddress, XEMACPS_DMACR_OFFSET, (XEmacPs_ReadReg(InstancePtr->Config.BaseAddress, XEMACPS_DMACR_OFFSET) | (u32)XEMACPS_DMACR_INCR4_AHB_BURST)); } #if EXTENDED_DESC_MODE XEmacPs_WriteReg(InstancePtr->Config.BaseAddress, XEMACPS_DMACR_OFFSET, (XEmacPs_ReadReg(InstancePtr->Config.BaseAddress, XEMACPS_DMACR_OFFSET) | XEMACPS_DMACR_TXEXTEND_MASK | XEMACPS_DMACR_RXEXTEND_MASK)); #endif XEmacPs_WriteReg(InstancePtr->Config.BaseAddress, XEMACPS_TXSR_OFFSET, 0x0U); XEmacPs_SetQueuePtr(InstancePtr, 0, 0x00U, (u16)XEMACPS_SEND); if (InstancePtr->Version > 2) XEmacPs_SetQueuePtr(InstancePtr, 0, 0x01U, (u16)XEMACPS_SEND); XEmacPs_SetQueuePtr(InstancePtr, 0, 0x00U, (u16)XEMACPS_RECV); XEmacPs_WriteReg(InstancePtr->Config.BaseAddress, XEMACPS_RXSR_OFFSET, 0x0U); XEmacPs_WriteReg(InstancePtr->Config.BaseAddress, XEMACPS_IDR_OFFSET, XEMACPS_IXR_ALL_MASK); Reg = XEmacPs_ReadReg(InstancePtr->Config.BaseAddress, XEMACPS_ISR_OFFSET); XEmacPs_WriteReg(InstancePtr->Config.BaseAddress, XEMACPS_ISR_OFFSET, Reg); XEmacPs_WriteReg(InstancePtr->Config.BaseAddress, XEMACPS_PHYMNTNC_OFFSET, 0x0U); XEmacPs_ClearHash(InstancePtr); for (i = 1U; i < 5U; i++) { (void)XEmacPs_SetMacAddress(InstancePtr, EmacPs_zero_MAC, i); (void)XEmacPs_SetTypeIdCheck(InstancePtr, 0x00000000U, i); } /* clear all counters */ for (i = 0U; i < (u8)((XEMACPS_LAST_OFFSET - XEMACPS_OCTTXL_OFFSET) / 4U); i++) { (void)XEmacPs_ReadReg(InstancePtr->Config.BaseAddress, XEMACPS_OCTTXL_OFFSET + (u32)(((u32)i) * ((u32)4))); } /* Disable the receiver */ Reg = XEmacPs_ReadReg(InstancePtr->Config.BaseAddress, XEMACPS_NWCTRL_OFFSET); Reg &= (u32)(~XEMACPS_NWCTRL_RXEN_MASK); XEmacPs_WriteReg(InstancePtr->Config.BaseAddress, XEMACPS_NWCTRL_OFFSET, Reg); /* Sync default options with hardware but leave receiver and * transmitter disabled. They get enabled with XEmacPs_Start() if * XEMACPS_TRANSMITTER_ENABLE_OPTION and * XEMACPS_RECEIVER_ENABLE_OPTION are set. */ (void)XEmacPs_SetOptions(InstancePtr, InstancePtr->Options & ~((u32)XEMACPS_TRANSMITTER_ENABLE_OPTION | (u32)XEMACPS_RECEIVER_ENABLE_OPTION)); (void)XEmacPs_ClearOptions(InstancePtr, ~InstancePtr->Options); } /******************************************************************************/ /** * This is a stub for the asynchronous callbacks. The stub is here in case the * upper layer forgot to set the handler(s). On initialization, all handlers are * set to this callback. It is considered an error for this handler to be * invoked. * ******************************************************************************/ void XEmacPs_StubHandler(void) { Xil_AssertVoidAlways(); } /*****************************************************************************/ /** * This function sets the start address of the transmit/receive buffer queue. * * @param InstancePtr is a pointer to the instance to be worked on. * @QPtr Address of the Queue to be written * @QueueNum Buffer Queue Index * @Direction Transmit/Recive * * @note * The buffer queue addresses has to be set before starting the transfer, so * this function has to be called in prior to XEmacPs_Start() * ******************************************************************************/ void XEmacPs_SetQueuePtr(XEmacPs *InstancePtr, UINTPTR QPtr, u8 QueueNum, u16 Direction) { /* Assert bad arguments and conditions */ Xil_AssertVoid(InstancePtr != NULL); Xil_AssertVoid(InstancePtr->IsReady == (u32)XIL_COMPONENT_IS_READY); /* If already started, then there is nothing to do */ if (InstancePtr->IsStarted == (u32)XIL_COMPONENT_IS_STARTED) { return; } if (QueueNum == 0x00U) { if (Direction == XEMACPS_SEND) { XEmacPs_WriteReg(InstancePtr->Config.BaseAddress, XEMACPS_TXQBASE_OFFSET, (QPtr & ULONG64_LO_MASK)); } else { XEmacPs_WriteReg(InstancePtr->Config.BaseAddress, XEMACPS_RXQBASE_OFFSET, (QPtr & ULONG64_LO_MASK)); } } else { XEmacPs_WriteReg(InstancePtr->Config.BaseAddress, XEMACPS_TXQ1BASE_OFFSET, (QPtr & ULONG64_LO_MASK)); } #if EXTENDED_DESC_MODE /* Set the MSB of Queue start address */ XEmacPs_WriteReg(InstancePtr->Config.BaseAddress, XEMACPS_MSBBUF_QBASE_OFFSET, (u32)((QPtr & (u32)ULONG64_HI_MASK) >> 32U)); #endif }