/* * Copyright (c) 2015, Freescale Semiconductor, Inc. * Copyright 2016-2020 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include "pin_mux.h" #include "clock_config.h" #include "board.h" #include "fsl_debug_console.h" #include "fsl_enet.h" #include "fsl_phy.h" #if defined(FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET) && FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET #include "fsl_memory.h" #endif #include "fsl_enet_mdio.h" #include "fsl_phyksz8081.h" #include "fsl_gpio.h" #include "fsl_iomuxc.h" /******************************************************************************* * Definitions ******************************************************************************/ #define EXAMPLE_ENET ENET #define EXAMPLE_PHY_ADDRESS 0x02U #define PTP_CLK_FREQ CLOCK_GetRootClockFreq(kCLOCK_Root_Enet_Timer1) /* MDIO operations. */ #define EXAMPLE_MDIO_OPS enet_ops /* PHY operations. */ #define EXAMPLE_PHY_OPS phyksz8081_ops /* ENET clock frequency. */ #define EXAMPLE_CLOCK_FREQ CLOCK_GetRootClockFreq(kCLOCK_Root_Bus) #define ENET_RXBD_NUM (4) #define ENET_TXBD_NUM (4) #define ENET_RXBUFF_SIZE (ENET_FRAME_MAX_FRAMELEN) #define ENET_TXBUFF_SIZE (ENET_FRAME_MAX_FRAMELEN) #define ENET_DATA_LENGTH (1000) #define ENET_TRANSMIT_DATA_NUM (20) #define ENET_PTP_SYNC_MSG 0x00U #ifndef APP_ENET_BUFF_ALIGNMENT #define APP_ENET_BUFF_ALIGNMENT ENET_BUFF_ALIGNMENT #endif #ifndef PHY_AUTONEGO_TIMEOUT_COUNT #define PHY_AUTONEGO_TIMEOUT_COUNT (300000) #endif #ifndef PHY_STABILITY_DELAY_US #define PHY_STABILITY_DELAY_US (0U) #endif /******************************************************************************* * Prototypes ******************************************************************************/ /*! @brief Build ENET PTP event frame. */ static void ENET_BuildPtpEventFrame(void); /******************************************************************************* * Variables ******************************************************************************/ /*! @brief Buffer descriptors should be in non-cacheable region and should be align to "ENET_BUFF_ALIGNMENT". */ AT_NONCACHEABLE_SECTION_ALIGN(enet_rx_bd_struct_t g_rxBuffDescrip[ENET_RXBD_NUM], ENET_BUFF_ALIGNMENT); AT_NONCACHEABLE_SECTION_ALIGN(enet_tx_bd_struct_t g_txBuffDescrip[ENET_TXBD_NUM], ENET_BUFF_ALIGNMENT); /*! @brief The data buffers can be in cacheable region or in non-cacheable region. * If use cacheable region, the alignment size should be the maximum size of "CACHE LINE SIZE" and "ENET_BUFF_ALIGNMENT" * If use non-cache region, the alignment size is the "ENET_BUFF_ALIGNMENT". */ SDK_ALIGN(uint8_t g_rxDataBuff[ENET_RXBD_NUM][SDK_SIZEALIGN(ENET_RXBUFF_SIZE, APP_ENET_BUFF_ALIGNMENT)], APP_ENET_BUFF_ALIGNMENT); SDK_ALIGN(uint8_t g_txDataBuff[ENET_TXBD_NUM][SDK_SIZEALIGN(ENET_TXBUFF_SIZE, APP_ENET_BUFF_ALIGNMENT)], APP_ENET_BUFF_ALIGNMENT); enet_handle_t g_handle; uint8_t g_frame[ENET_DATA_LENGTH + 14]; uint32_t g_testTxNum = 0; enet_frame_info_t txFrameInfoArray[ENET_TXBD_NUM]; /* The MAC address for ENET device. */ uint8_t g_macAddr[6] = {0xd4, 0xbe, 0xd9, 0x45, 0x22, 0x60}; static volatile bool tx_frame_over = false; static enet_frame_info_t txFrameInfo = {0}; /*! @brief Enet PHY and MDIO interface handler. */ static mdio_handle_t mdioHandle = {.ops = &EXAMPLE_MDIO_OPS}; static phy_handle_t phyHandle = {.phyAddr = EXAMPLE_PHY_ADDRESS, .mdioHandle = &mdioHandle, .ops = &EXAMPLE_PHY_OPS}; /******************************************************************************* * Code ******************************************************************************/ void BOARD_InitModuleClock(void) { const clock_sys_pll1_config_t sysPll1Config = { .pllDiv2En = true, }; CLOCK_InitSysPll1(&sysPll1Config); clock_root_config_t rootCfg = {.mux = 4, .div = 10}; /* Generate 50M root clock. */ CLOCK_SetRootClock(kCLOCK_Root_Enet1, &rootCfg); /* Generate 24M PTP clock. */ rootCfg.mux = 0; rootCfg.div = 1; CLOCK_SetRootClock(kCLOCK_Root_Enet_Timer1, &rootCfg); /* Select syspll2pfd3, 528*18/24 = 396M */ CLOCK_InitPfd(kCLOCK_PllSys2, kCLOCK_Pfd3, 24); rootCfg.mux = 7; rootCfg.div = 2; CLOCK_SetRootClock(kCLOCK_Root_Bus, &rootCfg); /* Generate 198M bus clock. */ } void IOMUXC_SelectENETClock(void) { IOMUXC_GPR->GPR4 |= 0x3; /* 50M ENET_REF_CLOCK output to PHY and ENET module. */ } /*! @brief Build Frame for transmit. */ static void ENET_BuildPtpEventFrame(void) { uint8_t mGAddr[6] = {0x01, 0x00, 0x5e, 0x01, 0x01, 0x1}; /* Build for PTP event message frame. */ memcpy(&g_frame[0], &mGAddr[0], 6); /* The six-byte source MAC address. */ memcpy(&g_frame[6], &g_macAddr[0], 6); /* The type/length: if data length is used make sure it's smaller than 1500 */ g_frame[12] = 0x08U; g_frame[13] = 0x00U; g_frame[0x0EU] = 0x40; /* Set the UDP PTP event port number. */ g_frame[0x24U] = (kENET_PtpEventPort >> 8) & 0xFFU; g_frame[0x25U] = kENET_PtpEventPort & 0xFFU; /* Set the UDP protocol. */ g_frame[0x17U] = 0x11U; /* Add ptp event message type: sync message. */ g_frame[0x2AU] = ENET_PTP_SYNC_MSG; /* Add sequence id. */ g_frame[0x48U] = 0; g_frame[0x48U + 1] = 0; } static void enetCallback(ENET_Type *base, enet_handle_t *handle, #if FSL_FEATURE_ENET_QUEUE > 1 uint32_t ringId, #endif /* FSL_FEATURE_ENET_QUEUE > 1 */ enet_event_t event, enet_frame_info_t *frameInfo, void *userData) { /* Get frame info after whole frame transmits out */ if ((event == kENET_TxEvent) && (frameInfo != NULL)) { txFrameInfo = *frameInfo; tx_frame_over = true; } } /*! * @brief Main function */ int main(void) { enet_config_t config; phy_config_t phyConfig = {0}; uint32_t length = 0; uint32_t ptpClock; uint32_t count = 0; bool link = false; bool autonego = false; phy_speed_t speed; phy_duplex_t duplex; status_t result; uint32_t txnumber = 0; enet_ptp_time_t ptpTime; status_t status; uint8_t mGAddr[6] = {0x01, 0x00, 0x5e, 0x01, 0x01, 0x1}; /* Hardware Initialization. */ gpio_pin_config_t gpio_config = {kGPIO_DigitalOutput, 0, kGPIO_NoIntmode}; BOARD_ConfigMPU(); BOARD_InitPins(); BOARD_BootClockRUN(); BOARD_InitDebugConsole(); BOARD_InitModuleClock(); IOMUXC_SelectENETClock(); GPIO_PinInit(GPIO9, 11, &gpio_config); GPIO_PinInit(GPIO12, 12, &gpio_config); /* Pull up the ENET_INT before RESET. */ GPIO_WritePinOutput(GPIO9, 11, 1); GPIO_WritePinOutput(GPIO12, 12, 0); SDK_DelayAtLeastUs(10000, CLOCK_GetFreq(kCLOCK_CpuClk)); GPIO_WritePinOutput(GPIO12, 12, 1); SDK_DelayAtLeastUs(6, CLOCK_GetFreq(kCLOCK_CpuClk)); PRINTF("\r\nENET PTP 1588 example start.\r\n"); /* prepare the buffer configuration. */ enet_buffer_config_t buffConfig[] = {{ ENET_RXBD_NUM, ENET_TXBD_NUM, SDK_SIZEALIGN(ENET_RXBUFF_SIZE, APP_ENET_BUFF_ALIGNMENT), SDK_SIZEALIGN(ENET_TXBUFF_SIZE, APP_ENET_BUFF_ALIGNMENT), &g_rxBuffDescrip[0], &g_txBuffDescrip[0], &g_rxDataBuff[0][0], &g_txDataBuff[0][0], true, true, &txFrameInfoArray[0], }}; ptpClock = PTP_CLK_FREQ; /* Prepare the PTP configure */ enet_ptp_config_t ptpConfig = { kENET_PtpTimerChannel1, // kENET_PtpTimerChannel2, // kENET_PtpTimerChannel3, // kENET_PtpTimerChannel4, ptpClock, }; /* * config.miiMode = kENET_RmiiMode; * config.miiSpeed = kENET_MiiSpeed100M; * config.miiDuplex = kENET_MiiFullDuplex; * config.rxMaxFrameLen = ENET_FRAME_MAX_FRAMELEN; */ ENET_GetDefaultConfig(&config); /* The miiMode should be set according to the different PHY interfaces. */ #ifdef EXAMPLE_PHY_INTERFACE_RGMII config.miiMode = kENET_RgmiiMode; #else config.miiMode = kENET_RmiiMode; #endif phyConfig.phyAddr = EXAMPLE_PHY_ADDRESS; phyConfig.autoNeg = true; mdioHandle.resource.base = EXAMPLE_ENET; mdioHandle.resource.csrClock_Hz = EXAMPLE_CLOCK_FREQ; /* Initialize PHY and wait auto-negotiation over. */ PRINTF("Wait for PHY init...\r\n"); do { status = PHY_Init(&phyHandle, &phyConfig); if (status == kStatus_Success) { PRINTF("Wait for PHY link up...\r\n"); /* Wait for auto-negotiation success and link up */ count = PHY_AUTONEGO_TIMEOUT_COUNT; do { PHY_GetAutoNegotiationStatus(&phyHandle, &autonego); PHY_GetLinkStatus(&phyHandle, &link); if (autonego && link) { break; } } while (--count); if (!autonego) { PRINTF("PHY Auto-negotiation failed. Please check the cable connection and link partner setting.\r\n"); } } } while (!(link && autonego)); #if PHY_STABILITY_DELAY_US /* Wait a moment for PHY status to be stable. */ SDK_DelayAtLeastUs(PHY_STABILITY_DELAY_US, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); #endif /* Change the MII speed and duplex for actual link status. */ PHY_GetLinkSpeedDuplex(&phyHandle, &speed, &duplex); config.miiSpeed = (enet_mii_speed_t)speed; config.miiDuplex = (enet_mii_duplex_t)duplex; /* Initialize ENET. */ ENET_Init(EXAMPLE_ENET, &g_handle, &config, &buffConfig[0], &g_macAddr[0], EXAMPLE_CLOCK_FREQ); /* Configure PTP */ ENET_Ptp1588Configure(EXAMPLE_ENET, &g_handle, &ptpConfig); /* Add to multicast group to receive ptp multicast frame. */ ENET_AddMulticastGroup(EXAMPLE_ENET, &mGAddr[0]); /* Active ENET read. */ ENET_ActiveRead(EXAMPLE_ENET); ENET_BuildPtpEventFrame(); #ifdef KERRY_REG //====================kerry add================== EXAMPLE_ENET->ATCR |= ENET_ATCR_PINPER_MASK | ENET_ATCR_PEREN(1) | ENET_ATCR_EN(1); EXAMPLE_ENET->CHANNEL[0].TCSR |= ENET_TCSR_TPWC(23) | ENET_TCSR_TMODE(0xf) | ENET_TCSR_TIE_MASK; //0101 - Timer Channel is configured for Output Compare - toggle output on compare. EXAMPLE_ENET->CHANNEL[0].TCCR = 1000000; EXAMPLE_ENET->CHANNEL[1].TCSR |= ENET_TCSR_TPWC(4) | ENET_TCSR_TMODE(0x5) | ENET_TCSR_TIE_MASK; //0101 - Timer Channel is configured for Output Compare - toggle output on compare. EXAMPLE_ENET->CHANNEL[1].TCCR = 2000000; EXAMPLE_ENET->CHANNEL[2].TCSR |= ENET_TCSR_TPWC(4) | ENET_TCSR_TMODE(0x5) | ENET_TCSR_TIE_MASK; //0101 - Timer Channel is configured for Output Compare - toggle output on compare. EXAMPLE_ENET->CHANNEL[2].TCCR = 3000000; EXAMPLE_ENET->CHANNEL[3].TCSR |= ENET_TCSR_TPWC(4) | ENET_TCSR_TMODE(0x5) | ENET_TCSR_TIE_MASK; //0101 - Timer Channel is configured for Output Compare - toggle output on compare. EXAMPLE_ENET->CHANNEL[3].TCCR = 4000000; #endif #define EXAMPLE_SYNC #define ENET_NANOSECOND_ONE_SECOND 1000000000U #ifdef EXAMPLE_SYNC { uint32_t next_counter = 0; uint32_t tmp_val = 0; /* clear capture or output compare interrupt status if have. */ ENET_Ptp1588ClearChannelStatus(EXAMPLE_ENET, ptpConfig.channel); /* It is recommended to double check the TMODE field in the * TCSR register to be cleared before the first compare counter * is written into TCCR register. Just add a double check. */ tmp_val = EXAMPLE_ENET->CHANNEL[ptpConfig.channel].TCSR; do { tmp_val &= ~(ENET_TCSR_TMODE_MASK); EXAMPLE_ENET->CHANNEL[ptpConfig.channel].TCSR = tmp_val; tmp_val = EXAMPLE_ENET->CHANNEL[ptpConfig.channel].TCSR; } while (tmp_val & ENET_TCSR_TMODE_MASK); tmp_val = (ENET_NANOSECOND_ONE_SECOND >> 1);//1000000; // ENET_Ptp1588SetChannelCmpValue(EXAMPLE_ENET, ptpConfig.channel, tmp_val); /* Calculate the second the compare event timestamp */ next_counter = tmp_val; /* Compare channel setting. */ ENET_Ptp1588ClearChannelStatus(EXAMPLE_ENET, ptpConfig.channel); ENET_Ptp1588SetChannelOutputPulseWidth(EXAMPLE_ENET, ptpConfig.channel, false, 24, true); /* Write the second compare event timestamp and calculate * the third timestamp. Refer the TCCR register detail in the spec.*/ ENET_Ptp1588SetChannelCmpValue(EXAMPLE_ENET, ptpConfig.channel, next_counter); /* Update next counter */ g_handle.ptpNextCounter = next_counter; } #endif /* while(1) {}*/ //==================kerry add /* Enable Tx Reclaim and set callback to get timestamp */ ENET_SetTxReclaim(&g_handle, true, 0); ENET_SetCallback(&g_handle, enetCallback, NULL); /* Check if the timestamp is running */ while(1) { for (count = 1; count <= 10; count++) { ENET_Ptp1588GetTimer(EXAMPLE_ENET, &g_handle, &ptpTime); PRINTF(" Get the %d-th time", count); PRINTF(" %d second,", (uint32_t)ptpTime.second); PRINTF(" %d nanosecond \r\n", ptpTime.nanosecond); SDK_DelayAtLeastUs(200000, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); } } while (1) { /* Get the Frame size */ result = ENET_GetRxFrameSize(&g_handle, &length, 0); /* Call ENET_ReadFrame when there is a received frame. */ if (length != 0) { /* Received valid frame. Deliver the rx buffer with the size equal to length. */ uint8_t *data = (uint8_t *)malloc(length); uint32_t ts; enet_ptp_time_t rxPtpTime; result = ENET_ReadFrame(EXAMPLE_ENET, &g_handle, data, length, 0, &ts); if (result == kStatus_Success) { ENET_Ptp1588GetTimer(EXAMPLE_ENET, &g_handle, &rxPtpTime); /* If latest timestamp reloads after getting from Rx BD, then second - 1 to make sure the actual Rx * timestamp is accurate */ if (rxPtpTime.nanosecond < ts) { rxPtpTime.second--; } PRINTF(" A frame received. the length %d ", length); PRINTF(" the timestamp is %d second,", (uint32_t)rxPtpTime.second); PRINTF(" %d nanosecond \r\n", ts); PRINTF(" Dest Address %02x:%02x:%02x:%02x:%02x:%02x Src Address %02x:%02x:%02x:%02x:%02x:%02x \r\n", data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11]); } free(data); } else if (result == kStatus_ENET_RxFrameError) { /* Update the received buffer when error happened. */ ENET_ReadFrame(EXAMPLE_ENET, &g_handle, NULL, 0, 0, NULL); } if (g_testTxNum < ENET_TRANSMIT_DATA_NUM) { /* Send a multicast frame when the PHY is link up. */ if (kStatus_Success == PHY_GetLinkStatus(&phyHandle, &link)) { if (link) { g_testTxNum++; tx_frame_over = false; if (kStatus_Success == ENET_SendFrame(EXAMPLE_ENET, &g_handle, &g_frame[0], ENET_DATA_LENGTH, 0, true, NULL)) { txnumber++; /* Wait for Tx over then check timestamp */ while (!tx_frame_over) { } PRINTF("The %d frame transmitted success!", txnumber); if (txFrameInfo.isTsAvail) { PRINTF(" the timestamp is %d second,", (uint32_t)txFrameInfo.timeStamp.second); PRINTF(" %d nanosecond \r\n", txFrameInfo.timeStamp.nanosecond); } else { PRINTF(" Get transmit timestamp failed! \r\n"); } } else { PRINTF(" \r\nTransmit frame failed!\r\n"); } } } } } }