/** * @file xmc_eth_mac.c * @date 2017-04-17 * * @cond ********************************************************************************************************************* * XMClib v2.1.12 - XMC Peripheral Driver Library * * Copyright (c) 2015-2017, Infineon Technologies AG * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification,are permitted provided that the * following conditions are met: * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following * disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided with the distribution. * * Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY,OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * To improve the quality of the software, users are encouraged to share modifications, enhancements or bug fixes with * Infineon Technologies AG dave@infineon.com). ********************************************************************************************************************* * * Change History * -------------- * * 2015-06-20: * - Initial * * 2015-09-01: * - Add clock gating control in enable/disable APIs * - Add transmit polling if run out of buffers * * 2015-11-30: * - Fix XMC_ETH_MAC_GetRxFrameSize return value in case of errors * * 2016-03-16: * - Fix XMC_ETH_MAC_DisableEvent * * 2016-05-19: * - Changed XMC_ETH_MAC_ReturnTxDescriptor and XMC_ETH_MAC_ReturnRxDescriptor * * 2016-08-30: * - Changed XMC_ETH_MAC_Init() to disable MMC interrupt events * * 2016-11-22: * - Changed XMC_ETH_MAC_Init() to optimize access to bus * * 2017-02-25: * - XMC_ETH_MAC_Enable() and XMC_ETH_MAC_Disable(), fixed compilation warnings * * 2017-03-27: * - Changed XMC_ETH_MAC_Init() to disable PMT and timestamp interrupt events * * 2017-04-02: * - Added XMC_ETH_MAC_InitPTPEx() * - Added XMC_ETH_MAC_SetPTPTime() * - Added XMC_ETH_MAC_UpdateAddend() * - Fixed XMC_ETH_MAC_InitPTP(), XMC_ETH_MAC_UpdatePTPTime(), XMC_ETH_MAC_SetPTPAlarm() * - nanoseconds initializazion * - added polling to wait for setup * * 2017-04-04: * - Changed XMC_ETH_MAC_Init() to disable MMC IPC receive interrupt events * * 2017-04-11: * - Fixed XMC_ETH_MAC_SetPTPAlarm() nanoseconds conversion * * 2017-04-17: * - Changed XMC_ETH_MAC_GetTxTimeStamp() and XMC_ETH_MAC_GetRxTimeStamp() return the timestamp depending on status bit in descriptor * * @endcond */ /******************************************************************************* * HEADER FILES *******************************************************************************/ #include #if defined (ETH0) #include #include /******************************************************************************* * MACROS *******************************************************************************/ /** * ETH MAC clock speed */ #define XMC_ETH_MAC_CLK_SPEED_35MHZ (35000000U) /**< ETH MAC clock speed 35 MHZ */ #define XMC_ETH_MAC_CLK_SPEED_60MHZ (60000000U) /**< ETH MAC clock speed 60 MHZ */ #define XMC_ETH_MAC_CLK_SPEED_100MHZ (100000000U) /**< ETH MAC clock speed 100 MHZ */ #define XMC_ETH_MAC_CLK_SPEED_150MHZ (150000000U) /**< ETH MAC clock speed 150 MHZ */ #define XMC_ETH_MAC_CLK_SPEED_200MHZ (200000000U) /**< ETH MAC clock speed 200 MHZ */ #define XMC_ETH_MAC_CLK_SPEED_250MHZ (250000000U) /**< ETH MAC clock speed 250 MHZ */ /** * ETH MAC MDC divider */ #define XMC_ETH_MAC_MDC_DIVIDER_16 (2U << ETH_GMII_ADDRESS_CR_Pos) /**< MDC clock: ETH clock/16 */ #define XMC_ETH_MAC_MDC_DIVIDER_26 (3U << ETH_GMII_ADDRESS_CR_Pos) /**< MDC clock: ETH clock/26 */ #define XMC_ETH_MAC_MDC_DIVIDER_42 (0U << ETH_GMII_ADDRESS_CR_Pos) /**< MDC clock: ETH clock/42 */ #define XMC_ETH_MAC_MDC_DIVIDER_62 (1U << ETH_GMII_ADDRESS_CR_Pos) /**< MDC clock: ETH clock/62 */ #define XMC_ETH_MAC_MDC_DIVIDER_102 (4U << ETH_GMII_ADDRESS_CR_Pos) /**< MDC clock: ETH clock/102 */ #define XMC_ETH_MAC_MDC_DIVIDER_124 (5U << ETH_GMII_ADDRESS_CR_Pos) /**< MDC clock: ETH clock/124 */ /** * RDES1 Descriptor RX Packet Control */ #define ETH_MAC_DMA_RDES1_RBS2 (0x1FFF0000U) /**< Receive buffer 2 size */ #define ETH_MAC_DMA_RDES1_RER (0x00008000U) /**< Receive end of ring */ #define ETH_MAC_DMA_RDES1_RCH (0x00004000U) /**< Second address chained */ #define ETH_MAC_DMA_RDES1_RBS1 (0x00001FFFU) /**< Receive buffer 1 size */ /** * Interrupt masking */ #define ETH_MAC_DISABLE_MMC_INTERRUPT_MSK (0x03ffffffU) /**< Bit mask to disable MMMC transmit and receive interrupts */ #define ETH_MAC_DISABLE_MMC_IPC_RECEIVE_INTERRUPT_MSK (0x3fff3fffU) /**< Bit mask to disable MMC IPC Receive Checksum Offload Interrupt Mask */ /** * Normal MAC events */ #define ETH_MAC_EVENT_NORMAL (XMC_ETH_MAC_EVENT_TRANSMIT |\ XMC_ETH_MAC_EVENT_TRANSMIT_BUFFER_UNAVAILABLE |\ XMC_ETH_MAC_EVENT_RECEIVE |\ XMC_ETH_MAC_EVENT_EARLY_RECEIVE) /** * Abnormal MAC events */ #define ETH_MAC_EVENT_ABNORMAL (XMC_ETH_MAC_EVENT_TRANSMIT_PROCESS_STOPPED |\ XMC_ETH_MAC_EVENT_TRANSMIT_JABBER_TIMEOUT |\ XMC_ETH_MAC_EVENT_RECEIVE_OVERFLOW |\ XMC_ETH_MAC_EVENT_TRANSMIT_UNDERFLOW |\ XMC_ETH_MAC_EVENT_RECEIVE_BUFFER_UNAVAILABLE |\ XMC_ETH_MAC_EVENT_RECEIVE_PROCESS_STOPPED |\ XMC_ETH_MAC_EVENT_RECEIVE_WATCHDOG_TIMEOUT |\ XMC_ETH_MAC_EVENT_EARLY_TRANSMIT |\ XMC_ETH_MAC_EVENT_BUS_ERROR) /* Definition needed in case of device header file previous to v1.5.1*/ #ifndef ETH_BUS_MODE_ATDS_Msk #define ETH_BUS_MODE_ATDS_Msk (0x00000080UL) #endif /******************************************************************************* * API IMPLEMENTATION *******************************************************************************/ /* Check if the event passed is a normal event */ __STATIC_INLINE bool XCM_ETH_MAC_IsNormalEvent(uint32_t event) { return (bool)((event & ((uint32_t)XMC_ETH_MAC_EVENT_TRANSMIT | (uint32_t)XMC_ETH_MAC_EVENT_TRANSMIT_BUFFER_UNAVAILABLE | (uint32_t)XMC_ETH_MAC_EVENT_RECEIVE | (uint32_t)XMC_ETH_MAC_EVENT_EARLY_RECEIVE)) != (uint32_t)0); } /* Check if the event passed is an abnormal event */ __STATIC_INLINE bool XCM_ETH_MAC_IsAbnormalEvent(uint32_t event) { return (bool)((event & ((uint32_t)XMC_ETH_MAC_EVENT_TRANSMIT_PROCESS_STOPPED | (uint32_t)XMC_ETH_MAC_EVENT_TRANSMIT_JABBER_TIMEOUT | (uint32_t)XMC_ETH_MAC_EVENT_RECEIVE_OVERFLOW | (uint32_t)XMC_ETH_MAC_EVENT_TRANSMIT_UNDERFLOW | (uint32_t)XMC_ETH_MAC_EVENT_RECEIVE_BUFFER_UNAVAILABLE | (uint32_t)XMC_ETH_MAC_EVENT_RECEIVE_PROCESS_STOPPED | (uint32_t)XMC_ETH_MAC_EVENT_RECEIVE_WATCHDOG_TIMEOUT | (uint32_t)XMC_ETH_MAC_EVENT_EARLY_TRANSMIT | (uint32_t)XMC_ETH_MAC_EVENT_BUS_ERROR)) != (uint32_t)0); } #ifdef XMC_ASSERT_ENABLE /* Check if the passed argument is a valid ETH module */ __STATIC_INLINE bool XMC_ETH_MAC_IsValidModule(ETH_GLOBAL_TypeDef *const eth) { return (eth == ETH0); } #endif /* ETH MAC initialize */ XMC_ETH_MAC_STATUS_t XMC_ETH_MAC_Init(XMC_ETH_MAC_t *const eth_mac) { XMC_ETH_MAC_STATUS_t status; XMC_ASSERT("XMC_ETH_MAC_Init: eth_mac is invalid", XMC_ETH_MAC_IsValidModule(eth_mac->regs)); XMC_ETH_MAC_Enable(eth_mac); XMC_ETH_MAC_Reset(eth_mac); status = XMC_ETH_MAC_SetManagmentClockDivider(eth_mac); XMC_ETH_MAC_SetAddress(eth_mac, eth_mac->address); /* Initialize MAC configuration */ eth_mac->regs->MAC_CONFIGURATION = (uint32_t)ETH_MAC_CONFIGURATION_IPC_Msk; /* Initialize Filter registers */ eth_mac->regs->FLOW_CONTROL = ETH_FLOW_CONTROL_DZPQ_Msk; /* Disable Zero Quanta Pause */ eth_mac->regs->OPERATION_MODE = (uint32_t)ETH_OPERATION_MODE_RSF_Msk | (uint32_t)ETH_OPERATION_MODE_TSF_Msk | (uint32_t)ETH_OPERATION_MODE_OSF_Msk; /* Increase enhanced descriptor to 8 WORDS, required when the Advanced Time-Stamp feature or Full IPC Offload Engine is enabled */ eth_mac->regs->BUS_MODE = (uint32_t)ETH_BUS_MODE_ATDS_Msk | (uint32_t)ETH_BUS_MODE_AAL_Msk | /* the AHB interface generates all bursts aligned to the start address LS bits */ (uint32_t)ETH_BUS_MODE_FB_Msk | /* DMA attempts to execute fixed-length Burst transfers on the AHB Master interface */ (uint32_t)(0x20 << ETH_BUS_MODE_PBL_Pos); /* maximum Burst length */ /* Initialize DMA Descriptors */ XMC_ETH_MAC_InitRxDescriptors(eth_mac); XMC_ETH_MAC_InitTxDescriptors(eth_mac); /* Clear interrupts */ eth_mac->regs->STATUS = 0xFFFFFFFFUL; /* Disable MMC interrupt events */ eth_mac->regs->MMC_TRANSMIT_INTERRUPT_MASK = ETH_MAC_DISABLE_MMC_INTERRUPT_MSK; eth_mac->regs->MMC_RECEIVE_INTERRUPT_MASK = ETH_MAC_DISABLE_MMC_INTERRUPT_MSK; eth_mac->regs->MMC_IPC_RECEIVE_INTERRUPT_MASK = ETH_MAC_DISABLE_MMC_IPC_RECEIVE_INTERRUPT_MSK; /* Disable PMT and timestamp interrupt events */ eth_mac->regs->INTERRUPT_MASK = ETH_INTERRUPT_MASK_PMTIM_Msk | ETH_INTERRUPT_MASK_TSIM_Msk; eth_mac->frame_end = NULL; return status; } /* Initialize RX descriptors */ void XMC_ETH_MAC_InitRxDescriptors(XMC_ETH_MAC_t *const eth_mac) { uint32_t i; uint32_t next; XMC_ASSERT("XMC_ETH_MAC_InitRxDescriptors: eth_mac is invalid", XMC_ETH_MAC_IsValidModule(eth_mac->regs)); /* * Chained structure (ETH_MAC_DMA_RDES1_RCH), second address in the descriptor * (buffer2) is the next descriptor address */ for (i = 0U; i < eth_mac->num_rx_buf; ++i) { eth_mac->rx_desc[i].status = (uint32_t)ETH_MAC_DMA_RDES0_OWN; eth_mac->rx_desc[i].length = (uint32_t)ETH_MAC_DMA_RDES1_RCH | (uint32_t)XMC_ETH_MAC_BUF_SIZE; eth_mac->rx_desc[i].buffer1 = (uint32_t)&(eth_mac->rx_buf[i * XMC_ETH_MAC_BUF_SIZE]); next = i + 1U; if (next == eth_mac->num_rx_buf) { next = 0U; } eth_mac->rx_desc[i].buffer2 = (uint32_t)&(eth_mac->rx_desc[next]); } eth_mac->regs->RECEIVE_DESCRIPTOR_LIST_ADDRESS = (uint32_t)&(eth_mac->rx_desc[0]); eth_mac->rx_index = 0U; } /* Initialize TX descriptors */ void XMC_ETH_MAC_InitTxDescriptors(XMC_ETH_MAC_t *const eth_mac) { uint32_t i; uint32_t next; XMC_ASSERT("XMC_ETH_MAC_InitTxDescriptors: eth_mac is invalid", XMC_ETH_MAC_IsValidModule(eth_mac->regs)); /* Chained structure (ETH_MAC_DMA_TDES0_TCH), second address in the descriptor (buffer2) is the next descriptor address */ for (i = 0U; i < eth_mac->num_tx_buf; ++i) { eth_mac->tx_desc[i].status = ETH_MAC_DMA_TDES0_TCH | ETH_MAC_DMA_TDES0_LS | ETH_MAC_DMA_TDES0_FS; eth_mac->tx_desc[i].buffer1 = (uint32_t)&(eth_mac->tx_buf[i * XMC_ETH_MAC_BUF_SIZE]); next = i + 1U; if (next == eth_mac->num_tx_buf) { next = 0U; } eth_mac->tx_desc[i].buffer2 = (uint32_t)&(eth_mac->tx_desc[next]); } eth_mac->regs->TRANSMIT_DESCRIPTOR_LIST_ADDRESS = (uint32_t)&(eth_mac->tx_desc[0]); eth_mac->tx_index = 0U; } /* Set address perfect filter */ void XMC_ETH_MAC_SetAddressPerfectFilter(XMC_ETH_MAC_t *const eth_mac, uint8_t index, const uint64_t addr, uint32_t flags) { __IO uint32_t *reg; XMC_ASSERT("XMC_ETH_MAC_SetAddressPerfectFilter: eth_mac is invalid", XMC_ETH_MAC_IsValidModule(eth_mac->regs)); XMC_ASSERT("XMC_ETH_MAC_SetAddressFilter: index is out of range", ((index > 0) && (index < 4))); reg = &(eth_mac->regs->MAC_ADDRESS0_HIGH); reg[index] = (uint32_t)(addr >> 32U) | flags; reg[index + 1U] = (uint32_t)addr; } /* Set address hash filter */ void XMC_ETH_MAC_SetAddressHashFilter(XMC_ETH_MAC_t *const eth_mac, const uint64_t hash) { eth_mac->regs->HASH_TABLE_HIGH = (uint32_t)(hash >> 32); eth_mac->regs->HASH_TABLE_LOW = (uint32_t)hash; } /* Send frame */ XMC_ETH_MAC_STATUS_t XMC_ETH_MAC_SendFrame(XMC_ETH_MAC_t *const eth_mac, const uint8_t *frame, uint32_t len, uint32_t flags) { XMC_ETH_MAC_STATUS_t status; uint8_t *dst; uint32_t ctrl; XMC_ASSERT("XMC_ETH_MAC_SendFrame:", eth_mac != NULL); XMC_ASSERT("XMC_ETH_MAC_SendFrame:", eth_mac->regs == ETH0); XMC_ASSERT("XMC_ETH_MAC_SendFrame:", (frame != NULL) && (len > 0)); dst = eth_mac->frame_end; if (eth_mac->tx_desc[eth_mac->tx_index].status & ETH_MAC_DMA_TDES0_OWN) { /* Transmitter is busy, wait */ status = XMC_ETH_MAC_STATUS_BUSY; if (eth_mac->regs->STATUS & ETH_STATUS_TU_Msk) { /* Receive buffer unavailable, resume DMA */ eth_mac->regs->STATUS = (uint32_t)ETH_STATUS_TU_Msk; eth_mac->regs->TRANSMIT_POLL_DEMAND = 0U; } } else { if (dst == NULL) { /* Start of a new transmit frame */ dst = (uint8_t *)eth_mac->tx_desc[eth_mac->tx_index].buffer1; eth_mac->tx_desc[eth_mac->tx_index].length = len; } else { /* Sending data fragments in progress */ eth_mac->tx_desc[eth_mac->tx_index].length += len; } memcpy(dst, frame, len); if (flags & (uint32_t)XMC_ETH_MAC_TX_FRAME_FRAGMENT) { /* More data to come, remember current write position */ eth_mac->frame_end = dst; } else { /* Frame is now ready, send it to DMA */ ctrl = eth_mac->tx_desc[eth_mac->tx_index].status | ETH_MAC_DMA_TDES0_CIC; ctrl &= ~(ETH_MAC_DMA_TDES0_IC | ETH_MAC_DMA_TDES0_TTSE); if (flags & (uint32_t)XMC_ETH_MAC_TX_FRAME_EVENT) { ctrl |= ETH_MAC_DMA_TDES0_IC; } if (flags & (uint32_t)XMC_ETH_MAC_TX_FRAME_TIMESTAMP) { ctrl |= ETH_MAC_DMA_TDES0_TTSE; } eth_mac->tx_ts_index = eth_mac->tx_index; eth_mac->tx_desc[eth_mac->tx_index].status = ctrl | ETH_MAC_DMA_TDES0_OWN; eth_mac->tx_index++; if (eth_mac->tx_index == eth_mac->num_tx_buf) { eth_mac->tx_index = 0U; } eth_mac->frame_end = NULL; /* Start frame transmission */ eth_mac->regs->STATUS = (uint32_t)ETH_STATUS_TPS_Msk; eth_mac->regs->TRANSMIT_POLL_DEMAND = 0U; } status = XMC_ETH_MAC_STATUS_OK; } return status; } /* Read frame */ uint32_t XMC_ETH_MAC_ReadFrame(XMC_ETH_MAC_t *const eth_mac, uint8_t *frame, uint32_t len) { uint8_t const *src; XMC_ASSERT("XMC_ETH_MAC_ReadFrame:", eth_mac != NULL); XMC_ASSERT("XMC_ETH_MAC_ReadFrame:", eth_mac->regs == ETH0); XMC_ASSERT("XMC_ETH_MAC_ReadFrame:", (frame != NULL) && (len > 0)); /* Fast-copy data to packet buffer */ src = (uint8_t const *)eth_mac->rx_desc[eth_mac->rx_index].buffer1; memcpy(frame, src, len); /* Return this block back to DMA */ eth_mac->rx_desc[eth_mac->rx_index].status = ETH_MAC_DMA_RDES0_OWN; eth_mac->rx_index++; if (eth_mac->rx_index == eth_mac->num_rx_buf) { eth_mac->rx_index = 0U; } if (eth_mac->regs->STATUS & ETH_STATUS_RU_Msk) { /* Receive buffer unavailable, resume DMA */ eth_mac->regs->STATUS = (uint32_t)ETH_STATUS_RU_Msk; eth_mac->regs->RECEIVE_POLL_DEMAND = 0U; } return (len); } /* Get RX frame size */ uint32_t XMC_ETH_MAC_GetRxFrameSize(XMC_ETH_MAC_t *const eth_mac) { uint32_t status; uint32_t len = 0U; status = eth_mac->rx_desc[eth_mac->rx_index].status; if (status & ETH_MAC_DMA_RDES0_OWN) { /* Owned by DMA */ len = 0U; } else if (((status & ETH_MAC_DMA_RDES0_ES) != 0U) || ((status & ETH_MAC_DMA_RDES0_FS) == 0U) || ((status & ETH_MAC_DMA_RDES0_LS) == 0U)) { /* Error, this block is invalid */ len = 0xFFFFFFFFU; } else { /* Subtract CRC */ len = ((status & ETH_MAC_DMA_RDES0_FL) >> 16U) - 4U; } return len; } /* Set management clock divider */ XMC_ETH_MAC_STATUS_t XMC_ETH_MAC_SetManagmentClockDivider(XMC_ETH_MAC_t *const eth_mac) { uint32_t eth_mac_clk; XMC_ETH_MAC_STATUS_t status; __IO uint32_t *reg; eth_mac_clk = XMC_SCU_CLOCK_GetEthernetClockFrequency(); status = XMC_ETH_MAC_STATUS_OK; reg = &(eth_mac->regs->GMII_ADDRESS); if (eth_mac_clk <= XMC_ETH_MAC_CLK_SPEED_35MHZ) { *reg = XMC_ETH_MAC_MDC_DIVIDER_16; } else if (eth_mac_clk <= XMC_ETH_MAC_CLK_SPEED_60MHZ) { *reg = XMC_ETH_MAC_MDC_DIVIDER_26; } else if (eth_mac_clk <= XMC_ETH_MAC_CLK_SPEED_100MHZ) { *reg = XMC_ETH_MAC_MDC_DIVIDER_42; } else if (eth_mac_clk <= XMC_ETH_MAC_CLK_SPEED_150MHZ) { *reg = XMC_ETH_MAC_MDC_DIVIDER_62; } else if (eth_mac_clk <= XMC_ETH_MAC_CLK_SPEED_200MHZ) { *reg = XMC_ETH_MAC_MDC_DIVIDER_102; } else if (eth_mac_clk <= XMC_ETH_MAC_CLK_SPEED_250MHZ) { *reg = XMC_ETH_MAC_MDC_DIVIDER_124; } else { status = XMC_ETH_MAC_STATUS_ERROR; } return status; } /* ETH MAC enable */ void XMC_ETH_MAC_Enable(XMC_ETH_MAC_t *const eth_mac) { XMC_UNUSED_ARG(eth_mac); XMC_SCU_CLOCK_EnableClock(XMC_SCU_CLOCK_ETH); #if UC_DEVICE != XMC4500 XMC_SCU_CLOCK_UngatePeripheralClock(XMC_SCU_PERIPHERAL_CLOCK_ETH0); #endif XMC_SCU_RESET_DeassertPeripheralReset(XMC_SCU_PERIPHERAL_RESET_ETH0); } /* ETH MAC disable */ void XMC_ETH_MAC_Disable(XMC_ETH_MAC_t *const eth_mac) { XMC_UNUSED_ARG(eth_mac); XMC_SCU_RESET_AssertPeripheralReset(XMC_SCU_PERIPHERAL_RESET_ETH0); #if UC_DEVICE != XMC4500 XMC_SCU_CLOCK_GatePeripheralClock(XMC_SCU_PERIPHERAL_CLOCK_ETH0); #endif XMC_SCU_CLOCK_DisableClock(XMC_SCU_CLOCK_ETH); } /* Read physical layer and obtain status */ XMC_ETH_MAC_STATUS_t XMC_ETH_MAC_ReadPhy(XMC_ETH_MAC_t *eth_mac, uint8_t phy_addr, uint8_t reg_addr, uint16_t *data) { uint32_t retries; XMC_ASSERT("XMC_ETH_MAC_PhyRead: Parameter error", data != NULL); eth_mac->regs->GMII_ADDRESS = (uint32_t)((eth_mac->regs->GMII_ADDRESS & (uint32_t)ETH_GMII_ADDRESS_CR_Msk) | (uint32_t)ETH_GMII_ADDRESS_MB_Msk | (uint32_t)((uint32_t)phy_addr << ETH_GMII_ADDRESS_PA_Pos) | (uint32_t)((uint32_t)reg_addr << ETH_GMII_ADDRESS_MR_Pos)); /* Poll busy bit during max PHY_TIMEOUT time */ retries = 0U; do { if ((eth_mac->regs->GMII_ADDRESS & ETH_GMII_ADDRESS_MB_Msk) == 0U) { *data = (uint16_t)(eth_mac->regs->GMII_DATA & ETH_GMII_DATA_MD_Msk); return XMC_ETH_MAC_STATUS_OK; } ++retries; } while (retries < XMC_ETH_MAC_PHY_MAX_RETRIES); return XMC_ETH_MAC_STATUS_ERROR; } /* Write physical layer and return status */ XMC_ETH_MAC_STATUS_t XMC_ETH_MAC_WritePhy(XMC_ETH_MAC_t *eth_mac, uint8_t phy_addr, uint8_t reg_addr, uint16_t data) { uint32_t retries; eth_mac->regs->GMII_DATA = data; eth_mac->regs->GMII_ADDRESS = (uint32_t)((eth_mac->regs->GMII_ADDRESS & (uint32_t)ETH_GMII_ADDRESS_CR_Msk) | (uint32_t)ETH_GMII_ADDRESS_MB_Msk | (uint32_t)ETH_GMII_ADDRESS_MW_Msk | (uint32_t)((uint32_t)phy_addr << ETH_GMII_ADDRESS_PA_Pos) | (uint32_t)((uint32_t)reg_addr << ETH_GMII_ADDRESS_MR_Pos)); /* Poll busy bit during max PHY_TIMEOUT time */ retries = 0U; do { if ((eth_mac->regs->GMII_ADDRESS & ETH_GMII_ADDRESS_MB_Msk) == 0U) { return XMC_ETH_MAC_STATUS_OK; } ++retries; } while (retries < XMC_ETH_MAC_PHY_MAX_RETRIES); return XMC_ETH_MAC_STATUS_ERROR; } /* Flush TX */ void XMC_ETH_MAC_FlushTx(XMC_ETH_MAC_t *const eth_mac) { eth_mac->regs->OPERATION_MODE &= (uint32_t)~ETH_OPERATION_MODE_ST_Msk; XMC_ETH_MAC_InitTxDescriptors(eth_mac); eth_mac->regs->OPERATION_MODE |= (uint32_t)ETH_OPERATION_MODE_ST_Msk; } /* Flush RX */ void XMC_ETH_MAC_FlushRx(XMC_ETH_MAC_t *const eth_mac) { eth_mac->regs->OPERATION_MODE &= (uint32_t)~ETH_OPERATION_MODE_SR_Msk; XMC_ETH_MAC_InitRxDescriptors(eth_mac); eth_mac->regs->OPERATION_MODE |= (uint32_t)ETH_OPERATION_MODE_SR_Msk; } /* Set wakeup frame filter */ void XMC_ETH_MAC_SetWakeUpFrameFilter(XMC_ETH_MAC_t *const eth_mac, const uint32_t (*const filter)[XMC_ETH_WAKEUP_REGISTER_LENGTH]) { uint32_t i = 0U; /* Fill Remote Wake-up frame filter register with buffer data */ for (i = 0U; i < XMC_ETH_WAKEUP_REGISTER_LENGTH; i++) { /* Write each time to the same register */ eth_mac->regs->REMOTE_WAKE_UP_FRAME_FILTER = (*filter)[i]; } } /* Enable event */ void XMC_ETH_MAC_EnableEvent(XMC_ETH_MAC_t *const eth_mac, uint32_t event) { XMC_ASSERT("XMC_ETH_MAC_EnableDMAEvent: eth_mac is invalid", XMC_ETH_MAC_IsValidModule(eth_mac->regs)); eth_mac->regs->INTERRUPT_MASK &= ~(event >> 16U); event &= (uint16_t)0x7fffU; if (XCM_ETH_MAC_IsNormalEvent(event)) { event |= (uint32_t)ETH_INTERRUPT_ENABLE_NIE_Msk; } if (XCM_ETH_MAC_IsAbnormalEvent(event)) { event |= (uint32_t)ETH_INTERRUPT_ENABLE_AIE_Msk; } eth_mac->regs->INTERRUPT_ENABLE |= event; } /* Disable event */ void XMC_ETH_MAC_DisableEvent(XMC_ETH_MAC_t *const eth_mac, uint32_t event) { XMC_ASSERT("XMC_ETH_MAC_DisableDMAEvent: eth_mac is invalid", XMC_ETH_MAC_IsValidModule(eth_mac->regs)); eth_mac->regs->INTERRUPT_MASK |= event >> 16U; event &= 0x7fffU; eth_mac->regs->INTERRUPT_ENABLE &= ~event; } /* Clear event status */ void XMC_ETH_MAC_ClearEventStatus(XMC_ETH_MAC_t *const eth_mac, uint32_t event) { XMC_ASSERT("XMC_ETH_MAC_ClearEventStatus: eth_mac is invalid", XMC_ETH_MAC_IsValidModule(eth_mac->regs)); if ((eth_mac->regs->STATUS & ETH_STATUS_NIS_Msk) != 0U) { event |= (uint32_t)ETH_STATUS_NIS_Msk; } if ((eth_mac->regs->STATUS & ETH_STATUS_AIS_Msk) != 0U) { event |= (uint32_t)ETH_STATUS_AIS_Msk; } eth_mac->regs->STATUS = event & 0x0001FFFFU; } /* Obtain event status */ uint32_t XMC_ETH_MAC_GetEventStatus(const XMC_ETH_MAC_t *const eth_mac) { uint32_t temp_status = 0; XMC_ASSERT("XMC_ETH_MAC_GetEventStatus: eth_mac is invalid", XMC_ETH_MAC_IsValidModule(eth_mac->regs)); temp_status = (eth_mac->regs->STATUS & (uint32_t)0x7ffUL); return ((uint32_t)((eth_mac->regs->INTERRUPT_STATUS & (ETH_INTERRUPT_MASK_PMTIM_Msk | ETH_INTERRUPT_MASK_TSIM_Msk)) << 16U) | temp_status); } /* Return RX descriptor */ void XMC_ETH_MAC_ReturnRxDescriptor(XMC_ETH_MAC_t *const eth_mac) { eth_mac->rx_desc[eth_mac->rx_index].status |= ETH_MAC_DMA_RDES0_OWN; eth_mac->rx_index++; if (eth_mac->rx_index == eth_mac->num_rx_buf) { eth_mac->rx_index = 0U; } } /* Return TX descriptor */ void XMC_ETH_MAC_ReturnTxDescriptor(XMC_ETH_MAC_t *const eth_mac) { eth_mac->tx_ts_index = eth_mac->tx_index; eth_mac->tx_desc[eth_mac->tx_index].status |= ETH_MAC_DMA_TDES0_CIC |ETH_MAC_DMA_TDES0_OWN; eth_mac->tx_index++; if (eth_mac->tx_index == eth_mac->num_tx_buf) { eth_mac->tx_index = 0U; } eth_mac->frame_end = NULL; } /* Set VLAN tag */ void XMC_ETH_MAC_SetVLANTag(XMC_ETH_MAC_t *const eth_mac, uint16_t tag) { XMC_ASSERT("XMC_ETH_MAC_SetVLANTag: eth_mac is invalid", XMC_ETH_MAC_IsValidModule(eth_mac->regs)); eth_mac->regs->VLAN_TAG = (uint32_t)tag; } /* Initialize PTP */ void XMC_ETH_MAC_InitPTP(XMC_ETH_MAC_t *const eth_mac, uint32_t config) { XMC_ASSERT("XMC_ETH_MAC_InitPTP: eth_mac is invalid", XMC_ETH_MAC_IsValidModule(eth_mac->regs)); /* Mask the time stamp interrupt */ eth_mac->regs->INTERRUPT_MASK |= (uint32_t)ETH_INTERRUPT_MASK_TSIM_Msk; /* Enable time stamp */ eth_mac->regs->TIMESTAMP_CONTROL = ETH_TIMESTAMP_CONTROL_TSENA_Msk; /* Program sub-second increment register based on PTP clock frequency = fSYS/2 */ /* the nanoseconds register has a resolution of ~0.465ns. */ eth_mac->regs->SUB_SECOND_INCREMENT = (uint32_t)((0x80000000U / (float)(XMC_SCU_CLOCK_GetSystemClockFrequency() / 2)) + 0.5F); if ((config & (uint32_t)XMC_ETH_MAC_TIMESTAMP_CONFIG_FINE_UPDATE) != 0U) { /* Program addend register to obtain fSYS/2 from reference clock (fSYS) */ eth_mac->regs->TIMESTAMP_ADDEND = (uint32_t)0x80000000U; /* Addend register update */ eth_mac->regs->TIMESTAMP_CONTROL |= (uint32_t)ETH_TIMESTAMP_CONTROL_TSADDREG_Msk; /* Poll the Timestamp Control register until the bit TSADDREG is cleared */ while (eth_mac->regs->TIMESTAMP_CONTROL & ETH_TIMESTAMP_CONTROL_TSADDREG_Msk); } eth_mac->regs->TIMESTAMP_CONTROL |= config | (uint32_t)ETH_TIMESTAMP_CONTROL_TSINIT_Msk; while (eth_mac->regs->TIMESTAMP_CONTROL & ETH_TIMESTAMP_CONTROL_TSINIT_Msk); } /* Initialize PTP using a given time */ void XMC_ETH_MAC_InitPTPEx(XMC_ETH_MAC_t *const eth_mac, uint32_t config, XMC_ETH_MAC_TIME_t *const time) { XMC_ASSERT("XMC_ETH_MAC_InitPTP: eth_mac is invalid", XMC_ETH_MAC_IsValidModule(eth_mac->regs)); /* Mask the time stamp interrupt */ eth_mac->regs->INTERRUPT_MASK |= (uint32_t)ETH_INTERRUPT_MASK_TSIM_Msk; /* Enable time stamp */ eth_mac->regs->TIMESTAMP_CONTROL = ETH_TIMESTAMP_CONTROL_TSENA_Msk; /* Program sub-second increment register based on PTP clock frequency = fSYS/2 */ /* the nanoseconds register has a resolution of ~0.465ns. */ eth_mac->regs->SUB_SECOND_INCREMENT = (uint32_t)((0x80000000U / (float)(XMC_SCU_CLOCK_GetSystemClockFrequency() / 2)) + 0.5F); if ((config & (uint32_t)XMC_ETH_MAC_TIMESTAMP_CONFIG_FINE_UPDATE) != 0U) { /* Program addend register to obtain fSYS/2 from reference clock (fSYS) */ eth_mac->regs->TIMESTAMP_ADDEND = (uint32_t)0x80000000U; /* Addend register update */ eth_mac->regs->TIMESTAMP_CONTROL |= (uint32_t)ETH_TIMESTAMP_CONTROL_TSADDREG_Msk; /* Poll the Timestamp Control register until the bit TSADDREG is cleared */ while (eth_mac->regs->TIMESTAMP_CONTROL & ETH_TIMESTAMP_CONTROL_TSADDREG_Msk); } /* Initialize the system time */ eth_mac->regs->SYSTEM_TIME_NANOSECONDS_UPDATE = time->nanoseconds; eth_mac->regs->SYSTEM_TIME_SECONDS_UPDATE = time->seconds; eth_mac->regs->TIMESTAMP_CONTROL |= config | (uint32_t)ETH_TIMESTAMP_CONTROL_TSINIT_Msk; while (eth_mac->regs->TIMESTAMP_CONTROL & ETH_TIMESTAMP_CONTROL_TSINIT_Msk); } /* Get PTP time */ void XMC_ETH_MAC_GetPTPTime(XMC_ETH_MAC_t *const eth_mac, XMC_ETH_MAC_TIME_t *const time) { XMC_ASSERT("XMC_ETH_MAC_GetPTPTime: eth_mac is invalid", XMC_ETH_MAC_IsValidModule(eth_mac->regs)); time->nanoseconds = (uint32_t)(eth_mac->regs->SYSTEM_TIME_NANOSECONDS * (1000000000.0F / 0x80000000U)); /* accuracy of 0.46 ns */ time->seconds = eth_mac->regs->SYSTEM_TIME_SECONDS; } /* Set PTP time */ void XMC_ETH_MAC_SetPTPTime(XMC_ETH_MAC_t *const eth_mac, XMC_ETH_MAC_TIME_t *const time) { XMC_ASSERT("XMC_ETH_MAC_GetPTPTime: eth_mac is invalid", XMC_ETH_MAC_IsValidModule(eth_mac->regs)); eth_mac->regs->SYSTEM_TIME_SECONDS_UPDATE = time->seconds; eth_mac->regs->SYSTEM_TIME_NANOSECONDS_UPDATE = time->nanoseconds; /* Initialize precision timer */ ETH0->TIMESTAMP_CONTROL |= ETH_TIMESTAMP_CONTROL_TSINIT_Msk; while (eth_mac->regs->TIMESTAMP_CONTROL & ETH_TIMESTAMP_CONTROL_TSINIT_Msk); } /* Update PTP time */ void XMC_ETH_MAC_UpdatePTPTime(XMC_ETH_MAC_t *const eth_mac, const XMC_ETH_MAC_TIME_t *const time) { uint32_t temp; XMC_ASSERT("XMC_ETH_MAC_UpdatePTPTime: eth_mac is invalid", XMC_ETH_MAC_IsValidModule(eth_mac->regs)); XMC_ASSERT("XMC_ETH_MAC_UpdatePTPTime: time.time_stamp_nanoseconds not in range", (time->nanoseconds < 1000000000.0F)); temp = (uint32_t)(abs(time->nanoseconds) * (0x80000000U / 1000000000.0F)); if (time->nanoseconds < 0) { temp |= (uint32_t)ETH_SYSTEM_TIME_NANOSECONDS_UPDATE_ADDSUB_Msk; } eth_mac->regs->SYSTEM_TIME_NANOSECONDS_UPDATE = temp; eth_mac->regs->SYSTEM_TIME_SECONDS_UPDATE = time->seconds; eth_mac->regs->TIMESTAMP_CONTROL |= (uint32_t)ETH_TIMESTAMP_CONTROL_TSUPDT_Msk; } /* Set PTP alarm */ void XMC_ETH_MAC_SetPTPAlarm(XMC_ETH_MAC_t *const eth_mac, const XMC_ETH_MAC_TIME_t *const time) { XMC_ASSERT("XMC_ETH_MAC_SetPTPAlarm: eth_mac is invalid", XMC_ETH_MAC_IsValidModule(eth_mac->regs)); XMC_ASSERT("XMC_ETH_MAC_SetPTPAlarm: time.time_stamp_nanoseconds not in range", (time->nanoseconds < 1000000000.0F)); eth_mac->regs->TARGET_TIME_NANOSECONDS = (uint32_t)(time->nanoseconds * (0x80000000U / 1000000000.0F)); eth_mac->regs->TARGET_TIME_SECONDS = time->seconds; } /* Adjust PTP clock */ void XMC_ETH_MAC_AdjustPTPClock(XMC_ETH_MAC_t *const eth_mac, uint32_t correction) { XMC_ASSERT("XMC_ETH_MAC_AdjustPTPClock: eth_mac is invalid", XMC_ETH_MAC_IsValidModule(eth_mac->regs)); /* Correction factor is Q31 (0x80000000 = 1.000000000) */ eth_mac->regs->TIMESTAMP_ADDEND = (uint32_t)(((uint64_t)correction * eth_mac->regs->TIMESTAMP_ADDEND) >> 31U); /* Update addend register */ eth_mac->regs->TIMESTAMP_CONTROL |= (uint32_t)ETH_TIMESTAMP_CONTROL_TSADDREG_Msk; /* Poll the Timestamp Control register until the bit TSADDREG is cleared */ while (eth_mac->regs->TIMESTAMP_CONTROL & ETH_TIMESTAMP_CONTROL_TSADDREG_Msk); } /* Update Addend */ void XMC_ETH_MAC_UpdateAddend(XMC_ETH_MAC_t *const eth_mac, uint32_t addend) { XMC_ASSERT("XMC_ETH_MAC_AdjustPTPClock: eth_mac is invalid", XMC_ETH_MAC_IsValidModule(eth_mac->regs)); eth_mac->regs->TIMESTAMP_ADDEND = addend; /* Update addend register */ eth_mac->regs->TIMESTAMP_CONTROL |= (uint32_t)ETH_TIMESTAMP_CONTROL_TSADDREG_Msk; /* Poll the Timestamp Control register until the bit TSADDREG is cleared */ while (eth_mac->regs->TIMESTAMP_CONTROL & ETH_TIMESTAMP_CONTROL_TSADDREG_Msk); } /* Set PTP status */ uint32_t XMC_ETH_MAC_GetPTPStatus(const XMC_ETH_MAC_t *const eth_mac) { XMC_ASSERT("XMC_ETH_MAC_GetPTPStatus: eth_mac is invalid", XMC_ETH_MAC_IsValidModule(eth_mac->regs)); return (eth_mac->regs->TIMESTAMP_STATUS); } /* Get TX time-stamp */ XMC_ETH_MAC_STATUS_t XMC_ETH_MAC_GetRxTimeStamp(XMC_ETH_MAC_t *const eth_mac, XMC_ETH_MAC_TIME_t *const time) { XMC_ETH_MAC_DMA_DESC_t *rx_desc; XMC_ETH_MAC_STATUS_t status; XMC_ASSERT("XMC_ETH_MAC_GetRxTimeStamp: eth_mac is invalid", XMC_ETH_MAC_IsValidModule(eth_mac->regs)); XMC_ASSERT("XMC_ETH_MAC_GetRxTimeStamp: time is invalid", time != NULL); rx_desc = ð_mac->rx_desc[eth_mac->rx_index]; if (rx_desc->status & ETH_MAC_DMA_RDES0_OWN) { status = XMC_ETH_MAC_STATUS_BUSY; } else { if ((rx_desc->status & (ETH_MAC_DMA_RDES0_TSA | ETH_MAC_DMA_RDES0_LS)) == (ETH_MAC_DMA_RDES0_TSA | ETH_MAC_DMA_RDES0_LS)) { time->nanoseconds = (int32_t)rx_desc->time_stamp_nanoseconds; time->seconds = rx_desc->time_stamp_seconds; status = XMC_ETH_MAC_STATUS_OK; } else { status = XMC_ETH_MAC_STATUS_ERROR; } } return status; } /* Get TX time-stamp */ XMC_ETH_MAC_STATUS_t XMC_ETH_MAC_GetTxTimeStamp(XMC_ETH_MAC_t *const eth_mac, XMC_ETH_MAC_TIME_t *const time) { XMC_ETH_MAC_DMA_DESC_t *tx_desc; XMC_ETH_MAC_STATUS_t status; XMC_ASSERT("XMC_ETH_MAC_GetTxTimeStamp: eth_mac is invalid", XMC_ETH_MAC_IsValidModule(eth_mac->regs)); XMC_ASSERT("XMC_ETH_MAC_GetTxTimeStamp: time is invalid", time != NULL); tx_desc = ð_mac->tx_desc[eth_mac->tx_ts_index]; if (tx_desc->status & ETH_MAC_DMA_TDES0_OWN) { status = XMC_ETH_MAC_STATUS_BUSY; } else { if ((tx_desc->status & (ETH_MAC_DMA_TDES0_TTSS | ETH_MAC_DMA_TDES0_LS)) == (ETH_MAC_DMA_TDES0_TTSS | ETH_MAC_DMA_TDES0_LS)) { time->nanoseconds = (int32_t)tx_desc->time_stamp_nanoseconds; time->seconds = tx_desc->time_stamp_seconds; status = XMC_ETH_MAC_STATUS_OK; } else { status = XMC_ETH_MAC_STATUS_ERROR; } } return status; } #endif