cpu/stm32: Add stm32_eth_auto for auto-negotiation
Expose the auto-negotiation feature of the Ethernet device via the pseudo-module stm32_eth_auto. With this enabled, the static speed configuration set in the boards periph_conf.h will only be used if the PHY lacks auto-negotiation capabilities - which is unlikely to ever happen.
This commit is contained in:
parent
d84caa50d2
commit
5f9b55a182
@ -12,6 +12,14 @@ ifneq (,$(filter periph_uart_nonblocking,$(USEMODULE)))
|
|||||||
USEMODULE += tsrb
|
USEMODULE += tsrb
|
||||||
endif
|
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)))
|
ifneq (,$(filter stm32_eth,$(USEMODULE)))
|
||||||
FEATURES_REQUIRED += periph_eth
|
FEATURES_REQUIRED += periph_eth
|
||||||
USEMODULE += netdev_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).
|
||||||
|
*/
|
||||||
@ -39,7 +39,7 @@
|
|||||||
#define STM32_ETH_LINK_UP_TIMEOUT_US (1UL * US_PER_SEC)
|
#define STM32_ETH_LINK_UP_TIMEOUT_US (1UL * US_PER_SEC)
|
||||||
|
|
||||||
static xtimer_t _link_status_timer;
|
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 */
|
/* Set the value of the divider with the clock configured */
|
||||||
#if !defined(CLOCK_CORECLOCK) || CLOCK_CORECLOCK < (20000000U)
|
#if !defined(CLOCK_CORECLOCK) || CLOCK_CORECLOCK < (20000000U)
|
||||||
@ -107,7 +107,7 @@ netdev_t *_netdev;
|
|||||||
#if IS_USED(MODULE_STM32_ETH_LINK_UP)
|
#if IS_USED(MODULE_STM32_ETH_LINK_UP)
|
||||||
/* Used for checking the link status */
|
/* Used for checking the link status */
|
||||||
static uint8_t _link_state = LINK_STATE_DOWN;
|
static uint8_t _link_state = LINK_STATE_DOWN;
|
||||||
#endif
|
#endif /* IS_USED(MODULE_STM32_ETH_LINK_UP) */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read or write a MII register
|
* @brief Read or write a MII register
|
||||||
@ -269,7 +269,89 @@ static void _timer_cb(void *arg)
|
|||||||
|
|
||||||
xtimer_set(&_link_status_timer, STM32_ETH_LINK_UP_TIMEOUT_US);
|
xtimer_set(&_link_status_timer, STM32_ETH_LINK_UP_TIMEOUT_US);
|
||||||
}
|
}
|
||||||
#endif
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
static int stm32_eth_init(netdev_t *netdev)
|
static int stm32_eth_init(netdev_t *netdev)
|
||||||
{
|
{
|
||||||
@ -310,10 +392,11 @@ static int stm32_eth_init(netdev_t *netdev)
|
|||||||
while (ETH->MACMIIAR & ETH_MACMIIAR_MB) {}
|
while (ETH->MACMIIAR & ETH_MACMIIAR_MB) {}
|
||||||
ETH->MACMIIAR = CLOCK_RANGE;
|
ETH->MACMIIAR = CLOCK_RANGE;
|
||||||
|
|
||||||
/* speed from conf */
|
/* ROD = Don't receive own frames in half-duplex mode
|
||||||
ETH->MACCR |= (ETH_MACCR_ROD | ETH_MACCR_IPCO | ETH_MACCR_APCS |
|
* IPCO = Drop IPv4 packets carrying TCP/UDP/ICMP when checksum is invalid
|
||||||
((eth_config.speed & 0x0100) << 3) |
|
* APCS = Do not pass padding and CRC fields to application (CRC is checked
|
||||||
((eth_config.speed & 0x2000) << 1));
|
* by hardware already) */
|
||||||
|
ETH->MACCR |= ETH_MACCR_ROD | ETH_MACCR_IPCO | ETH_MACCR_APCS;
|
||||||
|
|
||||||
/* pass all */
|
/* pass all */
|
||||||
//ETH->MACFFR |= ETH_MACFFR_RA;
|
//ETH->MACFFR |= ETH_MACFFR_RA;
|
||||||
@ -352,9 +435,7 @@ static int stm32_eth_init(netdev_t *netdev)
|
|||||||
/* enable DMA TX and RX */
|
/* enable DMA TX and RX */
|
||||||
ETH->DMAOMR |= ETH_DMAOMR_ST | ETH_DMAOMR_SR;
|
ETH->DMAOMR |= ETH_DMAOMR_ST | ETH_DMAOMR_SR;
|
||||||
|
|
||||||
/* configure speed, do it at the end so the PHY had time to
|
_setup_phy();
|
||||||
* reset */
|
|
||||||
_mii_reg_write(MII_BMCR, eth_config.speed);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -552,6 +633,10 @@ static void stm32_eth_isr(netdev_t *netdev)
|
|||||||
switch (_link_state) {
|
switch (_link_state) {
|
||||||
case LINK_STATE_UP:
|
case LINK_STATE_UP:
|
||||||
DEBUG("[stm32_eth] Link UP\n");
|
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);
|
netdev->event_callback(netdev, NETDEV_EVENT_LINK_UP);
|
||||||
_link_state = LINK_STATE_NOTIFIED_UP;
|
_link_state = LINK_STATE_NOTIFIED_UP;
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -121,6 +121,7 @@ PSEUDOMODULES += stdio_cdc_acm
|
|||||||
PSEUDOMODULES += stdio_ethos
|
PSEUDOMODULES += stdio_ethos
|
||||||
PSEUDOMODULES += stdio_uart_rx
|
PSEUDOMODULES += stdio_uart_rx
|
||||||
PSEUDOMODULES += stm32_eth
|
PSEUDOMODULES += stm32_eth
|
||||||
|
PSEUDOMODULES += stm32_eth_auto
|
||||||
PSEUDOMODULES += stm32_eth_link_up
|
PSEUDOMODULES += stm32_eth_link_up
|
||||||
PSEUDOMODULES += suit_transport_%
|
PSEUDOMODULES += suit_transport_%
|
||||||
PSEUDOMODULES += suit_storage_%
|
PSEUDOMODULES += suit_storage_%
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user