/* 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 #include #include #include #include #include #include #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_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) { 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_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); /* 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 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. */ 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); 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, ð_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 */