1 /*
2 * Copyright 2024-2025 NXP
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT nxp_imx_netc_psi
8
9 #define LOG_LEVEL CONFIG_ETHERNET_LOG_LEVEL
10 #include <zephyr/logging/log.h>
11 LOG_MODULE_REGISTER(nxp_imx_eth_psi);
12
13 #include <zephyr/kernel.h>
14 #include <zephyr/device.h>
15 #include <zephyr/drivers/pinctrl.h>
16 #include <zephyr/net/ethernet.h>
17 #include <zephyr/net/net_if.h>
18 #include <zephyr/net/net_pkt.h>
19 #include <zephyr/net/phy.h>
20 #include <ethernet/eth_stats.h>
21
22 #include "../eth.h"
23 #include "eth_nxp_imx_netc_priv.h"
24
netc_eth_phylink_callback(const struct device * pdev,struct phy_link_state * state,void * user_data)25 static void netc_eth_phylink_callback(const struct device *pdev, struct phy_link_state *state,
26 void *user_data)
27 {
28 const struct device *dev = (struct device *)user_data;
29 const struct netc_eth_config *cfg = dev->config;
30 struct netc_eth_data *data = dev->data;
31 status_t result;
32
33 ARG_UNUSED(pdev);
34
35 if (state->is_up) {
36 LOG_INF("ENETC%d Link up", getSiInstance(cfg->si_idx));
37 result = EP_Up(&data->handle, PHY_TO_NETC_SPEED(state->speed),
38 PHY_TO_NETC_DUPLEX_MODE(state->speed));
39 if (result != kStatus_Success) {
40 LOG_ERR("Failed to set MAC up");
41 }
42 net_eth_carrier_on(data->iface);
43 } else {
44 LOG_INF("ENETC%d Link down", getSiInstance(cfg->si_idx));
45 result = EP_Down(&data->handle);
46 if (result != kStatus_Success) {
47 LOG_ERR("Failed to set MAC down");
48 }
49 net_eth_carrier_off(data->iface);
50 }
51 }
52
netc_eth_iface_init(struct net_if * iface)53 static void netc_eth_iface_init(struct net_if *iface)
54 {
55 const struct device *dev = net_if_get_device(iface);
56 struct netc_eth_data *data = dev->data;
57 const struct netc_eth_config *cfg = dev->config;
58 status_t result;
59
60 /*
61 * For VLAN, this value is only used to get the correct L2 driver.
62 * The iface pointer in context should contain the main interface
63 * if the VLANs are enabled.
64 */
65 if (data->iface == NULL) {
66 data->iface = iface;
67 }
68
69 /* Set MAC address */
70 result = EP_SetPrimaryMacAddr(&data->handle, (uint8_t *)data->mac_addr);
71 if (result != kStatus_Success) {
72 LOG_ERR("Failed to set MAC address");
73 }
74
75 net_if_set_link_addr(iface, data->mac_addr, sizeof(data->mac_addr), NET_LINK_ETHERNET);
76
77 LOG_INF("ENETC%d MAC: %02x:%02x:%02x:%02x:%02x:%02x", getSiInstance(cfg->si_idx),
78 data->mac_addr[0], data->mac_addr[1], data->mac_addr[2], data->mac_addr[3],
79 data->mac_addr[4], data->mac_addr[5]);
80
81 ethernet_init(iface);
82
83 if (cfg->pseudo_mac) {
84 return;
85 }
86
87 /*
88 * PSI controls the PHY. If PHY is configured either as fixed
89 * link or autoneg, the callback is executed at least once
90 * immediately after setting it.
91 */
92 if (!device_is_ready(cfg->phy_dev)) {
93 LOG_ERR("PHY device (%p) is not ready, cannot init iface", cfg->phy_dev);
94 return;
95 }
96 phy_link_callback_set(cfg->phy_dev, &netc_eth_phylink_callback, (void *)dev);
97
98 /* Do not start the interface until PHY link is up */
99 net_if_carrier_off(iface);
100 }
101
netc_eth_init(const struct device * dev)102 static int netc_eth_init(const struct device *dev)
103 {
104 const struct netc_eth_config *cfg = dev->config;
105 int err;
106
107 if (cfg->pseudo_mac) {
108 goto init_common;
109 }
110
111 err = pinctrl_apply_state(cfg->pincfg, PINCTRL_STATE_DEFAULT);
112 if (err) {
113 return err;
114 }
115
116 init_common:
117 return netc_eth_init_common(dev);
118 }
119
netc_eth_get_phy(const struct device * dev)120 static const struct device *netc_eth_get_phy(const struct device *dev)
121 {
122 const struct netc_eth_config *cfg = dev->config;
123
124 return cfg->phy_dev;
125 }
126
127 static const struct ethernet_api netc_eth_api = {.iface_api.init = netc_eth_iface_init,
128 .get_capabilities = netc_eth_get_capabilities,
129 .get_phy = netc_eth_get_phy,
130 .set_config = netc_eth_set_config,
131 .send = netc_eth_tx};
132
133 #define NETC_PSI_INSTANCE_DEFINE(n) \
134 PINCTRL_DT_INST_DEFINE(n); \
135 NETC_GENERATE_MAC_ADDRESS(n) \
136 AT_NONCACHEABLE_SECTION_ALIGN( \
137 static uint8_t eth##n##_tx_buff[CONFIG_ETH_NXP_IMX_TX_RING_BUF_SIZE], \
138 NETC_BUFF_ALIGN); \
139 AT_NONCACHEABLE_SECTION_ALIGN( \
140 static netc_tx_bd_t eth##n##_txbd_array[CONFIG_ETH_NXP_IMX_TX_RING_NUM] \
141 [CONFIG_ETH_NXP_IMX_TX_RING_LEN], \
142 NETC_BD_ALIGN); \
143 static netc_tx_frame_info_t eth##n##_txdirty_array[CONFIG_ETH_NXP_IMX_TX_RING_NUM] \
144 [CONFIG_ETH_NXP_IMX_TX_RING_LEN]; \
145 AT_NONCACHEABLE_SECTION_ALIGN( \
146 static rx_buffer_t eth##n##_rx_buff[CONFIG_ETH_NXP_IMX_RX_RING_NUM] \
147 [CONFIG_ETH_NXP_IMX_RX_RING_LEN], \
148 NETC_BUFF_ALIGN); \
149 static uint64_t eth##n##_rx_buff_addr_array[CONFIG_ETH_NXP_IMX_RX_RING_NUM] \
150 [CONFIG_ETH_NXP_IMX_RX_RING_LEN]; \
151 AT_NONCACHEABLE_SECTION(static uint8_t eth##n##_rx_frame[NETC_RX_RING_BUF_SIZE_ALIGN]); \
152 AT_NONCACHEABLE_SECTION_ALIGN( \
153 static netc_rx_bd_t eth##n##_rxbd_array[CONFIG_ETH_NXP_IMX_RX_RING_NUM] \
154 [CONFIG_ETH_NXP_IMX_RX_RING_LEN], \
155 NETC_BD_ALIGN); \
156 static void netc_eth##n##_bdr_init(netc_bdr_config_t *bdr_config, \
157 netc_rx_bdr_config_t *rx_bdr_config, \
158 netc_tx_bdr_config_t *tx_bdr_config) \
159 { \
160 for (uint8_t ring = 0U; ring < CONFIG_ETH_NXP_IMX_RX_RING_NUM; ring++) { \
161 for (uint8_t bd = 0U; bd < CONFIG_ETH_NXP_IMX_RX_RING_LEN; bd++) { \
162 eth##n##_rx_buff_addr_array[ring][bd] = \
163 (uint64_t)(uintptr_t)ð##n##_rx_buff[ring][bd]; \
164 } \
165 } \
166 memset(bdr_config, 0, sizeof(netc_bdr_config_t)); \
167 memset(rx_bdr_config, 0, sizeof(netc_rx_bdr_config_t)); \
168 memset(tx_bdr_config, 0, sizeof(netc_tx_bdr_config_t)); \
169 bdr_config->rxBdrConfig = rx_bdr_config; \
170 bdr_config->txBdrConfig = tx_bdr_config; \
171 bdr_config->rxBdrConfig[0].bdArray = ð##n##_rxbd_array[0][0]; \
172 bdr_config->rxBdrConfig[0].len = CONFIG_ETH_NXP_IMX_RX_RING_LEN; \
173 bdr_config->rxBdrConfig[0].buffAddrArray = ð##n##_rx_buff_addr_array[0][0]; \
174 bdr_config->rxBdrConfig[0].buffSize = NETC_RX_RING_BUF_SIZE_ALIGN; \
175 bdr_config->rxBdrConfig[0].msixEntryIdx = NETC_RX_MSIX_ENTRY_IDX; \
176 bdr_config->rxBdrConfig[0].extendDescEn = false; \
177 bdr_config->rxBdrConfig[0].enThresIntr = true; \
178 bdr_config->rxBdrConfig[0].enCoalIntr = true; \
179 bdr_config->rxBdrConfig[0].intrThreshold = 1; \
180 bdr_config->txBdrConfig[0].bdArray = ð##n##_txbd_array[0][0]; \
181 bdr_config->txBdrConfig[0].len = CONFIG_ETH_NXP_IMX_TX_RING_LEN; \
182 bdr_config->txBdrConfig[0].dirtyArray = ð##n##_txdirty_array[0][0]; \
183 bdr_config->txBdrConfig[0].msixEntryIdx = NETC_TX_MSIX_ENTRY_IDX; \
184 bdr_config->txBdrConfig[0].enIntr = true; \
185 } \
186 static struct netc_eth_data netc_eth##n##_data = { \
187 .mac_addr = DT_INST_PROP_OR(n, local_mac_address, {0}), \
188 .tx_buff = eth##n##_tx_buff, \
189 .rx_frame = eth##n##_rx_frame, \
190 }; \
191 static const struct netc_eth_config netc_eth##n##_config = { \
192 .generate_mac = netc_eth##n##_generate_mac, \
193 .bdr_init = netc_eth##n##_bdr_init, \
194 .phy_dev = (COND_CODE_1(DT_INST_NODE_HAS_PROP(n, phy_handle), \
195 (DEVICE_DT_GET(DT_INST_PHANDLE(n, phy_handle))), NULL)), \
196 .phy_mode = NETC_PHY_MODE(DT_DRV_INST(n)), \
197 .pseudo_mac = DT_ENUM_HAS_VALUE(DT_DRV_INST(n), phy_connection_type, internal), \
198 .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
199 .si_idx = (DT_INST_PROP(n, mac_index) << 8) | DT_INST_PROP(n, si_index), \
200 .tx_intr_msg_data = NETC_TX_INTR_MSG_DATA_START + n, \
201 .rx_intr_msg_data = NETC_RX_INTR_MSG_DATA_START + n, \
202 }; \
203 ETH_NET_DEVICE_DT_INST_DEFINE(n, netc_eth_init, NULL, &netc_eth##n##_data, \
204 &netc_eth##n##_config, CONFIG_ETH_INIT_PRIORITY, \
205 &netc_eth_api, NET_ETH_MTU);
206 DT_INST_FOREACH_STATUS_OKAY(NETC_PSI_INSTANCE_DEFINE)
207