diff --git a/os/hal/ports/STM32/LLD/MACv2/driver.mk b/os/hal/ports/STM32/LLD/MACv2/driver.mk new file mode 100644 index 000000000..e746dc14b --- /dev/null +++ b/os/hal/ports/STM32/LLD/MACv2/driver.mk @@ -0,0 +1,9 @@ +ifeq ($(USE_SMART_BUILD),yes) +ifneq ($(findstring HAL_USE_MAC TRUE,$(HALCONF)),) +PLATFORMSRC += $(CHIBIOS)/os/hal/ports/STM32/LLD/MACv2/hal_mac_lld.c +endif +else +PLATFORMSRC += $(CHIBIOS)/os/hal/ports/STM32/LLD/MACv2/hal_mac_lld.c +endif + +PLATFORMINC += $(CHIBIOS)/os/hal/ports/STM32/LLD/MACv2 diff --git a/os/hal/ports/STM32/LLD/MACv2/hal_mac_lld.c b/os/hal/ports/STM32/LLD/MACv2/hal_mac_lld.c new file mode 100644 index 000000000..19d7d8b6a --- /dev/null +++ b/os/hal/ports/STM32/LLD/MACv2/hal_mac_lld.c @@ -0,0 +1,740 @@ +/* + ChibiOS - Copyright (C) 2006..2020 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file MACv2/hal_mac_lld.c + * @brief STM32 low level MAC driver code. + * + * @addtogroup MAC + * @{ + */ + +#include + +#include "hal.h" + +#if HAL_USE_MAC || defined(__DOXYGEN__) + +#include "hal_mii.h" + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define BUFFER_SIZE ((((STM32_MAC_BUFFERS_SIZE - 1) | 3) + 1) / 4) + +/* Fixing inconsistencies in ST headers.*/ +#if !defined(ETH_MACMDIOAR_CR_Div124) && defined(ETH_MACMDIOAR_CR_DIV124) +#define ETH_MACMDIOAR_CR_Div124 ETH_MACMDIOAR_CR_DIV124 +#endif +#if !defined(ETH_MACMDIOAR_CR_Div102) && defined(ETH_MACMDIOAR_CR_DIV102) +#define ETH_MACMDIOAR_CR_Div102 ETH_MACMDIOAR_CR_DIV102 +#endif +#if !defined(ETH_MACMDIOAR_CR_Div62) && defined(ETH_MACMDIOAR_CR_DIV62) +#define ETH_MACMDIOAR_CR_Div62 ETH_MACMDIOAR_CR_DIV62 +#endif +#if !defined(ETH_MACMDIOAR_CR_Div42) && defined(ETH_MACMDIOAR_CR_DIV42) +#define ETH_MACMDIOAR_CR_Div42 ETH_MACMDIOAR_CR_DIV42 +#endif +#if !defined(ETH_MACMDIOAR_CR_Div26) && defined(ETH_MACMDIOAR_CR_DIV26) +#define ETH_MACMDIOAR_CR_Div26 ETH_MACMDIOAR_CR_DIV26 +#endif +#if !defined(ETH_MACMDIOAR_CR_Div16) && defined(ETH_MACMDIOAR_CR_DIV16) +#define ETH_MACMDIOAR_CR_Div16 ETH_MACMDIOAR_CR_DIV16 +#endif + +/* MII divider optimal value.*/ +#if (STM32_HCLK > 300000000) +#error "STM32_HCLK above maximum frequency for ETH operations (300MHz)" +#elif (STM32_HCLK >= 250000000) +#define MACMDIODR_CR ETH_MACMDIOAR_CR_Div124 +#elif (STM32_HCLK >= 150000000) +#define MACMDIODR_CR ETH_MACMDIOAR_CR_Div102 +#elif (STM32_HCLK >= 100000000) +#define MACMDIODR_CR ETH_MACMDIOAR_CR_Div62 +#elif (STM32_HCLK >= 60000000) +#define MACMDIODR_CR ETH_MACMDIOAR_CR_Div42 +#elif (STM32_HCLK >= 35000000) +#define MACMDIODR_CR ETH_MACMDIOAR_CR_Div26 +#elif (STM32_HCLK >= 20000000) +#define MACMDIODR_CR ETH_MACMDIOAR_CR_Div16 +#else +#error "STM32_HCLK below minimum frequency for ETH operations (20MHz)" +#endif + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief Ethernet driver 1. + */ +MACDriver ETHD1; + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +static const uint8_t default_mac_address[] = {0xAA, 0x55, 0x13, + 0x37, 0x01, 0x10}; + +static stm32_eth_rx_descriptor_t __eth_rd[STM32_MAC_RECEIVE_BUFFERS] + __attribute__((aligned(4), __section__(".eth"))); +static stm32_eth_tx_descriptor_t __eth_td[STM32_MAC_TRANSMIT_BUFFERS] + __attribute__((aligned(4), __section__(".eth"))); + +static uint32_t __eth_rb[STM32_MAC_RECEIVE_BUFFERS][BUFFER_SIZE] + __attribute__((aligned(4), __section__(".eth"))); +static uint32_t __eth_tb[STM32_MAC_TRANSMIT_BUFFERS][BUFFER_SIZE] + __attribute__((aligned(4), __section__(".eth"))); + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Writes a PHY register. + * + * @param[in] macp pointer to the @p MACDriver object + * @param[in] reg register number + * @param[in] value new register value + * + * @notapi + */ +void mii_write(MACDriver *macp, uint32_t reg, uint32_t value) { + + ETH->MACMDIODR = value; + ETH->MACMDIOAR = macp->phyaddr | (reg << ETH_MACMDIOAR_RDA_Pos) | MACMDIODR_CR | + ETH_MACMDIOAR_MOC_WR | ETH_MACMDIOAR_MB; + while ((ETH->MACMDIOAR & ETH_MACMDIOAR_MB) != 0) + ; +} + +/** + * @brief Reads a PHY register. + * + * @param[in] macp pointer to the @p MACDriver object + * @param[in] reg register number + * + * @return The PHY register content. + * + * @notapi + */ +uint32_t mii_read(MACDriver *macp, uint32_t reg) { + + ETH->MACMDIOAR = macp->phyaddr | (reg << ETH_MACMDIOAR_RDA_Pos) | MACMDIODR_CR | + ETH_MACMDIOAR_MOC_RD | ETH_MACMDIOAR_MB; + while ((ETH->MACMDIOAR & ETH_MACMDIOAR_MB) != 0) + ; + return ETH->MACMDIODR; +} + +#if !defined(BOARD_PHY_ADDRESS) +/** + * @brief PHY address detection. + * + * @param[in] macp pointer to the @p MACDriver object + */ +static void mii_find_phy(MACDriver *macp) { + uint32_t i; + +#if STM32_MAC_PHY_TIMEOUT > 0 + unsigned n = STM32_MAC_PHY_TIMEOUT; + do { +#endif + for (i = 0U; i <= 31U; i++) { + macp->phyaddr = i << ETH_MACMDIOAR_PA_Pos; + ETH->MACMDIOAR = (i << ETH_MACMDIOAR_RDA_Pos) | MACMDIODR_CR; + ETH->MACMDIODR = (i << ETH_MACMDIODR_RA_Pos) | MACMDIODR_CR; + if ((mii_read(macp, MII_PHYSID1) == (BOARD_PHY_ID >> 16U)) && + ((mii_read(macp, MII_PHYSID2) & 0xFFF0U) == (BOARD_PHY_ID & 0xFFF0U))) { + return; + } + } +#if STM32_MAC_PHY_TIMEOUT > 0 + n--; + } while (n > 0U); +#endif + /* Wrong or defective board.*/ + osalSysHalt("MAC failure"); +} +#endif + +/** + * @brief MAC address setup. + * + * @param[in] p pointer to a six bytes buffer containing the MAC + * address + */ +static void mac_lld_set_address(const uint8_t *p) { + + /* MAC address configuration, only a single address comparator is used, + hash table not used.*/ + ETH->MACA0HR = ((uint32_t)p[5] << 8) | + ((uint32_t)p[4] << 0); + ETH->MACA0LR = ((uint32_t)p[3] << 24) | + ((uint32_t)p[2] << 16) | + ((uint32_t)p[1] << 8) | + ((uint32_t)p[0] << 0); + ETH->MACA1HR = 0; + ETH->MACA1LR = 0; + ETH->MACA2HR = 0; + ETH->MACA2LR = 0; + ETH->MACA3HR = 0; + ETH->MACA3LR = 0; + ETH->MACHT0R = 0; + ETH->MACHT1R = 0; +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +OSAL_IRQ_HANDLER(STM32_ETH_HANDLER) { + uint32_t dmasr; + + OSAL_IRQ_PROLOGUE(); + + dmasr = ETH->DMACSR; + + if (dmasr & ETH_DMACSR_RI) { + /* Data Received.*/ + ETH->DMACSR = ETH_DMACSR_RI; + ETH->DMACIER &= ~ETH_DMACIER_RIE; + osalSysLockFromISR(); + osalThreadDequeueAllI(ÐD1.rdqueue, MSG_RESET); +#if MAC_USE_EVENTS + osalEventBroadcastFlagsI(ÐD1.rdevent, 0); +#endif + osalSysUnlockFromISR(); + } + + if (dmasr & ETH_DMACSR_TI) { + /* Data Transmitted.*/ + ETH->DMACSR = ETH_DMACSR_TI; + osalSysLockFromISR(); + osalThreadDequeueAllI(ÐD1.tdqueue, MSG_RESET); + osalSysUnlockFromISR(); + } + + ETH->DMACSR |= ETH_DMACSR_NIS; + ETH->DMACIER = ETH_DMACIER_NIE | ETH_DMACIER_RIE | ETH_DMACIER_TIE; + + OSAL_IRQ_EPILOGUE(); +} + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level MAC initialization. + * + * @notapi + */ +void mac_lld_init(void) { + unsigned i,j; + + macObjectInit(ÐD1); + ETHD1.link_up = false; + + /* Descriptor tables are initialized in ring mode, note that the first + word is not initialized here but in mac_lld_start().*/ + for (i = 0; i < STM32_MAC_RECEIVE_BUFFERS; i++) { + __eth_rd[i].rdes0 = (uint32_t)__eth_rb[i]; + __eth_rd[i].rdes1 = 0; + __eth_rd[i].rdes2 = 0; + __eth_rd[i].rdes3 = STM32_RDES3_OWN | STM32_RDES3_IOC | STM32_RDES3_BUF1V; + for (j = 0; j < BUFFER_SIZE; j++) { + __eth_rb[i][j] = 825373492; /* telltale "1234" */ + } + } + for (i = 0; i < STM32_MAC_TRANSMIT_BUFFERS; i++) { + __eth_td[i].tdes0 = 0; + __eth_td[i].tdes1 = 0; + __eth_td[i].tdes2 = 0; + __eth_td[i].tdes3 = 0; + for (j = 0; j < BUFFER_SIZE; j++) { + __eth_tb[i][j] = 892745528; /* telltale "5678" */ + } + } + + /* Selection of the RMII or MII mode based on info exported by board.h.*/ +#if defined(STM32H7XX) + SYSCFG->PMCR |= SYSCFG_PMCR_PA1SO; +#if defined(BOARD_PHY_RMII) + SYSCFG->PMCR |= SYSCFG_PMCR_EPIS_SEL_2; + SYSCFG->PMCR &= ~SYSCFG_PMCR_EPIS_SEL_1; + SYSCFG->PMCR &= ~SYSCFG_PMCR_EPIS_SEL_0; +#else + SYSCFG->PMCR &= ~SYSCFG_PMCR_EPIS_SEL_2; + SYSCFG->PMCR &= ~SYSCFG_PMCR_EPIS_SEL_1; + SYSCFG->PMCR &= ~SYSCFG_PMCR_EPIS_SEL_0; +#endif +#else +#error "unsupported STM32 platform for MACv2 driver" +#endif + + /* Reset of the MAC core.*/ + rccResetETH(); + + /* MAC clocks temporary activation.*/ + rccEnableETH(true); + + /* PHY address setup.*/ +#if defined(BOARD_PHY_ADDRESS) + ETHD1.phyaddr = BOARD_PHY_ADDRESS << 11; +#else + mii_find_phy(ÐD1); +#endif + +#if defined(BOARD_PHY_RESET) + /* PHY board-specific reset procedure.*/ + BOARD_PHY_RESET(); +#else + /* PHY soft reset procedure.*/ + mii_write(ÐD1, MII_BMCR, BMCR_RESET); +#if defined(BOARD_PHY_RESET_DELAY) + osalSysPolledDelayX(BOARD_PHY_RESET_DELAY); +#endif + while (mii_read(ÐD1, MII_BMCR) & BMCR_RESET) + ; +#endif + +#if STM32_MAC_ETH1_CHANGE_PHY_STATE + /* PHY in power down mode until the driver will be started.*/ + mii_write(ÐD1, MII_BMCR, mii_read(ÐD1, MII_BMCR) | BMCR_PDOWN); +#endif + + /* MAC clocks stopped again.*/ + rccDisableETH(); +} + +/** + * @brief Configures and activates the MAC peripheral. + * + * @param[in] macp pointer to the @p MACDriver object + * + * @notapi + */ +void mac_lld_start(MACDriver *macp) { + unsigned i; + + /* Resets the state of all descriptors.*/ + for (i = 0; i < STM32_MAC_RECEIVE_BUFFERS; i++) + __eth_rd[i].rdes3 = STM32_RDES3_OWN | STM32_RDES3_IOC | STM32_RDES3_BUF1V; + macp->rdindex = 0; + for (i = 0; i < STM32_MAC_TRANSMIT_BUFFERS; i++) + __eth_td[i].tdes3 = 0; + macp->tdindex = 0; + + /* MAC clocks activation and commanded reset procedure.*/ + rccEnableETH(true); + + /* ISR vector enabled.*/ + nvicEnableVector(STM32_ETH_NUMBER, STM32_MAC_ETH1_IRQ_PRIORITY); + +#if STM32_MAC_ETH1_CHANGE_PHY_STATE + /* PHY in power up mode.*/ + mii_write(macp, MII_BMCR, mii_read(macp, MII_BMCR) & ~BMCR_PDOWN); +#endif + + ETH->DMAMR |= ETH_DMAMR_SWR; + while (ETH->DMAMR & ETH_DMAMR_SWR) + ; + + /* MAC configuration.*/ + ETH->MACCR = ETH_MACCR_DO; + ETH->MACPFR = 0; + ETH->MACTFCR = 0; + ETH->MACRFCR = 0; + ETH->MACVTR = 0; + + /* MAC address setup.*/ + if (macp->config->mac_address == NULL) + mac_lld_set_address(default_mac_address); + else + mac_lld_set_address(macp->config->mac_address); + + /* Transmitter and receiver enabled. + Note that the complete setup of the MAC is performed when the link + status is detected.*/ +#if STM32_MAC_IP_CHECKSUM_OFFLOAD + ETH->MACCR |= ETH_MACCR_IPCO | ETH_MACCR_RE | ETH_MACCR_TE; +#else + ETH->MACCR |= ETH_MACCR_RE | ETH_MACCR_TE; +#endif + + /* DMA general settings.*/ + ETH->DMASBMR = ETH_DMASBMR_AAL; + ETH->DMACCR = ETH_DMACCR_DSL_0BIT; + ETH->DMAMR = ETH_DMAMR_INTM_0 | ETH_DMAMR_PR_8_1 | ETH_DMAMR_TXPR; /* TX:RX 8:1 */ + /* DMA configuration: + Descriptor rings pointers.*/ + ETH->DMACTDLAR = (uint32_t)&__eth_td[0]; + ETH->DMACTDRLR = STM32_MAC_TRANSMIT_BUFFERS-1; + ETH->DMACRDLAR = (uint32_t)&__eth_rd[0]; + ETH->DMACRDRLR = STM32_MAC_RECEIVE_BUFFERS-1; + + /* Enabling required interrupt sources.*/ + ETH->DMACSR = ETH_DMACSR_NIS; + ETH->DMACIER = ETH_DMACIER_NIE | ETH_DMACIER_RIE | ETH_DMACIER_TIE; + + + /* Check because errata on some devices. There should be no need to + disable flushing because the TXFIFO should be empty on macStart().*/ +#if !defined(STM32_MAC_DISABLE_TX_FLUSH) + /* Transmit FIFO flush.*/ + ETH->MTLTQOMR = ETH_MTLTQOMR_FTQ; + while (ETH->MTLTQOMR & ETH_MTLTQOMR_FTQ) + ; +#endif + + /* DMA final configuration and start.*/ + ETH->MTLRQOMR = ETH_MTLRQOMR_DISTCPEF | ETH_MTLRQOMR_RSF; + ETH->MTLTQOMR = ETH_MTLTQOMR_TSF; + ETH->DMACTCR = ETH_DMACTCR_ST | ETH_DMACTCR_TPBL_1PBL; + ETH->DMACRCR = ETH_DMACRCR_SR | ETH_DMACRCR_RPBL_1PBL + | (STM32_MAC_BUFFERS_SIZE << ETH_DMACRCR_RBSZ_Pos + & ETH_DMACRCR_RBSZ); +} + +/** + * @brief Deactivates the MAC peripheral. + * + * @param[in] macp pointer to the @p MACDriver object + * + * @notapi + */ +void mac_lld_stop(MACDriver *macp) { + + if (macp->state != MAC_STOP) { +#if STM32_MAC_ETH1_CHANGE_PHY_STATE + /* PHY in power down mode until the driver will be restarted.*/ + mii_write(macp, MII_BMCR, mii_read(macp, MII_BMCR) | BMCR_PDOWN); +#endif + + /* MAC and DMA stopped.*/ + ETH->MACCR = 0; + ETH->MTLRQOMR = 0; + ETH->DMACIER = 0; + ETH->DMACSR = ETH->DMACSR; + + /* MAC clocks stopped.*/ + rccDisableETH(); + + /* ISR vector disabled.*/ + nvicDisableVector(STM32_ETH_NUMBER); + } +} + +/** + * @brief Returns a transmission descriptor. + * @details One of the available transmission descriptors is locked and + * returned. + * + * @param[in] macp pointer to the @p MACDriver object + * @param[out] tdp pointer to a @p MACTransmitDescriptor structure + * @return The operation status. + * @retval MSG_OK the descriptor has been obtained. + * @retval MSG_TIMEOUT descriptor not available. + * + * @notapi + */ +msg_t mac_lld_get_transmit_descriptor(MACDriver *macp, + MACTransmitDescriptor *tdp) { + stm32_eth_tx_descriptor_t *tdes; + + if (!macp->link_up) + return MSG_TIMEOUT; + + /* Get Current TX descriptor.*/ + tdes = (stm32_eth_tx_descriptor_t *)&__eth_td[macp->tdindex]; + + /* Ensure that descriptor isn't owned by the Ethernet DMA or locked by + another thread.*/ + if ((tdes->tdes3 & (STM32_TDES3_OWN)) | (tdes->tdes1 > 0)) { + return MSG_TIMEOUT; + } + + tdes->tdes0 = (uint32_t )__eth_tb[macp->tdindex]; + /* Marks the current descriptor as locked using a reserved bit.*/ + /*tdes->tdes0 |= STM32_TDES0_LOCKED; */ + tdes->tdes1++; + + /* Next TX descriptor to use.*/ + macp->tdindex++; + if (macp->tdindex >= STM32_MAC_TRANSMIT_BUFFERS) + macp->tdindex = 0; + + /* Set the buffer size and configuration.*/ + tdp->offset = 0; + tdp->size = STM32_MAC_BUFFERS_SIZE; + tdp->physdesc = tdes; + + return MSG_OK; +} + +/** + * @brief Releases a transmit descriptor and starts the transmission of the + * enqueued data as a single frame. + * + * @param[in] tdp the pointer to the @p MACTransmitDescriptor structure + * + * @notapi + */ +void mac_lld_release_transmit_descriptor(MACTransmitDescriptor *tdp) { + + osalDbgAssert(!(tdp->physdesc->tdes3 & STM32_TDES3_OWN), + "attempt to release descriptor already owned by DMA"); + + osalSysLock(); + + /* Unlocks the descriptor and returns it to the DMA engine.*/ + tdp->physdesc->tdes1 = 0; + tdp->physdesc->tdes2 = STM32_TDES2_IOC | (tdp->offset & STM32_TDES2_B1L_MASK); +#if STM32_MAC_IP_CHECKSUM_OFFLOAD + tdp->physdesc->tdes3 = STM32_TDES3_CIC(STM32_MAC_IP_CHECKSUM_OFFLOAD) | + STM32_TDES3_LD | STM32_TDES3_FD | + STM32_TDES3_OWN; +#else + tdp->physdesc->tdes3 = STM32_TDES3_LD | STM32_TDES3_FD | + STM32_TDES3_OWN; +#endif + + /* Wait for the write to tdes3 to go through before resuming the DMA.*/ + __DSB(); + + /* If the DMA engine is stalled then a restart request is issued.*/ + if ((ETH->DMACSR & ETH_DMACSR_TPS) == ETH_DMADSR_TPS_SUSPENDED) { + ETH->DMACSR = ETH_DMACSR_TBU; + } + ETH->DMACTDTPR = 0; + + osalSysUnlock(); +} + +/** + * @brief Returns a receive descriptor. + * + * @param[in] macp pointer to the @p MACDriver object + * @param[out] rdp pointer to a @p MACReceiveDescriptor structure + * @return The operation status. + * @retval MSG_OK the descriptor has been obtained. + * @retval MSG_TIMEOUT descriptor not available. + * + * @notapi + */ +msg_t mac_lld_get_receive_descriptor(MACDriver *macp, + MACReceiveDescriptor *rdp) { + stm32_eth_rx_descriptor_t *rdes; + + /* Get Current RX descriptor.*/ + rdes = (stm32_eth_rx_descriptor_t *)&__eth_rd[macp->rdindex]; + + /* Iterates through received frames until a valid one is found, invalid + frames are discarded.*/ + while (!(rdes->rdes3 & STM32_RDES3_OWN)) { + if (!(rdes->rdes3 & STM32_RDES3_ES) + && !(rdes->rdes2 & STM32_RDES2_DAF) +#if STM32_MAC_IP_CHECKSUM_OFFLOAD + && !(rdes->rdes1 & (STM32_RDES1_IHCE | STM32_RDES1_IPCE)) +#endif + && (rdes->rdes3 & STM32_RDES3_FD) && (rdes->rdes3 & STM32_RDES3_LD)) { + /* Found a valid one.*/ + rdp->offset = 0; + rdp->size = (rdes->rdes3 & STM32_RDES3_PL_MASK) -2; /* Lose CRC */ + rdp->physdesc = rdes; + /* Reposition in ring.*/ + macp->rdindex++; + if (macp->rdindex >= STM32_MAC_RECEIVE_BUFFERS) + macp->rdindex = 0; + + return MSG_OK; + } + /* Invalid frame found, purging.*/ + rdes->rdes3 = STM32_RDES3_OWN | STM32_RDES3_IOC | STM32_RDES3_BUF1V; + /* Reposition in ring.*/ + } + + return MSG_TIMEOUT; +} + +/* + * @brief Releases a receive descriptor. + * @details The descriptor and its buffer are made available for more incoming + * frames. + * + * @param[in] rdp the pointer to the @p MACReceiveDescriptor structure + * + * @notapi + */ +void mac_lld_release_receive_descriptor(MACReceiveDescriptor *rdp) { + + osalDbgAssert(!(rdp->physdesc->rdes3 & STM32_RDES3_OWN), + "attempt to release descriptor already owned by DMA"); + + osalSysLock(); + + /* Give buffer back to the Ethernet DMA.*/ + rdp->physdesc->rdes3 = STM32_RDES3_OWN | STM32_RDES3_IOC | STM32_RDES3_BUF1V; + + /* Wait for the write to rdes3 to go through before resuming the DMA.*/ + __DSB(); + + /* If the DMA engine is stalled then a restart request is issued.*/ + if ((ETH->DMACSR & ETH_DMACSR_RPS) == ETH_DMADSR_RPS_SUSPENDED) { + ETH->DMACSR = ETH_DMACSR_RBU; + } + ETH->DMACRDTPR = 0; + + osalSysUnlock(); +} + +/** + * @brief Updates and returns the link status. + * + * @param[in] macp pointer to the @p MACDriver object + * @return The link status. + * @retval true if the link is active. + * @retval false if the link is down. + * + * @notapi + */ +bool mac_lld_poll_link_status(MACDriver *macp) { + uint32_t maccr, bmsr, bmcr; + + maccr = ETH->MACCR; + + /* PHY CR and SR registers read.*/ + (void)mii_read(macp, MII_BMSR); + bmsr = mii_read(macp, MII_BMSR); + bmcr = mii_read(macp, MII_BMCR); + + /* Check on auto-negotiation mode.*/ + if (bmcr & BMCR_ANENABLE) { + uint32_t lpa; + + /* Auto-negotiation must be finished without faults and link established.*/ + if ((bmsr & (BMSR_LSTATUS | BMSR_RFAULT | BMSR_ANEGCOMPLETE)) != + (BMSR_LSTATUS | BMSR_ANEGCOMPLETE)) + return macp->link_up = false; + + /* Auto-negotiation enabled, checks the LPA register.*/ + lpa = mii_read(macp, MII_LPA); + + /* Check on link speed.*/ + if (lpa & (LPA_100HALF | LPA_100FULL | LPA_100BASE4)) + maccr |= ETH_MACCR_FES; + else + maccr &= ~ETH_MACCR_FES; + + /* Check on link mode.*/ + if (lpa & (LPA_10FULL | LPA_100FULL)) + maccr |= ETH_MACCR_DM; + else + maccr &= ~ETH_MACCR_DM; + } + else { + /* Link must be established.*/ + if (!(bmsr & BMSR_LSTATUS)) + return macp->link_up = false; + + /* Check on link speed.*/ + if (bmcr & BMCR_SPEED100) + maccr |= ETH_MACCR_FES; + else + maccr &= ~ETH_MACCR_FES; + + /* Check on link mode.*/ + if (bmcr & BMCR_FULLDPLX) + maccr |= ETH_MACCR_DM; + else + maccr &= ~ETH_MACCR_DM; + } + + /* Changes the mode in the MAC.*/ + ETH->MACCR = maccr; + + /* Returns the link status.*/ + return macp->link_up = true; +} + +/** + * @brief Writes to a transmit descriptor's stream. + * + * @param[in] tdp pointer to a @p MACTransmitDescriptor structure + * @param[in] buf pointer to the buffer containing the data to be + * written + * @param[in] size number of bytes to be written + * @return The number of bytes written into the descriptor's + * stream, this value can be less than the amount + * specified in the parameter @p size if the maximum + * frame size is reached. + * + * @notapi + */ +size_t mac_lld_write_transmit_descriptor(MACTransmitDescriptor *tdp, + uint8_t *buf, + size_t size) { + + osalDbgAssert(!(tdp->physdesc->tdes3 & STM32_TDES3_OWN), + "attempt to write descriptor already owned by DMA"); + + if (size > tdp->size - tdp->offset) + size = tdp->size - tdp->offset; + + if (size > 0) { + memcpy((uint8_t *)(tdp->physdesc->tdes0) + tdp->offset, buf, size); + tdp->offset += size; + } + return size; +} + +/** + * @brief Reads from a receive descriptor's stream. + * + * @param[in] rdp pointer to a @p MACReceiveDescriptor structure + * @param[in] buf pointer to the buffer that will receive the read data + * @param[in] size number of bytes to be read + * @return The number of bytes read from the descriptor's + * stream, this value can be less than the amount + * specified in the parameter @p size if there are + * no more bytes to read. + * + * @notapi + */ +size_t mac_lld_read_receive_descriptor(MACReceiveDescriptor *rdp, + uint8_t *buf, + size_t size) { + + osalDbgAssert(!(rdp->physdesc->rdes3 & STM32_RDES3_OWN), + "attempt to read descriptor already owned by DMA"); + + if (size > rdp->size - rdp->offset) + size = rdp->size - rdp->offset; + + if (size > 0) { + cacheBufferInvalidate((uint8_t *)(rdp->physdesc->rdes0) + rdp->offset, size); + memcpy(buf, (uint8_t *)(rdp->physdesc->rdes0) + rdp->offset, size); + rdp->offset += size; + } + return size; +} + +#endif /* HAL_USE_MAC */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/MACv2/hal_mac_lld.h b/os/hal/ports/STM32/LLD/MACv2/hal_mac_lld.h new file mode 100644 index 000000000..d756205d2 --- /dev/null +++ b/os/hal/ports/STM32/LLD/MACv2/hal_mac_lld.h @@ -0,0 +1,368 @@ +/* + ChibiOS - Copyright (C) 2006..2020 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file MACv2/hal_mac_lld.h + * @brief STM32 low level MAC driver header. + * + * @addtogroup MAC + * @{ + */ + +#ifndef HAL_MAC_LLD_H +#define HAL_MAC_LLD_H + +#if HAL_USE_MAC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @brief This implementation does not support the zero-copy mode API. + */ +#define MAC_SUPPORTS_ZERO_COPY FALSE + +/** + * @name RDES1 constants + * @{ + */ +#define STM32_RDES1_OPC_MASK 0xFFFF0000 +#define STM32_RDES1_TD 0x00008000 +#define STM32_RDES1_TSA 0x00004000 +#define STM32_RDES1_PV 0x00002000 +#define STM32_RDES1_PFT 0x00001000 +#define STM32_RDES1_PMT_MASK 0x00000F00 +#define STM32_RDES1_IPCE 0x00000080 +#define STM32_RDES1_IPCB 0x00000040 +#define STM32_RDES1_IPV6 0x00000020 +#define STM32_RDES1_IPV4 0x00000010 +#define STM32_RDES1_IPHE 0x00000008 +#define STM32_RDES1_PT_MASK 0x00000007 +/** @} */ + +/** + * @name RDES2 constants + * @{ + */ +#define STM32_RDES2_L3L4FM_MASK 0xE0000000 +#define STM32_RDES2_L4FM 0x01000000 +#define STM32_RDES2_L3FM 0x00800000 +#define STM32_RDES2_MADRM_MASK 0x007F8000 +#define STM32_RDES2_HF 0x00004000 +#define STM32_RDES2_DAF 0x00002000 +#define STM32_RDES2_SAF 0x00001000 +#define STM32_RDES2_VF 0x00000800 +#define STM32_RDES2_RES1 0x00000780 +#define STM32_RDES2_ARPNR 0x00000040 +#define STM32_RDES2_RES2 0x0000003F +/** @} */ + +/** + * @name RDES3 constants + * @{ + */ +#define STM32_RDES3_OWN 0x80000000 +#define STM32_RDES3_IOC 0x40000000 /* Read */ +#define STM32_RDES3_CTXT 0x40000000 /* Write */ +#define STM32_RDES3_FD 0x20000000 +#define STM32_RDES3_LD 0x10000000 +#define STM32_RDES3_RS2V 0x08000000 +#define STM32_RDES3_RS1V 0x04000000 +#define STM32_RDES3_BUF2V 0x02000000 /* Read */ +#define STM32_RDES3_RS0V 0x02000000 /* Write */ +#define STM32_RDES3_BUF1V 0x01000000 /* Read */ +#define STM32_RDES3_CE 0x01000000 /* Write */ +#define STM32_RDES3_GP 0x00800000 +#define STM32_RDES3_RWT 0x00400000 +#define STM32_RDES3_OE 0x00200000 +#define STM32_RDES3_RE 0x00100000 +#define STM32_RDES3_DE 0x00080000 +#define STM32_RDES3_LT_MASK 0x00070000 +#define STM32_RDES3_ES 0x00008000 +#define STM32_RDES3_PL_MASK 0x00007FFF +/** @} */ + +/** + * @name TDES2 constants + * @{ + */ +#define STM32_TDES2_IOC 0x80000000 +#define STM32_TDES2_TTSE 0x40000000 +#define STM32_TDES2_B2L_MASK 0x3FFF0000 +#define STM32_TDES2_VTIR_MASK 0x0000C000 +#define STM32_TDES2_VTIR(n) ((n) << 14) +#define STM32_TDES2_B1L_MASK 0x00003FFF +/** @} */ + +/** + * @name TDES3 constants + * @{ + */ +#define STM32_TDES3_OWN 0x80000000 +#define STM32_TDES3_CTXT 0x40000000 +#define STM32_TDES3_FD 0x20000000 +#define STM32_TDES3_LD 0x10000000 +#define STM32_TDES3_CPC_MASK 0x0C000000 +#define STM32_TDES3_CPC(n) ((n) << 26) +#define STM32_TDES3_SAIC_MASK 0x03800000 +#define STM32_TDES3_SAIC(n) ((n) << 23) +#define STM32_TDES3_THL_MASK 0x00780000 +#define STM32_TDES3_THL(n) ((n) << 19) +#define STM32_TDES3_TSE 0x00040000 +#define STM32_TDES3_CIC_MASK 0x00030000 +#define STM32_TDES3_CIC(n) ((n) << 16) +#define STM32_TDES3_TPL 0x00008000 +#define STM32_TDES3_FL 0x00007FFF +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief Number of available transmit buffers. + */ +#if !defined(STM32_MAC_TRANSMIT_BUFFERS) || defined(__DOXYGEN__) +#define STM32_MAC_TRANSMIT_BUFFERS 4 +#endif + +/** + * @brief Number of available receive buffers. + */ +#if !defined(STM32_MAC_RECEIVE_BUFFERS) || defined(__DOXYGEN__) +#define STM32_MAC_RECEIVE_BUFFERS 4 +#endif + +/** + * @brief Maximum supported frame size. + */ +#if !defined(STM32_MAC_BUFFERS_SIZE) || defined(__DOXYGEN__) +#define STM32_MAC_BUFFERS_SIZE 1524 +#endif + +/** + * @brief PHY detection timeout. + * @details Timeout for PHY address detection, the scan for a PHY is performed + * the specified number of times before invoking the failure handler. + * This setting applies only if the PHY address is not explicitly + * set in the board header file using @p BOARD_PHY_ADDRESS. A zero + * value disables the timeout and a single search is performed. + */ +#if !defined(STM32_MAC_PHY_TIMEOUT) || defined(__DOXYGEN__) +#define STM32_MAC_PHY_TIMEOUT 100 +#endif + +/** + * @brief Change the PHY power state inside the driver. + */ +#if !defined(STM32_MAC_ETH1_CHANGE_PHY_STATE) || defined(__DOXYGEN__) +#define STM32_MAC_ETH1_CHANGE_PHY_STATE TRUE +#endif + +/** + * @brief ETHD1 interrupt priority level setting. + */ +#if !defined(STM32_MAC_ETH1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_MAC_ETH1_IRQ_PRIORITY 13 +#endif + +/** + * @brief IP checksum offload. + * @details The following modes are available: + * - 0 Function disabled. + * - 1 Only IP header checksum calculation and insertion are enabled. + * - 2 IP header checksum and payload checksum calculation and + * insertion are enabled, but pseudo-header checksum is not + * calculated in hardware. + * - 3 IP Header checksum and payload checksum calculation and + * insertion are enabled, and pseudo-header checksum is + * calculated in hardware. + * . + */ +#if !defined(STM32_MAC_IP_CHECKSUM_OFFLOAD) || defined(__DOXYGEN__) +#define STM32_MAC_IP_CHECKSUM_OFFLOAD 0 +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of an STM32 Ethernet receive descriptor. + */ +typedef struct { + volatile uint32_t rdes0; + volatile uint32_t rdes1; + volatile uint32_t rdes2; + volatile uint32_t rdes3; +} stm32_eth_rx_descriptor_t; + +/** + * @brief Type of an STM32 Ethernet transmit descriptor. + */ +typedef struct { + volatile uint32_t tdes0; + volatile uint32_t tdes1; + volatile uint32_t tdes2; + volatile uint32_t tdes3; +} stm32_eth_tx_descriptor_t; + +/** + * @brief Driver configuration structure. + */ +typedef struct { + /** + * @brief MAC address. + */ + uint8_t *mac_address; + /* End of the mandatory fields.*/ +} MACConfig; + +/** + * @brief Structure representing a MAC driver. + */ +struct MACDriver { + /** + * @brief Driver state. + */ + macstate_t state; + /** + * @brief Current configuration data. + */ + const MACConfig *config; + /** + * @brief Transmit semaphore. + */ + threads_queue_t tdqueue; + /** + * @brief Receive semaphore. + */ + threads_queue_t rdqueue; +#if MAC_USE_EVENTS || defined(__DOXYGEN__) + /** + * @brief Receive event. + */ + event_source_t rdevent; +#endif + /* End of the mandatory fields.*/ + /** + * @brief Link status flag. + */ + bool link_up; + /** + * @brief PHY address (pre shifted). + */ + uint32_t phyaddr; + /** + * @brief Receive next frame index. + */ + uint16_t rdindex; + /** + * @brief Transmit next frame index. + */ + uint16_t tdindex; +}; + +/** + * @brief Structure representing a transmit descriptor. + */ +typedef struct { + /** + * @brief Current write offset. + */ + size_t offset; + /** + * @brief Available space size. + */ + size_t size; + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the physical descriptor. + */ + stm32_eth_tx_descriptor_t *physdesc; +} MACTransmitDescriptor; + +/** + * @brief Structure representing a receive descriptor. + */ +typedef struct { + /** + * @brief Current read offset. + */ + size_t offset; + /** + * @brief Available data size. + */ + size_t size; + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the physical descriptor. + */ + stm32_eth_rx_descriptor_t *physdesc; +} MACReceiveDescriptor; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if !defined(__DOXYGEN__) +extern MACDriver ETHD1; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void mii_write(MACDriver *macp, uint32_t reg, uint32_t value); + uint32_t mii_read(MACDriver *macp, uint32_t reg); + void mac_lld_init(void); + void mac_lld_start(MACDriver *macp); + void mac_lld_stop(MACDriver *macp); + msg_t mac_lld_get_transmit_descriptor(MACDriver *macp, + MACTransmitDescriptor *tdp); + void mac_lld_release_transmit_descriptor(MACTransmitDescriptor *tdp); + msg_t mac_lld_get_receive_descriptor(MACDriver *macp, + MACReceiveDescriptor *rdp); + void mac_lld_release_receive_descriptor(MACReceiveDescriptor *rdp); + bool mac_lld_poll_link_status(MACDriver *macp); + size_t mac_lld_write_transmit_descriptor(MACTransmitDescriptor *tdp, + uint8_t *buf, + size_t size); + size_t mac_lld_read_receive_descriptor(MACReceiveDescriptor *rdp, + uint8_t *buf, + size_t size); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_MAC */ + +#endif /* HAL_MAC_LLD_H */ + +/** @} */ diff --git a/os/hal/ports/STM32/STM32H7xx/platform.mk b/os/hal/ports/STM32/STM32H7xx/platform.mk index a60cde418..9bd4595f0 100644 --- a/os/hal/ports/STM32/STM32H7xx/platform.mk +++ b/os/hal/ports/STM32/STM32H7xx/platform.mk @@ -34,6 +34,7 @@ include $(CHIBIOS)/os/hal/ports/STM32/LLD/EXTIv1/driver.mk include $(CHIBIOS)/os/hal/ports/STM32/LLD/FDCANv1/driver.mk include $(CHIBIOS)/os/hal/ports/STM32/LLD/GPIOv2/driver.mk include $(CHIBIOS)/os/hal/ports/STM32/LLD/I2Cv3/driver.mk +include $(CHIBIOS)/os/hal/ports/STM32/LLD/MACv2/driver.mk include $(CHIBIOS)/os/hal/ports/STM32/LLD/MDMAv1/driver.mk include $(CHIBIOS)/os/hal/ports/STM32/LLD/OTGv1/driver.mk include $(CHIBIOS)/os/hal/ports/STM32/LLD/QUADSPIv2/driver.mk diff --git a/os/hal/ports/STM32/STM32H7xx/stm32_rcc.h b/os/hal/ports/STM32/STM32H7xx/stm32_rcc.h index 1e7e10d0b..58e6ac2ab 100644 --- a/os/hal/ports/STM32/STM32H7xx/stm32_rcc.h +++ b/os/hal/ports/STM32/STM32H7xx/stm32_rcc.h @@ -1,5 +1,5 @@ /* - ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio + ChibiOS - Copyright (C) 2006..2020 Giovanni Di Sirio Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -694,25 +694,25 @@ * * @api */ -#define rccEnableETH(lp) rccEnableAHB1(RCC_AHB1ENR_ETHMACEN | \ - RCC_AHB1ENR_ETHMACTXEN | \ - RCC_AHB1ENR_ETHMACRXEN, lp) +#define rccEnableETH(lp) rccEnableAHB1(RCC_AHB1ENR_ETH1MACEN | \ + RCC_AHB1ENR_ETH1TXEN | \ + RCC_AHB1ENR_ETH1RXEN, lp) /** * @brief Disables the ETH peripheral clock. * * @api */ -#define rccDisableETH() rccDisableAHB1(RCC_AHB1ENR_ETHMACEN | \ - RCC_AHB1ENR_ETHMACTXEN | \ - RCC_AHB1ENR_ETHMACRXEN) +#define rccDisableETH() rccDisableAHB1(RCC_AHB1ENR_ETH1MACEN | \ + RCC_AHB1ENR_ETH1TXEN | \ + RCC_AHB1ENR_ETH1RXEN) /** * @brief Resets the ETH peripheral. * * @api */ -#define rccResetETH() rccResetAHB1(RCC_AHB1RSTR_ETHMACRST) +#define rccResetETH() rccResetAHB1(RCC_AHB1RSTR_ETH1MACRST) /** @} */ /** @@ -1689,30 +1689,6 @@ #define rccResetUART8() rccResetAPB1L(RCC_APB1LRSTR_UART8RST) /** @} */ -/** - * @brief Enables the LPUART1 peripheral clock. - * - * @param[in] lp low power enable flag - * - * @api - */ -#define rccEnableLPUART1(lp) rccEnableAPB4(RCC_APB4ENR_LPUART1EN, lp) - -/** - * @brief Disables the LPUART1 peripheral clock. - * - * @api - */ -#define rccDisableLPUART1() rccDisableAPB4(RCC_APB4ENR_LPUART1EN) - -/** - * @brief Resets the LPUART1 peripheral. - * - * @api - */ -#define rccResetLPUART1() rccResetAPB4(RCC_APB4RSTR_LPUART1RST) -/** @} */ - /** * @name LTDC peripheral specific RCC operations * @{