1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

drivers: added driver for enc28j60 ethernet chip

This commit is contained in:
Hauke Petersen 2015-11-01 16:41:27 +01:00
parent 0f80e688f3
commit 22cc26e9d7
7 changed files with 1055 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base

466
drivers/enc28j60/enc28j60.c Normal file
View File

@ -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 <hauke.petersen@fu-berlin.de>
*
* @}
*/
#include <errno.h>
#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 */
}

View File

@ -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 <hauke.petersen@fu-berlin.de>
*/
#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 */
/** @} */

View File

@ -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 <hauke.petersen@fu-berlin.de>
*/
#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 */
/** @} */

View File

@ -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 <hauke.petersen@fu-berlin.de>
*/
#ifndef ENC28J60_H
#define ENC28J60_H
#include <stdint.h>
#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 */
/** @} */