diff --git a/boards/nucleo-f767zi/Makefile.dep b/boards/nucleo-f767zi/Makefile.dep index 7294858272..b610f47b8e 100644 --- a/boards/nucleo-f767zi/Makefile.dep +++ b/boards/nucleo-f767zi/Makefile.dep @@ -1 +1,5 @@ +ifneq (,$(filter netdev_default gnrc_netdev_default,$(USEMODULE))) + USEMODULE += stm32_eth +endif + include $(RIOTBOARD)/common/nucleo/Makefile.dep diff --git a/boards/nucleo-f767zi/Makefile.features b/boards/nucleo-f767zi/Makefile.features index c4dbc5e064..1fec6ceb12 100644 --- a/boards/nucleo-f767zi/Makefile.features +++ b/boards/nucleo-f767zi/Makefile.features @@ -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 diff --git a/boards/nucleo-f767zi/include/periph_conf.h b/boards/nucleo-f767zi/include/periph_conf.h index 15ae0f3b8e..29237d3f87 100644 --- a/boards/nucleo-f767zi/include/periph_conf.h +++ b/boards/nucleo-f767zi/include/periph_conf.h @@ -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 diff --git a/cpu/stm32_common/include/periph_cpu_common.h b/cpu/stm32_common/include/periph_cpu_common.h index 19e1bc060a..2a2123d3ab 100644 --- a/cpu/stm32_common/include/periph_cpu_common.h +++ b/cpu/stm32_common/include/periph_cpu_common.h @@ -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 diff --git a/cpu/stm32_common/periph/eth.c b/cpu/stm32_common/periph/eth.c new file mode 100644 index 0000000000..0fb9728a69 --- /dev/null +++ b/cpu/stm32_common/periph/eth.c @@ -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 + * + * @} + */ +#include + +#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(); +} diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index edb2466367..a328eb2b49 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -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 diff --git a/drivers/include/stm32_eth.h b/drivers/include/stm32_eth.h new file mode 100644 index 0000000000..bb91d38cbd --- /dev/null +++ b/drivers/include/stm32_eth.h @@ -0,0 +1,31 @@ +/* + * + * @file + * @brief Interface definition for the stm32 ethernet driver + * + * @author Robin Lösch + * + * @{ + */ + +#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 */ +/* @} */ diff --git a/drivers/stm32_eth/Makefile b/drivers/stm32_eth/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/drivers/stm32_eth/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/stm32_eth/doc.txt b/drivers/stm32_eth/doc.txt new file mode 100644 index 0000000000..0bff9e38df --- /dev/null +++ b/drivers/stm32_eth/doc.txt @@ -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 + +*/ diff --git a/drivers/stm32_eth/stm32_eth.c b/drivers/stm32_eth/stm32_eth.c new file mode 100644 index 0000000000..34e868cac1 --- /dev/null +++ b/drivers/stm32_eth/stm32_eth.c @@ -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 + * + * @} + */ + +#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 +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; +} diff --git a/examples/default/Makefile b/examples/default/Makefile index 430fca7f39..87bca54625 100644 --- a/examples/default/Makefile +++ b/examples/default/Makefile @@ -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 diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c index 8a75f881c6..dba8576067 100644 --- a/sys/auto_init/auto_init.c +++ b/sys/auto_init/auto_init.c @@ -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(); diff --git a/sys/auto_init/netif/auto_init_stm32_eth.c b/sys/auto_init/netif/auto_init_stm32_eth.c new file mode 100644 index 0000000000..90096922f2 --- /dev/null +++ b/sys/auto_init/netif/auto_init_stm32_eth.c @@ -0,0 +1,30 @@ +/** + * @ingroup sys_auto_init_gnrc_netif + * @{ + * + * @brief Auto initzialize stm32 ethernet driver + * + * @author Robin Lösch + */ + +#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 */ +/** @} */