driver: ENC28J60 Ethernet SPI module

Adds the ENC28J60 Ethernet module driver.

Origin: Original
JIRA: ZEP-291

Change-Id: I2b5790ecb251f9059f172bcaafadef24bd27207a
Signed-off-by: Juan Manuel Cruz <juan.m.cruz.alcaraz@intel.com>
This commit is contained in:
Juan Manuel Cruz 2016-07-08 14:52:03 -05:00 committed by Inaky Perez-Gonzalez
commit 184a399f61
6 changed files with 1058 additions and 0 deletions

View file

@ -91,3 +91,5 @@ config ETH_DW_0_IRQ_PRI
IRQ priority
endif # ETH_DW
source "drivers/ethernet/Kconfig.enc28j60"

View file

@ -0,0 +1,103 @@
# Kconfig - ETH_ENC28J60 Ethernet driver configuration options
#
# Copyright (c) 2015 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
menuconfig ETH_ENC28J60
bool "ENC28J60C Ethernet Controller"
depends on ETHERNET
depends on SPI
default n
help
ENC28J60C Stand-Alone Ethernet Controller
with SPI Interface
config ETH_ENC28J60_0
bool "ENC28J60C Ethernet port 0"
depends on ETH_ENC28J60
default n
help
Include port 0 driver
if ETH_ENC28J60 && ETH_ENC28J60_0
config ETH_ENC28J60_0_NAME
string "Driver's name"
default "ETH_0"
config ETH_ENC28J60_0_INIT_PRIORITY
int
prompt "ENC28J60C init priority"
default 80
help
Device driver initialization priority.
Since the device is connected to SPI bus, its driver has
to be initialized after the SPI one.
config ETH_ENC28J60_0_GPIO_PORT_NAME
string "GPIO controller port name"
default "GPIO_0"
help
GPIO port name through which ENC28J60C interruption is received.
config ETH_ENC28J60_0_GPIO_PIN
int "ENC28J60C INT GPIO PIN"
default 24
help
GPIO pin number used to conect INT
config ETH_ENC28J60_0_SPI_PORT_NAME
string "SPI master controller port name"
default "SPI_0"
help
Master I2C port name through which ENC28J60C chip is accessed.
config ETH_ENC28J60_0_SLAVE
hex "ETH_ENC28J60 SPI slave select pin"
default 1
help
ENC28J60C chip select pin.
config ETH_ENC28J60_0_SPI_BUS_FREQ
int "ENC28J60C SPI bus speed in Hz"
default 128
help
This is the maximum supported SPI bus frequency.
config ETH_ENC28J60_0_MAC3
hex "MAC Address Byte 3"
default 0
help
MACADDR<0:23> are Microchip's OUI.
This is the byte 3 of the MAC address.
MACADDR<31:24>
config ETH_ENC28J60_0_MAC4
hex "MAC Address Byte 4"
default 0
help
MACADDR<0:23> are Microchip's OUI.
This is the byte 4 of the MAC address.
MACADDR<40:32>
config ETH_ENC28J60_0_MAC5
hex "MAC Address Byte 5"
default 0
help
MACADDR<0:23> are Microchip's OUI.
This is the byte 5 of the MAC address.
MACADDR<48:41>
endif #ETH_ENC28J60 && ETH_ENC28J60_0

View file

@ -5,3 +5,4 @@ ccflags-y += -I${srctree}/net/ip
ccflags-y += -I${srctree}
obj-$(CONFIG_ETH_DW) += eth_dw.o
obj-$(CONFIG_ETH_ENC28J60) += eth_enc28j60.o

View file

@ -0,0 +1,619 @@
/* ENC28J60 Stand-alone Ethernet Controller with SPI
*
* Copyright (c) 2016 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <net/ip/net_driver_ethernet.h>
#include <zephyr.h>
#include <string.h>
#include <errno.h>
#include <gpio.h>
#include <spi.h>
#include <eth.h>
#include "eth_enc28j60_priv.h"
#define D10D24S 11
#define MAX_BUFFER_LENGTH 100
static int eth_net_tx(struct net_buf *buf);
static void enc28j60_fiber_main(int arg1, int unused);
static int eth_enc28j60_soft_reset(struct device *dev)
{
struct eth_enc28j60_runtime *context = dev->driver_data;
uint8_t tx_buf[2] = {ENC28J60_SPI_SC, 0xFF};
return spi_write(context->spi, tx_buf, 2);
}
static void eth_enc28j60_set_bank(struct device *dev, uint16_t reg_addr)
{
struct eth_enc28j60_runtime *context = dev->driver_data;
uint8_t tx_buf[2];
tx_buf[0] = ENC28J60_SPI_RCR | ENC28J60_REG_ECON1;
tx_buf[1] = 0x0;
spi_transceive(context->spi, tx_buf, 2, tx_buf, 2);
tx_buf[0] = ENC28J60_SPI_WCR | ENC28J60_REG_ECON1;
tx_buf[1] = (tx_buf[1] & 0xFC) | ((reg_addr >> 8) & 0x0F);
spi_write(context->spi, tx_buf, 2);
}
static void eth_enc28j60_write_reg(struct device *dev, uint16_t reg_addr,
uint8_t value)
{
struct eth_enc28j60_runtime *context = dev->driver_data;
uint8_t tx_buf[2];
tx_buf[0] = ENC28J60_SPI_WCR | (reg_addr & 0xFF);
tx_buf[1] = value;
spi_write(context->spi, tx_buf, 2);
}
static void eth_enc28j60_read_reg(struct device *dev, uint16_t reg_addr,
uint8_t *value)
{
struct eth_enc28j60_runtime *context = dev->driver_data;
uint8_t tx_size = 2;
uint8_t tx_buf[3];
if (reg_addr & 0xF000) {
tx_size = 3;
}
tx_buf[0] = ENC28J60_SPI_RCR | (reg_addr & 0xFF);
tx_buf[1] = 0x0;
spi_transceive(context->spi, tx_buf, tx_size, tx_buf, tx_size);
*value = tx_buf[tx_size - 1];
}
static void eth_enc28j60_set_eth_reg(struct device *dev, uint16_t reg_addr,
uint8_t value)
{
struct eth_enc28j60_runtime *context = dev->driver_data;
uint8_t tx_buf[2];
tx_buf[0] = ENC28J60_SPI_BFS | (reg_addr & 0xFF);
tx_buf[1] = value;
spi_write(context->spi, tx_buf, 2);
}
static void eth_enc28j60_clear_eth_reg(struct device *dev, uint16_t reg_addr,
uint8_t value)
{
struct eth_enc28j60_runtime *context = dev->driver_data;
uint8_t tx_buf[2];
tx_buf[0] = ENC28J60_SPI_BFC | (reg_addr & 0xFF);
tx_buf[1] = value;
spi_write(context->spi, tx_buf, 2);
}
static void eth_enc28j60_write_mem(struct device *dev, uint8_t *data_buffer,
uint8_t buf_len)
{
struct eth_enc28j60_runtime *context = dev->driver_data;
uint8_t tx_buf[MAX_BUFFER_LENGTH + 1];
uint8_t *index_buf;
uint16_t num_segments;
uint16_t num_remanents;
index_buf = data_buffer;
num_segments = buf_len / MAX_BUFFER_LENGTH;
num_remanents = buf_len - MAX_BUFFER_LENGTH * num_segments;
tx_buf[0] = ENC28J60_SPI_WBM;
for (int i = 0; i < num_segments;
++i, index_buf += i * MAX_BUFFER_LENGTH) {
memcpy(tx_buf + 1, index_buf, MAX_BUFFER_LENGTH);
spi_write(context->spi, tx_buf, MAX_BUFFER_LENGTH + 1);
}
memcpy(tx_buf + 1, index_buf, num_remanents);
spi_write(context->spi, tx_buf, num_remanents + 1);
}
static void eth_enc28j60_read_mem(struct device *dev, uint8_t *data_buffer,
uint8_t buf_len)
{
struct eth_enc28j60_runtime *context = dev->driver_data;
uint8_t *index_buf;
uint8_t tx_buf[MAX_BUFFER_LENGTH + 1];
uint16_t num_segments;
uint16_t num_remanents;
index_buf = data_buffer;
num_segments = buf_len / MAX_BUFFER_LENGTH;
num_remanents = buf_len - MAX_BUFFER_LENGTH * num_segments;
tx_buf[0] = ENC28J60_SPI_RBM;
for (int i = 0; i < num_segments;
++i, index_buf += i * MAX_BUFFER_LENGTH) {
spi_transceive(context->spi, tx_buf, MAX_BUFFER_LENGTH + 1,
tx_buf, MAX_BUFFER_LENGTH + 1);
memcpy(index_buf, tx_buf + 1, MAX_BUFFER_LENGTH);
}
spi_transceive(context->spi, tx_buf, num_remanents + 1,
tx_buf, num_remanents + 1);
memcpy(index_buf, tx_buf + 1, num_remanents);
}
static void eth_enc28j60_read_phy(struct device *dev, uint16_t reg_addr,
int16_t *data)
{
uint8_t rl, rh;
uint8_t data_mistat;
eth_enc28j60_set_bank(dev, ENC28J60_REG_MIREGADR);
eth_enc28j60_write_reg(dev, ENC28J60_REG_MIREGADR, reg_addr);
eth_enc28j60_write_reg(dev, ENC28J60_REG_MICMD,
ENC28J60_BIT_MICMD_MIIRD);
eth_enc28j60_set_bank(dev, ENC28J60_REG_MISTAT);
do {
/* wait 10.24 useconds */
sys_thread_busy_wait(D10D24S);
eth_enc28j60_read_reg(dev, ENC28J60_REG_MISTAT,
&data_mistat);
} while ((data_mistat & ENC28J60_BIT_MISTAT_BUSY));
eth_enc28j60_set_bank(dev, ENC28J60_REG_MIREGADR);
eth_enc28j60_write_reg(dev, ENC28J60_REG_MICMD, 0x00);
eth_enc28j60_read_reg(dev, ENC28J60_REG_MIRDL, &rl);
eth_enc28j60_read_reg(dev, ENC28J60_REG_MIRDH, &rh);
*data = rl | ((uint16_t)rh << 8);
}
static void eth_enc28j60_write_phy(struct device *dev, uint16_t reg_addr,
int16_t data)
{
uint8_t data_mistat;
eth_enc28j60_set_bank(dev, ENC28J60_REG_MIREGADR);
eth_enc28j60_write_reg(dev, ENC28J60_REG_MIREGADR, reg_addr);
eth_enc28j60_write_reg(dev, ENC28J60_REG_MIWRL, data & 0xFF);
eth_enc28j60_write_reg(dev, ENC28J60_REG_MIWRH, data >> 8);
eth_enc28j60_set_bank(dev, ENC28J60_REG_MISTAT);
do {
/* wait 10.24 useconds */
sys_thread_busy_wait(D10D24S);
eth_enc28j60_read_reg(dev, ENC28J60_REG_MISTAT,
&data_mistat);
} while ((data_mistat & ENC28J60_BIT_MISTAT_BUSY));
}
static void eth_enc28j60_gpio_callback(struct device *dev,
struct gpio_callback *cb,
uint32_t pins)
{
struct eth_enc28j60_runtime *context =
CONTAINER_OF(cb, struct eth_enc28j60_runtime, gpio_cb);
nano_fiber_sem_give(&context->int_sem);
}
static void eth_enc28j60_init_buffers(struct device *dev)
{
uint8_t data_estat;
/* Reception buffers initialization */
eth_enc28j60_set_bank(dev, ENC28J60_REG_ERXSTL);
eth_enc28j60_write_reg(dev, ENC28J60_REG_ERXSTL,
ENC28J60_RXSTART & 0xFF);
eth_enc28j60_write_reg(dev, ENC28J60_REG_ERXSTH,
ENC28J60_RXSTART >> 8);
eth_enc28j60_write_reg(dev, ENC28J60_REG_ERXRDPTL,
ENC28J60_RXSTART & 0xFF);
eth_enc28j60_write_reg(dev, ENC28J60_REG_ERXRDPTH,
ENC28J60_RXSTART >> 8);
eth_enc28j60_write_reg(dev, ENC28J60_REG_ERXNDL,
ENC28J60_RXEND & 0xFF);
eth_enc28j60_write_reg(dev, ENC28J60_REG_ERXNDH,
ENC28J60_RXEND >> 8);
eth_enc28j60_write_reg(dev, ENC28J60_REG_ETXSTL,
ENC28J60_TXSTART & 0xFF);
eth_enc28j60_write_reg(dev, ENC28J60_REG_ETXSTH,
ENC28J60_TXSTART >> 8);
eth_enc28j60_write_reg(dev, ENC28J60_REG_ETXNDL,
ENC28J60_TXEND & 0xFF);
eth_enc28j60_write_reg(dev, ENC28J60_REG_ETXNDH,
ENC28J60_TXEND >> 8);
eth_enc28j60_write_reg(dev, ENC28J60_REG_ERDPTL,
ENC28J60_RXSTART & 0xFF);
eth_enc28j60_write_reg(dev, ENC28J60_REG_ERDPTH,
ENC28J60_RXSTART >> 8);
eth_enc28j60_write_reg(dev, ENC28J60_REG_EWRPTL,
ENC28J60_TXSTART & 0xFF);
eth_enc28j60_write_reg(dev, ENC28J60_REG_EWRPTH,
ENC28J60_TXSTART >> 8);
eth_enc28j60_set_bank(dev, ENC28J60_REG_ERXFCON);
eth_enc28j60_write_reg(dev, ENC28J60_REG_ERXFCON,
ENC28J60_RECEIVE_FILTERS);
/* Waiting for OST */
do {
/* wait 10.24 useconds */
sys_thread_busy_wait(D10D24S);
eth_enc28j60_read_reg(dev, ENC28J60_REG_ESTAT, &data_estat);
} while (!(data_estat & ENC28J60_BIT_ESTAT_CLKRDY));
}
static void eth_enc28j60_init_mac(struct device *dev)
{
struct eth_enc28j60_runtime *context = dev->driver_data;
uint8_t data_macon;
uint8_t mac_address[6];
eth_enc28j60_set_bank(dev, ENC28J60_REG_MACON1);
/* Set MARXEN to enable MAC to receive frames */
eth_enc28j60_read_reg(dev, ENC28J60_REG_MACON1, &data_macon);
data_macon |= ENC28J60_BIT_MACON1_MARXEN | ENC28J60_BIT_MACON1_RXPAUS
| ENC28J60_BIT_MACON1_TXPAUS;
eth_enc28j60_write_reg(dev, ENC28J60_REG_MACON1, data_macon);
data_macon = ENC28J60_MAC_CONFIG;
if (context->full_duplex) {
data_macon |= ENC28J60_BIT_MACON3_FULDPX;
}
eth_enc28j60_write_reg(dev, ENC28J60_REG_MACON3, data_macon);
eth_enc28j60_write_reg(dev, ENC28J60_REG_MAIPGL, ENC28J60_MAC_NBBIPGL);
if (context->full_duplex) {
eth_enc28j60_write_reg(dev, ENC28J60_REG_MAIPGH,
ENC28J60_MAC_NBBIPGH);
eth_enc28j60_write_reg(dev, ENC28J60_REG_MABBIPG,
ENC28J60_MAC_BBIPG_FD);
} else {
eth_enc28j60_write_reg(dev, ENC28J60_REG_MABBIPG,
ENC28J60_MAC_BBIPG_HD);
eth_enc28j60_write_reg(dev, ENC28J60_REG_MACON4, 1 << 6);
}
/* Configure MAC address */
mac_address[0] = MICROCHIP_OUI_B0;
mac_address[1] = MICROCHIP_OUI_B1;
mac_address[2] = MICROCHIP_OUI_B2;
mac_address[3] = CONFIG_ETH_ENC28J60_0_MAC3;
mac_address[4] = CONFIG_ETH_ENC28J60_0_MAC4;
mac_address[5] = CONFIG_ETH_ENC28J60_0_MAC5;
net_set_mac(mac_address, sizeof(mac_address));
eth_enc28j60_write_reg(dev, ENC28J60_REG_MAADR0,
CONFIG_ETH_ENC28J60_0_MAC5);
eth_enc28j60_write_reg(dev, ENC28J60_REG_MAADR1,
CONFIG_ETH_ENC28J60_0_MAC4);
eth_enc28j60_write_reg(dev, ENC28J60_REG_MAADR2,
CONFIG_ETH_ENC28J60_0_MAC3);
eth_enc28j60_write_reg(dev, ENC28J60_REG_MAADR3, MICROCHIP_OUI_B2);
eth_enc28j60_write_reg(dev, ENC28J60_REG_MAADR4, MICROCHIP_OUI_B1);
eth_enc28j60_write_reg(dev, ENC28J60_REG_MAADR5, MICROCHIP_OUI_B0);
}
static void eth_enc28j60_init_phy(struct device *dev)
{
struct eth_enc28j60_runtime *context = dev->driver_data;
if (context->full_duplex) {
eth_enc28j60_write_phy(dev, ENC28J60_PHY_PHCON1,
ENC28J60_BIT_PHCON1_PDPXMD);
eth_enc28j60_write_phy(dev, ENC28J60_PHY_PHCON2, 0x0);
} else {
eth_enc28j60_write_phy(dev, ENC28J60_PHY_PHCON1, 0x0);
eth_enc28j60_write_phy(dev, ENC28J60_PHY_PHCON2,
ENC28J60_BIT_PHCON2_HDLDIS);
}
}
static int eth_enc28j60_init(struct device *dev)
{
struct eth_enc28j60_config *config = dev->config->config_info;
struct eth_enc28j60_runtime *context = dev->driver_data;
struct spi_config spi_cfg;
uint16_t data_phy;
context->gpio = device_get_binding((char *)config->gpio_port);
if (!context->gpio) {
return -EINVAL;
}
context->spi = device_get_binding((char *)config->spi_port);
if (!context->spi) {
return -EINVAL;
}
/* Initialize GPIO */
if (gpio_pin_configure(context->gpio, config->gpio_pin,
(GPIO_DIR_IN | GPIO_INT | GPIO_INT_EDGE
| GPIO_INT_ACTIVE_LOW | GPIO_INT_DEBOUNCE))) {
return -EINVAL;
}
gpio_init_callback(&(context->gpio_cb), eth_enc28j60_gpio_callback,
BIT(config->gpio_pin));
if (gpio_add_callback(context->gpio, &(context->gpio_cb))) {
return -EINVAL;
}
if (gpio_pin_enable_callback(context->gpio, config->gpio_pin)) {
return -EINVAL;
}
/* Initialize SPI:
* Mode: 0/0; Size: 8 bits; MSB
*/
spi_cfg.config = 8 << 4;
spi_cfg.max_sys_freq = config->spi_freq;
if (spi_configure(context->spi, &spi_cfg) < 0) {
return -EIO;
}
if (spi_slave_select(context->spi, config->spi_slave) < 0) {
return -EIO;
}
if (eth_enc28j60_soft_reset(dev)) {
return -EIO;
}
/* Errata B7/2 */
sys_thread_busy_wait(D10D24S);
/* Select half-duplex/full-duplex from LED configuration */
eth_enc28j60_read_phy(dev, ENC28J60_PHY_PHCON1, &data_phy);
data_phy &= ENC28J60_BIT_PHCON1_PDPXMD;
context->full_duplex = !!(data_phy);
eth_enc28j60_init_buffers(dev);
eth_enc28j60_init_mac(dev);
eth_enc28j60_init_phy(dev);
/* Enable interruptions */
eth_enc28j60_set_eth_reg(dev, ENC28J60_REG_EIE, ENC28J60_BIT_EIE_INTIE);
eth_enc28j60_set_eth_reg(dev, ENC28J60_REG_EIE, ENC28J60_BIT_EIE_TXIE);
eth_enc28j60_set_eth_reg(dev, ENC28J60_REG_EIE, ENC28J60_BIT_EIE_PKTIE);
/* Enable Reception */
eth_enc28j60_set_eth_reg(dev, ENC28J60_REG_ECON1,
ENC28J60_BIT_ECON1_RXEN);
/* Register tx callback into IP stack */
net_driver_ethernet_register_tx(eth_net_tx);
/* Initialize semaphores */
nano_sem_init(&context->tx_sem);
nano_sem_init(&context->int_sem);
/* Start interruption-poll fiber */
fiber_start(context->fiber_stack, ENC28J60_FIBER_STACK_SIZE,
enc28j60_fiber_main, (int)dev, 0,
ENC28J60_FIBER_PRIORITY, 0);
return 0;
}
static int eth_enc28j60_tx(struct device *dev, uint8_t *buf, uint16_t len)
{
struct eth_enc28j60_runtime *context = dev->driver_data;
uint16_t tx_bufaddr = ENC28J60_TXSTART;
uint8_t per_packet_control;
uint16_t tx_bufaddr_end;
uint8_t tx_end;
nano_fiber_sem_take(&context->tx_sem, TICKS_UNLIMITED);
/* Latest errata sheet: DS80349C
* always reset transmit logic (Errata Issue 12)
* the Microchip TCP/IP stack implementation used to first check
* whether TXERIF is set and only then reset the transmit logic
* but this has been changed in later versions; possibly they
* have a reason for this; they don't mention this in the errata
* sheet
*/
eth_enc28j60_set_eth_reg(dev, ENC28J60_REG_ECON1,
ENC28J60_BIT_ECON1_TXRST);
eth_enc28j60_clear_eth_reg(dev, ENC28J60_REG_ECON1,
ENC28J60_BIT_ECON1_TXRST);
/* Write the buffer content into the transmission buffer */
eth_enc28j60_set_bank(dev, ENC28J60_REG_ETXSTL);
eth_enc28j60_write_reg(dev, ENC28J60_REG_EWRPTL, tx_bufaddr & 0xFF);
eth_enc28j60_write_reg(dev, ENC28J60_REG_EWRPTH, tx_bufaddr >> 8);
eth_enc28j60_write_reg(dev, ENC28J60_REG_ETXSTL, tx_bufaddr & 0xFF);
eth_enc28j60_write_reg(dev, ENC28J60_REG_ETXSTH, tx_bufaddr >> 8);
/* Write the data into the buffer */
per_packet_control = ENC28J60_PPCTL_BYTE;
eth_enc28j60_write_mem(dev, &per_packet_control, 1);
eth_enc28j60_write_mem(dev, buf, len);
tx_bufaddr_end = tx_bufaddr + len;
eth_enc28j60_write_reg(dev, ENC28J60_REG_ETXNDL,
tx_bufaddr_end & 0xFF);
eth_enc28j60_write_reg(dev, ENC28J60_REG_ETXNDH, tx_bufaddr_end >> 8);
/* Clear transmit interruption flag */
eth_enc28j60_clear_eth_reg(dev, ENC28J60_REG_EIR,
ENC28J60_BIT_EIR_TXIF
| ENC28J60_BIT_EIR_TXERIF);
/* Signal ENC28J60 to send the buffer */
eth_enc28j60_set_eth_reg(dev, ENC28J60_REG_ECON1,
ENC28J60_BIT_ECON1_TXRTS);
do {
/* wait 10.24 useconds */
sys_thread_busy_wait(D10D24S);
eth_enc28j60_read_reg(dev, ENC28J60_REG_EIR, &tx_end);
tx_end &= ENC28J60_BIT_EIR_TXIF;
} while (!tx_end);
eth_enc28j60_read_reg(dev, ENC28J60_REG_EIR, &tx_end);
nano_sem_give(&context->tx_sem);
if (tx_end & ENC28J60_BIT_ESTAT_TXABRT) {
return -1;
}
return 0;
}
static int eth_enc28j60_rx(struct device *dev)
{
struct eth_enc28j60_runtime *context = dev->driver_data;
uint8_t rx_flag;
/* Check if there are received frames in buffer */
eth_enc28j60_read_reg(dev, ENC28J60_REG_EIR, &rx_flag);
rx_flag &= ENC28J60_BIT_EIR_PKTIF;
while (rx_flag) {
struct net_buf *buf;
uint8_t np[2];
uint8_t *reception_buf;
uint16_t frm_len = 0;
uint16_t next_packet;
/* Read address for next packet */
eth_enc28j60_read_mem(dev, np, 2);
next_packet = np[0] | (uint16_t)np[1] << 8;
/* Read reception status vector */
eth_enc28j60_read_mem(dev, context->rx_rsv, 4);
/* Get the frame length from the rx status vector */
frm_len = (context->rx_rsv[1] << 8) | context->rx_rsv[0];
if (frm_len > UIP_BUFSIZE) {
return -EIO;
}
/* Get the frame from the buffer */
buf = ip_buf_get_reserve_rx(0);
reception_buf = net_buf_add(buf, frm_len);
eth_enc28j60_read_mem(dev, reception_buf, frm_len);
uip_len(buf) = frm_len;
/* Register the buffer frame with the IP stack */
net_driver_ethernet_recv(buf);
/* Free buffer memory and decrement rx counter */
eth_enc28j60_set_bank(dev, ENC28J60_REG_ERXRDPTL);
eth_enc28j60_write_reg(dev, ENC28J60_REG_ERXRDPTL,
next_packet & 0xFF);
eth_enc28j60_write_reg(dev, ENC28J60_REG_ERXRDPTH,
next_packet >> 8);
eth_enc28j60_set_eth_reg(dev, ENC28J60_REG_ECON2,
ENC28J60_BIT_ECON2_PKTDEC);
/* Call receive callback */
if (context->receive_callback) {
context->receive_callback(reception_buf, frm_len);
}
/* Check if there are frames to clean from the buffer */
eth_enc28j60_read_reg(dev, ENC28J60_REG_EIR, &rx_flag);
rx_flag &= ENC28J60_BIT_EIR_PKTIF;
}
return 0;
}
void eth_enc28j60_reg_cb(struct device *dev,
void (*cb)(uint8_t *buffer, uint16_t len))
{
struct eth_enc28j60_runtime *context = dev->driver_data;
context->receive_callback = cb;
}
static void enc28j60_fiber_main(int arg1, int unused)
{
struct device *dev = (struct device *)arg1;
struct eth_enc28j60_runtime *context;
uint8_t int_stat;
ARG_UNUSED(unused);
context = dev->driver_data;
while (1) {
nano_fiber_sem_take(&context->int_sem, TICKS_UNLIMITED);
eth_enc28j60_read_reg(dev, ENC28J60_REG_EIR, &int_stat);
if (int_stat & ENC28J60_BIT_EIR_PKTIF) {
eth_enc28j60_rx(dev);
/* Clear rx interruption flag */
eth_enc28j60_clear_eth_reg(dev, ENC28J60_REG_EIR,
ENC28J60_BIT_EIR_PKTIF
| ENC28J60_BIT_EIR_RXERIF);
}
}
}
static struct eth_driver_api api_funcs = {
.send = eth_enc28j60_tx,
.register_callback = eth_enc28j60_reg_cb,
};
#ifdef CONFIG_ETH_ENC28J60_0
static struct eth_enc28j60_runtime eth_enc28j60_0_runtime = {
.receive_callback = NULL,
};
static struct eth_enc28j60_config eth_enc28j60_0_config = {
.gpio_port = CONFIG_ETH_ENC28J60_0_GPIO_PORT_NAME,
.gpio_pin = CONFIG_ETH_ENC28J60_0_GPIO_PIN,
.spi_port = CONFIG_ETH_ENC28J60_0_SPI_PORT_NAME,
.spi_freq = CONFIG_ETH_ENC28J60_0_SPI_BUS_FREQ,
.spi_slave = CONFIG_ETH_ENC28J60_0_SLAVE,
};
DEVICE_AND_API_INIT(eth_enc28j60_0, CONFIG_ETH_ENC28J60_0_NAME,
&eth_enc28j60_init, &eth_enc28j60_0_runtime,
&eth_enc28j60_0_config, SECONDARY,
CONFIG_ETH_ENC28J60_0_INIT_PRIORITY, &api_funcs);
static int eth_net_tx(struct net_buf *buf)
{
return eth_enc28j60_tx(DEVICE_GET(eth_enc28j60_0),
uip_buf(buf), uip_len(buf));
}
#endif /* CONFIG_ETH_ENC28J60_0 */

View file

@ -0,0 +1,248 @@
/* ENC28J60 Stand-alone Ethernet Controller with SPI
*
* Copyright (c) 2016 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <nanokernel.h>
#include <gpio.h>
#ifndef _ENC28J60_
#define _ENC28J60_
/* Any Bank Registers */
#define ENC28J60_REG_EIE 0x1B
#define ENC28J60_REG_EIR 0x1C
#define ENC28J60_REG_ESTAT 0x1D
#define ENC28J60_REG_ECON2 0x1E
#define ENC28J60_REG_ECON1 0x1F
/* Register Encoding
* Byte 3 : 0x0 ETH Register
* 0x1 MAC Register
* 0x2 MII Register
* Byte 2 : Bank number
* Byte 1-0: Register address
*/
/* Bank 0 Registers */
#define ENC28J60_REG_ERDPTL 0x0000
#define ENC28J60_REG_ERDPTH 0x0001
#define ENC28J60_REG_EWRPTL 0x0002
#define ENC28J60_REG_EWRPTH 0x0003
#define ENC28J60_REG_ETXSTL 0x0004
#define ENC28J60_REG_ETXSTH 0x0005
#define ENC28J60_REG_ETXNDL 0x0006
#define ENC28J60_REG_ETXNDH 0x0007
#define ENC28J60_REG_ERXSTL 0x0008
#define ENC28J60_REG_ERXSTH 0x0009
#define ENC28J60_REG_ERXNDL 0x000A
#define ENC28J60_REG_ERXNDH 0x000B
#define ENC28J60_REG_ERXRDPTL 0x000C
#define ENC28J60_REG_ERXRDPTH 0x000D
#define ENC28J60_REG_ERXWRPTL 0x000E
#define ENC28J60_REG_ERXWRPTH 0x000F
#define ENC28J60_REG_EDMASTL 0x0010
#define ENC28J60_REG_EDMASTH 0x0011
#define ENC28J60_REG_EDMANDL 0x0012
#define ENC28J60_REG_EDMANDH 0x0013
#define ENC28J60_REG_EDMADSTL 0x0014
#define ENC28J60_REG_EDMADSTH 0x0015
#define ENC28J60_REG_EDMACSL 0x0016
#define ENC28J60_REG_EDMACSH 0x0017
/* Bank 1 Registers */
#define ENC28J60_REG_EHT0 0x0100
#define ENC28J60_REG_EHT1 0x0101
#define ENC28J60_REG_EHT2 0x0102
#define ENC28J60_REG_EHT3 0x0103
#define ENC28J60_REG_EHT4 0x0104
#define ENC28J60_REG_EHT5 0x0105
#define ENC28J60_REG_EHT6 0x0106
#define ENC28J60_REG_EHT7 0x0107
#define ENC28J60_REG_EPMM0 0x0108
#define ENC28J60_REG_EPMM1 0x0109
#define ENC28J60_REG_EPMM2 0x010A
#define ENC28J60_REG_EPMM3 0x010B
#define ENC28J60_REG_EPMM4 0x010C
#define ENC28J60_REG_EPMM5 0x010D
#define ENC28J60_REG_EPMM6 0x010E
#define ENC28J60_REG_EPMM7 0x010F
#define ENC28J60_REG_EPMCSL 0x0110
#define ENC28J60_REG_EPMCSH 0x0111
#define ENC28J60_REG_EPMOL 0x0114
#define ENC28J60_REG_EPMOH 0x0115
#define ENC28J60_REG_EWOLIE 0x0116
#define ENC28J60_REG_EWOLIR 0x0117
#define ENC28J60_REG_ERXFCON 0x0118
#define ENC28J60_REG_EPKTCNT 0x0119
/* Bank 2 Registers */
#define ENC28J60_REG_MACON1 0x1200
#define ENC28J60_REG_MACON3 0x1202
#define ENC28J60_REG_MACON4 0x1203
#define ENC28J60_REG_MABBIPG 0x1204
#define ENC28J60_REG_MAIPGL 0x1206
#define ENC28J60_REG_MAIPGH 0x1207
#define ENC28J60_REG_MACLCON1 0x1208
#define ENC28J60_REG_MACLCON2 0x1209
#define ENC28J60_REG_MAMXFLL 0x120A
#define ENC28J60_REG_MAMXFLH 0x120B
#define ENC28J60_REG_MAPHSUP 0x120C
#define ENC28J60_REG_MICON 0x2211
#define ENC28J60_REG_MICMD 0x2212
#define ENC28J60_REG_MIREGADR 0x2214
#define ENC28J60_REG_MIWRL 0x2216
#define ENC28J60_REG_MIWRH 0x2217
#define ENC28J60_REG_MIRDL 0x2218
#define ENC28J60_REG_MIRDH 0x2219
/* Bank 3 Registers */
#define ENC28J60_REG_MAADR1 0x1300
#define ENC28J60_REG_MAADR0 0x1301
#define ENC28J60_REG_MAADR3 0x1302
#define ENC28J60_REG_MAADR2 0x1303
#define ENC28J60_REG_MAADR5 0x1304
#define ENC28J60_REG_MAADR4 0x1305
#define ENC28J60_REG_EBSTSD 0x0306
#define ENC28J60_REG_EBSTCON 0x0307
#define ENC28J60_REG_EBSTCSL 0x0308
#define ENC28J60_REG_EBSTCSH 0x0309
#define ENC28J60_REG_MISTAT 0x230A
#define ENC28J60_REG_EREVID 0x0312
#define ENC28J60_REG_ECOCON 0x0315
#define ENC28J60_REG_EFLOCON 0x0317
#define ENC28J60_REG_EPAUSL 0x0318
#define ENC28J60_REG_EPAUSH 0x0319
/* PHY Registers */
#define ENC28J60_PHY_PHCON1 0x00
#define ENC28J60_PHY_PHSTAT1 0x01
#define ENC28J60_PHY_PHID1 0x02
#define ENC28J60_PHY_PHID2 0x03
#define ENC28J60_PHY_PHCON2 0x10
#define ENC28J60_PHY_PHSTAT2 0x11
#define ENC28J60_PHY_PHIE 0x12
#define ENC28J60_PHY_PHIR 0x13
#define ENC28J60_PHY_PHLCON 0x14
/* SPI Instruction Opcodes */
#define ENC28J60_SPI_RCR (0x0)
#define ENC28J60_SPI_RBM (0x3A)
#define ENC28J60_SPI_WCR (0x2 << 5)
#define ENC28J60_SPI_WBM (0x7A)
#define ENC28J60_SPI_BFS (0x4 << 5)
#define ENC28J60_SPI_BFC (0x5 << 5)
#define ENC28J60_SPI_SC (0xFF)
/* Significant bits */
#define ENC28J60_BIT_MICMD_MIIRD (0x01)
#define ENC28J60_BIT_MISTAT_BUSY (0x01)
#define ENC28J60_BIT_ESTAT_CLKRDY (0x01)
#define ENC28J60_BIT_MACON1_MARXEN (0x01)
#define ENC28J60_BIT_MACON1_RXPAUS (0x04)
#define ENC28J60_BIT_MACON1_TXPAUS (0x08)
#define ENC28J60_BIT_MACON1_MARXEN (0x01)
#define ENC28J60_BIT_MACON2_MARST (0x80)
#define ENC28J60_BIT_MACON3_FULDPX (0x01)
#define ENC28J60_BIT_ECON1_TXRST (0x80)
#define ENC28J60_BIT_ECON1_TXRTS (0x08)
#define ENC28J60_BIT_ECON1_RXEN (0x02)
#define ENC28J60_BIT_ECON2_PKTDEC (0x40)
#define ENC28J60_BIT_EIR_PKTIF (0x40)
#define ENC28J60_BIT_EIE_TXIE (0x08)
#define ENC28J60_BIT_EIE_PKTIE (0x40)
#define ENC28J60_BIT_EIE_INTIE (0x80)
#define ENC28J60_BIT_EIR_PKTIF (0x40)
#define ENC28J60_BIT_EIR_DMAIF (0x20)
#define ENC28J60_BIT_EIR_LINKIF (0x10)
#define ENC28J60_BIT_EIR_TXIF (0x08)
#define ENC28J60_BIT_EIR_WOLIF (0x04)
#define ENC28J60_BIT_EIR_TXERIF (0x02)
#define ENC28J60_BIT_EIR_RXERIF (0x01)
#define ENC28J60_BIT_ESTAT_TXABRT (0x02)
#define ENC28J60_BIT_ESTAT_LATECOL (0x10)
#define ENC28J60_BIT_PHCON1_PDPXMD (0x0100)
#define ENC28J60_BIT_PHCON2_HDLDIS (0x0001)
/* Driver Static Configuration */
/* Receive filters enabled:
* - Unicast
* - Broadcast
* - CRC Check
*/
#define ENC28J60_RECEIVE_FILTERS 0xA1
/* MAC configuration:
* - Automatic Padding
* - Automatic CRC
* - Frame Length Checking
*/
#define ENC28J60_MAC_CONFIG 0xF2
#define ENC28J60_MAC_BBIPG_HD 0x12
#define ENC28J60_MAC_BBIPG_FD 0x15
#define ENC28J60_MAC_NBBIPGL 0x12
#define ENC28J60_MAC_NBBIPGH 0x0C
#define ENC28J60_PHY_LEDCONF 0x3422
/* Status Vector size plus per packet control byte: 8 bytes */
#define ENC28J60_SV_SIZE 8
/* Per Packet Control Byte configured to follow MACON3 configuration */
#define ENC28J60_PPCTL_BYTE 0x0
/* Start of RX buffer, (must be zero, Rev. B4 Errata point 5) */
#define ENC28J60_RXSTART 0x0000
/* End of RX buffer, room for 2 packets */
#define ENC28J60_RXEND 0x0BFF
/* Start of TX buffer, room for 1 packet */
#define ENC28J60_TXSTART 0x0C00
/* End of TX buffer */
#define ENC28J60_TXEND 0x11FF
/* Status vectors array size */
#define TSV_SIZE 7
#define RSV_SIZE 4
/* Fiber Configuration */
#define ENC28J60_FIBER_STACK_SIZE 256
#define ENC28J60_FIBER_PRIORITY 100
/* Microchip's OUI*/
#define MICROCHIP_OUI_B0 0x00
#define MICROCHIP_OUI_B1 0x04
#define MICROCHIP_OUI_B2 0xA3
struct eth_enc28j60_config {
const char *gpio_port;
uint8_t gpio_pin;
const char *spi_port;
uint32_t spi_freq;
uint8_t spi_slave;
};
struct eth_enc28j60_runtime {
char __stack fiber_stack[ENC28J60_FIBER_STACK_SIZE];
struct device *gpio;
struct device *spi;
struct gpio_callback gpio_cb;
char full_duplex;
uint8_t tx_tsv[TSV_SIZE];
uint8_t rx_rsv[RSV_SIZE];
struct nano_sem tx_sem;
struct nano_sem int_sem;
void (*receive_callback)(uint8_t *buffer, uint16_t len);
};
#endif /*_ENC28J60_*/

85
include/eth.h Normal file
View file

@ -0,0 +1,85 @@
/**
* @file
* @brief Ethernet public API header file.
*/
/*
* Copyright (c) 2015 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __INCLUDE_ETH_H__
#define __INCLUDE_ETH_H__
#include <device.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Ethernet driver API
*
* This structure holds all API function pointers.
*/
struct eth_driver_api {
int (*send)(struct device *dev, uint8_t *buffer, uint16_t len);
void (*register_callback)(struct device *dev,
void (*cb)(uint8_t *buffer, uint16_t len));
};
/**
* @brief Sends a frame to ethernet hardware
*
* This routine writes a buffer to be sent through the ethernet hardware.
*
* @param dev Pointer to the device structure for the driver instance.
* @param buffer Byte array to be sent through the device
* @param len Length of the array
*
* @return Error code
*/
static inline int eth_send(struct device *dev, uint8_t *buffer, uint16_t len)
{
struct eth_driver_api *api;
api = (struct eth_driver_api *)dev->driver_api;
return api->send(dev, buffer, len);
}
/**
* @brief Register callback for received frame
*
* This routine writes a buffer to be sent through the ethernet hardware.
*
* @param dev Pointer to the device structure for the driver instance.
* @param cb Callback function to be registered in the driver.
*
* @return Error code
*/
static inline void eth_register_callback(struct device *dev,
void (*cb)(uint8_t *buffer, uint16_t len))
{
struct eth_driver_api *api;
api = (struct eth_driver_api *)dev->driver_api;
api->register_callback(dev, cb);
}
#ifdef __cplusplus
}
#endif
#endif /*__INCLUDE_ETH_H__*/