mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #10633 from crest42/stm32_eth_new
drivers: stm32 eth peripheral driver
This commit is contained in:
commit
d167cf334d
@ -1 +1,5 @@
|
||||
ifneq (,$(filter netdev_default gnrc_netdev_default,$(USEMODULE)))
|
||||
USEMODULE += stm32_eth
|
||||
endif
|
||||
|
||||
include $(RIOTBOARD)/common/nucleo/Makefile.dep
|
||||
|
@ -6,6 +6,7 @@ FEATURES_PROVIDED += periph_rtt
|
||||
FEATURES_PROVIDED += periph_spi
|
||||
FEATURES_PROVIDED += periph_timer
|
||||
FEATURES_PROVIDED += periph_uart
|
||||
FEATURES_PROVIDED += periph_eth
|
||||
|
||||
# load the common Makefile.features for Nucleo boards
|
||||
include $(RIOTBOARD)/common/nucleo144/Makefile.features
|
||||
|
@ -40,11 +40,13 @@ static const dma_conf_t dma_config[] = {
|
||||
{ .stream = 4 }, /* DMA1 Stream 4 - USART3_TX */
|
||||
{ .stream = 14 }, /* DMA2 Stream 6 - USART6_TX */
|
||||
{ .stream = 6 }, /* DMA1 Stream 6 - USART2_TX */
|
||||
{ .stream = 8 }, /* DMA2 Stream 0 - ETH_TX */
|
||||
};
|
||||
|
||||
#define DMA_0_ISR isr_dma1_stream4
|
||||
#define DMA_1_ISR isr_dma2_stream6
|
||||
#define DMA_2_ISR isr_dma1_stream6
|
||||
#define DMA_3_ISR isr_dma2_stream0
|
||||
|
||||
#define DMA_NUMOF (sizeof(dma_config) / sizeof(dma_config[0]))
|
||||
#endif
|
||||
@ -158,6 +160,40 @@ static const spi_conf_t spi_config[] = {
|
||||
#define SPI_NUMOF (sizeof(spi_config) / sizeof(spi_config[0]))
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name ETH configuration
|
||||
* @{
|
||||
*/
|
||||
static const eth_conf_t eth_config = {
|
||||
.mode = RMII,
|
||||
.mac = { 0 },
|
||||
.speed = ETH_SPEED_100TX_FD,
|
||||
.dma = 3,
|
||||
.dma_chan = 8,
|
||||
.phy_addr = 0x01,
|
||||
.pins = {
|
||||
GPIO_PIN(PORT_G, 13),
|
||||
GPIO_PIN(PORT_B, 13),
|
||||
GPIO_PIN(PORT_G, 11),
|
||||
GPIO_PIN(PORT_C, 4),
|
||||
GPIO_PIN(PORT_C, 5),
|
||||
GPIO_PIN(PORT_A, 7),
|
||||
GPIO_PIN(PORT_C, 1),
|
||||
GPIO_PIN(PORT_A, 2),
|
||||
GPIO_PIN(PORT_A, 1),
|
||||
}
|
||||
};
|
||||
|
||||
#define ETH_RX_BUFFER_COUNT (4)
|
||||
#define ETH_TX_BUFFER_COUNT (4)
|
||||
|
||||
#define ETH_RX_BUFFER_SIZE (1524)
|
||||
#define ETH_TX_BUFFER_SIZE (1524)
|
||||
|
||||
#define ETH_DMA_ISR isr_dma2_stream0
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -708,6 +708,177 @@ int dma_configure(dma_t dma, int chan, const volatile void *src, volatile void *
|
||||
#include "candev_stm32.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief STM32 Ethernet configuration mode
|
||||
*/
|
||||
typedef enum {
|
||||
MII = 18, /**< Configuration for MII */
|
||||
RMII = 9, /**< Configuration for RMII */
|
||||
SMI = 2, /**< Configuration for SMI */
|
||||
} eth_mode_t;
|
||||
|
||||
/**
|
||||
* @brief STM32 Ethernet speed options
|
||||
*/
|
||||
typedef enum {
|
||||
ETH_SPEED_10T_HD = 0x0000,
|
||||
ETH_SPEED_10T_FD = 0x0100,
|
||||
ETH_SPEED_100TX_HD = 0x2000,
|
||||
ETH_SPEED_100TX_FD = 0x2100,
|
||||
} eth_speed_t;
|
||||
|
||||
/**
|
||||
* @brief Ethernet Peripheral configuration
|
||||
*/
|
||||
typedef struct {
|
||||
eth_mode_t mode; /**< Select configuration mode */
|
||||
char mac[6]; /**< Ethernet MAC address */
|
||||
eth_speed_t speed; /**< Speed selection */
|
||||
uint8_t dma; /**< Locical CMA Descriptor used for TX */
|
||||
uint8_t dma_chan; /**< DMA channel used for TX */
|
||||
char phy_addr; /**< PHY address */
|
||||
gpio_t pins[]; /**< Pins to use. MII requires 18 pins,
|
||||
RMII 9 and SMI 9. Not all speeds are
|
||||
supported by all modes. */
|
||||
} eth_conf_t;
|
||||
|
||||
/**
|
||||
* @name Ethernet PHY Common Registers
|
||||
* @{
|
||||
*/
|
||||
#define PHY_BMCR (0x00)
|
||||
#define PHY_BSMR (0x01)
|
||||
#define PHY_PHYIDR1 (0x02)
|
||||
#define PHY_PHYIDR2 (0x03)
|
||||
#define PHY_ANAR (0x04)
|
||||
#define PHY_ANLPAR (0x05)
|
||||
#define PHY_ANER (0x06)
|
||||
#define PHY_ANNPTR (0x07)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Ethernet PHY BMCR Fields
|
||||
* @{
|
||||
*/
|
||||
#define BMCR_RESET (0x8000)
|
||||
#define BMCR_LOOPBACK (0x4000)
|
||||
#define BMCR_SPEED_SELECT (0x2000)
|
||||
#define BMCR_AN (0x1000)
|
||||
#define BMCR_POWER_DOWN (0x0800)
|
||||
#define BMCR_ISOLATE (0x0400)
|
||||
#define BMCR_RESTART_AN (0x0200)
|
||||
#define BMCR_DUPLEX_MODE (0x0100)
|
||||
#define BMCR_COLLISION_TEST (0x0080)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Ethernet PHY BSMR Fields
|
||||
* @{
|
||||
*/
|
||||
#define BSMR_100BASE_T4 (0x8000)
|
||||
#define BSMR_100BASE_TX_FDUPLEX (0x4000)
|
||||
#define BSMR_100BASE_TX_HDUPLEX (0x2000)
|
||||
#define BSMR_10BASE_T_FDUPLEX (0x1000)
|
||||
#define BSMR_10BASE_T_HDUPLEX (0x0800)
|
||||
#define BSMR_NO_PREAMBLE (0x0040)
|
||||
#define BSMR_AN_COMPLETE (0x0020)
|
||||
#define BSMR_REMOTE_FAULT (0x0010)
|
||||
#define BSMR_AN_ABILITY (0x0008)
|
||||
#define BSMR_LINK_STATUS (0x0004)
|
||||
#define BSMR_JABBER_DETECT (0x0002)
|
||||
#define BSMR_EXTENDED_CAP (0x0001)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Ethernet PHY PHYIDR1 Fields
|
||||
*/
|
||||
#define PHYIDR1_OUI (0xffff)
|
||||
|
||||
/**
|
||||
* @name Ethernet PHY PHYIDR2 Fields
|
||||
* @{
|
||||
*/
|
||||
#define PHYIDR2_OUI (0xfe00)
|
||||
#define PHYIDR2_MODEL (0x01f0)
|
||||
#define PHYIDR2_REV (0x0007)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Ethernet PHY ANAR Fields
|
||||
* @{
|
||||
*/
|
||||
#define ANAR_NEXT_PAGE (0x8000)
|
||||
#define ANAR_REMOTE_FAULT (0x2000)
|
||||
#define ANAR_PAUSE (0x0600)
|
||||
#define ANAR_100BASE_T4 (0x0200)
|
||||
#define ANAR_100BASE_TX_FDUPLEX (0x0100)
|
||||
#define ANAR_100BASE_TX_HDUPLEX (0x0080)
|
||||
#define ANAR_10BASE_T_FDUPLEX (0x0040)
|
||||
#define ANAR_10BASE_T_HDUPLEX (0x0020)
|
||||
#define ANAR_SELECTOR (0x000f)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Ethernet PHY ANLPAR Fields
|
||||
* @{
|
||||
*/
|
||||
#define ANLPAR_NEXT_PAGE (0x8000)
|
||||
#define ANLPAR_ACK (0x4000)
|
||||
#define ANLPAR_REMOTE_FAULT (0x2000)
|
||||
#define ANLPAR_PAUSE (0x0600)
|
||||
#define ANLPAR_100BASE_T4 (0x0200)
|
||||
#define ANLPAR_100BASE_TX_FDUPLEX (0x0100)
|
||||
#define ANLPAR_100BASE_TX_HDUPLEX (0x0080)
|
||||
#define ANLPAR_10BASE_T_FDUPLEX (0x0040)
|
||||
#define ANLPAR_10BASE_T_HDUPLEX (0x0020)
|
||||
#define ANLPAR_SELECTOR (0x000f)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Ethernet PHY ANNPTR Fields
|
||||
* @{
|
||||
*/
|
||||
#define ANNPTR_NEXT_PAGE (0x8000)
|
||||
#define ANNPTR_MSG_PAGE (0x2000)
|
||||
#define ANNPTR_ACK2 (0x1000)
|
||||
#define ANNPTR_TOGGLE_TX (0x0800)
|
||||
#define ANNPTR_CODE (0x03ff)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Ethernet PHY ANER Fields
|
||||
* @{
|
||||
*/
|
||||
#define ANER_PDF (0x0010)
|
||||
#define ANER_LP_NEXT_PAGE_ABLE (0x0008)
|
||||
#define ANER_NEXT_PAGE_ABLE (0x0004)
|
||||
#define ANER_PAGE_RX (0x0002)
|
||||
#define ANER_LP_AN_ABLE (0x0001)
|
||||
/** @} */
|
||||
|
||||
#ifdef MODULE_STM32_ETH
|
||||
/**
|
||||
* @brief Read a PHY register
|
||||
*
|
||||
* @param[in] addr address of the PHY to read
|
||||
* @param[in] reg register to be read
|
||||
*
|
||||
* @return value in the register, or <=0 on error
|
||||
*/
|
||||
int32_t stm32_eth_phy_read(uint16_t addr, uint8_t reg);
|
||||
|
||||
/**
|
||||
* @brief Write a PHY register
|
||||
*
|
||||
* @param[in] addr address of the PHY to write
|
||||
* @param[in] reg register to be written
|
||||
* @param[in] value value to write into the register
|
||||
*
|
||||
* @return 0 in case of success or <=0 on error
|
||||
*/
|
||||
int32_t stm32_eth_phy_write(uint16_t addr, uint8_t reg, uint16_t value);
|
||||
#endif /* MODULE_STM32_ETH */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
360
cpu/stm32_common/periph/eth.c
Normal file
360
cpu/stm32_common/periph/eth.c
Normal file
@ -0,0 +1,360 @@
|
||||
/*
|
||||
* Copyright (C) 2016 TriaGnoSys GmbH
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||
* Public License v2.1. See the file LICENSE in the top level directory for more
|
||||
* details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_stm32_common
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Low-level ETH driver implementation
|
||||
*
|
||||
* @author Víctor Ariño <victor.arino@triagnosys.com>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
#include <string.h>
|
||||
|
||||
#include "mutex.h"
|
||||
#include "luid.h"
|
||||
|
||||
#include "iolist.h"
|
||||
#include "net/ethernet.h"
|
||||
|
||||
#include "periph/gpio.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
/* Set the value of the divider with the clock configured */
|
||||
#if !defined(CLOCK_CORECLOCK) || CLOCK_CORECLOCK < (20000000U)
|
||||
#error This peripheral requires a CORECLOCK of at least 20MHz
|
||||
#elif CLOCK_CORECLOCK < (35000000U)
|
||||
#define CLOCK_RANGE ETH_MACMIIAR_CR_Div16
|
||||
#elif CLOCK_CORECLOCK < (60000000U)
|
||||
#define CLOCK_RANGE ETH_MACMIIAR_CR_Div26
|
||||
#elif CLOCK_CORECLOCK < (100000000U)
|
||||
#define CLOCK_RANGE ETH_MACMIIAR_CR_Div42
|
||||
#elif CLOCK_CORECLOCK < (150000000U)
|
||||
#define CLOCK_RANGE ETH_MACMIIAR_CR_Div62
|
||||
#else /* CLOCK_CORECLOCK < (20000000U) */
|
||||
#define CLOCK_RANGE ETH_MACMIIAR_CR_Div102
|
||||
#endif /* CLOCK_CORECLOCK < (20000000U) */
|
||||
|
||||
/* Internal flags for the DMA descriptors */
|
||||
#define DESC_OWN (0x80000000)
|
||||
#define RX_DESC_FL (0x3FFF0000)
|
||||
#define RX_DESC_FS (0x00000200)
|
||||
#define RX_DESC_LS (0x00000100)
|
||||
#define RX_DESC_RCH (0x00004000)
|
||||
#define TX_DESC_TCH (0x00100000)
|
||||
#define TX_DESC_IC (0x40000000)
|
||||
#define TX_DESC_CIC (0x00C00000)
|
||||
#define TX_DESC_LS (0x20000000)
|
||||
#define TX_DESC_FS (0x10000000)
|
||||
|
||||
struct eth_dma_desc {
|
||||
uint32_t status;
|
||||
uint32_t control;
|
||||
char *buffer_addr;
|
||||
struct eth_dma_desc *desc_next;
|
||||
uint32_t reserved1_ext;
|
||||
uint32_t reserved2;
|
||||
uint32_t ts_low;
|
||||
uint32_t ts_high;
|
||||
} __attribute__((packed));
|
||||
|
||||
typedef struct eth_dma_desc edma_desc_t;
|
||||
|
||||
/* Descriptors */
|
||||
static edma_desc_t rx_desc[ETH_RX_BUFFER_COUNT];
|
||||
static edma_desc_t tx_desc[ETH_TX_BUFFER_COUNT];
|
||||
static edma_desc_t *rx_curr;
|
||||
static edma_desc_t *tx_curr;
|
||||
|
||||
/* Buffers */
|
||||
static char rx_buffer[ETH_RX_BUFFER_COUNT][ETH_RX_BUFFER_SIZE];
|
||||
static char tx_buffer[ETH_TX_BUFFER_COUNT][ETH_TX_BUFFER_SIZE];
|
||||
|
||||
/** Read or write a phy register, to write the register ETH_MACMIIAR_MW is to
|
||||
* be passed as the higher nibble of the value */
|
||||
static unsigned _rw_phy(unsigned addr, unsigned reg, unsigned value)
|
||||
{
|
||||
unsigned tmp;
|
||||
|
||||
while (ETH->MACMIIAR & ETH_MACMIIAR_MB) {}
|
||||
DEBUG("stm32_eth: rw_phy %x (%x): %x\n", addr, reg, value);
|
||||
|
||||
tmp = (ETH->MACMIIAR & ETH_MACMIIAR_CR) | ETH_MACMIIAR_MB;
|
||||
tmp |= (((addr & 0x1f) << 11) | ((reg & 0x1f) << 6));
|
||||
tmp |= (value >> 16);
|
||||
|
||||
ETH->MACMIIDR = (value & 0xffff);
|
||||
ETH->MACMIIAR = tmp;
|
||||
while (ETH->MACMIIAR & ETH_MACMIIAR_MB) {}
|
||||
|
||||
DEBUG("stm32_eth: %lx\n", ETH->MACMIIDR);
|
||||
return (ETH->MACMIIDR & 0x0000ffff);
|
||||
}
|
||||
|
||||
int32_t stm32_eth_phy_read(uint16_t addr, uint8_t reg)
|
||||
{
|
||||
return _rw_phy(addr, reg, 0);
|
||||
}
|
||||
|
||||
int32_t stm32_eth_phy_write(uint16_t addr, uint8_t reg, uint16_t value)
|
||||
{
|
||||
_rw_phy(addr, reg, (value & 0xffff) | (ETH_MACMIIAR_MW << 16));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void stm32_eth_get_mac(char *out)
|
||||
{
|
||||
unsigned t;
|
||||
|
||||
t = ETH->MACA0HR;
|
||||
out[0] = (t >> 8);
|
||||
out[1] = (t & 0xff);
|
||||
|
||||
t = ETH->MACA0LR;
|
||||
out[2] = (t >> 24);
|
||||
out[3] = (t >> 16);
|
||||
out[4] = (t >> 8);
|
||||
out[5] = (t & 0xff);
|
||||
}
|
||||
|
||||
/** Set the mac address. The peripheral supports up to 4 MACs but only one is
|
||||
* implemented */
|
||||
void stm32_eth_set_mac(const char *mac)
|
||||
{
|
||||
ETH->MACA0HR &= 0xffff0000;
|
||||
ETH->MACA0HR |= ((mac[0] << 8) | mac[1]);
|
||||
ETH->MACA0LR = ((mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5]);
|
||||
}
|
||||
|
||||
/** Initialization of the DMA descriptors to be used */
|
||||
static void _init_buffer(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ETH_RX_BUFFER_COUNT; i++) {
|
||||
rx_desc[i].status = DESC_OWN;
|
||||
rx_desc[i].control = RX_DESC_RCH | (ETH_RX_BUFFER_SIZE & 0x0fff);
|
||||
rx_desc[i].buffer_addr = &rx_buffer[i][0];
|
||||
if((i+1) < ETH_RX_BUFFER_COUNT) {
|
||||
rx_desc[i].desc_next = &rx_desc[i + 1];
|
||||
}
|
||||
}
|
||||
rx_desc[i - 1].desc_next = &rx_desc[0];
|
||||
|
||||
for (i = 0; i < ETH_TX_BUFFER_COUNT; i++) {
|
||||
tx_desc[i].status = TX_DESC_TCH | TX_DESC_CIC;
|
||||
tx_desc[i].buffer_addr = &tx_buffer[i][0];
|
||||
if ((i + 1) < ETH_RX_BUFFER_COUNT) {
|
||||
tx_desc[i].desc_next = &tx_desc[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
tx_desc[i - 1].desc_next = &tx_desc[0];
|
||||
|
||||
rx_curr = &rx_desc[0];
|
||||
tx_curr = &tx_desc[0];
|
||||
|
||||
ETH->DMARDLAR = (uint32_t)rx_curr;
|
||||
ETH->DMATDLAR = (uint32_t)tx_curr;
|
||||
}
|
||||
|
||||
int stm32_eth_init(void)
|
||||
{
|
||||
char hwaddr[ETHERNET_ADDR_LEN];
|
||||
/* enable APB2 clock */
|
||||
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
|
||||
|
||||
/* select RMII if necessary */
|
||||
if (eth_config.mode == RMII) {
|
||||
SYSCFG->PMC |= SYSCFG_PMC_MII_RMII_SEL;
|
||||
}
|
||||
|
||||
/* initialize GPIO */
|
||||
for (int i = 0; i < (int) eth_config.mode; i++) {
|
||||
gpio_init(eth_config.pins[i], GPIO_OUT);
|
||||
gpio_init_af(eth_config.pins[i], GPIO_AF11);
|
||||
}
|
||||
|
||||
/* enable all clocks */
|
||||
RCC->AHB1ENR |= (RCC_AHB1ENR_ETHMACEN | RCC_AHB1ENR_ETHMACTXEN |
|
||||
RCC_AHB1ENR_ETHMACRXEN | RCC_AHB1ENR_ETHMACPTPEN);
|
||||
|
||||
/* reset the peripheral */
|
||||
RCC->AHB1RSTR |= RCC_AHB1RSTR_ETHMACRST;
|
||||
RCC->AHB1RSTR &= ~RCC_AHB1RSTR_ETHMACRST;
|
||||
|
||||
/* software reset */
|
||||
ETH->DMABMR |= ETH_DMABMR_SR;
|
||||
while (ETH->DMABMR & ETH_DMABMR_SR) {}
|
||||
|
||||
/* set the clock divider */
|
||||
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 */
|
||||
stm32_eth_phy_write(eth_config.phy_addr, PHY_BMCR, 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));
|
||||
|
||||
/* pass all */
|
||||
//ETH->MACFFR |= ETH_MACFFR_RA;
|
||||
/* perfect filter on address */
|
||||
ETH->MACFFR |= (ETH_MACFFR_PAM | ETH_MACFFR_DAIF);
|
||||
|
||||
/* store forward */
|
||||
ETH->DMAOMR |= (ETH_DMAOMR_RSF | ETH_DMAOMR_TSF | ETH_DMAOMR_OSF);
|
||||
|
||||
/* configure DMA */
|
||||
ETH->DMABMR = (ETH_DMABMR_DA | ETH_DMABMR_AAB | ETH_DMABMR_FB |
|
||||
ETH_DMABMR_RDP_32Beat | ETH_DMABMR_PBL_32Beat | ETH_DMABMR_EDE);
|
||||
|
||||
if(eth_config.mac[0] != 0) {
|
||||
stm32_eth_set_mac(eth_config.mac);
|
||||
}
|
||||
else {
|
||||
luid_get(hwaddr, ETHERNET_ADDR_LEN);
|
||||
stm32_eth_set_mac(hwaddr);
|
||||
}
|
||||
|
||||
_init_buffer();
|
||||
|
||||
NVIC_EnableIRQ(ETH_IRQn);
|
||||
ETH->DMAIER |= ETH_DMAIER_NISE | ETH_DMAIER_TIE | ETH_DMAIER_RIE;
|
||||
|
||||
/* enable */
|
||||
ETH->MACCR |= ETH_MACCR_TE;
|
||||
ETH->DMAOMR |= ETH_DMAOMR_FTF;
|
||||
ETH->MACCR |= ETH_MACCR_RE;
|
||||
|
||||
ETH->DMAOMR |= ETH_DMAOMR_ST;
|
||||
ETH->DMAOMR |= ETH_DMAOMR_SR;
|
||||
|
||||
/* configure speed, do it at the end so the PHY had time to
|
||||
* reset */
|
||||
stm32_eth_phy_write(eth_config.phy_addr, PHY_BMCR, eth_config.speed);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int stm32_eth_send(const struct iolist *iolist)
|
||||
{
|
||||
unsigned len = iolist_size(iolist);
|
||||
int ret = 0;
|
||||
|
||||
/* safety check */
|
||||
if (len > ETH_TX_BUFFER_SIZE) {
|
||||
DEBUG("stm32_eth: Error iolist_size > ETH_TX_BUFFER_SIZE\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* block until there's an available descriptor */
|
||||
while (tx_curr->status & DESC_OWN) {
|
||||
DEBUG("stm32_eth: not avail\n");
|
||||
}
|
||||
|
||||
/* clear status field */
|
||||
tx_curr->status &= 0x0fffffff;
|
||||
|
||||
dma_acquire(eth_config.dma);
|
||||
for (; iolist; iolist = iolist->iol_next) {
|
||||
ret += dma_transfer(eth_config.dma, eth_config.dma_chan, iolist->iol_base,
|
||||
tx_curr->buffer_addr+ret, iolist->iol_len, DMA_MEM_TO_MEM, DMA_INC_BOTH_ADDR);
|
||||
}
|
||||
|
||||
dma_release(eth_config.dma);
|
||||
if (ret < 0) {
|
||||
DEBUG("stm32_eth: Failure in dma_transfer\n");
|
||||
return ret;
|
||||
}
|
||||
tx_curr->control = (len & 0x1fff);
|
||||
|
||||
/* set flags for first and last frames */
|
||||
tx_curr->status |= TX_DESC_FS;
|
||||
tx_curr->status |= TX_DESC_LS | TX_DESC_IC;
|
||||
|
||||
/* give the descriptors to the DMA */
|
||||
tx_curr->status |= DESC_OWN;
|
||||
tx_curr = tx_curr->desc_next;
|
||||
|
||||
/* start tx */
|
||||
ETH->DMATPDR = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _try_receive(char *data, int max_len, int block)
|
||||
{
|
||||
int copy, len = 0;
|
||||
int copied = 0;
|
||||
int drop = (data || max_len > 0);
|
||||
|
||||
edma_desc_t *p = rx_curr;
|
||||
for (int i = 0; i < ETH_RX_BUFFER_COUNT && len == 0; i++) {
|
||||
/* try receiving, if the block is set, simply wait for the rest of
|
||||
* the packet to complete, otherwise just break */
|
||||
while (p->status & DESC_OWN) {
|
||||
if (!block) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* amount of data to copy */
|
||||
copy = ETH_RX_BUFFER_SIZE;
|
||||
if (p->status & (RX_DESC_LS | RX_DESC_FL)) {
|
||||
len = ((p->status >> 16) & 0x3FFF) - 4;
|
||||
copy = len - copied;
|
||||
}
|
||||
|
||||
if (drop) {
|
||||
/* copy the data if possible */
|
||||
if (data && max_len >= copy) {
|
||||
memcpy(data, p->buffer_addr, copy);
|
||||
max_len -= copy;
|
||||
}
|
||||
else if (max_len < copy) {
|
||||
len = -1;
|
||||
}
|
||||
p->status = DESC_OWN;
|
||||
}
|
||||
p = p->desc_next;
|
||||
}
|
||||
|
||||
if (drop) {
|
||||
rx_curr = p;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int stm32_eth_try_receive(char *data, unsigned max_len)
|
||||
{
|
||||
return _try_receive(data, max_len, 0);
|
||||
}
|
||||
|
||||
int stm32_eth_receive_blocking(char *data, unsigned max_len)
|
||||
{
|
||||
return _try_receive(data, max_len, 1);
|
||||
}
|
||||
|
||||
int stm32_eth_get_rx_status_owned(void)
|
||||
{
|
||||
return (!(rx_curr->status & DESC_OWN));
|
||||
}
|
||||
|
||||
void stm32_eth_isr_eth_wkup(void)
|
||||
{
|
||||
cortexm_isr_end();
|
||||
}
|
@ -477,6 +477,14 @@ ifneq (,$(filter srf08,$(USEMODULE)))
|
||||
USEMODULE += xtimer
|
||||
endif
|
||||
|
||||
ifneq (,$(filter stm32_eth,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_eth
|
||||
FEATURES_REQUIRED += periph_dma
|
||||
USEMODULE += netdev_eth
|
||||
USEMODULE += iolist
|
||||
USEMODULE += luid
|
||||
endif
|
||||
|
||||
ifneq (,$(filter sx127%,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_gpio
|
||||
FEATURES_REQUIRED += periph_gpio_irq
|
||||
|
31
drivers/include/stm32_eth.h
Normal file
31
drivers/include/stm32_eth.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
*
|
||||
* @file
|
||||
* @brief Interface definition for the stm32 ethernet driver
|
||||
*
|
||||
* @author Robin Lösch <robin@chilio.net>
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef STM32_ETH_H
|
||||
#define STM32_ETH_H
|
||||
|
||||
#include "net/netdev.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Setup netdev
|
||||
*
|
||||
*/
|
||||
void stm32_eth_netdev_setup(netdev_t *netdev);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* STM32_ETH_H */
|
||||
/* @} */
|
1
drivers/stm32_eth/Makefile
Normal file
1
drivers/stm32_eth/Makefile
Normal file
@ -0,0 +1 @@
|
||||
include $(RIOTBASE)/Makefile.base
|
13
drivers/stm32_eth/doc.txt
Normal file
13
drivers/stm32_eth/doc.txt
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
@defgroup drivers_stm32_common Driver for stm32 ethernet
|
||||
@ingroup drivers_netdev
|
||||
@brief Device Driver for STM32 Ethernet
|
||||
|
||||
*/
|
157
drivers/stm32_eth/stm32_eth.c
Normal file
157
drivers/stm32_eth/stm32_eth.c
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) 2016 TriaGnoSys GmbH
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||
* Public License v2.1. See the file LICENSE in the top level directory for more
|
||||
* details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup drivers_stm32_common
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Netdev wrapper for stm32 ethernet
|
||||
*
|
||||
* @author Víctor Ariño <victor.arino@triagnosys.com>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "periph_conf.h"
|
||||
#include "mutex.h"
|
||||
#include "net/netdev/eth.h"
|
||||
#include "net/ethernet.h"
|
||||
#include "iolist.h"
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#include <string.h>
|
||||
static mutex_t _tx = MUTEX_INIT;
|
||||
static mutex_t _rx = MUTEX_INIT;
|
||||
netdev_t *_netdev;
|
||||
|
||||
void stm32_eth_set_mac(const char *mac);
|
||||
void stm32_eth_get_mac(char *out);
|
||||
int stm32_eth_init(void);
|
||||
int stm32_eth_receive_blocking(char *data, unsigned max_len);
|
||||
int stm32_eth_send(const struct iolist *iolist);
|
||||
int stm32_eth_get_rx_status_owned(void);
|
||||
|
||||
static void _isr(netdev_t *netdev) {
|
||||
if(stm32_eth_get_rx_status_owned()) {
|
||||
netdev->event_callback(netdev, NETDEV_EVENT_RX_COMPLETE);
|
||||
}
|
||||
}
|
||||
|
||||
void isr_eth(void)
|
||||
{
|
||||
volatile unsigned tmp = ETH->DMASR;
|
||||
|
||||
if ((tmp & ETH_DMASR_TS)) {
|
||||
ETH->DMASR = ETH_DMASR_TS | ETH_DMASR_NIS;
|
||||
mutex_unlock(&_tx);
|
||||
}
|
||||
|
||||
if ((tmp & ETH_DMASR_RS)) {
|
||||
ETH->DMASR = ETH_DMASR_RS | ETH_DMASR_NIS;
|
||||
mutex_unlock(&_rx);
|
||||
if (_netdev) {
|
||||
_netdev->event_callback(_netdev, NETDEV_EVENT_ISR);
|
||||
}
|
||||
}
|
||||
|
||||
/* printf("r:%x\n\n", tmp); */
|
||||
|
||||
cortexm_isr_end();
|
||||
}
|
||||
|
||||
static int _recv(netdev_t *netdev, void *buf, size_t len, void *info)
|
||||
{
|
||||
(void)info;
|
||||
(void)netdev;
|
||||
if(!stm32_eth_get_rx_status_owned()){
|
||||
mutex_lock(&_rx);
|
||||
}
|
||||
int ret = stm32_eth_receive_blocking((char *)buf, len);
|
||||
DEBUG("stm32_eth_netdev: _recev: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _send(netdev_t *netdev, const struct iolist *iolist)
|
||||
{
|
||||
(void)netdev;
|
||||
int ret = 0;
|
||||
if(stm32_eth_get_rx_status_owned()) {
|
||||
mutex_lock(&_tx);
|
||||
}
|
||||
netdev->event_callback(netdev, NETDEV_EVENT_TX_STARTED);
|
||||
ret = stm32_eth_send(iolist);
|
||||
DEBUG("stm32_eth_netdev: _send: %d %d\n", ret, iolist_size(iolist));
|
||||
if (ret < 0)
|
||||
{
|
||||
netdev->event_callback(netdev, NETDEV_EVENT_TX_MEDIUM_BUSY);
|
||||
return ret;
|
||||
}
|
||||
netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _set(netdev_t *dev, netopt_t opt, const void *value, size_t max_len)
|
||||
{
|
||||
int res = -1;
|
||||
|
||||
switch (opt) {
|
||||
case NETOPT_ADDRESS:
|
||||
assert(max_len >= ETHERNET_ADDR_LEN);
|
||||
stm32_eth_set_mac((char *)value);
|
||||
res = ETHERNET_ADDR_LEN;
|
||||
break;
|
||||
default:
|
||||
res = netdev_eth_set(dev, opt, value, max_len);
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int _get(netdev_t *dev, netopt_t opt, void *value, size_t max_len)
|
||||
{
|
||||
int res = -1;
|
||||
|
||||
switch (opt) {
|
||||
case NETOPT_ADDRESS:
|
||||
assert(max_len >= ETHERNET_ADDR_LEN);
|
||||
stm32_eth_get_mac((char *)value);
|
||||
res = ETHERNET_ADDR_LEN;
|
||||
break;
|
||||
default:
|
||||
res = netdev_eth_get(dev, opt, value, max_len);
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int _init(netdev_t *netdev)
|
||||
{
|
||||
(void)netdev;
|
||||
return stm32_eth_init();
|
||||
}
|
||||
|
||||
static const netdev_driver_t netdev_driver_stm32f4eth = {
|
||||
.send = _send,
|
||||
.recv = _recv,
|
||||
.init = _init,
|
||||
.isr = _isr,
|
||||
.get = _get,
|
||||
.set = _set,
|
||||
};
|
||||
|
||||
void stm32_eth_netdev_setup(netdev_t *netdev)
|
||||
{
|
||||
_netdev = netdev;
|
||||
netdev->driver = &netdev_driver_stm32f4eth;
|
||||
}
|
@ -39,7 +39,7 @@ USEMODULE += saul_default
|
||||
|
||||
BOARD_PROVIDES_NETIF := acd52832 airfy-beacon b-l072z-lrwan1 cc2538dk fox \
|
||||
iotlab-m3 iotlab-a8-m3 lobaro-lorabox lsn50 mulle microbit native nrf51dk \
|
||||
nrf51dongle nrf52dk nrf52840dk nrf52840-mdk nrf6310 \
|
||||
nrf51dongle nrf52dk nrf52840dk nrf52840-mdk nrf6310 nucleo-f767zi \
|
||||
openmote-cc2538 pba-d-01-kw2x remote-pa remote-reva samr21-xpro \
|
||||
spark-core telosb yunjia-nrf51822 z1
|
||||
|
||||
|
@ -191,6 +191,11 @@ void auto_init(void)
|
||||
/* initialize network devices */
|
||||
#ifdef MODULE_AUTO_INIT_GNRC_NETIF
|
||||
|
||||
#ifdef MODULE_STM32_ETH
|
||||
extern void auto_init_stm32_eth(void);
|
||||
auto_init_stm32_eth();
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_AT86RF2XX
|
||||
extern void auto_init_at86rf2xx(void);
|
||||
auto_init_at86rf2xx();
|
||||
|
30
sys/auto_init/netif/auto_init_stm32_eth.c
Normal file
30
sys/auto_init/netif/auto_init_stm32_eth.c
Normal file
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* @ingroup sys_auto_init_gnrc_netif
|
||||
* @{
|
||||
*
|
||||
* @brief Auto initzialize stm32 ethernet driver
|
||||
*
|
||||
* @author Robin Lösch <robin@chilio.net>
|
||||
*/
|
||||
|
||||
#ifdef MODULE_STM32_ETH
|
||||
|
||||
#include "stm32_eth.h"
|
||||
#include "net/gnrc/netif/ethernet.h"
|
||||
|
||||
static netdev_t stm32eth;
|
||||
static char stack[THREAD_STACKSIZE_DEFAULT];
|
||||
|
||||
void auto_init_stm32_eth(void)
|
||||
{
|
||||
/* setup netdev device */
|
||||
stm32_eth_netdev_setup(&stm32eth);
|
||||
/* initialize netdev <-> gnrc adapter state */
|
||||
gnrc_netif_ethernet_create(stack, THREAD_STACKSIZE_DEFAULT, GNRC_NETIF_PRIO, "stm32_eth",
|
||||
&stm32eth);
|
||||
}
|
||||
|
||||
#else
|
||||
typedef int dont_be_pedantic;
|
||||
#endif /* MODULE_STM32_ETH */
|
||||
/** @} */
|
Loading…
Reference in New Issue
Block a user