blob: c013c4946a7ac561d37d20a92e0dee47290ee764 [file] [log] [blame]
/******************************************************************************
*
* Copyright (C) 2010 - 2015 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
* XILINX 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
* @addtogroup emacps_v3_7
* @{
*
* 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.
*
* <pre>
* 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.
* 3.00 kvn 02/13/15 Modified code for MISRA-C:2012 compliance.
* 3.0 hk 02/20/15 Added support for jumbo frames. Increase AHB burst.
* Disable extended mode. Perform all 64 bit changes under
* check for arch64.
* 3.1 hk 08/10/15 Update upper 32 bit tx and rx queue ptr registers
* 3.5 hk 08/14/17 Update cache coherency information of the interface in
* its config structure.
*
* </pre>
******************************************************************************/
/***************************** 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;
InstancePtr->Config.IsCacheCoherent = CfgPtr->IsCacheCoherent;
/* 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;
InstancePtr->MaxMtuSize = XEMACPS_MTU;
InstancePtr->MaxFrameSize = XEMACPS_MTU + XEMACPS_HDR_SIZE +
XEMACPS_TRL_SIZE;
InstancePtr->MaxVlanFrameSize = InstancePtr->MaxFrameSize +
XEMACPS_HDR_VLAN_SIZE;
InstancePtr->RxBufMask = XEMACPS_RXBUF_LEN_MASK;
/* 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));
Reg = XEmacPs_ReadReg(InstancePtr->Config.BaseAddress,
XEMACPS_NWCFG_OFFSET);
Reg &= XEMACPS_NWCFG_MDCCLKDIV_MASK;
Reg = Reg | (u32)XEMACPS_NWCFG_100_MASK |
(u32)XEMACPS_NWCFG_FDEN_MASK |
(u32)XEMACPS_NWCFG_UCASTHASHEN_MASK;
XEmacPs_WriteReg(InstancePtr->Config.BaseAddress,
XEMACPS_NWCFG_OFFSET, Reg);
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) |
#ifdef __aarch64__
(u32)XEMACPS_DMACR_ADDR_WIDTH_64 |
#endif
(u32)XEMACPS_DMACR_INCR16_AHB_BURST));
}
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_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));
}
#ifdef __aarch64__
if (Direction == XEMACPS_SEND) {
/* Set the MSB of TX Queue start address */
XEmacPs_WriteReg(InstancePtr->Config.BaseAddress,
XEMACPS_MSBBUF_TXQBASE_OFFSET,
(u32)((QPtr & ULONG64_HI_MASK) >> 32U));
} else {
/* Set the MSB of RX Queue start address */
XEmacPs_WriteReg(InstancePtr->Config.BaseAddress,
XEMACPS_MSBBUF_RXQBASE_OFFSET,
(u32)((QPtr & ULONG64_HI_MASK) >> 32U));
}
#endif
}
/** @} */