848 lines
26 KiB
C
848 lines
26 KiB
C
/*
|
|
* This file is part of the MicroPython project, http://micropython.org/
|
|
*
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2019 Damien P. George
|
|
*
|
|
* 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.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS 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.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include "py/mphal.h"
|
|
#include "py/mperrno.h"
|
|
#include "shared/netutils/netutils.h"
|
|
#include "pin_static_af.h"
|
|
#include "modnetwork.h"
|
|
#include "mpu.h"
|
|
#include "eth.h"
|
|
|
|
#if defined(MICROPY_HW_ETH_MDC)
|
|
|
|
#include "lwip/etharp.h"
|
|
#include "lwip/dns.h"
|
|
#include "lwip/dhcp.h"
|
|
#include "netif/ethernet.h"
|
|
|
|
// ETH PHY register definitions (for LAN8742)
|
|
|
|
#undef PHY_BCR
|
|
#define PHY_BCR (0x0000)
|
|
#define PHY_BCR_SOFT_RESET (0x8000)
|
|
#define PHY_BCR_AUTONEG_EN (0x1000)
|
|
#define PHY_BCR_POWER_DOWN (0x0800U)
|
|
|
|
#undef PHY_BSR
|
|
#define PHY_BSR (0x0001)
|
|
#define PHY_BSR_LINK_STATUS (0x0004)
|
|
#define PHY_BSR_AUTONEG_DONE (0x0020)
|
|
|
|
#define PHY_SCSR (0x001f)
|
|
#define PHY_SCSR_SPEED_Pos (2)
|
|
#define PHY_SCSR_SPEED_Msk (7 << PHY_SCSR_SPEED_Pos)
|
|
#define PHY_SCSR_SPEED_10HALF (1 << PHY_SCSR_SPEED_Pos)
|
|
#define PHY_SCSR_SPEED_10FULL (5 << PHY_SCSR_SPEED_Pos)
|
|
#define PHY_SCSR_SPEED_100HALF (2 << PHY_SCSR_SPEED_Pos)
|
|
#define PHY_SCSR_SPEED_100FULL (6 << PHY_SCSR_SPEED_Pos)
|
|
|
|
// ETH DMA RX and TX descriptor definitions
|
|
#if defined(STM32H7)
|
|
#define RX_DESCR_3_OWN_Pos (31)
|
|
#define RX_DESCR_3_IOC_Pos (30)
|
|
#define RX_DESCR_3_BUF1V_Pos (24)
|
|
#define RX_DESCR_3_PL_Msk (0x7fff)
|
|
|
|
#define TX_DESCR_3_OWN_Pos (31)
|
|
#define TX_DESCR_3_LD_Pos (29)
|
|
#define TX_DESCR_3_FD_Pos (28)
|
|
#define TX_DESCR_3_CIC_Pos (16)
|
|
#define TX_DESCR_2_B1L_Pos (0)
|
|
#define TX_DESCR_2_B1L_Msk (0x3fff << TX_DESCR_2_B1L_Pos)
|
|
#else
|
|
#define RX_DESCR_0_OWN_Pos (31)
|
|
#define RX_DESCR_0_FL_Pos (16)
|
|
#define RX_DESCR_0_FL_Msk (0x3fff << RX_DESCR_0_FL_Pos)
|
|
#define RX_DESCR_1_RER_Pos (15)
|
|
#define RX_DESCR_1_RCH_Pos (14)
|
|
#define RX_DESCR_1_RBS2_Pos (16)
|
|
#define RX_DESCR_1_RBS1_Pos (0)
|
|
|
|
#define TX_DESCR_0_OWN_Pos (31)
|
|
#define TX_DESCR_0_LS_Pos (29)
|
|
#define TX_DESCR_0_FS_Pos (28)
|
|
#define TX_DESCR_0_DP_Pos (26)
|
|
#define TX_DESCR_0_CIC_Pos (22)
|
|
#define TX_DESCR_0_TER_Pos (21)
|
|
#define TX_DESCR_0_TCH_Pos (20)
|
|
#define TX_DESCR_1_TBS1_Pos (0)
|
|
#endif
|
|
|
|
// Configuration values
|
|
|
|
#define PHY_INIT_TIMEOUT_MS (10000)
|
|
|
|
#define RX_BUF_SIZE (1524) // includes 4-byte CRC at end
|
|
#define TX_BUF_SIZE (1524)
|
|
|
|
#define RX_BUF_NUM (5)
|
|
#define TX_BUF_NUM (5)
|
|
|
|
typedef struct _eth_dma_rx_descr_t {
|
|
volatile uint32_t rdes0, rdes1, rdes2, rdes3;
|
|
} eth_dma_rx_descr_t;
|
|
|
|
typedef struct _eth_dma_tx_descr_t {
|
|
volatile uint32_t tdes0, tdes1, tdes2, tdes3;
|
|
} eth_dma_tx_descr_t;
|
|
|
|
typedef struct _eth_dma_t {
|
|
eth_dma_rx_descr_t rx_descr[RX_BUF_NUM];
|
|
eth_dma_tx_descr_t tx_descr[TX_BUF_NUM];
|
|
uint8_t rx_buf[RX_BUF_NUM * RX_BUF_SIZE] __attribute__((aligned(4)));
|
|
uint8_t tx_buf[TX_BUF_NUM * TX_BUF_SIZE] __attribute__((aligned(4)));
|
|
size_t rx_descr_idx;
|
|
size_t tx_descr_idx;
|
|
uint8_t padding[16384 - 15408];
|
|
} eth_dma_t;
|
|
|
|
typedef struct _eth_t {
|
|
uint32_t trace_flags;
|
|
struct netif netif;
|
|
struct dhcp dhcp_struct;
|
|
} eth_t;
|
|
|
|
static eth_dma_t eth_dma __attribute__((aligned(16384)));
|
|
|
|
eth_t eth_instance;
|
|
|
|
STATIC void eth_mac_deinit(eth_t *self);
|
|
STATIC void eth_process_frame(eth_t *self, size_t len, const uint8_t *buf);
|
|
|
|
STATIC void eth_phy_write(uint32_t reg, uint32_t val) {
|
|
#if defined(STM32H7)
|
|
while (ETH->MACMDIOAR & ETH_MACMDIOAR_MB) {
|
|
}
|
|
uint32_t ar = ETH->MACMDIOAR;
|
|
ar &= ~ETH_MACMDIOAR_RDA_Msk;
|
|
ar |= reg << ETH_MACMDIOAR_RDA_Pos;
|
|
ar &= ~ETH_MACMDIOAR_MOC_Msk;
|
|
ar |= ETH_MACMDIOAR_MOC_WR;
|
|
ar |= ETH_MACMDIOAR_MB;
|
|
ETH->MACMDIODR = val;
|
|
ETH->MACMDIOAR = ar;
|
|
while (ETH->MACMDIOAR & ETH_MACMDIOAR_MB) {
|
|
}
|
|
#else
|
|
while (ETH->MACMIIAR & ETH_MACMIIAR_MB) {
|
|
}
|
|
ETH->MACMIIDR = val;
|
|
uint32_t ar = ETH->MACMIIAR;
|
|
ar = reg << ETH_MACMIIAR_MR_Pos | (ar & ETH_MACMIIAR_CR_Msk) | ETH_MACMIIAR_MW | ETH_MACMIIAR_MB;
|
|
ETH->MACMIIAR = ar;
|
|
while (ETH->MACMIIAR & ETH_MACMIIAR_MB) {
|
|
}
|
|
#endif
|
|
}
|
|
|
|
STATIC uint32_t eth_phy_read(uint32_t reg) {
|
|
#if defined(STM32H7)
|
|
while (ETH->MACMDIOAR & ETH_MACMDIOAR_MB) {
|
|
}
|
|
uint32_t ar = ETH->MACMDIOAR;
|
|
ar &= ~ETH_MACMDIOAR_RDA_Msk;
|
|
ar |= reg << ETH_MACMDIOAR_RDA_Pos;
|
|
ar &= ~ETH_MACMDIOAR_MOC_Msk;
|
|
ar |= ETH_MACMDIOAR_MOC_RD;
|
|
ar |= ETH_MACMDIOAR_MB;
|
|
ETH->MACMDIOAR = ar;
|
|
while (ETH->MACMDIOAR & ETH_MACMDIOAR_MB) {
|
|
}
|
|
return ETH->MACMDIODR;
|
|
#else
|
|
while (ETH->MACMIIAR & ETH_MACMIIAR_MB) {
|
|
}
|
|
uint32_t ar = ETH->MACMIIAR;
|
|
ar = reg << ETH_MACMIIAR_MR_Pos | (ar & ETH_MACMIIAR_CR_Msk) | ETH_MACMIIAR_MB;
|
|
ETH->MACMIIAR = ar;
|
|
while (ETH->MACMIIAR & ETH_MACMIIAR_MB) {
|
|
}
|
|
return ETH->MACMIIDR;
|
|
#endif
|
|
}
|
|
|
|
void eth_init(eth_t *self, int mac_idx) {
|
|
mp_hal_get_mac(mac_idx, &self->netif.hwaddr[0]);
|
|
self->netif.hwaddr_len = 6;
|
|
|
|
// Configure GPIO
|
|
mp_hal_pin_config_alt_static(MICROPY_HW_ETH_MDC, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_MDC);
|
|
mp_hal_pin_config_alt_static(MICROPY_HW_ETH_MDIO, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_MDIO);
|
|
mp_hal_pin_config_alt_static_speed(MICROPY_HW_ETH_RMII_REF_CLK, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_MEDIUM, STATIC_AF_ETH_RMII_REF_CLK);
|
|
mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_CRS_DV, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_CRS_DV);
|
|
mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_RXD0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_RXD0);
|
|
mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_RXD1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_RXD1);
|
|
mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_TX_EN, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_TX_EN);
|
|
mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_TXD0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_TXD0);
|
|
mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_TXD1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_TXD1);
|
|
|
|
// Enable peripheral clock
|
|
#if defined(STM32H7)
|
|
__HAL_RCC_ETH1MAC_CLK_ENABLE();
|
|
__HAL_RCC_ETH1TX_CLK_ENABLE();
|
|
__HAL_RCC_ETH1RX_CLK_ENABLE();
|
|
#else
|
|
__HAL_RCC_ETH_CLK_ENABLE();
|
|
#endif
|
|
}
|
|
|
|
void eth_set_trace(eth_t *self, uint32_t value) {
|
|
self->trace_flags = value;
|
|
}
|
|
|
|
STATIC int eth_mac_init(eth_t *self) {
|
|
// Configure MPU
|
|
uint32_t irq_state = mpu_config_start();
|
|
mpu_config_region(MPU_REGION_ETH, (uint32_t)ð_dma, MPU_CONFIG_ETH(MPU_REGION_SIZE_16KB));
|
|
mpu_config_end(irq_state);
|
|
|
|
// Enable peripheral clock
|
|
#if defined(STM32H7)
|
|
__HAL_RCC_ETH1MAC_CLK_ENABLE();
|
|
__HAL_RCC_ETH1TX_CLK_ENABLE();
|
|
__HAL_RCC_ETH1RX_CLK_ENABLE();
|
|
__HAL_RCC_ETH1MAC_FORCE_RESET();
|
|
#else
|
|
__HAL_RCC_ETH_CLK_ENABLE();
|
|
__HAL_RCC_ETHMAC_FORCE_RESET();
|
|
#endif
|
|
|
|
// Select RMII interface
|
|
#if defined(STM32H7)
|
|
SYSCFG->PMCR = (SYSCFG->PMCR & ~SYSCFG_PMCR_EPIS_SEL_Msk) | SYSCFG_PMCR_EPIS_SEL_2;
|
|
#else
|
|
__HAL_RCC_SYSCFG_CLK_ENABLE();
|
|
SYSCFG->PMC |= SYSCFG_PMC_MII_RMII_SEL;
|
|
#endif
|
|
|
|
#if defined(STM32H7)
|
|
__HAL_RCC_ETH1MAC_RELEASE_RESET();
|
|
|
|
__HAL_RCC_ETH1MAC_CLK_SLEEP_ENABLE();
|
|
__HAL_RCC_ETH1TX_CLK_SLEEP_ENABLE();
|
|
__HAL_RCC_ETH1RX_CLK_SLEEP_ENABLE();
|
|
#else
|
|
__HAL_RCC_ETHMAC_RELEASE_RESET();
|
|
|
|
__HAL_RCC_ETHMAC_CLK_SLEEP_ENABLE();
|
|
__HAL_RCC_ETHMACTX_CLK_SLEEP_ENABLE();
|
|
__HAL_RCC_ETHMACRX_CLK_SLEEP_ENABLE();
|
|
#endif
|
|
|
|
// Do a soft reset of the MAC core
|
|
#if defined(STM32H7)
|
|
#define ETH_SOFT_RESET(eth) do { eth->DMAMR = ETH_DMAMR_SWR; } while (0)
|
|
#define ETH_IS_RESET(eth) (eth->DMAMR & ETH_DMAMR_SWR)
|
|
#else
|
|
#define ETH_SOFT_RESET(eth) do { eth->DMABMR = ETH_DMABMR_SR; } while (0)
|
|
#define ETH_IS_RESET(eth) (eth->DMABMR & ETH_DMABMR_SR)
|
|
#endif
|
|
|
|
ETH_SOFT_RESET(ETH);
|
|
mp_hal_delay_ms(2);
|
|
|
|
// Wait for soft reset to finish
|
|
uint32_t t0 = mp_hal_ticks_ms();
|
|
while (ETH_IS_RESET(ETH)) {
|
|
if (mp_hal_ticks_ms() - t0 > 1000) {
|
|
return -MP_ETIMEDOUT;
|
|
}
|
|
}
|
|
|
|
// Set MII clock range
|
|
uint32_t hclk = HAL_RCC_GetHCLKFreq();
|
|
uint32_t cr_div;
|
|
#if defined(STM32H7)
|
|
cr_div = ETH->MACMDIOAR & ~ETH_MACMDIOAR_CR;
|
|
if (hclk < 35000000) {
|
|
cr_div |= ETH_MACMDIOAR_CR_DIV16;
|
|
} else if (hclk < 60000000) {
|
|
cr_div |= ETH_MACMDIOAR_CR_DIV26;
|
|
} else if (hclk < 100000000) {
|
|
cr_div |= ETH_MACMDIOAR_CR_DIV42;
|
|
} else if (hclk < 150000000) {
|
|
cr_div |= ETH_MACMDIOAR_CR_DIV62;
|
|
} else {
|
|
cr_div |= ETH_MACMDIOAR_CR_DIV102;
|
|
}
|
|
ETH->MACMDIOAR = cr_div;
|
|
#else
|
|
if (hclk < 35000000) {
|
|
cr_div = ETH_MACMIIAR_CR_Div16;
|
|
} else if (hclk < 60000000) {
|
|
cr_div = ETH_MACMIIAR_CR_Div26;
|
|
} else if (hclk < 100000000) {
|
|
cr_div = ETH_MACMIIAR_CR_Div42;
|
|
} else if (hclk < 150000000) {
|
|
cr_div = ETH_MACMIIAR_CR_Div62;
|
|
} else {
|
|
cr_div = ETH_MACMIIAR_CR_Div102;
|
|
}
|
|
ETH->MACMIIAR = cr_div;
|
|
#endif
|
|
|
|
#if defined(STM32H7)
|
|
// don't skip 32bit words since our desriptors are continuous in memory
|
|
ETH->DMACCR &= ~(ETH_DMACCR_DSL_Msk);
|
|
#endif
|
|
|
|
// Reset the PHY
|
|
eth_phy_write(PHY_BCR, PHY_BCR_SOFT_RESET);
|
|
mp_hal_delay_ms(50);
|
|
|
|
// Wait for the PHY link to be established
|
|
int phy_state = 0;
|
|
t0 = mp_hal_ticks_ms();
|
|
while (phy_state != 3) {
|
|
if (mp_hal_ticks_ms() - t0 > PHY_INIT_TIMEOUT_MS) {
|
|
eth_mac_deinit(self);
|
|
return -MP_ETIMEDOUT;
|
|
}
|
|
uint16_t bcr = eth_phy_read(0);
|
|
uint16_t bsr = eth_phy_read(1);
|
|
switch (phy_state) {
|
|
case 0:
|
|
if (!(bcr & PHY_BCR_SOFT_RESET)) {
|
|
phy_state = 1;
|
|
}
|
|
break;
|
|
case 1:
|
|
if (bsr & PHY_BSR_LINK_STATUS) {
|
|
eth_phy_write(PHY_BCR, PHY_BCR_AUTONEG_EN);
|
|
phy_state = 2;
|
|
}
|
|
break;
|
|
case 2:
|
|
if ((bsr & (PHY_BSR_AUTONEG_DONE | PHY_BSR_LINK_STATUS))
|
|
== (PHY_BSR_AUTONEG_DONE | PHY_BSR_LINK_STATUS)) {
|
|
phy_state = 3;
|
|
}
|
|
break;
|
|
}
|
|
mp_hal_delay_ms(2);
|
|
}
|
|
|
|
// Get register with link status
|
|
uint16_t phy_scsr = eth_phy_read(PHY_SCSR);
|
|
|
|
// Burst mode configuration
|
|
#if defined(STM32H7)
|
|
ETH->DMASBMR = ETH->DMASBMR & ~ETH_DMASBMR_AAL & ~ETH_DMASBMR_FB;
|
|
#else
|
|
ETH->DMABMR = 0;
|
|
#endif
|
|
mp_hal_delay_ms(2);
|
|
|
|
// Select DMA interrupts
|
|
#if defined(STM32H7)
|
|
ETH->DMACIER = ETH->DMACIER
|
|
| ETH_DMACIER_NIE // enable normal interrupts
|
|
| ETH_DMACIER_RIE // enable RX interrupt
|
|
;
|
|
#else
|
|
ETH->DMAIER =
|
|
ETH_DMAIER_NISE // enable normal interrupts
|
|
| ETH_DMAIER_RIE // enable RX interrupt
|
|
;
|
|
#endif
|
|
|
|
// Configure RX descriptor lists
|
|
for (size_t i = 0; i < RX_BUF_NUM; ++i) {
|
|
#if defined(STM32H7)
|
|
eth_dma.rx_descr[i].rdes3 =
|
|
1 << RX_DESCR_3_OWN_Pos
|
|
| (1 << RX_DESCR_3_BUF1V_Pos) // buf1 address valid
|
|
| (1 << RX_DESCR_3_IOC_Pos) // Interrupt Enabled on Completion
|
|
;
|
|
eth_dma.rx_descr[i].rdes0 = (uint32_t)ð_dma.rx_buf[i * RX_BUF_SIZE]; // buf 1 address
|
|
#else
|
|
eth_dma.rx_descr[i].rdes0 = 1 << RX_DESCR_0_OWN_Pos;
|
|
eth_dma.rx_descr[i].rdes1 =
|
|
1 << RX_DESCR_1_RCH_Pos // chained
|
|
| RX_BUF_SIZE << RX_DESCR_1_RBS1_Pos
|
|
;
|
|
eth_dma.rx_descr[i].rdes2 = (uint32_t)ð_dma.rx_buf[i * RX_BUF_SIZE];
|
|
eth_dma.rx_descr[i].rdes3 = (uint32_t)ð_dma.rx_descr[(i + 1) % RX_BUF_NUM];
|
|
#endif
|
|
}
|
|
|
|
#if defined(STM32H7)
|
|
ETH->DMACRDLAR = (uint32_t)ð_dma.rx_descr[0];
|
|
#else
|
|
ETH->DMARDLAR = (uint32_t)ð_dma.rx_descr[0];
|
|
#endif
|
|
eth_dma.rx_descr_idx = 0;
|
|
|
|
// Configure TX descriptor lists
|
|
for (size_t i = 0; i < TX_BUF_NUM; ++i) {
|
|
#if defined(STM32H7)
|
|
eth_dma.tx_descr[i].tdes0 = 0;
|
|
eth_dma.tx_descr[i].tdes1 = 0;
|
|
eth_dma.tx_descr[i].tdes2 = TX_BUF_SIZE & TX_DESCR_2_B1L_Msk;
|
|
eth_dma.tx_descr[i].tdes3 = 0;
|
|
#else
|
|
eth_dma.tx_descr[i].tdes0 = 1 << TX_DESCR_0_TCH_Pos;
|
|
eth_dma.tx_descr[i].tdes1 = 0;
|
|
eth_dma.tx_descr[i].tdes2 = 0;
|
|
eth_dma.tx_descr[i].tdes3 = (uint32_t)ð_dma.tx_descr[(i + 1) % TX_BUF_NUM];
|
|
#endif
|
|
}
|
|
|
|
#if defined(STM32H7)
|
|
// set number of descriptors and buffers
|
|
ETH->DMACTDRLR = TX_BUF_NUM - 1;
|
|
ETH->DMACRDRLR = RX_BUF_NUM - 1;
|
|
|
|
ETH->DMACTDLAR = (uint32_t)ð_dma.tx_descr[0];
|
|
#else
|
|
ETH->DMATDLAR = (uint32_t)ð_dma.tx_descr[0];
|
|
#endif
|
|
eth_dma.tx_descr_idx = 0;
|
|
|
|
// Configure DMA
|
|
#if defined(STM32H7)
|
|
// read from RX FIFO only after a full frame is written
|
|
ETH->MTLRQOMR = ETH_MTLRQOMR_RSF;
|
|
// transmission starts when a full packet resides in the Tx queue
|
|
ETH->MTLTQOMR = ETH_MTLTQOMR_TSF;
|
|
#else
|
|
ETH->DMAOMR =
|
|
ETH_DMAOMR_RSF // read from RX FIFO after a full frame is written
|
|
| ETH_DMAOMR_TSF // transmit when a full frame is in TX FIFO (needed by errata)
|
|
;
|
|
#endif
|
|
mp_hal_delay_ms(2);
|
|
|
|
// Select MAC filtering options
|
|
#if defined(STM32H7)
|
|
ETH->MACPFR = ETH_MACPFR_RA; // pass all frames up
|
|
#else
|
|
ETH->MACFFR =
|
|
ETH_MACFFR_RA // pass all frames up
|
|
;
|
|
#endif
|
|
mp_hal_delay_ms(2);
|
|
|
|
// Set MAC address
|
|
u8_t *mac = &self->netif.hwaddr[0];
|
|
ETH->MACA0HR = mac[5] << 8 | mac[4];
|
|
mp_hal_delay_ms(2);
|
|
ETH->MACA0LR = mac[3] << 24 | mac[2] << 16 | mac[1] << 8 | mac[0];
|
|
mp_hal_delay_ms(2);
|
|
|
|
// Set main MAC control register
|
|
ETH->MACCR =
|
|
(phy_scsr & PHY_SCSR_SPEED_Msk) == PHY_SCSR_SPEED_10FULL ? ETH_MACCR_DM
|
|
: (phy_scsr & PHY_SCSR_SPEED_Msk) == PHY_SCSR_SPEED_100HALF ? ETH_MACCR_FES
|
|
: (phy_scsr & PHY_SCSR_SPEED_Msk) == PHY_SCSR_SPEED_100FULL ? (ETH_MACCR_FES | ETH_MACCR_DM)
|
|
: 0
|
|
;
|
|
mp_hal_delay_ms(2);
|
|
|
|
// Start MAC layer
|
|
ETH->MACCR |=
|
|
ETH_MACCR_TE // enable TX
|
|
| ETH_MACCR_RE // enable RX
|
|
;
|
|
mp_hal_delay_ms(2);
|
|
|
|
// Start DMA layer
|
|
#if defined(STM32H7)
|
|
ETH->DMACRCR |= ETH_DMACRCR_SR; // start RX
|
|
ETH->DMACTCR |= ETH_DMACTCR_ST; // start TX
|
|
#else
|
|
ETH->DMAOMR |=
|
|
ETH_DMAOMR_ST // start TX
|
|
| ETH_DMAOMR_SR // start RX
|
|
;
|
|
#endif
|
|
mp_hal_delay_ms(2);
|
|
|
|
// Enable interrupts
|
|
NVIC_SetPriority(ETH_IRQn, IRQ_PRI_PENDSV);
|
|
HAL_NVIC_EnableIRQ(ETH_IRQn);
|
|
|
|
return 0;
|
|
}
|
|
|
|
STATIC void eth_mac_deinit(eth_t *self) {
|
|
(void)self;
|
|
HAL_NVIC_DisableIRQ(ETH_IRQn);
|
|
#if defined(STM32H7)
|
|
__HAL_RCC_ETH1MAC_FORCE_RESET();
|
|
__HAL_RCC_ETH1MAC_RELEASE_RESET();
|
|
__HAL_RCC_ETH1MAC_CLK_DISABLE();
|
|
#else
|
|
__HAL_RCC_ETHMAC_FORCE_RESET();
|
|
__HAL_RCC_ETHMAC_RELEASE_RESET();
|
|
__HAL_RCC_ETH_CLK_DISABLE();
|
|
#endif
|
|
}
|
|
|
|
STATIC int eth_tx_buf_get(size_t len, uint8_t **buf) {
|
|
if (len > TX_BUF_SIZE) {
|
|
return -MP_EINVAL;
|
|
}
|
|
|
|
// Wait for DMA to release the current TX descriptor (if it has it)
|
|
eth_dma_tx_descr_t *tx_descr = ð_dma.tx_descr[eth_dma.tx_descr_idx];
|
|
uint32_t t0 = mp_hal_ticks_ms();
|
|
for (;;) {
|
|
#if defined(STM32H7)
|
|
if (!(tx_descr->tdes3 & (1 << TX_DESCR_3_OWN_Pos))) {
|
|
break;
|
|
}
|
|
#else
|
|
if (!(tx_descr->tdes0 & (1 << TX_DESCR_0_OWN_Pos))) {
|
|
break;
|
|
}
|
|
#endif
|
|
if (mp_hal_ticks_ms() - t0 > 1000) {
|
|
return -MP_ETIMEDOUT;
|
|
}
|
|
}
|
|
|
|
#if defined(STM32H7)
|
|
// Update TX descriptor with length and buffer pointer
|
|
*buf = ð_dma.tx_buf[eth_dma.tx_descr_idx * TX_BUF_SIZE];
|
|
tx_descr->tdes2 = len & TX_DESCR_2_B1L_Msk;
|
|
tx_descr->tdes0 = (uint32_t)*buf;
|
|
#else
|
|
// Update TX descriptor with length, buffer pointer and linked list pointer
|
|
*buf = ð_dma.tx_buf[eth_dma.tx_descr_idx * TX_BUF_SIZE];
|
|
tx_descr->tdes1 = len << TX_DESCR_1_TBS1_Pos;
|
|
tx_descr->tdes2 = (uint32_t)*buf;
|
|
tx_descr->tdes3 = (uint32_t)ð_dma.tx_descr[(eth_dma.tx_descr_idx + 1) % TX_BUF_NUM];
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
STATIC int eth_tx_buf_send(void) {
|
|
// Get TX descriptor and move to next one
|
|
eth_dma_tx_descr_t *tx_descr = ð_dma.tx_descr[eth_dma.tx_descr_idx];
|
|
eth_dma.tx_descr_idx = (eth_dma.tx_descr_idx + 1) % TX_BUF_NUM;
|
|
|
|
// Schedule to send next outgoing frame
|
|
#if defined(STM32H7)
|
|
tx_descr->tdes3 =
|
|
1 << TX_DESCR_3_OWN_Pos // owned by DMA
|
|
| 1 << TX_DESCR_3_LD_Pos // last segment
|
|
| 1 << TX_DESCR_3_FD_Pos // first segment
|
|
| 3 << TX_DESCR_3_CIC_Pos // enable all checksums inserted by hardware
|
|
;
|
|
#else
|
|
tx_descr->tdes0 =
|
|
1 << TX_DESCR_0_OWN_Pos // owned by DMA
|
|
| 1 << TX_DESCR_0_LS_Pos // last segment
|
|
| 1 << TX_DESCR_0_FS_Pos // first segment
|
|
| 3 << TX_DESCR_0_CIC_Pos // enable all checksums inserted by hardware
|
|
| 1 << TX_DESCR_0_TCH_Pos // TX descriptor is chained
|
|
;
|
|
#endif
|
|
|
|
// Notify ETH DMA that there is a new TX descriptor for sending
|
|
__DMB();
|
|
#if defined(STM32H7)
|
|
if (ETH->DMACSR & ETH_DMACSR_TBU) {
|
|
ETH->DMACSR = ETH_DMACSR_TBU;
|
|
}
|
|
ETH->DMACTDTPR = (uint32_t)ð_dma.tx_descr[eth_dma.tx_descr_idx];
|
|
#else
|
|
if (ETH->DMASR & ETH_DMASR_TBUS) {
|
|
ETH->DMASR = ETH_DMASR_TBUS;
|
|
ETH->DMATPDR = 0;
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
STATIC void eth_dma_rx_free(void) {
|
|
// Get RX descriptor, RX buffer and move to next one
|
|
eth_dma_rx_descr_t *rx_descr = ð_dma.rx_descr[eth_dma.rx_descr_idx];
|
|
uint8_t *buf = ð_dma.rx_buf[eth_dma.rx_descr_idx * RX_BUF_SIZE];
|
|
eth_dma.rx_descr_idx = (eth_dma.rx_descr_idx + 1) % RX_BUF_NUM;
|
|
|
|
// Schedule to get next incoming frame
|
|
#if defined(STM32H7)
|
|
rx_descr->rdes0 = (uint32_t)buf;
|
|
rx_descr->rdes3 = 1 << RX_DESCR_3_OWN_Pos; // owned by DMA
|
|
rx_descr->rdes3 |= 1 << RX_DESCR_3_BUF1V_Pos; // buf 1 address valid
|
|
rx_descr->rdes3 |= 1 << RX_DESCR_3_IOC_Pos; // Interrupt Enabled on Completion
|
|
#else
|
|
rx_descr->rdes1 =
|
|
1 << RX_DESCR_1_RCH_Pos // RX descriptor is chained
|
|
| RX_BUF_SIZE << RX_DESCR_1_RBS1_Pos // maximum buffer length
|
|
;
|
|
rx_descr->rdes2 = (uint32_t)buf;
|
|
rx_descr->rdes3 = (uint32_t)ð_dma.rx_descr[eth_dma.rx_descr_idx];
|
|
rx_descr->rdes0 = 1 << RX_DESCR_0_OWN_Pos; // owned by DMA
|
|
#endif
|
|
|
|
// Notify ETH DMA that there is a new RX descriptor available
|
|
__DMB();
|
|
#if defined(STM32H7)
|
|
ETH->DMACRDTPR = (uint32_t)&rx_descr[eth_dma.rx_descr_idx];
|
|
#else
|
|
ETH->DMARPDR = 0;
|
|
#endif
|
|
}
|
|
|
|
void ETH_IRQHandler(void) {
|
|
#if defined(STM32H7)
|
|
uint32_t sr = ETH->DMACSR;
|
|
ETH->DMACSR = ETH_DMACSR_NIS;
|
|
uint32_t rx_interrupt = sr & ETH_DMACSR_RI;
|
|
#else
|
|
uint32_t sr = ETH->DMASR;
|
|
ETH->DMASR = ETH_DMASR_NIS;
|
|
uint32_t rx_interrupt = sr & ETH_DMASR_RS;
|
|
#endif
|
|
if (rx_interrupt) {
|
|
#if defined(STM32H7)
|
|
ETH->DMACSR = ETH_DMACSR_RI;
|
|
#else
|
|
ETH->DMASR = ETH_DMASR_RS;
|
|
#endif
|
|
for (;;) {
|
|
#if defined(STM32H7)
|
|
eth_dma_rx_descr_t *rx_descr_l = ð_dma.rx_descr[eth_dma.rx_descr_idx];
|
|
if (rx_descr_l->rdes3 & (1 << RX_DESCR_3_OWN_Pos)) {
|
|
// No more RX descriptors ready to read
|
|
break;
|
|
}
|
|
#else
|
|
eth_dma_rx_descr_t *rx_descr = ð_dma.rx_descr[eth_dma.rx_descr_idx];
|
|
if (rx_descr->rdes0 & (1 << RX_DESCR_0_OWN_Pos)) {
|
|
// No more RX descriptors ready to read
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
// Get RX buffer containing new frame
|
|
#if defined(STM32H7)
|
|
size_t len = (rx_descr_l->rdes3 & RX_DESCR_3_PL_Msk);
|
|
#else
|
|
size_t len = (rx_descr->rdes0 & RX_DESCR_0_FL_Msk) >> RX_DESCR_0_FL_Pos;
|
|
#endif
|
|
len -= 4; // discard CRC at end
|
|
#if defined(STM32H7)
|
|
uint8_t *buf = ð_dma.rx_buf[eth_dma.rx_descr_idx * RX_BUF_SIZE];
|
|
#else
|
|
uint8_t *buf = (uint8_t *)rx_descr->rdes2;
|
|
#endif
|
|
|
|
// Process frame
|
|
eth_process_frame(ð_instance, len, buf);
|
|
eth_dma_rx_free();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
// ETH-LwIP bindings
|
|
|
|
#define TRACE_ASYNC_EV (0x0001)
|
|
#define TRACE_ETH_TX (0x0002)
|
|
#define TRACE_ETH_RX (0x0004)
|
|
#define TRACE_ETH_FULL (0x0008)
|
|
|
|
STATIC void eth_trace(eth_t *self, size_t len, const void *data, unsigned int flags) {
|
|
if (((flags & NETUTILS_TRACE_IS_TX) && (self->trace_flags & TRACE_ETH_TX))
|
|
|| (!(flags & NETUTILS_TRACE_IS_TX) && (self->trace_flags & TRACE_ETH_RX))) {
|
|
const uint8_t *buf;
|
|
if (len == (size_t)-1) {
|
|
// data is a pbuf
|
|
const struct pbuf *pbuf = data;
|
|
buf = pbuf->payload;
|
|
len = pbuf->len; // restricted to print only the first chunk of the pbuf
|
|
} else {
|
|
// data is actual data buffer
|
|
buf = data;
|
|
}
|
|
if (self->trace_flags & TRACE_ETH_FULL) {
|
|
flags |= NETUTILS_TRACE_PAYLOAD;
|
|
}
|
|
netutils_ethernet_trace(MP_PYTHON_PRINTER, len, buf, flags);
|
|
}
|
|
}
|
|
|
|
STATIC err_t eth_netif_output(struct netif *netif, struct pbuf *p) {
|
|
// This function should always be called from a context where PendSV-level IRQs are disabled
|
|
|
|
LINK_STATS_INC(link.xmit);
|
|
eth_trace(netif->state, (size_t)-1, p, NETUTILS_TRACE_IS_TX | NETUTILS_TRACE_NEWLINE);
|
|
|
|
uint8_t *buf;
|
|
int ret = eth_tx_buf_get(p->tot_len, &buf);
|
|
if (ret == 0) {
|
|
pbuf_copy_partial(p, buf, p->tot_len, 0);
|
|
ret = eth_tx_buf_send();
|
|
}
|
|
|
|
return ret ? ERR_BUF : ERR_OK;
|
|
}
|
|
|
|
STATIC err_t eth_netif_init(struct netif *netif) {
|
|
netif->linkoutput = eth_netif_output;
|
|
netif->output = etharp_output;
|
|
netif->mtu = 1500;
|
|
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_IGMP;
|
|
// Checksums only need to be checked on incoming frames, not computed on outgoing frames
|
|
NETIF_SET_CHECKSUM_CTRL(netif,
|
|
NETIF_CHECKSUM_CHECK_IP
|
|
| NETIF_CHECKSUM_CHECK_UDP
|
|
| NETIF_CHECKSUM_CHECK_TCP
|
|
| NETIF_CHECKSUM_CHECK_ICMP
|
|
| NETIF_CHECKSUM_CHECK_ICMP6);
|
|
return ERR_OK;
|
|
}
|
|
|
|
STATIC void eth_lwip_init(eth_t *self) {
|
|
ip_addr_t ipconfig[4];
|
|
IP4_ADDR(&ipconfig[0], 0, 0, 0, 0);
|
|
IP4_ADDR(&ipconfig[2], 192, 168, 0, 1);
|
|
IP4_ADDR(&ipconfig[1], 255, 255, 255, 0);
|
|
IP4_ADDR(&ipconfig[3], 8, 8, 8, 8);
|
|
|
|
MICROPY_PY_LWIP_ENTER
|
|
|
|
struct netif *n = &self->netif;
|
|
n->name[0] = 'e';
|
|
n->name[1] = '0';
|
|
netif_add(n, &ipconfig[0], &ipconfig[1], &ipconfig[2], self, eth_netif_init, ethernet_input);
|
|
netif_set_hostname(n, "MPY");
|
|
netif_set_default(n);
|
|
netif_set_up(n);
|
|
|
|
dns_setserver(0, &ipconfig[3]);
|
|
dhcp_set_struct(n, &self->dhcp_struct);
|
|
dhcp_start(n);
|
|
|
|
netif_set_link_up(n);
|
|
|
|
MICROPY_PY_LWIP_EXIT
|
|
}
|
|
|
|
STATIC void eth_lwip_deinit(eth_t *self) {
|
|
MICROPY_PY_LWIP_ENTER
|
|
for (struct netif *netif = netif_list; netif != NULL; netif = netif->next) {
|
|
if (netif == &self->netif) {
|
|
netif_remove(netif);
|
|
netif->ip_addr.addr = 0;
|
|
netif->flags = 0;
|
|
}
|
|
}
|
|
MICROPY_PY_LWIP_EXIT
|
|
}
|
|
|
|
STATIC void eth_process_frame(eth_t *self, size_t len, const uint8_t *buf) {
|
|
eth_trace(self, len, buf, NETUTILS_TRACE_NEWLINE);
|
|
|
|
struct netif *netif = &self->netif;
|
|
if (netif->flags & NETIF_FLAG_LINK_UP) {
|
|
struct pbuf *p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
|
|
if (p != NULL) {
|
|
pbuf_take(p, buf, len);
|
|
if (netif->input(p, netif) != ERR_OK) {
|
|
pbuf_free(p);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct netif *eth_netif(eth_t *self) {
|
|
return &self->netif;
|
|
}
|
|
|
|
int eth_link_status(eth_t *self) {
|
|
struct netif *netif = &self->netif;
|
|
if ((netif->flags & (NETIF_FLAG_UP | NETIF_FLAG_LINK_UP))
|
|
== (NETIF_FLAG_UP | NETIF_FLAG_LINK_UP)) {
|
|
if (netif->ip_addr.addr != 0) {
|
|
return 3; // link up
|
|
} else {
|
|
return 2; // link no-ip;
|
|
}
|
|
} else {
|
|
if (eth_phy_read(PHY_BSR) & PHY_BSR_LINK_STATUS) {
|
|
return 1; // link up
|
|
} else {
|
|
return 0; // link down
|
|
}
|
|
}
|
|
}
|
|
|
|
int eth_start(eth_t *self) {
|
|
eth_lwip_deinit(self);
|
|
|
|
// Make sure Eth is Not in low power mode.
|
|
eth_low_power_mode(self, false);
|
|
|
|
int ret = eth_mac_init(self);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
eth_lwip_init(self);
|
|
return 0;
|
|
}
|
|
|
|
int eth_stop(eth_t *self) {
|
|
eth_lwip_deinit(self);
|
|
eth_mac_deinit(self);
|
|
return 0;
|
|
}
|
|
|
|
void eth_low_power_mode(eth_t *self, bool enable) {
|
|
(void)self;
|
|
|
|
// Enable eth clock
|
|
#if defined(STM32H7)
|
|
__HAL_RCC_ETH1MAC_CLK_ENABLE();
|
|
#else
|
|
__HAL_RCC_ETH_CLK_ENABLE();
|
|
#endif
|
|
|
|
uint16_t bcr = eth_phy_read(PHY_BCR);
|
|
if (enable) {
|
|
// Enable low-power mode.
|
|
eth_phy_write(PHY_BCR, bcr | PHY_BCR_POWER_DOWN);
|
|
// Disable eth clock.
|
|
#if defined(STM32H7)
|
|
__HAL_RCC_ETH1MAC_CLK_DISABLE();
|
|
#else
|
|
__HAL_RCC_ETH_CLK_DISABLE();
|
|
#endif
|
|
} else {
|
|
// Disable low-power mode.
|
|
eth_phy_write(PHY_BCR, bcr & (~PHY_BCR_POWER_DOWN));
|
|
}
|
|
}
|
|
#endif // defined(MICROPY_HW_ETH_MDC)
|