| /* |
| * Copyright (c) 2023 DENX Software Engineering GmbH |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT microchip_lan865x |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(eth_lan865x, CONFIG_ETHERNET_LOG_LEVEL); |
| |
| #include <zephyr/net/ethernet.h> |
| #include <zephyr/net/phy.h> |
| |
| #include <string.h> |
| #include <errno.h> |
| |
| #include <zephyr/net/net_if.h> |
| #include <zephyr/net/ethernet.h> |
| #include <zephyr/net/phy.h> |
| |
| #include "eth_lan865x_priv.h" |
| |
| static int lan865x_mac_rxtx_control(const struct device *dev, bool en) |
| { |
| struct lan865x_data *ctx = dev->data; |
| uint32_t ctl = 0; |
| |
| if (en) { |
| ctl = LAN865x_MAC_NCR_TXEN | LAN865x_MAC_NCR_RXEN; |
| } |
| |
| return oa_tc6_reg_write(ctx->tc6, LAN865x_MAC_NCR, ctl); |
| } |
| |
| static void lan865x_iface_init(struct net_if *iface) |
| { |
| const struct device *dev = net_if_get_device(iface); |
| struct lan865x_data *ctx = dev->data; |
| |
| net_if_set_link_addr(iface, ctx->mac_address, sizeof(ctx->mac_address), |
| NET_LINK_ETHERNET); |
| |
| if (ctx->iface == NULL) { |
| ctx->iface = iface; |
| } |
| |
| ethernet_init(iface); |
| |
| net_eth_carrier_on(iface); |
| ctx->iface_initialized = true; |
| } |
| |
| static enum ethernet_hw_caps lan865x_port_get_capabilities(const struct device *dev) |
| { |
| ARG_UNUSED(dev); |
| return ETHERNET_LINK_10BASE_T | ETHERNET_PROMISC_MODE; |
| } |
| |
| static int lan865x_gpio_reset(const struct device *dev); |
| static void lan865x_write_macaddress(const struct device *dev); |
| static int lan865x_set_config(const struct device *dev, enum ethernet_config_type type, |
| const struct ethernet_config *config) |
| { |
| const struct lan865x_config *cfg = dev->config; |
| struct lan865x_data *ctx = dev->data; |
| int ret = -ENOTSUP; |
| |
| if (type == ETHERNET_CONFIG_TYPE_PROMISC_MODE) { |
| return oa_tc6_reg_write(ctx->tc6, LAN865x_MAC_NCFGR, |
| LAN865x_MAC_NCFGR_CAF); |
| } |
| |
| if (type == ETHERNET_CONFIG_TYPE_MAC_ADDRESS) { |
| memcpy(ctx->mac_address, config->mac_address.addr, |
| sizeof(ctx->mac_address)); |
| |
| lan865x_write_macaddress(dev); |
| |
| return net_if_set_link_addr(ctx->iface, ctx->mac_address, |
| sizeof(ctx->mac_address), |
| NET_LINK_ETHERNET); |
| } |
| |
| if (type == ETHERNET_CONFIG_TYPE_T1S_PARAM) { |
| ret = lan865x_mac_rxtx_control(dev, LAN865x_MAC_TXRX_OFF); |
| if (ret) { |
| return ret; |
| } |
| |
| if (config->t1s_param.type == ETHERNET_T1S_PARAM_TYPE_PLCA_CONFIG) { |
| cfg->plca->enable = config->t1s_param.plca.enable; |
| cfg->plca->node_id = config->t1s_param.plca.node_id; |
| cfg->plca->node_count = config->t1s_param.plca.node_count; |
| cfg->plca->burst_count = config->t1s_param.plca.burst_count; |
| cfg->plca->burst_timer = config->t1s_param.plca.burst_timer; |
| cfg->plca->to_timer = config->t1s_param.plca.to_timer; |
| } |
| |
| /* Reset is required to re-program PLCA new configuration */ |
| lan865x_gpio_reset(dev); |
| } |
| |
| return ret; |
| } |
| |
| static int lan865x_wait_for_reset(const struct device *dev) |
| { |
| struct lan865x_data *ctx = dev->data; |
| uint8_t i; |
| |
| /* Wait for end of LAN865x reset */ |
| for (i = 0; !ctx->reset && i < LAN865X_RESET_TIMEOUT; i++) { |
| k_msleep(1); |
| } |
| |
| if (i == LAN865X_RESET_TIMEOUT) { |
| LOG_ERR("LAN865x reset timeout reached!"); |
| return -ENODEV; |
| } |
| |
| return 0; |
| } |
| |
| static int lan865x_gpio_reset(const struct device *dev) |
| { |
| const struct lan865x_config *cfg = dev->config; |
| struct lan865x_data *ctx = dev->data; |
| |
| ctx->reset = false; |
| ctx->tc6->protected = false; |
| |
| /* Perform (GPIO based) HW reset */ |
| /* assert RESET_N low for 10 µs (5 µs min) */ |
| gpio_pin_set_dt(&cfg->reset, 1); |
| k_busy_wait(10U); |
| /* deassert - end of reset indicated by IRQ_N low */ |
| gpio_pin_set_dt(&cfg->reset, 0); |
| |
| return lan865x_wait_for_reset(dev); |
| } |
| |
| static int lan865x_check_spi(const struct device *dev) |
| { |
| struct lan865x_data *ctx = dev->data; |
| uint32_t val; |
| int ret; |
| |
| ret = oa_tc6_reg_read(ctx->tc6, LAN865x_DEVID, &val); |
| if (ret < 0) { |
| return -ENODEV; |
| } |
| |
| ctx->silicon_rev = val & LAN865X_REV_MASK; |
| if (ctx->silicon_rev != 1 && ctx->silicon_rev != 2) { |
| return -ENODEV; |
| } |
| |
| ctx->chip_id = (val >> 4) & 0xFFFF; |
| if (ctx->chip_id != LAN8650_DEVID && ctx->chip_id != LAN8651_DEVID) { |
| return -ENODEV; |
| } |
| |
| return ret; |
| } |
| |
| /* Implementation of pseudo code from AN1760 */ |
| static uint8_t lan865x_read_indirect_reg(const struct device *dev, uint8_t addr, |
| uint8_t mask) |
| { |
| struct lan865x_data *ctx = dev->data; |
| uint32_t val; |
| |
| oa_tc6_reg_write(ctx->tc6, 0x000400D8, addr); |
| oa_tc6_reg_write(ctx->tc6, 0x000400DA, 0x02); |
| |
| oa_tc6_reg_read(ctx->tc6, 0x000400D9, &val); |
| |
| return (uint8_t) val & mask; |
| } |
| |
| /* |
| * Values in the below table for LAN865x rev. B0 and B1 (with place |
| * holders for cfgparamX. |
| */ |
| static oa_mem_map_t lan865x_conf[] = { |
| { .mms = 0x1, .address = 0x00, .value = 0x0000 }, |
| { .mms = 0x4, .address = 0xD0, .value = 0x3F31 }, |
| { .mms = 0x4, .address = 0xE0, .value = 0xC000 }, |
| { .mms = 0x4, .address = 0x84, .value = 0x0000 }, /* cfgparam1 */ |
| { .mms = 0x4, .address = 0x8A, .value = 0x0000 }, /* cfgparam2 */ |
| { .mms = 0x4, .address = 0xE9, .value = 0x9E50 }, |
| { .mms = 0x4, .address = 0xF5, .value = 0x1CF8 }, |
| { .mms = 0x4, .address = 0xF4, .value = 0xC020 }, |
| { .mms = 0x4, .address = 0xF8, .value = 0xB900 }, |
| { .mms = 0x4, .address = 0xF9, .value = 0x4E53 }, |
| { .mms = 0x4, .address = 0x91, .value = 0x9660 }, |
| { .mms = 0x4, .address = 0x77, .value = 0x0028 }, |
| { .mms = 0x4, .address = 0x43, .value = 0x00FF }, |
| { .mms = 0x4, .address = 0x44, .value = 0xFFFF }, |
| { .mms = 0x4, .address = 0x45, .value = 0x0000 }, |
| { .mms = 0x4, .address = 0x53, .value = 0x00FF }, |
| { .mms = 0x4, .address = 0x54, .value = 0xFFFF }, |
| { .mms = 0x4, .address = 0x55, .value = 0x0000 }, |
| { .mms = 0x4, .address = 0x40, .value = 0x0002 }, |
| { .mms = 0x4, .address = 0x50, .value = 0x0002 }, |
| { .mms = 0x4, .address = 0xAD, .value = 0x0000 }, /* cfgparam3 */ |
| { .mms = 0x4, .address = 0xAE, .value = 0x0000 }, /* cfgparam4 */ |
| { .mms = 0x4, .address = 0xAF, .value = 0x0000 }, /* cfgparam5 */ |
| { .mms = 0x4, .address = 0xB0, .value = 0x0103 }, |
| { .mms = 0x4, .address = 0xB1, .value = 0x0910 }, |
| { .mms = 0x4, .address = 0xB2, .value = 0x1D26 }, |
| { .mms = 0x4, .address = 0xB3, .value = 0x002A }, |
| { .mms = 0x4, .address = 0xB4, .value = 0x0103 }, |
| { .mms = 0x4, .address = 0xB5, .value = 0x070D }, |
| { .mms = 0x4, .address = 0xB6, .value = 0x1720 }, |
| { .mms = 0x4, .address = 0xB7, .value = 0x0027 }, |
| { .mms = 0x4, .address = 0xB8, .value = 0x0509 }, |
| { .mms = 0x4, .address = 0xB9, .value = 0x0E13 }, |
| { .mms = 0x4, .address = 0xBA, .value = 0x1C25 }, |
| { .mms = 0x4, .address = 0xBB, .value = 0x002B }, |
| { .mms = 0x4, .address = 0x0C, .value = 0x0100 }, |
| { .mms = 0x4, .address = 0x81, .value = 0x00E0 }, |
| }; |
| |
| /* Based on AN1760 DS60001760G pseudo code */ |
| static int lan865x_init_chip(const struct device *dev, uint8_t silicon_rev) |
| { |
| uint16_t cfgparam1, cfgparam2, cfgparam3, cfgparam4, cfgparam5; |
| uint8_t i, size = ARRAY_SIZE(lan865x_conf); |
| struct lan865x_data *ctx = dev->data; |
| int8_t offset1 = 0, offset2 = 0; |
| uint8_t value1, value2; |
| |
| /* Enable protected control RW */ |
| oa_tc6_set_protected_ctrl(ctx->tc6, true); |
| |
| value1 = lan865x_read_indirect_reg(dev, 0x04, 0x1F); |
| if ((value1 & 0x10) != 0) { /* Convert uint8_t to int8_t */ |
| offset1 = (int8_t)((uint8_t)value1 - 0x20); |
| } else { |
| offset1 = (int8_t)value1; |
| } |
| |
| value2 = lan865x_read_indirect_reg(dev, 0x08, 0x1F); |
| if ((value2 & 0x10) != 0) { /* Convert uint8_t to int8_t */ |
| offset2 = (int8_t)((uint8_t)value2 - 0x20); |
| } else { |
| offset2 = (int8_t)value2; |
| } |
| |
| cfgparam1 = (uint16_t) (((9 + offset1) & 0x3F) << 10) | |
| (uint16_t) (((14 + offset1) & 0x3F) << 4) | 0x03; |
| cfgparam2 = (uint16_t) (((40 + offset2) & 0x3F) << 10); |
| cfgparam3 = (uint16_t) (((5 + offset1) & 0x3F) << 8) | |
| (uint16_t) ((9 + offset1) & 0x3F); |
| cfgparam4 = (uint16_t) (((9 + offset1) & 0x3F) << 8) | |
| (uint16_t) ((14 + offset1) & 0x3F); |
| cfgparam5 = (uint16_t) (((17 + offset1) & 0x3F) << 8) | |
| (uint16_t) ((22 + offset1) & 0x3F); |
| |
| lan865x_update_dev_cfg_array(lan865x_conf, size, |
| MMS_REG(0x4, 0x84), cfgparam1); |
| lan865x_update_dev_cfg_array(lan865x_conf, size, |
| MMS_REG(0x4, 0x8A), cfgparam2); |
| lan865x_update_dev_cfg_array(lan865x_conf, size, |
| MMS_REG(0x4, 0xAD), cfgparam3); |
| lan865x_update_dev_cfg_array(lan865x_conf, size, |
| MMS_REG(0x4, 0xAE), cfgparam4); |
| lan865x_update_dev_cfg_array(lan865x_conf, size, |
| MMS_REG(0x4, 0xAF), cfgparam5); |
| |
| if (silicon_rev == 1) { |
| /* For silicon rev 1 (B0): (bit [3..0] from 0x0A0084 */ |
| lan865x_update_dev_cfg_array(lan865x_conf, size, |
| MMS_REG(0x4, 0xD0), 0x5F21); |
| } |
| |
| /* Write LAN865x config with correct order */ |
| for (i = 0; i < size; i++) { |
| oa_tc6_reg_write(ctx->tc6, MMS_REG(lan865x_conf[i].mms, |
| lan865x_conf[i].address), |
| (uint32_t)lan865x_conf[i].value); |
| } |
| |
| return 0; |
| } |
| /* Implementation of pseudo code from AN1760 - END */ |
| |
| static int lan865x_config_plca(const struct device *dev, uint8_t node_id, |
| uint8_t node_cnt, uint8_t burst_cnt, uint8_t burst_timer) |
| { |
| struct lan865x_data *ctx = dev->data; |
| uint32_t val; |
| |
| /* Collision Detection */ |
| oa_tc6_reg_write(ctx->tc6, 0x00040087, 0x0083u); /* COL_DET_CTRL0 */ |
| |
| /* T1S Phy Node ID and Max Node Count */ |
| if (node_id == 0) { |
| val = (uint32_t)node_cnt << 8; |
| } else { |
| val = (uint32_t)node_id; |
| } |
| oa_tc6_reg_write(ctx->tc6, 0x0004CA02, val); /* PLCA_CONTROL_1_REGISTER */ |
| |
| /* PLCA Burst Count and Burst Timer */ |
| val = ((uint32_t)burst_cnt << 8) | burst_timer; |
| oa_tc6_reg_write(ctx->tc6, 0x0004CA05, val); /* PLCA_BURST_MODE_REGISTER */ |
| |
| /* Enable PLCA */ |
| oa_tc6_reg_write(ctx->tc6, 0x0004CA01, BIT(15)); /* PLCA_CONTROL_0_REGISTER */ |
| |
| return 0; |
| } |
| |
| static void lan865x_write_macaddress(const struct device *dev) |
| { |
| struct lan865x_data *ctx = dev->data; |
| uint8_t *mac = &ctx->mac_address[0]; |
| uint32_t val; |
| |
| /* SPEC_ADD2_BOTTOM */ |
| val = (mac[3] << 24) | (mac[2] << 16) | (mac[1] << 8) | mac[0]; |
| oa_tc6_reg_write(ctx->tc6, LAN865x_MAC_SAB2, val); |
| |
| /* SPEC_ADD2_TOP */ |
| val = (mac[5] << 8) | mac[4]; |
| oa_tc6_reg_write(ctx->tc6, LAN865x_MAC_SAT2, val); |
| |
| /* |
| * SPEC_ADD1_BOTTOM - setting unique lower MAC address, back off time is |
| * generated out of it. |
| */ |
| val = (mac[5] << 24) | (mac[4] << 16) | (mac[3] << 8) | mac[2]; |
| oa_tc6_reg_write(ctx->tc6, LAN865x_MAC_SAB1, val); |
| } |
| |
| static int lan865x_set_specific_multicast_addr(const struct device *dev) |
| { |
| struct lan865x_data *ctx = dev->data; |
| uint32_t mac_h_hash = 0xffffffff; |
| uint32_t mac_l_hash = 0xffffffff; |
| int ret; |
| |
| /* Enable hash for all multicast addresses */ |
| ret = oa_tc6_reg_write(ctx->tc6, LAN865x_MAC_HRT, mac_h_hash); |
| if (ret) { |
| return ret; |
| } |
| |
| ret = oa_tc6_reg_write(ctx->tc6, LAN865x_MAC_HRB, mac_l_hash); |
| if (ret) { |
| return ret; |
| } |
| |
| return oa_tc6_reg_write(ctx->tc6, LAN865x_MAC_NCFGR, |
| LAN865x_MAC_NCFGR_MTIHEN); |
| } |
| |
| static int lan865x_default_config(const struct device *dev, uint8_t silicon_rev) |
| { |
| const struct lan865x_config *cfg = dev->config; |
| int ret; |
| |
| lan865x_write_macaddress(dev); |
| lan865x_set_specific_multicast_addr(dev); |
| |
| ret = lan865x_init_chip(dev, silicon_rev); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| if (cfg->plca->enable) { |
| ret = lan865x_config_plca(dev, cfg->plca->node_id, |
| cfg->plca->node_count, |
| cfg->plca->burst_count, |
| cfg->plca->burst_timer); |
| if (ret < 0) { |
| return ret; |
| } |
| } |
| return 0; |
| } |
| |
| static void lan865x_int_callback(const struct device *dev, |
| struct gpio_callback *cb, |
| uint32_t pins) |
| { |
| ARG_UNUSED(dev); |
| ARG_UNUSED(pins); |
| |
| struct lan865x_data *ctx = |
| CONTAINER_OF(cb, struct lan865x_data, gpio_int_callback); |
| |
| k_sem_give(&ctx->int_sem); |
| } |
| |
| static void lan865x_read_chunks(const struct device *dev) |
| { |
| const struct lan865x_config *cfg = dev->config; |
| struct lan865x_data *ctx = dev->data; |
| struct oa_tc6 *tc6 = ctx->tc6; |
| struct net_pkt *pkt; |
| int ret; |
| |
| pkt = net_pkt_rx_alloc(K_MSEC(cfg->timeout)); |
| if (!pkt) { |
| LOG_ERR("OA RX: Could not allocate packet!"); |
| return; |
| } |
| |
| k_sem_take(&ctx->tx_rx_sem, K_FOREVER); |
| ret = oa_tc6_read_chunks(tc6, pkt); |
| if (ret < 0) { |
| eth_stats_update_errors_rx(ctx->iface); |
| net_pkt_unref(pkt); |
| k_sem_give(&ctx->tx_rx_sem); |
| return; |
| } |
| |
| /* Feed buffer frame to IP stack */ |
| ret = net_recv_data(ctx->iface, pkt); |
| if (ret < 0) { |
| LOG_ERR("OA RX: Could not process packet (%d)!", ret); |
| net_pkt_unref(pkt); |
| } |
| k_sem_give(&ctx->tx_rx_sem); |
| } |
| |
| static void lan865x_int_thread(const struct device *dev) |
| { |
| struct lan865x_data *ctx = dev->data; |
| struct oa_tc6 *tc6 = ctx->tc6; |
| uint32_t sts, val, ftr; |
| int ret; |
| |
| while (true) { |
| k_sem_take(&ctx->int_sem, K_FOREVER); |
| if (!ctx->reset) { |
| oa_tc6_reg_read(tc6, OA_STATUS0, &sts); |
| if (sts & OA_STATUS0_RESETC) { |
| oa_tc6_reg_write(tc6, OA_STATUS0, sts); |
| lan865x_default_config(dev, ctx->silicon_rev); |
| oa_tc6_reg_read(tc6, OA_CONFIG0, &val); |
| val |= OA_CONFIG0_SYNC | OA_CONFIG0_RFA_ZARFE; |
| oa_tc6_reg_write(tc6, OA_CONFIG0, val); |
| lan865x_mac_rxtx_control(dev, LAN865x_MAC_TXRX_ON); |
| |
| ctx->reset = true; |
| /* |
| * According to OA T1S standard - it is mandatory to |
| * read chunk of data to get the IRQ_N negated (deasserted). |
| */ |
| oa_tc6_read_status(tc6, &ftr); |
| continue; |
| } |
| } |
| |
| /* |
| * The IRQ_N is asserted when RCA becomes > 0. As described in |
| * OPEN Alliance 10BASE-T1x standard it is deasserted when first |
| * data header is received by LAN865x. |
| * |
| * Hence, it is mandatory to ALWAYS read at least one data chunk! |
| */ |
| do { |
| lan865x_read_chunks(dev); |
| } while (tc6->rca > 0); |
| |
| ret = oa_tc6_check_status(tc6); |
| if (ret == -EIO) { |
| lan865x_gpio_reset(dev); |
| } |
| } |
| } |
| |
| static int lan865x_init(const struct device *dev) |
| { |
| const struct lan865x_config *cfg = dev->config; |
| struct lan865x_data *ctx = dev->data; |
| int ret; |
| |
| __ASSERT(cfg->spi.config.frequency <= LAN865X_SPI_MAX_FREQUENCY, |
| "SPI frequency exceeds supported maximum\n"); |
| |
| if (!spi_is_ready_dt(&cfg->spi)) { |
| LOG_ERR("SPI bus %s not ready", cfg->spi.bus->name); |
| return -ENODEV; |
| } |
| |
| if (!gpio_is_ready_dt(&cfg->interrupt)) { |
| LOG_ERR("Interrupt GPIO device %s is not ready", |
| cfg->interrupt.port->name); |
| return -ENODEV; |
| } |
| |
| /* Check SPI communication after reset */ |
| ret = lan865x_check_spi(dev); |
| if (ret < 0) { |
| LOG_ERR("SPI communication not working, %d", ret); |
| return ret; |
| } |
| |
| /* |
| * Configure interrupt service routine for LAN865x IRQ |
| */ |
| ret = gpio_pin_configure_dt(&cfg->interrupt, GPIO_INPUT); |
| if (ret < 0) { |
| LOG_ERR("Failed to configure interrupt GPIO, %d", ret); |
| return ret; |
| } |
| |
| gpio_init_callback(&(ctx->gpio_int_callback), lan865x_int_callback, |
| BIT(cfg->interrupt.pin)); |
| |
| ret = gpio_add_callback(cfg->interrupt.port, &ctx->gpio_int_callback); |
| if (ret < 0) { |
| LOG_ERR("Failed to add INT callback, %d", ret); |
| return ret; |
| } |
| |
| gpio_pin_interrupt_configure_dt(&cfg->interrupt, GPIO_INT_EDGE_TO_ACTIVE); |
| |
| /* Start interruption-poll thread */ |
| ctx->tid_int = |
| k_thread_create(&ctx->thread, ctx->thread_stack, |
| CONFIG_ETH_LAN865X_IRQ_THREAD_STACK_SIZE, |
| (k_thread_entry_t)lan865x_int_thread, |
| (void *)dev, NULL, NULL, |
| K_PRIO_COOP(CONFIG_ETH_LAN865X_IRQ_THREAD_PRIO), |
| 0, K_NO_WAIT); |
| k_thread_name_set(ctx->tid_int, "lan865x_interrupt"); |
| |
| /* Perform HW reset - 'rst-gpios' required property set in DT */ |
| if (!gpio_is_ready_dt(&cfg->reset)) { |
| LOG_ERR("Reset GPIO device %s is not ready", |
| cfg->reset.port->name); |
| return -ENODEV; |
| } |
| |
| ret = gpio_pin_configure_dt(&cfg->reset, GPIO_OUTPUT_INACTIVE); |
| if (ret < 0) { |
| LOG_ERR("Failed to configure reset GPIO, %d", ret); |
| return ret; |
| } |
| |
| return lan865x_gpio_reset(dev); |
| } |
| |
| static int lan865x_port_send(const struct device *dev, struct net_pkt *pkt) |
| { |
| struct lan865x_data *ctx = dev->data; |
| struct oa_tc6 *tc6 = ctx->tc6; |
| int ret; |
| |
| k_sem_take(&ctx->tx_rx_sem, K_FOREVER); |
| ret = oa_tc6_send_chunks(tc6, pkt); |
| |
| /* Check if rca > 0 during half-duplex TX transmission */ |
| if (tc6->rca > 0) { |
| k_sem_give(&ctx->int_sem); |
| } |
| |
| k_sem_give(&ctx->tx_rx_sem); |
| if (ret < 0) { |
| LOG_ERR("TX transmission error, %d", ret); |
| eth_stats_update_errors_tx(net_pkt_iface(pkt)); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static const struct ethernet_api lan865x_api_func = { |
| .iface_api.init = lan865x_iface_init, |
| .get_capabilities = lan865x_port_get_capabilities, |
| .set_config = lan865x_set_config, |
| .send = lan865x_port_send, |
| }; |
| |
| #define LAN865X_DEFINE(inst) \ |
| static struct lan865x_config_plca lan865x_config_plca_##inst = { \ |
| .node_id = DT_INST_PROP(inst, plca_node_id), \ |
| .node_count = DT_INST_PROP(inst, plca_node_count), \ |
| .burst_count = DT_INST_PROP(inst, plca_burst_count), \ |
| .burst_timer = DT_INST_PROP(inst, plca_burst_timer), \ |
| .to_timer = DT_INST_PROP(inst, plca_to_timer), \ |
| .enable = DT_INST_PROP(inst, plca_enable), \ |
| }; \ |
| \ |
| static const struct lan865x_config lan865x_config_##inst = { \ |
| .spi = SPI_DT_SPEC_INST_GET(inst, SPI_WORD_SET(8), 0), \ |
| .interrupt = GPIO_DT_SPEC_INST_GET(inst, int_gpios), \ |
| .reset = GPIO_DT_SPEC_INST_GET(inst, rst_gpios), \ |
| .timeout = CONFIG_ETH_LAN865X_TIMEOUT, \ |
| .plca = &lan865x_config_plca_##inst, \ |
| }; \ |
| \ |
| struct oa_tc6 oa_tc6_##inst = { \ |
| .cps = 64, \ |
| .protected = 0, \ |
| .spi = &lan865x_config_##inst.spi \ |
| }; \ |
| static struct lan865x_data lan865x_data_##inst = { \ |
| .mac_address = DT_INST_PROP(inst, local_mac_address), \ |
| .tx_rx_sem = \ |
| Z_SEM_INITIALIZER((lan865x_data_##inst).tx_rx_sem, 1, 1), \ |
| .int_sem = Z_SEM_INITIALIZER((lan865x_data_##inst).int_sem, 0, 1), \ |
| .tc6 = &oa_tc6_##inst \ |
| }; \ |
| \ |
| ETH_NET_DEVICE_DT_INST_DEFINE(inst, lan865x_init, NULL, &lan865x_data_##inst, \ |
| &lan865x_config_##inst, CONFIG_ETH_INIT_PRIORITY, \ |
| &lan865x_api_func, NET_ETH_MTU); |
| |
| DT_INST_FOREACH_STATUS_OKAY(LAN865X_DEFINE); |