1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #15203 from maribu/stm32-eth-negotiate

cpu/stm32: periph_eth: Use auto-negotation
This commit is contained in:
benpicco 2020-10-23 14:22:56 +02:00 committed by GitHub
commit ad294aa340
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 162 additions and 28 deletions

View File

@ -12,6 +12,14 @@ ifneq (,$(filter periph_uart_nonblocking,$(USEMODULE)))
USEMODULE += tsrb
endif
ifneq (,$(filter stm32_eth_%,$(USEMODULE)))
USEMODULE += stm32_eth
endif
ifneq (,$(filter stm32_eth_auto,$(USEMODULE)))
USEMODULE += stm32_eth_link_up
endif
ifneq (,$(filter stm32_eth,$(USEMODULE)))
FEATURES_REQUIRED += periph_eth
USEMODULE += netdev_eth

32
cpu/stm32/periph/doc.txt Normal file
View File

@ -0,0 +1,32 @@
/**
* @defgroup stm32_eth STM32 Peripheral Ethernet Driver
* @ingroup drivers_netdev
* @brief Driver for the Peripheral STM32 Ethernet block used across
* all families of STM32 MCUs
*
* Link Auto Negotiation
* =====================
*
* To enable Link Auto Negotiation, use the (pseudo) module `stm32_eth_auto`
* by amending your applications `Makefile` as follows:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* # Add the following line to your Makefile
* USEMODULE += stm32_eth_auto
*
* # Note: Any addition of modules to USEMODULE must be done prior to the
* # following line:
* include $(RIOTBASE)/Makefile.include
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* In general, it is highly recommended to use auto-negotiation, as this can
* avoid various communication issues on the PHY layer due to configuration
* mismatch of the link partners. Note that this feature depends on the link
* state events feature.
*
* Link State Events
* =================
*
* To enable Link Events, use the (pseudo) module `stm32_eth_link_up` (as
* described above).
*/

View File

@ -38,11 +38,10 @@
#if IS_USED(MODULE_STM32_ETH_LINK_UP)
#include "xtimer.h"
#define STM32_ETH_LINK_UP_TIMEOUT_US (1UL * US_PER_SEC)
static xtimer_t _link_status_timer;
#endif
#endif /* IS_USED(MODULE_STM32_ETH_LINK_UP) */
/* Set the value of the divider with the clock configured */
#if !defined(CLOCK_CORECLOCK) || CLOCK_CORECLOCK < (20000000U)
@ -61,18 +60,23 @@ static xtimer_t _link_status_timer;
/* Default DMA buffer setup */
#ifndef ETH_RX_DESCRIPTOR_COUNT
#define ETH_RX_DESCRIPTOR_COUNT (6U)
#define ETH_RX_DESCRIPTOR_COUNT (6U)
#endif
#ifndef ETH_TX_DESCRIPTOR_COUNT
#define ETH_TX_DESCRIPTOR_COUNT (8U)
#define ETH_TX_DESCRIPTOR_COUNT (8U)
#endif
#ifndef ETH_RX_BUFFER_SIZE
#define ETH_RX_BUFFER_SIZE (256U)
#define ETH_RX_BUFFER_SIZE (256U)
#endif
#define LINK_STATE_DOWN (0x00)
#define LINK_STATE_UP (0x01)
#define LINK_STATE_NOTIFIED_UP (0x02)
/* Bitmask to extract link state */
#define LINK_STATE (0x01)
/* Bitmask to extract notification state */
#define LINK_STATE_NOTIFIED (0x02)
#define LINK_STATE_DOWN (0x00)
#define LINK_STATE_UP (0x01)
#define LINK_STATE_NOTIFIED_DOWN (LINK_STATE_NOTIFIED | LINK_STATE_DOWN)
#define LINK_STATE_NOTIFIED_UP (LINK_STATE_NOTIFIED | LINK_STATE_UP)
#if (ETH_RX_BUFFER_SIZE % 16) != 0
/* For compatibility with 128bit memory interfaces, the buffer size needs to
@ -105,7 +109,7 @@ netdev_t *_netdev;
#if IS_USED(MODULE_STM32_ETH_LINK_UP)
/* Used for checking the link status */
static uint8_t _link_state = LINK_STATE_DOWN;
#endif
#endif /* IS_USED(MODULE_STM32_ETH_LINK_UP) */
/**
* @brief Read or write a MII register
@ -124,7 +128,6 @@ static uint16_t _mii_reg_transfer(unsigned reg, uint16_t value, bool write)
const uint16_t phy_addr = eth_config.phy_addr;
while (ETH->MACMIIAR & ETH_MACMIIAR_MB) {}
DEBUG("[stm32_eth] rw_phy %x (%x): %x\n", (unsigned)phy_addr, reg, value);
tmp = CLOCK_RANGE | ETH_MACMIIAR_MB
| (((phy_addr & 0x1f) << 11) | ((reg & 0x1f) << 6));
@ -137,7 +140,6 @@ static uint16_t _mii_reg_transfer(unsigned reg, uint16_t value, bool write)
ETH->MACMIIAR = tmp;
while (ETH->MACMIIAR & ETH_MACMIIAR_MB) {}
DEBUG("[stm32_eth] %lx\n", ETH->MACMIIDR);
return ETH->MACMIIDR;
}
@ -256,16 +258,102 @@ static int stm32_eth_get(netdev_t *dev, netopt_t opt,
static void _timer_cb(void *arg)
{
netdev_t *dev = (netdev_t *)arg;
uint8_t state = LINK_STATE_DOWN;
if (_get_link_status()) {
_link_state = LINK_STATE_UP;
state = LINK_STATE_UP;
}
if ((_link_state & LINK_STATE) != state) {
/* link state changed, notify upper layer */
_link_state = state;
dev->event_callback(dev, NETDEV_EVENT_ISR);
}
else {
_link_state = LINK_STATE_DOWN;
xtimer_set(&_link_status_timer, STM32_ETH_LINK_UP_TIMEOUT_US);
}
xtimer_set(&_link_status_timer, STM32_ETH_LINK_UP_TIMEOUT_US);
}
#endif /* IS_USED(MODULE_STM32_ETH_LINK_UP) */
static bool _phy_can_negotiate(void)
{
return (_mii_reg_read(MII_BMSR) & MII_BMSR_HAS_AN);
}
#if IS_USED(MODULE_STM32_ETH_AUTO)
static void _complete_auto_negotiation(void)
{
/* first, wait until auto-negotiation really has completed */
uint16_t bmsr;
do {
bmsr = _mii_reg_read(MII_BMSR);
if (!(bmsr & MII_BMSR_LINK)) {
/* disconnected during auto-negotiation */
return;
}
} while (!(bmsr & MII_BMSR_AN_DONE));
DEBUG("[stm32_eth] PHY auto-negotiation completed, PHY link up\n");
/* Get current MACCR state without speed config */
uint32_t maccr = ETH->MACCR & ~(ETH_MACCR_FES | ETH_MACCR_DM);
/* stupidly, there is seemingly no way to get current connection speed
* and duplex mode. But we can deduce it from our advertised capabilities
* and the link partner abilities */
uint16_t adv = _mii_reg_read(MII_ADVERTISE);
uint16_t lpa = _mii_reg_read(MII_LPA);
if ((adv & MII_ADVERTISE_100) && (lpa & MII_LPA_100)) {
/* 100 Mbps */
maccr |= ETH_MACCR_FES;
if ((adv & MII_ADVERTISE_100_F) && (lpa & MII_LPA_100_F)) {
/* full duplex */
maccr |= ETH_MACCR_DM;
}
}
else if ((adv & MII_ADVERTISE_10_F) && (lpa & MII_LPA_10_F)) {
/* full duplex */
maccr |= ETH_MACCR_DM;
}
DEBUG("[stm32_eth] %s Mbps %s duplex \n",
(maccr & ETH_MACCR_FES) ? "100" : "10",
(maccr & ETH_MACCR_DM) ? "full" : "half");
ETH->MACCR = maccr;
}
#endif /* IS_USED(MODULE_STM32_ETH_AUTO) */
static void _setup_phy(void)
{
DEBUG("[stm32_eth] Reset PHY\n");
/* reset PHY */
_mii_reg_write(MII_BMCR, MII_BMCR_RESET);
/* wait till PHY reset is completed */
while (MII_BMCR_RESET & _mii_reg_read(MII_BMCR)) {}
/* check if auto-negotiation is enabled and supported */
if (IS_USED(MODULE_STM32_ETH_AUTO) && _phy_can_negotiate()) {
_mii_reg_write(MII_BMCR, MII_BMCR_AN_ENABLE);
DEBUG("[stm32_eth] Enabled auto-negotiation\n");
/* We'll continue link setup once auto-negotiation is done */
return;
}
/* Get current MACCR state without speed config */
uint32_t maccr = ETH->MACCR & ~(ETH_MACCR_FES | ETH_MACCR_DM);
DEBUG("[stm32_eth] No PHY auto-negotiation disabled or unsupported\n");
/* disable auto-negotiation and force manually configured speed to be
* used */
_mii_reg_write(MII_BMCR, eth_config.speed);
/* configure MACCR to match PHY speed */
if (eth_config.speed & MII_BMCR_FULL_DPLX) {
maccr |= ETH_MACCR_DM;
}
if (eth_config.speed & MII_BMCR_SPEED_100) {
maccr |= ETH_MACCR_FES;
}
DEBUG("[stm32_eth] %s Mbps %s duplex \n",
(maccr & ETH_MACCR_FES) ? "100" : "10",
(maccr & ETH_MACCR_DM) ? "full" : "half");
/* Apply new duplex & speed configuration in MAC */
ETH->MACCR = maccr;
}
#endif
static int stm32_eth_init(netdev_t *netdev)
{
@ -306,14 +394,11 @@ static int stm32_eth_init(netdev_t *netdev)
while (ETH->MACMIIAR & ETH_MACMIIAR_MB) {}
ETH->MACMIIAR = CLOCK_RANGE;
/* configure the PHY (standard for all PHY's) */
/* if there's no PHY, this has no effect */
_mii_reg_write(MII_BMCR, MII_BMCR_RESET);
/* speed from conf */
ETH->MACCR |= (ETH_MACCR_ROD | ETH_MACCR_IPCO | ETH_MACCR_APCS |
((eth_config.speed & 0x0100) << 3) |
((eth_config.speed & 0x2000) << 1));
/* ROD = Don't receive own frames in half-duplex mode
* IPCO = Drop IPv4 packets carrying TCP/UDP/ICMP when checksum is invalid
* APCS = Do not pass padding and CRC fields to application (CRC is checked
* by hardware already) */
ETH->MACCR |= ETH_MACCR_ROD | ETH_MACCR_IPCO | ETH_MACCR_APCS;
/* pass all */
//ETH->MACFFR |= ETH_MACFFR_RA;
@ -352,9 +437,7 @@ static int stm32_eth_init(netdev_t *netdev)
/* enable DMA TX and RX */
ETH->DMAOMR |= ETH_DMAOMR_ST | ETH_DMAOMR_SR;
/* configure speed, do it at the end so the PHY had time to
* reset */
_mii_reg_write(MII_BMCR, eth_config.speed);
_setup_phy();
return 0;
}
@ -551,9 +634,19 @@ static void stm32_eth_isr(netdev_t *netdev)
#if IS_USED(MODULE_STM32_ETH_LINK_UP)
switch (_link_state) {
case LINK_STATE_UP:
DEBUG("[stm32_eth] Link UP\n");
if (IS_USED(MODULE_STM32_ETH_AUTO)) {
/* Complete auto-negotiation of the link */
_complete_auto_negotiation();
}
netdev->event_callback(netdev, NETDEV_EVENT_LINK_UP);
_link_state = LINK_STATE_NOTIFIED_UP;
return;
case LINK_STATE_DOWN:
DEBUG("[stm32_eth] Link DOWN\n");
netdev->event_callback(netdev, NETDEV_EVENT_LINK_DOWN);
_link_state = LINK_STATE_NOTIFIED_DOWN;
return;
default:
break;
}

View File

@ -122,6 +122,7 @@ PSEUDOMODULES += stdio_cdc_acm
PSEUDOMODULES += stdio_ethos
PSEUDOMODULES += stdio_uart_rx
PSEUDOMODULES += stm32_eth
PSEUDOMODULES += stm32_eth_auto
PSEUDOMODULES += stm32_eth_link_up
PSEUDOMODULES += suit_transport_%
PSEUDOMODULES += suit_storage_%