|  | /* 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]; | 
|  |  | 
|  | nano_fiber_sem_take(&context->spi_sem, TICKS_UNLIMITED); | 
|  |  | 
|  | 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); | 
|  |  | 
|  | nano_fiber_sem_give(&context->spi_sem); | 
|  | } | 
|  |  | 
|  | 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]; | 
|  |  | 
|  | nano_fiber_sem_take(&context->spi_sem, TICKS_UNLIMITED); | 
|  |  | 
|  | tx_buf[0] = ENC28J60_SPI_WCR | (reg_addr & 0xFF); | 
|  | tx_buf[1] = value; | 
|  |  | 
|  | spi_write(context->spi, tx_buf, 2); | 
|  |  | 
|  | nano_fiber_sem_give(&context->spi_sem); | 
|  | } | 
|  |  | 
|  | 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]; | 
|  |  | 
|  | nano_fiber_sem_take(&context->spi_sem, TICKS_UNLIMITED); | 
|  |  | 
|  | 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]; | 
|  |  | 
|  | nano_fiber_sem_give(&context->spi_sem); | 
|  | } | 
|  |  | 
|  | 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]; | 
|  |  | 
|  | nano_fiber_sem_take(&context->spi_sem, TICKS_UNLIMITED); | 
|  |  | 
|  | tx_buf[0] = ENC28J60_SPI_BFS | (reg_addr & 0xFF); | 
|  | tx_buf[1] = value; | 
|  |  | 
|  | spi_write(context->spi, tx_buf, 2); | 
|  |  | 
|  | nano_fiber_sem_give(&context->spi_sem); | 
|  | } | 
|  |  | 
|  |  | 
|  | 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]; | 
|  |  | 
|  | nano_fiber_sem_take(&context->spi_sem, TICKS_UNLIMITED); | 
|  |  | 
|  | tx_buf[0] = ENC28J60_SPI_BFC | (reg_addr & 0xFF); | 
|  | tx_buf[1] = value; | 
|  |  | 
|  | spi_write(context->spi, tx_buf, 2); | 
|  |  | 
|  | nano_fiber_sem_give(&context->spi_sem); | 
|  | } | 
|  |  | 
|  | 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; | 
|  |  | 
|  | nano_fiber_sem_take(&context->spi_sem, TICKS_UNLIMITED); | 
|  |  | 
|  | 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); | 
|  |  | 
|  | nano_fiber_sem_give(&context->spi_sem); | 
|  | } | 
|  |  | 
|  | 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; | 
|  |  | 
|  | nano_fiber_sem_take(&context->spi_sem, TICKS_UNLIMITED); | 
|  |  | 
|  | 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); | 
|  |  | 
|  | nano_fiber_sem_give(&context->spi_sem); | 
|  | } | 
|  |  | 
|  | 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) | 
|  | { | 
|  | const struct eth_enc28j60_config *config = dev->config->config_info; | 
|  | 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 (config->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 (config->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_set_bank(dev, ENC28J60_REG_MAADR0); | 
|  | 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) | 
|  | { | 
|  | const struct eth_enc28j60_config *config = dev->config->config_info; | 
|  |  | 
|  | if (config->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) | 
|  | { | 
|  | const struct eth_enc28j60_config *config = dev->config->config_info; | 
|  | struct eth_enc28j60_runtime *context = dev->driver_data; | 
|  | struct spi_config spi_cfg; | 
|  |  | 
|  | nano_sem_init(&context->spi_sem); | 
|  | nano_fiber_sem_give(&context->spi_sem); | 
|  |  | 
|  | 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); | 
|  |  | 
|  | 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_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); | 
|  | nano_sem_give(&context->tx_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); | 
|  |  | 
|  | /* 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 econ1_bkup; | 
|  | uint8_t counter; | 
|  |  | 
|  | /* Errata 6. The Receive Packet Pending Interrupt Flag (EIR.PKTIF) | 
|  | * does not reliably/accurately report the status of pending packet. | 
|  | * Use EPKTCNT register instead. | 
|  | */ | 
|  |  | 
|  | /* Backup ECON1 register in case the rx interrupted a tx process */ | 
|  | eth_enc28j60_read_reg(dev, ENC28J60_REG_ECON1, &econ1_bkup); | 
|  |  | 
|  | do { | 
|  | uint8_t *reception_buf = NULL; | 
|  | uint16_t frm_len = 0; | 
|  | uint16_t next_packet; | 
|  | struct net_buf *buf; | 
|  | uint8_t np[2]; | 
|  |  | 
|  | /* Read address for next packet */ | 
|  | eth_enc28j60_read_mem(dev, np, 2); | 
|  | next_packet = np[0] | (uint16_t)np[1] << 8; | 
|  |  | 
|  | /* Errata 14. Even values in ERXRDPT | 
|  | * may corrupt receive buffer. | 
|  | */ | 
|  | if (next_packet == 0) { | 
|  | next_packet = ENC28J60_RXEND; | 
|  | } else if (!(next_packet & 0x01)) { | 
|  | next_packet--; | 
|  | } | 
|  |  | 
|  | /* 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)) { | 
|  | goto done; | 
|  | } | 
|  | /* Get the frame from the buffer */ | 
|  | buf = ip_buf_get_reserve_rx(0); | 
|  |  | 
|  | reception_buf = net_buf_add(buf, frm_len); | 
|  |  | 
|  | if (reception_buf == NULL) { | 
|  | frm_len = 0; | 
|  | goto done; | 
|  | } | 
|  |  | 
|  | 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); | 
|  | done: | 
|  | /* 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_EPKTCNT, &counter); | 
|  | } while (counter); | 
|  |  | 
|  | /* Recover ECON1 register */ | 
|  | eth_enc28j60_write_reg(dev, ENC28J60_REG_ECON1, econ1_bkup); | 
|  |  | 
|  | 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 const 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, | 
|  | .full_duplex = CONFIG_ETH_EN28J60_0_FULL_DUPLEX, | 
|  | }; | 
|  |  | 
|  | DEVICE_AND_API_INIT(eth_enc28j60_0, CONFIG_ETH_ENC28J60_0_NAME, | 
|  | ð_enc28j60_init, ð_enc28j60_0_runtime, | 
|  | ð_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 */ |