diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index 8b10a7daed..8085fbd9a4 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -19,6 +19,11 @@ ifneq (,$(filter dht,$(USEMODULE))) USEMODULE += xtimer endif +ifneq (,$(filter enc28j60,$(USEMODULE))) + USEMODULE += netdev2_eth + USEMODULE += xtimer +endif + ifneq (,$(filter encx24j600,$(USEMODULE))) USEMODULE += netdev2_eth USEMODULE += xtimer diff --git a/drivers/Makefile.include b/drivers/Makefile.include index b73bb24fec..7400131488 100644 --- a/drivers/Makefile.include +++ b/drivers/Makefile.include @@ -52,3 +52,6 @@ endif ifneq (,$(filter tcs37727,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/tcs37727/include endif +ifneq (,$(filter enc28j60,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/drivers/enc28j60/include +endif diff --git a/drivers/enc28j60/Makefile b/drivers/enc28j60/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/drivers/enc28j60/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/enc28j60/enc28j60.c b/drivers/enc28j60/enc28j60.c new file mode 100644 index 0000000000..8f82491852 --- /dev/null +++ b/drivers/enc28j60/enc28j60.c @@ -0,0 +1,466 @@ +/* + * Copyright (C) 2015 Freie Universität Berlin + * + * 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 driver_enc28j60 + * @{ + * + * @file + * @brief Implementation of ENC28J60 driver interfaces + * + * @author Hauke Petersen + * + * @} + */ + +#include + +#include "mutex.h" +#include "xtimer.h" +#include "assert.h" +#include "net/ethernet.h" +#include "net/netdev2_eth.h" + +#include "enc28j60.h" +#include "enc28j60_regs.h" + +#if CPUID_ID_LEN +#include "periph/cpuid.h" +#endif + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/** + * @brief Amount of time to hold the reset pin low on reset + */ +#define DELAY_RESET (1000U) /* 1ms */ + +/** + * @brief If the clock is not stable after these amount of tries we abort the + * initialization + */ +#define STARTUP_TIMEOUT (1000U) + +/** + * @brief Set SPI speed fixed to 10MHz + * + * The SPI speed is set to a fixed value, as it must be > 8MHz (see the devices + * errata sheet). + */ +#define SPI_SPEED SPI_SPEED_10MHZ + +/** + * @brief The devices build-in buffer size + * + * This is a shared buffer that is freely configurable to be used for TX and RX + */ +#define BUFFER_SIZE (0x2000) + +/** + * @brief Buffer configuration + * + * We use the split the buffer in the following way: + * - RX: 6k [0x0000 to 0x17fe] + * - TX: 2k [0x1800 to 0x1ffe] + * + * RX must start at buffer address 0x0000 (see errata sheet, section 5) + */ +#define BUF_TX_START ((BUFFER_SIZE / 4) * 3) +#define BUF_TX_END (BUFFER_SIZE - 2) +#define BUF_RX_START (0) +#define BUF_RX_END (BUF_TX_START - 2) + + +static void switch_bank(enc28j60_t *dev, int8_t bank) +{ + /* only switch bank if needed */ + if ((bank < 0) || (dev->bank == bank)) { + return; + } + /* clear old value */ + gpio_clear(dev->cs_pin); + spi_transfer_reg(dev->spi, CMD_BFC | REG_ECON1, 0x03, 0); + gpio_set(dev->cs_pin); + /* set new value */ + gpio_clear(dev->cs_pin); + spi_transfer_reg(dev->spi, CMD_BFS | REG_ECON1, bank, 0); + gpio_set(dev->cs_pin); + /* remember active bank */ + dev->bank = bank; +} + +static uint8_t cmd_rcr(enc28j60_t *dev, uint8_t reg, int8_t bank) +{ + char res; + + switch_bank(dev, bank); + gpio_clear(dev->cs_pin); + spi_transfer_reg(dev->spi, CMD_RCR | reg, 0, &res); + gpio_set(dev->cs_pin); + return (uint8_t)res; +} + +static uint8_t cmd_rcr_miimac(enc28j60_t *dev, uint8_t reg, int8_t bank) +{ + char res[2]; + + switch_bank(dev, bank); + gpio_clear(dev->cs_pin); + spi_transfer_regs(dev->spi, CMD_RCR | reg, NULL, res, 2); + gpio_set(dev->cs_pin); + return (uint8_t)res[1]; +} + +static void cmd_wcr(enc28j60_t *dev, uint8_t reg, int8_t bank, uint8_t value) +{ + switch_bank(dev, bank); + gpio_clear(dev->cs_pin); + spi_transfer_reg(dev->spi, CMD_WCR | reg, (char)value, 0); + gpio_set(dev->cs_pin); +} + +static void cmd_bfs(enc28j60_t *dev, uint8_t reg, int8_t bank, uint8_t mask) +{ + switch_bank(dev, bank); + gpio_clear(dev->cs_pin); + spi_transfer_reg(dev->spi, CMD_BFS | reg, mask, 0); + gpio_set(dev->cs_pin); +} + +static void cmd_bfc(enc28j60_t *dev, uint8_t reg, int8_t bank, uint8_t mask) +{ + switch_bank(dev, bank); + gpio_clear(dev->cs_pin); + spi_transfer_reg(dev->spi, CMD_BFC | reg, mask, 0); + gpio_set(dev->cs_pin); +} + +static uint16_t cmd_r_addr(enc28j60_t *dev, uint8_t addr) +{ + uint8_t low = cmd_rcr(dev, addr, 0); + uint8_t high = cmd_rcr(dev, addr + 1, 0); + return (uint16_t)((high << 8) | low); +} + +static void cmd_w_addr(enc28j60_t *dev, uint8_t addr, uint16_t val) +{ + cmd_wcr(dev, addr, 0, (val & 0xff)); + cmd_wcr(dev, addr + 1, 0, (val >> 8)); +} + +static uint16_t cmd_r_phy(enc28j60_t *dev, uint8_t reg) +{ + /* set target register for reading */ + cmd_wcr(dev, REG_B2_MIREGADR, 2, reg); + /* trigger register read and wait for results */ + cmd_wcr(dev, REG_B2_MICMD, 2, MICMD_MIIRD); + cmd_wcr(dev, REG_B2_MICMD, 2, 0x00); + while (cmd_rcr_miimac(dev, REG_B3_MISTAT, 3) & MISTAT_BUSY); + /* results */ + uint8_t low = cmd_rcr_miimac(dev, REG_B2_MIRDL, 2); + uint8_t high = cmd_rcr_miimac(dev, REG_B2_MIRDH, 2); + return (uint16_t)((high << 8) | low); +} + +static void cmd_w_phy(enc28j60_t *dev, uint8_t reg, uint16_t val) +{ + /* set target register and values to write */ + cmd_wcr(dev, REG_B2_MIREGADR, 2, reg); + cmd_wcr(dev, REG_B2_MIWRL, 2, (val & 0xff)); + cmd_wcr(dev, REG_B2_MIWRH, 2, (val >> 8)); + /* wait until the transaction is finished */ + while (cmd_rcr_miimac(dev, REG_B3_MISTAT, 3) & MISTAT_BUSY); +} + +static void cmd_rbm(enc28j60_t *dev, uint8_t *data, size_t len) +{ + gpio_clear(dev->cs_pin); + spi_transfer_regs(dev->spi, CMD_RBM, NULL, (char *)data, len); + gpio_set(dev->cs_pin); +} + +static void cmd_wbm(enc28j60_t *dev, uint8_t *data, size_t len) +{ + gpio_clear(dev->cs_pin); + spi_transfer_regs(dev->spi, CMD_WBM, (char *)data, NULL, len); + gpio_set(dev->cs_pin); +} + +static void mac_get(enc28j60_t *dev, uint8_t *mac) +{ + mac[0] = cmd_rcr_miimac(dev, REG_B3_MAADR6, 3); + mac[1] = cmd_rcr_miimac(dev, REG_B3_MAADR5, 3); + mac[2] = cmd_rcr_miimac(dev, REG_B3_MAADR4, 3); + mac[3] = cmd_rcr_miimac(dev, REG_B3_MAADR3, 3); + mac[4] = cmd_rcr_miimac(dev, REG_B3_MAADR2, 3); + mac[5] = cmd_rcr_miimac(dev, REG_B3_MAADR1, 3); +} + +static void mac_set(enc28j60_t *dev, uint8_t *mac) +{ + cmd_wcr(dev, REG_B3_MAADR6, 3, mac[0]); + cmd_wcr(dev, REG_B3_MAADR5, 3, mac[1]); + cmd_wcr(dev, REG_B3_MAADR4, 3, mac[2]); + cmd_wcr(dev, REG_B3_MAADR3, 3, mac[3]); + cmd_wcr(dev, REG_B3_MAADR2, 3, mac[4]); + cmd_wcr(dev, REG_B3_MAADR1, 3, mac[5]); +} + +static void on_int(void *arg) +{ + netdev2_t *netdev = (netdev2_t *)arg; + netdev->event_callback(arg, NETDEV2_EVENT_ISR, NULL); +} + +static int nd_send(netdev2_t *netdev, const struct iovec *data, int count) +{ + enc28j60_t *dev = (enc28j60_t *)netdev; + uint8_t ctrl = 0; + int c = 0; + + mutex_lock(&dev->devlock); + + /* set write pointer */ + cmd_w_addr(dev, ADDR_WRITE_PTR, BUF_TX_START); + /* write control byte and the actual data into the buffer */ + cmd_wbm(dev, &ctrl, 1); + for (int i = 0; i < count; i++) { + c += data[i].iov_len; + cmd_wbm(dev, (uint8_t *)data[i].iov_base, data[i].iov_len); + } + /* set TX end pointer */ + cmd_w_addr(dev, ADDR_TX_END, cmd_r_addr(dev, ADDR_WRITE_PTR) - 1); + /* trigger the send process */ + cmd_bfs(dev, REG_ECON1, -1, ECON1_TXRTS); + + mutex_unlock(&dev->devlock); + return c; +} + +static int nd_recv(netdev2_t *netdev, char *buf, int max_len) +{ + enc28j60_t *dev = (enc28j60_t *)netdev; + uint8_t head[6]; + size_t size; + uint16_t next; + + mutex_lock(&dev->devlock); + + /* set read pointer to RX read address */ + cmd_w_addr(dev, ADDR_READ_PTR, cmd_r_addr(dev, ADDR_RX_READ)); + /* read packet header */ + cmd_rbm(dev, head, 6); + /* TODO: care for endianess */ + next = (uint16_t)((head[1] << 8) | head[0]); + size = (size_t)((head[3] << 8) | head[2]) - 4; /* discard CRC */ + + if (buf != NULL) { + /* read packet content into the supplied buffer */ + if (size <= max_len) { + cmd_rbm(dev, (uint8_t *)buf, size); + } else { + DEBUG("[enc28j60] recv: unable to get packet - buffer too small\n"); + size = 0; + } + /* release memory */ + cmd_w_addr(dev, ADDR_RX_READ, next); + cmd_bfs(dev, REG_ECON2, -1, ECON2_PKTDEC); + } + + mutex_unlock(&dev->devlock); + return (int)size; +} + +static int nd_init(netdev2_t *netdev) +{ + enc28j60_t *dev = (enc28j60_t *)netdev; + int res; + uint8_t tmp; + + /* get exclusive access of the device */ + mutex_lock(&dev->devlock); + + /* setup the low-level interfaces */ + gpio_init(dev->reset_pin, GPIO_DIR_OUT, GPIO_NOPULL); + gpio_clear(dev->reset_pin); /* this puts the device into reset state */ + gpio_init(dev->cs_pin, GPIO_DIR_OUT, GPIO_NOPULL); + gpio_set(dev->cs_pin); + gpio_init_int(dev->int_pin, GPIO_NOPULL, GPIO_FALLING, on_int, (void *)dev); + res = spi_init_master(dev->spi, SPI_CONF_FIRST_RISING, SPI_SPEED); + if (res < 0) { + DEBUG("[enc28j60] init: error initializing SPI bus [%i]\n", res); + return -1; + } + + /* wait at least 1ms and then release device from reset state */ + xtimer_usleep(DELAY_RESET); + gpio_set(dev->reset_pin); + + /* wait for oscillator to be stable before proceeding */ + res = 0; + do { + tmp = cmd_rcr(dev, REG_ESTAT, -1); + if (res++ >= STARTUP_TIMEOUT) { + DEBUG("[enc28j60] init: error waiting for stable clock, SPI ok?\n"); + return -1; + } + } while (!(tmp & ESTAT_CLKRDY)); + + /* disable clock output to save a little power */ + cmd_wcr(dev, REG_B3_ECOCON, 3, 0x00); + + /* BUFFER configuration */ + /* configure the RX buffer */ + cmd_w_addr(dev, ADDR_RX_START, BUF_RX_START); + cmd_w_addr(dev, ADDR_RX_END, BUF_RX_END); + cmd_w_addr(dev, ADDR_RX_READ, BUF_RX_START); + /* configure the TX buffer */ + cmd_w_addr(dev, ADDR_TX_START, BUF_TX_START); + cmd_w_addr(dev, ADDR_TX_END, BUF_TX_END); + + /* FILTER configuration */ + /* setup receive filters - we accept everything per default */ + cmd_wcr(dev, REG_B1_ERXFCON, 1, 0); + + /* MAC configuration */ + /* enable RX through filter and enable sending of RX and TX pause frames */ + tmp = (MACON1_TXPAUS | MACON1_RXPAUS | MACON1_MARXEN); + cmd_wcr(dev, REG_B2_MACON1, 2, tmp); + /* enable full duplex mode, padding to min 60 byte + CRC for all frames and + * CRC creation for all packets before transmission */ + tmp = (MACON3_FULDPX | MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN); + cmd_wcr(dev, REG_B2_MACON3, 2, tmp); + /* defer TX infinitely if medium is busy for IEEE 802.3 compliance */ + tmp = (MACON4_DEFER); + cmd_wcr(dev, REG_B2_MACON4, 2, tmp); + /* set back-to-back inter-packet gap -> 0x15 for full duplex */ + cmd_wcr(dev, REG_B2_MABBIPG, 2, MABBIPG_FD); + /* set non-back-to-back inter packet gap -> 0x12 is default */ + cmd_wcr(dev, REG_B2_MAIPGL, 2, MAIPGL_FD); + /* set default MAC address */ +#if CPUID_ID_LEN + uint8_t macbuf[CPUID_ID_LEN]; + cpuid_get(&macbuf); /* we get the full ID but use only parts of it */ + macbuf[0] |= 0x02; /* locally administered address */ + macbuf[0] &= ~0x01; /* unicast address */ +#else + uint8_t macbuf[] = ENC28J60_FALLBACK_MAC; +#endif + mac_set(dev, macbuf); + + /* PHY configuration */ + cmd_w_phy(dev, REG_PHY_PHCON1, PHCON1_PDPXMD); + cmd_w_phy(dev, REG_PHY_PHIE, PHIE_PLNKIE | PHIE_PGEIE); + + /* Finishing touches */ + /* enable hardware flow control */ + cmd_wcr(dev, REG_B3_EFLOCON, 3, EFLOCON_FULDPXS | EFLOCON_FCEN1); + /* enable auto-inc of read and write pointers for the RBM/WBM commands */ + cmd_bfs(dev, REG_ECON2, -1, ECON2_AUTOINC); + /* enable receive, link and tx interrupts */ + cmd_bfc(dev, REG_EIR, -1, (EIR_LINKIF | EIR_PKTIF | EIR_RXERIF | EIR_TXIF | + EIR_TXERIF)); + cmd_bfs(dev, REG_EIE, -1, (EIE_INTIE | EIE_LINKIE | EIE_PKTIE | EIE_RXERIE | + EIE_TXIE | EIE_TXERIE)); + /* allow receiving bytes from now on */ + cmd_bfs(dev, REG_ECON1, -1, ECON1_RXEN); + + mutex_unlock(&dev->devlock); + return 0; +} + +static void nd_isr(netdev2_t *netdev) +{ + enc28j60_t *dev = (enc28j60_t *)netdev; + uint8_t eir = cmd_rcr(dev, REG_EIR, -1); + + while (eir != 0) { + if (eir & EIR_LINKIF) { + /* clear link state interrupt flag */ + cmd_r_phy(dev, REG_PHY_PHIR); + /* go and tell the new link layer state to upper layers */ + if (cmd_r_phy(dev, REG_PHY_PHSTAT2) & PHSTAT2_LSTAT) { + DEBUG("[enc28j60] isr: link up!\n"); + netdev->event_callback(netdev, NETDEV2_EVENT_LINK_UP, NULL); + } + else { + DEBUG("[enc28j60] isr: link down!\n"); + netdev->event_callback(netdev, NETDEV2_EVENT_LINK_DOWN, NULL); + } + } + if (eir & EIR_PKTIF) { + do { + DEBUG("[enc28j60] isr: packet received\n"); + netdev->event_callback(netdev, NETDEV2_EVENT_RX_COMPLETE, NULL); + } while (cmd_rcr(dev, REG_B1_EPKTCNT, 1) > 0); + } + if (eir & EIR_RXERIF) { + DEBUG("[enc28j60] isr: incoming packet dropped - RX buffer full\n"); + cmd_bfc(dev, REG_EIR, -1, EIR_RXERIF); + } + if (eir & EIR_TXIF) { + DEBUG("[enc28j60] isr: packet transmitted\n"); + netdev->event_callback(netdev, NETDEV2_EVENT_TX_COMPLETE, NULL); + cmd_bfc(dev, REG_EIR, -1, EIR_TXIF); + } + if (eir & EIR_TXERIF) { + DEBUG("[enc28j60] isr: error during transmission - pkt dropped\n"); + cmd_bfc(dev, REG_EIR, -1, EIR_TXERIF); + } + eir = cmd_rcr(dev, REG_EIR, -1); + } +} + +static int nd_get(netdev2_t *netdev, netopt_t opt, void *value, size_t max_len) +{ + enc28j60_t *dev = (enc28j60_t *)netdev; + + switch (opt) { + case NETOPT_ADDRESS: + assert(max_len >= ETHERNET_ADDR_LEN); + mac_get(dev, (uint8_t *)value); + return ETHERNET_ADDR_LEN; + default: + return netdev2_eth_get(netdev, opt, value, max_len); + } +} + +static int nd_set(netdev2_t *netdev, netopt_t opt, void *value, size_t value_len) +{ + enc28j60_t *dev = (enc28j60_t *)netdev; + + switch (opt) { + case NETOPT_ADDRESS: + assert(value_len == ETHERNET_ADDR_LEN); + mac_set(dev, (uint8_t *)value); + return ETHERNET_ADDR_LEN; + default: + return netdev2_eth_set(netdev, opt, value, value_len); + } +} + +const static netdev2_driver_t netdev2_driver_enc28j60 = { + .send = nd_send, + .recv = nd_recv, + .init = nd_init, + .isr = nd_isr, + .get = nd_get, + .set = nd_set, +}; + +void enc28j60_setup(enc28j60_t *dev, const enc28j60_params_t *params) +{ + dev->netdev.driver = &netdev2_driver_enc28j60; + dev->spi = params->spi; + dev->cs_pin = params->cs_pin; + dev->int_pin = params->int_pin; + dev->reset_pin = params->reset_pin; + mutex_init(&dev->devlock); + dev->bank = 99; /* mark as invalid */ +} diff --git a/drivers/enc28j60/include/enc28j60_params.h b/drivers/enc28j60/include/enc28j60_params.h new file mode 100644 index 0000000000..6546ae4481 --- /dev/null +++ b/drivers/enc28j60/include/enc28j60_params.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2015 Freie Universität Berlin + * + * 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 driver_enc28j60 + * @{ + * + * @file + * @brief Default configuration for the ENC28J60 driver + * + * @author Hauke Petersen + */ + +#ifndef ENC28J60_PARAMS_H +#define ENC28J60_PARAMS_H + +#include "board.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Set default configuration parameters for the ENC28J60 driver + * @{ + */ +#ifndef ENC28J60_PARAM_SPI +#define ENC28J60_PARAM_SPI (SPI_0) +#endif +#ifndef ENC28J60_PARAM_CS +#define ENC28J60_PARAM_CS (GPIO_PIN(0, 0)) +#endif +#ifndef ENC28J60_PARAM_INT +#define ENC28J60_PARAM_INT (GPIO_PIN(0, 1)) +#endif +#ifndef ENC28J60_PARAM_RESET +#define ENC28J60_PARAM_RESET (GPIO_PIN(0, 2)) +#endif +/** @} */ + +/** + * @brief ENC28J60 configuration + */ +static const enc28j60_params_t enc28j60_params[] = { + { + .spi = ENC28J60_PARAM_SPI, + .cs_pin = ENC28J60_PARAM_CS, + .int_pin = ENC28J60_PARAM_INT, + .reset_pin = ENC28J60_PARAM_RESET, + }, +}; +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* ENC28J60_PARAMS_H */ +/** @} */ diff --git a/drivers/enc28j60/include/enc28j60_regs.h b/drivers/enc28j60/include/enc28j60_regs.h new file mode 100644 index 0000000000..b74aa02581 --- /dev/null +++ b/drivers/enc28j60/include/enc28j60_regs.h @@ -0,0 +1,436 @@ +/* + * Copyright (C) 2015 Freie Universität Berlin + * + * 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 driver_enc28j60 + * @{ + * + * @file + * @brief Register definitions for the ENC28J60 Ethernet device + * + * @author Hauke Petersen + */ + +#ifndef ENC28J60_REGS_H +#define ENC28J60_REGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief SPI instruction set + * @{ + */ +#define CMD_RCR 0x00 /* read control register */ +#define CMD_RBM 0x3a /* read buffer memory */ +#define CMD_WCR 0x40 /* write control register */ +#define CMD_WBM 0x7a /* write buffer memory */ +#define CMD_BFS 0x80 /* bit field set */ +#define CMD_BFC 0xa0 /* bit field clear */ +#define CMD_SRC 0xff /* system reset command (soft reset) */ +/** @} */ + +/** + * @brief Available address pointers + * @{ + */ +#define ADDR_READ_PTR 0x00 /**< Read pointer */ +#define ADDR_WRITE_PTR 0x02 /**< Write data pointer */ +#define ADDR_TX_START 0x04 /**< TX buffer start */ +#define ADDR_TX_END 0x06 /**< TX buffer end */ +#define ADDR_RX_START 0x08 /**< RX buffer start */ +#define ADDR_RX_END 0x0a /**< RX buffer end */ +#define ADDR_RX_READ 0x0c /**< start of oldest packet in RX buffer */ +#define ADDR_RX_WRITE 0x0e /**< start of free space in RX buffer */ +/** @} */ + +/** + * @brief Shared registers (accessible on each bank) + * @{ + */ +#define REG_EIE 0x1b /**< interrupt enable */ +#define REG_EIR 0x1c /**< interrupt flags */ +#define REG_ESTAT 0x1d /**< status */ +#define REG_ECON2 0x1e /**< configuration 1 */ +#define REG_ECON1 0x1f /**< configuration 2 */ +/** @} */ + +/** + * @brief Register in bank 0 - Ethernet registers + * @{ + */ +#define REG_B0_ERDPTL 0x00 /* read data pointer - low byte */ +#define REG_B0_ERDPTH 0x01 /* read data pointer - high byte */ +#define REG_B0_EWRPTL 0x02 /* write data pointer - low byte */ +#define REG_B0_EWRPTH 0x03 /* write data pointer - high byte */ +#define REG_B0_ETXSTL 0x04 /* TX start pointer - low byte */ +#define REG_B0_ETXSTH 0x05 /* TX start pointer - high byte */ +#define REG_B0_ETXNDL 0x06 /* TX end pointer - low byte */ +#define REG_B0_ETXNDH 0x07 /* TX end pointer - high byte */ +#define REG_B0_ERXSTL 0x08 /* RX start pointer - low byte */ +#define REG_B0_ERXSTH 0x09 /* RX start pointer - high byte */ +#define REG_B0_ERXNDL 0x0a /* RX end pointer - low byte */ +#define REG_B0_ERXNDH 0x0b /* RX end pointer - high byte */ +#define REG_B0_ERXRDPTL 0x0c /* RX read pointer - low byte */ +#define REG_B0_ERXRDPTH 0x0d /* RX read pointer - high byte */ +#define REG_B0_ERXWRPTL 0x0e /* RX write pointer - low byte */ +#define REG_B0_ERXWRPTH 0x0f /* RX write pointer - high byte */ +#define REG_B0_EDMASTL 0x10 /* DMA start pointer - low byte */ +#define REG_B0_EDMASTH 0x11 /* DMA start pointer - high byte */ +#define REG_B0_EDMANDL 0x12 /* DMA end pointer - low byte */ +#define REG_B0_EDMANDH 0x13 /* DMA end pointer - high byte */ +#define REG_B0_EDMADSTL 0x14 /* DMA destination pointer - low byte */ +#define REG_B0_EDMADSTH 0x15 /* DMA destination pointer - high byte */ +#define REG_B0_EDMACSL 0x16 /* DMA checksum - low byte */ +#define REG_B0_EDMACSH 0x17 /* DMA checksum - high byte */ +/** @} */ + +/** + * @brief Registers in bank 1 - Ethernet registers + * @{ + */ +#define REG_B1_EHT0 0x00 /* hash table - byte 0 */ +#define REG_B1_EHT1 0x01 /* hash table - byte 1 */ +#define REG_B1_EHT2 0x02 /* hash table - byte 2 */ +#define REG_B1_EHT3 0x03 /* hash table - byte 3 */ +#define REG_B1_EHT4 0x04 /* hash table - byte 4 */ +#define REG_B1_EHT5 0x05 /* hash table - byte 5 */ +#define REG_B1_EHT6 0x06 /* hash table - byte 6 */ +#define REG_B1_EHT7 0x07 /* hash table - byte 7 */ +#define REG_B1_EPMM0 0x08 /* pattern match mask - byte 0 */ +#define REG_B1_EPMM1 0x09 /* pattern match mask - byte 1 */ +#define REG_B1_EPMM2 0x0a /* pattern match mask - byte 2 */ +#define REG_B1_EPMM3 0x0b /* pattern match mask - byte 3 */ +#define REG_B1_EPMM4 0x0c /* pattern match mask - byte 4 */ +#define REG_B1_EPMM5 0x0d /* pattern match mask - byte 5 */ +#define REG_B1_EPMM6 0x0e /* pattern match mask - byte 6 */ +#define REG_B1_EPMM7 0x0f /* pattern match mask - byte 7 */ +#define REG_B1_EPMCSL 0x10 /* pattern match checksum - low byte */ +#define REG_B1_EPMCSH 0x11 /* pattern match checksum - high byte */ +#define REG_B1_EPMOL 0x14 /* pattern match offset - low byte */ +#define REG_B1_EPMOH 0x15 /* pattern match offset - high byte */ +#define REG_B1_ERXFCON 0x18 /* receive filter control register */ +#define REG_B1_EPKTCNT 0x19 /* packet count */ +/** @} */ + +/** + * @brief Registers in bank 2 - MAC registers + * @{ + */ +#define REG_B2_MACON1 0x00 /* MAC control register 1 */ +#define REG_B2_MACON3 0x02 /* MAC control register 3 */ +#define REG_B2_MACON4 0x03 /* MAC control register 4 */ +#define REG_B2_MABBIPG 0x04 /* back-to-back inter-packet gap */ +#define REG_B2_MAIPGL 0x06 /* non-back-to-back inter-packet gap - low byte */ +#define REG_B2_MAIPGH 0x07 /* non-back-to-back inter-packet gap - high byte */ +#define REG_B2_MACLCON1 0x08 /* retransmission maximum */ +#define REG_B2_MACLCON2 0x09 /* collision window */ +#define REG_B2_MAMXFLL 0x0a /* maximum frame length - low byte */ +#define REG_B2_MAMXFLH 0x0b /* maximum frame length - high byte */ +#define REG_B2_MICMD 0x12 /* MIIM command */ +#define REG_B2_MIREGADR 0x14 /* MIIM register address */ +#define REG_B2_MIWRL 0x16 /* MIIM write data register - low byte */ +#define REG_B2_MIWRH 0x17 /* MIIM write data register - high byte */ +#define REG_B2_MIRDL 0x18 /* MIIM read data register - low byte */ +#define REG_B2_MIRDH 0x19 /* MIIM read data register - high byte */ +/** @} */ + +/** + * @brief Registers in bank 3 - MIXED registers + * @{ + */ +#define REG_B3_MAADR5 0x00 /* MAC address - byte 5 */ +#define REG_B3_MAADR6 0x01 /* MAC address - byte 6 */ +#define REG_B3_MAADR3 0x02 /* MAC address - byte 3 */ +#define REG_B3_MAADR4 0x03 /* MAC address - byte 4 */ +#define REG_B3_MAADR1 0x04 /* MAC address - byte 1 */ +#define REG_B3_MAADR2 0x05 /* MAC address - byte 2 */ +#define REG_B3_EBSTSD 0x06 /* built-in self-test fill seed */ +#define REG_B3_EBSTCON 0x07 /* built-in self-test control register */ +#define REG_B3_EBSTCSL 0x08 /* built-in self-test checksum - low byte */ +#define REG_B3_EBSTCSH 0x09 /* built-in self-test checksum - high byte */ +#define REG_B3_MISTAT 0x0a /* MIIM status register */ +#define REG_B3_EREVID 0x12 /* Ethernet revision ID */ +#define REG_B3_ECOCON 0x15 /* clock output control */ +#define REG_B3_EFLOCON 0x17 /* Ethernet flow control */ +#define REG_B3_EPAUSL 0x18 /* pause timer value - low byte */ +#define REG_B3_EPAUSH 0x19 /* pause timer value - high byte */ +/** @} */ + +/** + * @brief PHY Registers + * @{ + */ +#define REG_PHY_PHCON1 0x00 +#define REG_PHY_PHSTAT1 0x01 +#define REG_PHY_PHID1 0x02 +#define REG_PHY_PHID2 0x03 +#define REG_PHY_PHCON2 0x10 +#define REG_PHY_PHSTAT2 0x11 +#define REG_PHY_PHIE 0x12 +#define REG_PHY_PHIR 0x13 +#define REG_PHY_PHLCON 0x14 +/** @} */ + +/** + * @brief EIE bitfields + * @{ + */ +#define EIE_INTIE 0x80 +#define EIE_PKTIE 0x40 +#define EIE_DMAIE 0x20 +#define EIE_LINKIE 0x10 +#define EIE_TXIE 0x08 +#define EIE_TXERIE 0x02 +#define EIE_RXERIE 0x01 +/** @} */ + +/** + * @brief EIR bitfields + * @{ + */ +#define EIR_PKTIF 0x40 +#define EIR_DMAIF 0x20 +#define EIR_LINKIF 0x10 +#define EIR_TXIF 0x08 +#define EIR_TXERIF 0x02 +#define EIR_RXERIF 0x01 +/** @} */ + +/** + * @brief ESTAT bitfields + * @{ + */ +#define ESTAT_INT 0x80 +#define ESTAT_BUFFER 0x40 +#define ESTAT_LATECOL 0x10 +#define ESTAT_RXBUSY 0x40 +#define ESTAT_TXABRT 0x20 +#define ESTAT_CLKRDY 0x01 +/** @} */ + +/** + * @brief ECON1 bitfields + * @{ + */ +#define ECON1_TXRST 0x80 +#define ECON1_RXRST 0x40 +#define ECON1_DMAST 0x20 +#define ECON1_CSUMEN 0x10 +#define ECON1_TXRTS 0x08 +#define ECON1_RXEN 0x04 +#define ECON1_BSEL1 0x02 +#define ECON1_BSEL0 0x01 +#define ECON1_BSEL_MASK 0x03 +/** @} */ + +/** + * @brief ECON2 bitfields + * @{ + */ +#define ECON2_AUTOINC 0x80 +#define ECON2_PKTDEC 0x40 +#define ECON2_PWRSV 0x20 +#define ECON2_VRPS 0x40 +/** @} */ + +/** + * @brief ERXFCON bitfields + * @{ + */ +#define ERXFCON_UCEN 0x80 +#define ERXFCON_ANDOR 0x40 +#define ERXFCON_CRCEN 0x20 +#define ERXFCON_PMEN 0x10 +#define ERXFCON_MPEN 0x08 +#define ERXFCON_HTEN 0x04 +#define ERXFCON_MCEN 0x02 +#define ERXFCON_BCEN 0x01 +/** @} */ + +/** + * @brief MACON1 bitfields + * @{ + */ +#define MACON1_TXPAUS 0x08 +#define MACON1_RXPAUS 0x04 +#define MACON1_PASSALL 0x02 +#define MACON1_MARXEN 0x01 +/** @} */ + +/** + * @brief MACON3 bitfields + * @{ + */ +#define MACON3_PADCFG2 0x80 +#define MACON3_PADCFG1 0x40 +#define MACON3_PADCFG0 0x20 +#define MACON3_TXCRCEN 0x10 +#define MACON3_PHDREN 0x08 +#define MACON3_HFRMEN 0x04 +#define MACON3_FRMLNEN 0x02 +#define MACON3_FULDPX 0x01 +/** @} */ + +/** + * @brief MACON4 bitfields + * @{ + */ +#define MACON4_DEFER 0x40 +#define MACON4_BPEN 0x20 +#define MACON4_NOBKOFF 0x10 +/** @} */ + +/** + * @brief MABBIPG bitfields + * @{ + */ +#define MABBIPG_FD 0x15 +#define MABBIPG_HD 0x12 +/** @} */ + +/** + * @brief MAIPGL bitfields + * @{ + */ +#define MAIPGL_FD 0x12 +/** @} */ + +/** + * @brief MICMD bitfields + * @{ + */ +#define MICMD_MIISCAN 0x02 +#define MICMD_MIIRD 0x01 +/** @} */ + +/** + * @brief MISTAT bitfields + * @{ + */ +#define MISTAT_NVALID 0x04 +#define MISTAT_SCAN 0x02 +#define MISTAT_BUSY 0x01 +/** @} */ + +/** + * @brief EFLOCON bitfields + * @{ + */ +#define EFLOCON_FULDPXS 0x04 +#define EFLOCON_FCEN1 0x02 +#define EFLOCON_FCEN0 0x01 +#define EFLOCON_FCEN_MASK 0x03 +/** @} */ + + +/** + * @brief PHCON1 bitfields + * @{ + */ +#define PHCON1_PRST 0x8000 +#define PHCON1_PLOOPBK 0x4000 +#define PHCON1_PPWRSV 0x0800 +#define PHCON1_PDPXMD 0x0100 +/** @} */ + +/** + * @brief PHSTAT1 bitfields + * @{ + */ +#define PHSTAT1_PFDPX 0x1000 +#define PHSTAT1_PHDPX 0x0800 +#define PHSTAT1_LLSTAT 0x0004 +#define PHSTAT1_JBSTAT 0x0002 +/** @} */ + +/** + * @brief PHCON2 bitfields + * @{ + */ +#define PHCON2_FRCLNK 0x4000 +#define PHCON2_TXDIS 0x2000 +#define PHCON2_JABBER 0x0400 +#define PHCON2_HDLDIS 0x0100 +/** @} */ + +/** + * @brief PHSTAT2 bitfields + * @{ + */ +#define PHSTAT2_TXSTAT 0x2000 +#define PHSTAT2_RXSTAT 0x1000 +#define PHSTAT2_COLSTAT 0x0800 +#define PHSTAT2_LSTAT 0x0400 +#define PHSTAT2_DPXSTAT 0x0200 +#define PHSTAT2_PLRITY 0x0020 +/** @} */ + +/** + * @brief PHIE bitfields + * @{ + */ +#define PHIE_PLNKIE 0x0010 +#define PHIE_PGEIE 0x0002 +/** @} */ + +/** + * @brief PHIR bitfields + * @{ + */ +#define PHIR_PLNKIF 0x0010 +#define PHIR_PGIF 0x0004 +/** @} */ + +/** + * @brief PHLCON bitfields + * @{ + */ +#define PHLCON_LACFG(x) ((x & 0xf) << 8) +#define PHLCON_LBCFG(x) ((x & 0xf) << 4) +#define PHLCON_LFRQ(x) ((x & 0x3) << 2) +#define PHLCON_STRCH 0x0002 +/** @} */ + +/** + * @brief Frame status bitfields + * @{ + */ +#define FRAME_4_RECV_OK 0x80 +#define FRAME_4_LENGTH_OOR 0x40 +#define FRAME_4_LENGTH_ERR 0x20 +#define FRAME_4_CRC_ERR 0x10 +#define FRAME_4_CARRIER_EVT 0x04 +#define FRAME_4_LONG_EVT 0x01 + +#define FRAME_5_VLAN 0x40 +#define FRAME_5_UKWN_OPCODE 0x20 +#define FRAME_5_PAUSE 0x10 +#define FRAME_5_RCV_CTRL 0x08 +#define FRAME_5_DRIPPLE 0x04 +#define FRAME_5_BCAST 0x02 +#define FRAME_5_MCAST 0x01 +/** @} */ + +/** + * @brief TX control byte bitfields + * @{ + */ +#define TX_PHUGEEN 0x08 +#define TX_PPADEN 0x04 +#define TX_PCRCEN 0x02 +#define TX_POVERRIDE 0x01 +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* ENC28J60_REGS_H */ +/** @} */ diff --git a/drivers/include/enc28j60.h b/drivers/include/enc28j60.h new file mode 100644 index 0000000000..0eb2330ce4 --- /dev/null +++ b/drivers/include/enc28j60.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2015 Freie Universität Berlin + * + * 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 driver_enc28j60 ENC28J60 + * @ingroup drivers + * @brief Driver for the ENC28J60 Ethernet Adapter + * @{ + * + * @file + * @brief Interface definition for the ENC28J60 driver + * + * @author Hauke Petersen + */ + +#ifndef ENC28J60_H +#define ENC28J60_H + +#include + +#include "mutex.h" +#include "periph/spi.h" +#include "periph/gpio.h" +#include "net/netdev2.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Fallback MAC address in case CPUID is not available + * + * The enc28j60 does not provide a globally unique, pre-set MAC address, so we + * need to create one. For this the CPUID module is used to create a locally + * administered MAC. If this is not available, we use the MAC address below. + */ +#define ENC28J60_FALLBACK_MAC {0x02, 0x22, 0x33, 0x44, 0x55, 0x66} + +/** + * @brief Struct containing the needed peripheral configuration + */ +typedef struct { + spi_t spi; /**< If I drink */ + gpio_t cs_pin; /**< beer in the evening, */ + gpio_t int_pin; /**< I will be most certainly */ + gpio_t reset_pin; /**< drunk in the morning?! */ +} enc28j60_params_t; + +/** + * @brief ENC28J60 device descriptor + */ +typedef struct { + netdev2_t netdev; /**< pull in the netdev2 fields */ + spi_t spi; /**< SPI bus the transceiver is connected to */ + gpio_t cs_pin; /**< pin connected to the CHIP SELECT line */ + gpio_t int_pin; /**< pin connected to the INT line */ + gpio_t reset_pin; /**< pin connected to the RESET line */ + mutex_t devlock; /**< lock the device on access */ + int8_t bank; /**< remember the active register bank */ +} enc28j60_t; + +/** + * @brief Ready the device for initialization through it's netdev2 interface + * + * @param[in] dev device descriptor + * @param[in] params peripheral configuration to use + */ +void enc28j60_setup(enc28j60_t *dev, const enc28j60_params_t *params); + +#ifdef __cplusplus +} +#endif + +#endif /* ENC28J60_H */ +/** @} */