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:
commit
ad294aa340
@ -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
32
cpu/stm32/periph/doc.txt
Normal 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).
|
||||
*/
|
@ -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;
|
||||
}
|
||||
|
@ -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_%
|
||||
|
Loading…
Reference in New Issue
Block a user