| /* |
| * Copyright 2023 NXP |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT nxp_s32_gmac |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(nxp_s32_eth, CONFIG_ETHERNET_LOG_LEVEL); |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/device.h> |
| #include <zephyr/drivers/pinctrl.h> |
| #include <zephyr/net/ethernet.h> |
| #include <zephyr/net/net_if.h> |
| #include <zephyr/net/net_pkt.h> |
| #include <zephyr/net/phy.h> |
| #include <ethernet/eth_stats.h> |
| #include <soc.h> |
| |
| #include <Gmac_Ip.h> |
| #include <Gmac_Ip_Hw_Access.h> |
| #include <Gmac_Ip_Irq.h> |
| #include <Clock_Ip.h> |
| |
| #include "eth.h" |
| |
| #define ETH_NXP_S32_BUF_TIMEOUT K_MSEC(20) |
| #define ETH_NXP_S32_DMA_TX_TIMEOUT K_MSEC(20) |
| |
| #define ETH_NXP_S32_MAC_ADDR_LEN 6U |
| |
| #define FREESCALE_OUI_B0 0x00 |
| #define FREESCALE_OUI_B1 0x04 |
| #define FREESCALE_OUI_B2 0x9f |
| |
| struct eth_nxp_s32_config { |
| uint8_t instance; |
| uint8_t tx_ring_idx; |
| uint8_t rx_ring_idx; |
| uint32_t rx_irq; |
| uint32_t tx_irq; |
| void (*do_config)(void); |
| const struct pinctrl_dev_config *pincfg; |
| const struct device *phy_dev; |
| |
| const Gmac_CtrlConfigType ctrl_cfg; |
| GMAC_Type *base; |
| }; |
| |
| struct eth_nxp_s32_data { |
| struct net_if *iface; |
| uint8_t mac_addr[ETH_NXP_S32_MAC_ADDR_LEN]; |
| uint8_t if_suspended; |
| struct k_mutex tx_mutex; |
| struct k_sem rx_sem; |
| struct k_sem tx_sem; |
| struct k_thread rx_thread; |
| |
| K_KERNEL_STACK_MEMBER(rx_thread_stack, CONFIG_ETH_NXP_S32_RX_THREAD_STACK_SIZE); |
| }; |
| |
| static void eth_nxp_s32_rx_thread(void *arg1, void *unused1, void *unused2); |
| |
| static inline struct net_if *get_iface(struct eth_nxp_s32_data *ctx) |
| { |
| return ctx->iface; |
| } |
| |
| static void convert_phy_to_mac_config(Gmac_Ip_ConfigType *gmac_cfg, enum phy_link_speed phy_speed) |
| { |
| switch (phy_speed) { |
| case LINK_HALF_10BASE_T: |
| gmac_cfg->Speed = GMAC_SPEED_10M; |
| gmac_cfg->Duplex = GMAC_HALF_DUPLEX; |
| break; |
| case LINK_FULL_10BASE_T: |
| gmac_cfg->Speed = GMAC_SPEED_10M; |
| gmac_cfg->Duplex = GMAC_FULL_DUPLEX; |
| break; |
| case LINK_HALF_100BASE_T: |
| gmac_cfg->Speed = GMAC_SPEED_100M; |
| gmac_cfg->Duplex = GMAC_HALF_DUPLEX; |
| break; |
| case LINK_FULL_100BASE_T: |
| gmac_cfg->Speed = GMAC_SPEED_100M; |
| gmac_cfg->Duplex = GMAC_FULL_DUPLEX; |
| break; |
| case LINK_HALF_1000BASE_T: |
| gmac_cfg->Speed = GMAC_SPEED_1G; |
| gmac_cfg->Duplex = GMAC_HALF_DUPLEX; |
| break; |
| case LINK_FULL_1000BASE_T: |
| __fallthrough; |
| default: |
| gmac_cfg->Speed = GMAC_SPEED_1G; |
| gmac_cfg->Duplex = GMAC_FULL_DUPLEX; |
| break; |
| } |
| } |
| |
| static void phy_link_state_changed(const struct device *pdev, |
| struct phy_link_state *state, |
| void *user_data) |
| { |
| const struct device *dev = (struct device *)user_data; |
| const struct eth_nxp_s32_config *cfg = dev->config; |
| struct eth_nxp_s32_data *ctx = dev->data; |
| Gmac_Ip_ConfigType gmac_cfg; |
| |
| ARG_UNUSED(pdev); |
| |
| if (state->is_up) { |
| /* Porting phy link config to mac */ |
| convert_phy_to_mac_config(&gmac_cfg, state->speed); |
| /* Set MAC configuration */ |
| Gmac_Ip_SetSpeed(cfg->instance, gmac_cfg.Speed); |
| |
| cfg->base->MAC_CONFIGURATION |= GMAC_MAC_CONFIGURATION_DM(gmac_cfg.Duplex); |
| |
| /* net iface should be down even if PHY link state is up |
| * till the upper network layers have suspended the iface. |
| */ |
| if (ctx->if_suspended) { |
| return; |
| } |
| |
| LOG_DBG("Link up"); |
| net_eth_carrier_on(ctx->iface); |
| } else { |
| LOG_DBG("Link down"); |
| net_eth_carrier_off(ctx->iface); |
| } |
| } |
| |
| static const struct device *eth_nxp_s32_get_phy(const struct device *dev) |
| { |
| const struct eth_nxp_s32_config *cfg = dev->config; |
| |
| return cfg->phy_dev; |
| } |
| |
| #if defined(CONFIG_SOC_SERIES_S32K3) |
| static int select_phy_interface(Gmac_Ip_MiiModeType mode) |
| { |
| uint32_t regval; |
| |
| switch (mode) { |
| case GMAC_MII_MODE: |
| regval = DCM_GPR_DCMRWF1_EMAC_CONF_SEL(0U); |
| break; |
| case GMAC_RMII_MODE: |
| regval = DCM_GPR_DCMRWF1_EMAC_CONF_SEL(2U); |
| break; |
| #if (FEATURE_GMAC_RGMII_EN == 1U) |
| case GMAC_RGMII_MODE: |
| regval = DCM_GPR_DCMRWF1_EMAC_CONF_SEL(1U); |
| break; |
| #endif |
| default: |
| return -EINVAL; |
| } |
| |
| IP_DCM_GPR->DCMRWF1 = (IP_DCM_GPR->DCMRWF1 & ~DCM_GPR_DCMRWF1_EMAC_CONF_SEL_MASK) | regval; |
| |
| return 0; |
| } |
| #else |
| #error "SoC not supported" |
| #endif /* CONFIG_SOC_SERIES_S32K3 */ |
| |
| static int eth_nxp_s32_init(const struct device *dev) |
| { |
| const struct eth_nxp_s32_config *cfg = dev->config; |
| struct eth_nxp_s32_data *ctx = dev->data; |
| Gmac_Ip_StatusType mac_status; |
| Clock_Ip_StatusType clk_status; |
| int err; |
| |
| err = pinctrl_apply_state(cfg->pincfg, PINCTRL_STATE_DEFAULT); |
| if (err != 0) { |
| return err; |
| } |
| |
| /* |
| * Currently, clock control shim driver does not support configuring clock |
| * muxes individually, so use the HAL directly. |
| */ |
| clk_status = Clock_Ip_Init(&Clock_Ip_aClockConfig[CONFIG_ETH_NXP_S32_CLOCK_CONFIG_IDX]); |
| if (clk_status != CLOCK_IP_SUCCESS) { |
| LOG_ERR("Failed to configure clocks (%d)", clk_status); |
| return -EIO; |
| } |
| |
| /* |
| * PHY mode selection must be done before the controller is reset, |
| * because the interface type is latched at controller's reset |
| */ |
| err = select_phy_interface(cfg->ctrl_cfg.Gmac_pCtrlConfig->MiiMode); |
| if (err != 0) { |
| LOG_ERR("Failed to select PHY interface (%d)", err); |
| return -EIO; |
| } |
| |
| mac_status = Gmac_Ip_Init(cfg->instance, &cfg->ctrl_cfg); |
| if (mac_status != GMAC_STATUS_SUCCESS) { |
| LOG_ERR("Failed to initialize GMAC%d (%d)", cfg->instance, mac_status); |
| return -EIO; |
| } |
| |
| k_mutex_init(&ctx->tx_mutex); |
| k_sem_init(&ctx->rx_sem, 0, 1); |
| k_sem_init(&ctx->tx_sem, 0, 1); |
| |
| k_thread_create(&ctx->rx_thread, ctx->rx_thread_stack, |
| K_KERNEL_STACK_SIZEOF(ctx->rx_thread_stack), |
| eth_nxp_s32_rx_thread, (void *)dev, NULL, NULL, |
| K_PRIO_COOP(CONFIG_ETH_NXP_S32_RX_THREAD_PRIO), |
| 0, K_NO_WAIT); |
| k_thread_name_set(&ctx->rx_thread, "eth_nxp_s32_rx"); |
| |
| if (cfg->do_config != NULL) { |
| cfg->do_config(); |
| } |
| |
| return 0; |
| } |
| |
| static int eth_nxp_s32_start(const struct device *dev) |
| { |
| const struct eth_nxp_s32_config *cfg = dev->config; |
| struct eth_nxp_s32_data *ctx = dev->data; |
| struct phy_link_state state; |
| |
| Gmac_Ip_EnableController(cfg->instance); |
| |
| irq_enable(cfg->rx_irq); |
| irq_enable(cfg->tx_irq); |
| |
| /* If upper layers enable the net iface then mark it as |
| * not suspended so that PHY Link changes can have the impact |
| */ |
| ctx->if_suspended = false; |
| |
| if (cfg->phy_dev) { |
| phy_get_link_state(cfg->phy_dev, &state); |
| |
| /* Enable net_iface only when Ethernet PHY link is up or else |
| * if net_iface is enabled when link is down and tx happens |
| * in this state then the used tx buffers will never be recovered back. |
| */ |
| if (state.is_up == true) { |
| net_eth_carrier_on(ctx->iface); |
| } |
| } else { |
| net_eth_carrier_on(ctx->iface); |
| } |
| |
| LOG_DBG("GMAC%d started", cfg->instance); |
| |
| return 0; |
| } |
| |
| static int eth_nxp_s32_stop(const struct device *dev) |
| { |
| const struct eth_nxp_s32_config *cfg = dev->config; |
| struct eth_nxp_s32_data *ctx = dev->data; |
| Gmac_Ip_StatusType status; |
| int err = 0; |
| |
| irq_disable(cfg->rx_irq); |
| irq_disable(cfg->tx_irq); |
| |
| /* If upper layers disable the net iface then mark it as suspended |
| * in order to save it from the PHY link state changes |
| */ |
| ctx->if_suspended = true; |
| |
| net_eth_carrier_off(ctx->iface); |
| |
| status = Gmac_Ip_DisableController(cfg->instance); |
| if (status != GMAC_STATUS_SUCCESS) { |
| LOG_ERR("Failed to disable controller GMAC%d (%d)", cfg->instance, status); |
| err = -EIO; |
| } |
| |
| LOG_DBG("GMAC%d stopped", cfg->instance); |
| |
| return err; |
| } |
| |
| static void eth_nxp_s32_iface_init(struct net_if *iface) |
| { |
| const struct device *dev = net_if_get_device(iface); |
| const struct eth_nxp_s32_config *cfg = dev->config; |
| struct eth_nxp_s32_data *ctx = dev->data; |
| |
| if (ctx->iface == NULL) { |
| ctx->iface = iface; |
| } |
| |
| ethernet_init(iface); |
| |
| net_if_set_link_addr(iface, ctx->mac_addr, sizeof(ctx->mac_addr), NET_LINK_ETHERNET); |
| |
| LOG_INF("GMAC%d MAC address %02x:%02x:%02x:%02x:%02x:%02x", cfg->instance, |
| ctx->mac_addr[0], ctx->mac_addr[1], ctx->mac_addr[2], |
| ctx->mac_addr[3], ctx->mac_addr[4], ctx->mac_addr[5]); |
| |
| /* Make sure that the net iface state is not suspended unless |
| * upper layers explicitly stop the iface |
| */ |
| ctx->if_suspended = false; |
| |
| /* No PHY available, link is always up and MAC speed/duplex settings are fixed */ |
| if (cfg->phy_dev == NULL) { |
| net_if_carrier_on(iface); |
| return; |
| } |
| |
| /* |
| * GMAC controls the PHY. If PHY is configured either as fixed |
| * link or autoneg, the callback is executed at least once |
| * immediately after setting it. |
| */ |
| if (!device_is_ready(cfg->phy_dev)) { |
| LOG_ERR("PHY device (%p) is not ready, cannot init iface", |
| cfg->phy_dev); |
| return; |
| } |
| |
| phy_link_callback_set(cfg->phy_dev, &phy_link_state_changed, (void *)dev); |
| } |
| |
| static int eth_nxp_s32_tx(const struct device *dev, struct net_pkt *pkt) |
| { |
| struct eth_nxp_s32_data *ctx = dev->data; |
| const struct eth_nxp_s32_config *cfg = dev->config; |
| size_t pkt_len = net_pkt_get_len(pkt); |
| int res = 0; |
| Gmac_Ip_BufferType buf; |
| Gmac_Ip_TxInfoType tx_info; |
| Gmac_Ip_StatusType status; |
| Gmac_Ip_TxOptionsType tx_options = { |
| .NoInt = FALSE, |
| .CrcPadIns = GMAC_CRC_AND_PAD_INSERTION, |
| .ChecksumIns = GMAC_CHECKSUM_INSERTION_PROTO_PSEUDOH |
| }; |
| |
| __ASSERT(pkt, "Packet pointer is NULL"); |
| |
| k_mutex_lock(&ctx->tx_mutex, K_FOREVER); |
| k_sem_reset(&ctx->tx_sem); |
| |
| buf.Length = (uint16_t)pkt_len; |
| buf.Data = NULL; |
| status = Gmac_Ip_GetTxBuff(cfg->instance, cfg->tx_ring_idx, &buf, NULL); |
| if (status != GMAC_STATUS_SUCCESS) { |
| LOG_ERR("Failed to get tx buffer (%d)", status); |
| res = -ENOBUFS; |
| goto error; |
| } |
| |
| res = net_pkt_read(pkt, buf.Data, pkt_len); |
| if (res) { |
| LOG_ERR("Failed to copy packet to tx buffer (%d)", res); |
| res = -ENOBUFS; |
| goto error; |
| } |
| |
| buf.Length = (uint16_t)pkt_len; |
| status = Gmac_Ip_SendFrame(cfg->instance, cfg->tx_ring_idx, &buf, &tx_options); |
| if (status != GMAC_STATUS_SUCCESS) { |
| LOG_ERR("Failed to tx frame (%d)", status); |
| res = -EIO; |
| goto error; |
| } |
| |
| /* Wait for the transmission to complete */ |
| if (k_sem_take(&ctx->tx_sem, ETH_NXP_S32_DMA_TX_TIMEOUT) != 0) { |
| LOG_ERR("Timeout transmitting frame"); |
| res = -EIO; |
| goto error; |
| } |
| |
| /* Restore the buffer address pointer and clear the descriptor after the status is read */ |
| status = Gmac_Ip_GetTransmitStatus(cfg->instance, cfg->tx_ring_idx, &buf, &tx_info); |
| if (status != GMAC_STATUS_SUCCESS) { |
| LOG_ERR("Failed to restore tx buffer: %s (%d) ", |
| (status == GMAC_STATUS_BUSY ? "busy" : "buf not found"), status); |
| res = -EIO; |
| } else if (tx_info.ErrMask != 0U) { |
| LOG_ERR("Tx frame has errors (error mask 0x%X)", tx_info.ErrMask); |
| res = -EIO; |
| } |
| |
| error: |
| k_mutex_unlock(&ctx->tx_mutex); |
| |
| if (res != 0) { |
| eth_stats_update_errors_tx(ctx->iface); |
| } |
| return res; |
| } |
| |
| static struct net_pkt *eth_nxp_s32_get_pkt(const struct device *dev, |
| Gmac_Ip_BufferType *buf, |
| Gmac_Ip_RxInfoType *rx_info) |
| { |
| struct eth_nxp_s32_data *ctx = dev->data; |
| struct net_pkt *pkt = NULL; |
| int res = 0; |
| |
| /* Using root iface, it will be updated in net_recv_data() */ |
| pkt = net_pkt_rx_alloc_with_buffer(ctx->iface, rx_info->PktLen, |
| AF_UNSPEC, 0, ETH_NXP_S32_BUF_TIMEOUT); |
| if (!pkt) { |
| LOG_ERR("Failed to allocate rx buffer of length %u", rx_info->PktLen); |
| goto exit; |
| } |
| |
| res = net_pkt_write(pkt, buf->Data, rx_info->PktLen); |
| if (res) { |
| LOG_ERR("Failed to write rx frame into pkt buffer (%d)", res); |
| net_pkt_unref(pkt); |
| pkt = NULL; |
| goto exit; |
| } |
| |
| exit: |
| if (!pkt) { |
| eth_stats_update_errors_rx(get_iface(ctx)); |
| } |
| |
| return pkt; |
| } |
| |
| static void eth_nxp_s32_rx(const struct device *dev) |
| { |
| struct eth_nxp_s32_data *ctx = dev->data; |
| const struct eth_nxp_s32_config *cfg = dev->config; |
| struct net_pkt *pkt; |
| int res = 0; |
| Gmac_Ip_RxInfoType rx_info = {0}; |
| Gmac_Ip_BufferType buf; |
| Gmac_Ip_StatusType status; |
| |
| status = Gmac_Ip_ReadFrame(cfg->instance, cfg->rx_ring_idx, &buf, &rx_info); |
| if (rx_info.ErrMask != 0U) { |
| Gmac_Ip_ProvideRxBuff(cfg->instance, cfg->rx_ring_idx, &buf); |
| LOG_ERR("Rx frame has errors (error mask 0x%X)", rx_info.ErrMask); |
| } else if (status == GMAC_STATUS_SUCCESS) { |
| pkt = eth_nxp_s32_get_pkt(dev, &buf, &rx_info); |
| Gmac_Ip_ProvideRxBuff(cfg->instance, cfg->rx_ring_idx, &buf); |
| if (pkt != NULL) { |
| res = net_recv_data(get_iface(ctx), pkt); |
| if (res < 0) { |
| eth_stats_update_errors_rx(get_iface(ctx)); |
| net_pkt_unref(pkt); |
| LOG_ERR("Failed to enqueue frame into rx queue (%d)", res); |
| } |
| } |
| } |
| } |
| |
| static void eth_nxp_s32_rx_thread(void *arg1, void *unused1, void *unused2) |
| { |
| const struct device *dev = (const struct device *)arg1; |
| struct eth_nxp_s32_data *ctx = dev->data; |
| const struct eth_nxp_s32_config *cfg = dev->config; |
| int res; |
| int work; |
| |
| ARG_UNUSED(unused1); |
| ARG_UNUSED(unused2); |
| __ASSERT_NO_MSG(arg1 != NULL); |
| __ASSERT_NO_MSG(ctx != NULL); |
| |
| while (1) { |
| res = k_sem_take(&ctx->rx_sem, K_FOREVER); |
| __ASSERT_NO_MSG(res == 0); |
| |
| work = 0; |
| while (Gmac_Ip_IsFrameAvailable(cfg->instance, cfg->rx_ring_idx)) { |
| eth_nxp_s32_rx(dev); |
| if (++work == CONFIG_ETH_NXP_S32_RX_BUDGET) { |
| /* More work to do, reschedule */ |
| work = 0; |
| k_yield(); |
| } |
| } |
| |
| /* All work done, re-enable rx interrupt and exit polling */ |
| irq_enable(cfg->rx_irq); |
| |
| /* In case a frame arrived after last eth_nxp_s32_rx() and before irq_enable() */ |
| if (Gmac_Ip_IsFrameAvailable(cfg->instance, cfg->rx_ring_idx)) { |
| eth_nxp_s32_rx(dev); |
| } |
| } |
| } |
| |
| static int eth_nxp_s32_set_config(const struct device *dev, |
| enum ethernet_config_type type, |
| const struct ethernet_config *config) |
| { |
| struct eth_nxp_s32_data *ctx = dev->data; |
| const struct eth_nxp_s32_config *cfg = dev->config; |
| int res = 0; |
| uint32_t regval; |
| |
| ARG_UNUSED(cfg); |
| ARG_UNUSED(regval); |
| |
| switch (type) { |
| case ETHERNET_CONFIG_TYPE_MAC_ADDRESS: |
| /* Set new Ethernet MAC address and register it with the upper layer */ |
| memcpy(ctx->mac_addr, config->mac_address.addr, sizeof(ctx->mac_addr)); |
| Gmac_Ip_SetMacAddr(cfg->instance, (const uint8_t *)ctx->mac_addr); |
| net_if_set_link_addr(ctx->iface, ctx->mac_addr, sizeof(ctx->mac_addr), |
| NET_LINK_ETHERNET); |
| LOG_INF("MAC set to: %02x:%02x:%02x:%02x:%02x:%02x", |
| ctx->mac_addr[0], ctx->mac_addr[1], ctx->mac_addr[2], |
| ctx->mac_addr[3], ctx->mac_addr[4], ctx->mac_addr[5]); |
| break; |
| #if defined(CONFIG_NET_PROMISCUOUS_MODE) |
| case ETHERNET_CONFIG_TYPE_PROMISC_MODE: |
| regval = cfg->base->MAC_PACKET_FILTER; |
| if (config->promisc_mode && !(regval & GMAC_MAC_PACKET_FILTER_PR_MASK)) { |
| cfg->base->MAC_PACKET_FILTER |= GMAC_MAC_PACKET_FILTER_PR_MASK; |
| } else if (!config->promisc_mode && (regval & GMAC_MAC_PACKET_FILTER_PR_MASK)) { |
| cfg->base->MAC_PACKET_FILTER &= ~GMAC_MAC_PACKET_FILTER_PR_MASK; |
| } else { |
| res = -EALREADY; |
| } |
| break; |
| #endif |
| #if defined(CONFIG_ETH_NXP_S32_MULTICAST_FILTER) |
| case ETHERNET_HW_FILTERING: |
| if (config->filter.set) { |
| Gmac_Ip_AddDstAddrToHashFilter(cfg->instance, |
| config->filter.mac_address.addr); |
| } else { |
| Gmac_Ip_RemoveDstAddrFromHashFilter(cfg->instance, |
| config->filter.mac_address.addr); |
| } |
| break; |
| #endif |
| default: |
| res = -ENOTSUP; |
| break; |
| } |
| |
| return res; |
| } |
| |
| static enum ethernet_hw_caps eth_nxp_s32_get_capabilities(const struct device *dev) |
| { |
| ARG_UNUSED(dev); |
| |
| return (ETHERNET_LINK_10BASE_T |
| | ETHERNET_LINK_100BASE_T |
| #if (FEATURE_GMAC_RGMII_EN == 1U) |
| | ETHERNET_LINK_1000BASE_T |
| #endif |
| | ETHERNET_DUPLEX_SET |
| | ETHERNET_HW_TX_CHKSUM_OFFLOAD |
| | ETHERNET_HW_RX_CHKSUM_OFFLOAD |
| #if defined(CONFIG_NET_VLAN) |
| | ETHERNET_HW_VLAN |
| #endif |
| #if defined(CONFIG_NET_PROMISCUOUS_MODE) |
| | ETHERNET_PROMISC_MODE |
| #endif |
| #if defined(CONFIG_ETH_NXP_S32_MULTICAST_FILTER) |
| | ETHERNET_HW_FILTERING |
| #endif |
| ); |
| } |
| |
| static void eth_nxp_s32_tx_irq(const struct device *dev) |
| { |
| const struct eth_nxp_s32_config *cfg = dev->config; |
| |
| GMAC_TxIRQHandler(cfg->instance, cfg->tx_ring_idx); |
| } |
| |
| static void eth_nxp_s32_rx_irq(const struct device *dev) |
| { |
| const struct eth_nxp_s32_config *cfg = dev->config; |
| |
| GMAC_RxIRQHandler(cfg->instance, cfg->rx_ring_idx); |
| } |
| |
| static const struct ethernet_api eth_api = { |
| .iface_api.init = eth_nxp_s32_iface_init, |
| .get_capabilities = eth_nxp_s32_get_capabilities, |
| .get_phy = eth_nxp_s32_get_phy, |
| .start = eth_nxp_s32_start, |
| .stop = eth_nxp_s32_stop, |
| .send = eth_nxp_s32_tx, |
| .set_config = eth_nxp_s32_set_config, |
| }; |
| |
| |
| BUILD_ASSERT(((CONFIG_ETH_NXP_S32_RX_RING_BUF_SIZE * CONFIG_ETH_NXP_S32_RX_RING_LEN) |
| % FEATURE_GMAC_MTL_RX_FIFO_BLOCK_SIZE) == 0, |
| "CONFIG_ETH_NXP_S32_RX_RING_BUF_SIZE * CONFIG_ETH_NXP_S32_RX_RING_LEN " |
| "must be multiple of RX FIFO block size"); |
| BUILD_ASSERT(((CONFIG_ETH_NXP_S32_TX_RING_BUF_SIZE * CONFIG_ETH_NXP_S32_TX_RING_LEN) |
| % FEATURE_GMAC_MTL_TX_FIFO_BLOCK_SIZE) == 0, |
| "CONFIG_ETH_NXP_S32_TX_RING_BUF_SIZE * CONFIG_ETH_NXP_S32_TX_RING_LEN " |
| "must be multiple of TX FIFO block size"); |
| BUILD_ASSERT((CONFIG_ETH_NXP_S32_RX_RING_BUF_SIZE % FEATURE_GMAC_DATA_BUS_WIDTH_BYTES) == 0, |
| "CONFIG_ETH_NXP_S32_RX_RING_BUF_SIZE must be multiple of the data bus width"); |
| BUILD_ASSERT((CONFIG_ETH_NXP_S32_TX_RING_BUF_SIZE % FEATURE_GMAC_DATA_BUS_WIDTH_BYTES) == 0, |
| "CONFIG_ETH_NXP_S32_TX_RING_BUF_SIZE must be multiple of the data bus width"); |
| |
| #define ETH_NXP_S32_MAC_MII(n) \ |
| _CONCAT(_CONCAT(GMAC_, DT_INST_STRING_UPPER_TOKEN(n, phy_connection_type)), _MODE) |
| |
| #define ETH_NXP_S32_IRQ_INIT(n, name) \ |
| IRQ_CONNECT(DT_INST_IRQ_BY_NAME(n, name, irq), \ |
| DT_INST_IRQ_BY_NAME(n, name, priority), \ |
| eth_nxp_s32_##name##_irq, \ |
| DEVICE_DT_INST_GET(n), \ |
| COND_CODE_1(DT_INST_IRQ_HAS_CELL(n, flags), \ |
| (DT_INST_IRQ_BY_NAME(n, name, flags)), (0))); |
| |
| #define ETH_NXP_S32_INIT_CONFIG(n) \ |
| static void eth_nxp_s32_init_config_##n(void) \ |
| { \ |
| const struct device *dev = DEVICE_DT_INST_GET(n); \ |
| struct eth_nxp_s32_data *ctx = dev->data; \ |
| const struct eth_nxp_s32_config *cfg = dev->config; \ |
| \ |
| ETH_NXP_S32_IRQ_INIT(n, tx); \ |
| ETH_NXP_S32_IRQ_INIT(n, rx); \ |
| \ |
| COND_CODE_1(DT_INST_PROP(n, zephyr_random_mac_address), ( \ |
| gen_random_mac(ctx->mac_addr, FREESCALE_OUI_B0, \ |
| FREESCALE_OUI_B1, FREESCALE_OUI_B2); \ |
| Gmac_Ip_SetMacAddr(cfg->instance, ctx->mac_addr); \ |
| ), ( \ |
| Gmac_Ip_GetMacAddr(cfg->instance, ctx->mac_addr); \ |
| )) \ |
| } |
| |
| #define ETH_NXP_S32_RX_CALLBACK(n) \ |
| static void eth_nxp_s32_rx_callback_##n(uint8_t inst, uint8_t chan) \ |
| { \ |
| const struct device *dev = DEVICE_DT_INST_GET(n); \ |
| struct eth_nxp_s32_data *ctx = dev->data; \ |
| const struct eth_nxp_s32_config *cfg = dev->config; \ |
| \ |
| ARG_UNUSED(inst); \ |
| ARG_UNUSED(chan); \ |
| \ |
| /* Rx irq will be re-enabled from Rx thread */ \ |
| irq_disable(cfg->rx_irq); \ |
| k_sem_give(&ctx->rx_sem); \ |
| } |
| |
| #define ETH_NXP_S32_TX_CALLBACK(n) \ |
| static void eth_nxp_s32_tx_callback_##n(uint8_t inst, uint8_t chan) \ |
| { \ |
| const struct device *dev = DEVICE_DT_INST_GET(n); \ |
| struct eth_nxp_s32_data *ctx = dev->data; \ |
| \ |
| ARG_UNUSED(inst); \ |
| ARG_UNUSED(chan); \ |
| \ |
| k_sem_give(&ctx->tx_sem); \ |
| } |
| |
| #define _ETH_NXP_S32_RING(n, name, len, buf_size) \ |
| static Gmac_Ip_BufferDescriptorType eth_nxp_s32_##name##ring_desc_##n[len] \ |
| __nocache __aligned(FEATURE_GMAC_BUFFDESCR_ALIGNMENT_BYTES); \ |
| static uint8_t eth_nxp_s32_##name##ring_buf_##n[len * buf_size] \ |
| __nocache __aligned(FEATURE_GMAC_BUFF_ALIGNMENT_BYTES) |
| |
| #define ETH_NXP_S32_RX_RING(n) \ |
| _ETH_NXP_S32_RING(n, rx, \ |
| CONFIG_ETH_NXP_S32_RX_RING_LEN, \ |
| CONFIG_ETH_NXP_S32_RX_RING_BUF_SIZE) |
| |
| #define ETH_NXP_S32_TX_RING(n) \ |
| _ETH_NXP_S32_RING(n, tx, \ |
| CONFIG_ETH_NXP_S32_TX_RING_LEN, \ |
| CONFIG_ETH_NXP_S32_TX_RING_BUF_SIZE) |
| |
| #define ETH_NXP_S32_MAC_TXTIMESHAPER_CONFIG(n) \ |
| static const Gmac_Ip_TxTimeAwareShaper eth_nxp_s32_mac_txtimeshaper_config_##n = {\ |
| .GateControlList = NULL, \ |
| } |
| |
| #define ETH_NXP_S32_MAC_RXRING_CONFIG(n) \ |
| static const Gmac_Ip_RxRingConfigType eth_nxp_s32_mac_rxring_config_##n = { \ |
| .RingDesc = eth_nxp_s32_rxring_desc_##n, \ |
| .Callback = eth_nxp_s32_rx_callback_##n, \ |
| .Buffer = eth_nxp_s32_rxring_buf_##n, \ |
| .Interrupts = (uint32_t)GMAC_CH_INTERRUPT_RI, \ |
| .BufferLen = CONFIG_ETH_NXP_S32_RX_RING_BUF_SIZE, \ |
| .RingSize = CONFIG_ETH_NXP_S32_RX_RING_LEN, \ |
| .PriorityMask = 0U, \ |
| .DmaBurstLength = 32U, \ |
| } |
| |
| #define ETH_NXP_S32_MAC_TXRING_CONFIG(n) \ |
| static const Gmac_Ip_TxRingConfigType eth_nxp_s32_mac_txring_config_##n = { \ |
| .Weight = 0U, \ |
| .IdleSlopeCredit = 0U, \ |
| .SendSlopeCredit = 0U, \ |
| .HiCredit = 0U, \ |
| .LoCredit = 0, \ |
| .RingDesc = eth_nxp_s32_txring_desc_##n, \ |
| .Callback = eth_nxp_s32_tx_callback_##n, \ |
| .Buffer = eth_nxp_s32_txring_buf_##n, \ |
| .Interrupts = (uint32_t)GMAC_CH_INTERRUPT_TI, \ |
| .BufferLen = CONFIG_ETH_NXP_S32_TX_RING_BUF_SIZE, \ |
| .RingSize = CONFIG_ETH_NXP_S32_TX_RING_LEN, \ |
| .PriorityMask = 0U, \ |
| .DmaBurstLength = 32U, \ |
| .QueueOpMode = GMAC_OP_MODE_DCB_GEN, \ |
| } |
| |
| #define ETH_NXP_S32_MAC_PKT_FILTER(n) \ |
| ((uint32_t)(0U \ |
| COND_CODE_1(CONFIG_ETH_NXP_S32_MULTICAST_FILTER, \ |
| (|GMAC_PKT_FILTER_HASH_MULTICAST), \ |
| (|GMAC_PKT_FILTER_PASS_ALL_MULTICAST)) \ |
| )) |
| |
| #define ETH_NXP_S32_MAC_CONF(n) \ |
| ((uint32_t)(GMAC_MAC_CONFIG_CRC_STRIPPING \ |
| | GMAC_MAC_CONFIG_AUTO_PAD \ |
| | GMAC_MAC_CONFIG_CHECKSUM_OFFLOAD \ |
| IF_ENABLED(CONFIG_ETH_NXP_S32_LOOPBACK, \ |
| (|GMAC_MAC_CONFIG_LOOPBACK)) \ |
| )) |
| |
| #define ETH_NXP_S32_MAC_CONFIG(n) \ |
| static const Gmac_Ip_ConfigType eth_nxp_s32_mac_config_##n = { \ |
| .RxRingCount = 1U, \ |
| .TxRingCount = 1U, \ |
| .Interrupts = 0U, \ |
| .Callback = NULL, \ |
| .TxSchedAlgo = GMAC_SCHED_ALGO_SP, \ |
| .MiiMode = ETH_NXP_S32_MAC_MII(n), \ |
| .Speed = GMAC_SPEED_100M, \ |
| .Duplex = GMAC_FULL_DUPLEX, \ |
| .MacConfig = ETH_NXP_S32_MAC_CONF(n), \ |
| .MacPktFilterConfig = ETH_NXP_S32_MAC_PKT_FILTER(n), \ |
| .EnableCtrl = false, \ |
| } |
| |
| #define ETH_NXP_S32_MAC_ADDR(n) \ |
| BUILD_ASSERT(DT_INST_PROP(n, zephyr_random_mac_address) || \ |
| NODE_HAS_VALID_MAC_ADDR(DT_DRV_INST(n)), \ |
| "eth_nxp_s32_gmac requires either a fixed or random MAC address"); \ |
| static const uint8_t eth_nxp_s32_mac_addr_##n[ETH_NXP_S32_MAC_ADDR_LEN] = \ |
| DT_INST_PROP_OR(n, local_mac_address, {0U}) |
| |
| #define ETH_NXP_S32_MAC_STATE(n) Gmac_Ip_StateType eth_nxp_s32_mac_state_##n |
| |
| #define ETH_NXP_S32_CTRL_CONFIG(n) \ |
| { \ |
| .Gmac_pCtrlState = ð_nxp_s32_mac_state_##n, \ |
| .Gmac_pCtrlConfig = ð_nxp_s32_mac_config_##n, \ |
| .Gmac_paCtrlRxRingConfig = ð_nxp_s32_mac_rxring_config_##n, \ |
| .Gmac_paCtrlTxRingConfig = ð_nxp_s32_mac_txring_config_##n, \ |
| .Gmac_pau8CtrlPhysAddr = ð_nxp_s32_mac_addr_##n[0], \ |
| .Gmac_pCtrlTxTimeAwareShaper = ð_nxp_s32_mac_txtimeshaper_config_##n,\ |
| } |
| |
| #define ETH_NXP_S32_HW_INSTANCE_CHECK(i, n) \ |
| ((DT_INST_REG_ADDR(n) == IP_GMAC_##i##_BASE) ? i : 0) |
| |
| #define ETH_NXP_S32_HW_INSTANCE(n) \ |
| LISTIFY(__DEBRACKET FEATURE_GMAC_NUM_INSTANCES, \ |
| ETH_NXP_S32_HW_INSTANCE_CHECK, (|), n) |
| |
| #define ETH_NXP_S32_PHY_DEV(n) \ |
| (COND_CODE_1(DT_INST_NODE_HAS_PROP(n, phy_handle), \ |
| (DEVICE_DT_GET(DT_INST_PHANDLE(n, phy_handle))), NULL)) |
| |
| #define ETH_NXP_S32_DEVICE(n) \ |
| ETH_NXP_S32_TX_CALLBACK(n) \ |
| ETH_NXP_S32_RX_CALLBACK(n) \ |
| ETH_NXP_S32_INIT_CONFIG(n) \ |
| ETH_NXP_S32_RX_RING(n); \ |
| ETH_NXP_S32_TX_RING(n); \ |
| ETH_NXP_S32_MAC_STATE(n); \ |
| ETH_NXP_S32_MAC_TXTIMESHAPER_CONFIG(n); \ |
| ETH_NXP_S32_MAC_RXRING_CONFIG(n); \ |
| ETH_NXP_S32_MAC_TXRING_CONFIG(n); \ |
| ETH_NXP_S32_MAC_CONFIG(n); \ |
| ETH_NXP_S32_MAC_ADDR(n); \ |
| PINCTRL_DT_INST_DEFINE(n); \ |
| \ |
| static const struct eth_nxp_s32_config eth_nxp_s32_config_##n = { \ |
| .instance = ETH_NXP_S32_HW_INSTANCE(n), \ |
| .base = (GMAC_Type *)DT_INST_REG_ADDR(n), \ |
| .ctrl_cfg = ETH_NXP_S32_CTRL_CONFIG(n), \ |
| .do_config = eth_nxp_s32_init_config_##n, \ |
| .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ |
| .phy_dev = ETH_NXP_S32_PHY_DEV(n), \ |
| .rx_irq = DT_INST_IRQ_BY_NAME(n, rx, irq), \ |
| .tx_irq = DT_INST_IRQ_BY_NAME(n, tx, irq), \ |
| .tx_ring_idx = 0U, \ |
| .rx_ring_idx = 0U, \ |
| }; \ |
| \ |
| static struct eth_nxp_s32_data eth_nxp_s32_data_##n; \ |
| \ |
| ETH_NET_DEVICE_DT_INST_DEFINE(n, \ |
| eth_nxp_s32_init, \ |
| NULL, \ |
| ð_nxp_s32_data_##n, \ |
| ð_nxp_s32_config_##n, \ |
| CONFIG_ETH_INIT_PRIORITY, \ |
| ð_api, \ |
| NET_ETH_MTU); |
| |
| DT_INST_FOREACH_STATUS_OKAY(ETH_NXP_S32_DEVICE) |