1 /*
2 * Copyright 2025 NXP
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define LOG_MODULE_NAME dsa_netc
8
9 #include <zephyr/logging/log.h>
10 LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_ETHERNET_LOG_LEVEL);
11
12 #include <zephyr/device.h>
13 #include <zephyr/drivers/gpio.h>
14 #include <zephyr/kernel.h>
15 #include <errno.h>
16 #include <zephyr/sys/util.h>
17 #include <zephyr/net/ethernet.h>
18 #include <zephyr/linker/sections.h>
19 #include <zephyr/toolchain/common.h>
20 #include <zephyr/drivers/pinctrl.h>
21 #include <zephyr/net/phy.h>
22 #include <zephyr/devicetree.h>
23
24 #include "fsl_netc_switch.h"
25
26 #define DT_DRV_COMPAT nxp_netc_switch
27 #include "nxp_imx_netc.h"
28
29 #define PRV_DATA(ctx) ((struct dsa_netc_data *const)(ctx)->prv_data)
30
31 struct dsa_netc_data {
32 int port_num;
33 int port_init_count;
34 swt_config_t swt_config;
35 swt_handle_t swt_handle;
36 const struct device *dev_master;
37 netc_cmd_bd_t *cmd_bd;
38 };
39
40 struct dsa_netc_slave_config {
41 /** MAC address for each LAN{123.,} ports */
42 uint8_t mac_addr[6];
43 const struct device *phy_dev;
44 netc_hw_mii_mode_t phy_mode;
45 volatile bool pseudo_mac;
46 const struct pinctrl_dev_config *pincfg;
47 int port_idx;
48 const struct device *ethernet_connection;
49 };
50
dsa_netc_port_init(const struct device * dev)51 int dsa_netc_port_init(const struct device *dev)
52 {
53 const struct dsa_netc_slave_config *cfg = dev->config;
54 struct dsa_context *ctx = dev->data;
55 struct dsa_netc_data *prv = PRV_DATA(ctx);
56 swt_config_t *swt_config = &prv->swt_config;
57
58 status_t result;
59 int err;
60
61 /* Get default config for whole switch before first port init */
62 if (prv->port_init_count == 0) {
63 SWT_GetDefaultConfig(swt_config);
64 swt_config->bridgeCfg.dVFCfg.portMembership = 0x0;
65 }
66
67 prv->port_init_count++;
68
69 if (prv->dev_master == NULL) {
70 prv->dev_master = cfg->ethernet_connection;
71 }
72
73 if (cfg->pseudo_mac) {
74 goto port_init;
75 }
76
77 err = pinctrl_apply_state(cfg->pincfg, PINCTRL_STATE_DEFAULT);
78 if (err) {
79 return err;
80 }
81
82 port_init:
83 /* miiSpeed and miiDuplex will get set correctly when link is up */
84 swt_config->ports[cfg->port_idx].ethMac.miiMode = cfg->phy_mode;
85
86 swt_config->bridgeCfg.dVFCfg.portMembership |= (1 << cfg->port_idx);
87
88 swt_config->ports[cfg->port_idx].bridgeCfg.enMacStationMove = true;
89
90 /* Switch init after all ports init */
91 if (prv->port_init_count == prv->port_num) {
92 swt_config->cmdRingUse = 1U;
93 swt_config->cmdBdrCfg[0].bdBase = prv->cmd_bd;
94 swt_config->cmdBdrCfg[0].bdLength = 8U;
95
96 result = SWT_Init(&prv->swt_handle, &prv->swt_config);
97 if (result != kStatus_Success) {
98 return -EIO;
99 }
100 }
101
102 return 0;
103 }
104
dsa_netc_sw_write_reg(const struct device * dev,uint16_t reg_addr,uint8_t value)105 static int dsa_netc_sw_write_reg(const struct device *dev, uint16_t reg_addr, uint8_t value)
106 {
107 /* This API is for PHY switch. Not available here. */
108 return -ENOTSUP;
109 }
110
dsa_netc_sw_read_reg(const struct device * dev,uint16_t reg_addr,uint8_t * value)111 static int dsa_netc_sw_read_reg(const struct device *dev, uint16_t reg_addr, uint8_t *value)
112 {
113 /* This API is for PHY switch. Not available here. */
114 return -ENOTSUP;
115 }
116
dsa_netc_set_mac_table_entry(const struct device * dev,const uint8_t * mac,uint8_t fw_port,uint16_t tbl_entry_idx,uint16_t flags)117 static int dsa_netc_set_mac_table_entry(const struct device *dev, const uint8_t *mac,
118 uint8_t fw_port, uint16_t tbl_entry_idx, uint16_t flags)
119 {
120 /* TODO: support it */
121 return -ENOTSUP;
122 }
123
dsa_netc_get_mac_table_entry(const struct device * dev,uint8_t * buf,uint16_t tbl_entry_idx)124 static int dsa_netc_get_mac_table_entry(const struct device *dev, uint8_t *buf,
125 uint16_t tbl_entry_idx)
126 {
127 /* TODO: support it */
128 return -ENOTSUP;
129 }
130
netc_eth_phylink_callback(const struct device * dev,struct phy_link_state * state,void * user_data)131 static void netc_eth_phylink_callback(const struct device *dev, struct phy_link_state *state,
132 void *user_data)
133 {
134 const struct device *ndev = (struct device *)user_data;
135 struct dsa_context *context = ndev->data;
136 struct dsa_netc_data *prv = PRV_DATA(context);
137
138 struct net_if *iface = net_if_lookup_by_dev(ndev);
139 struct ethernet_context *ctx = net_if_l2_data(iface);
140 status_t result;
141
142 if (state->is_up) {
143 LOG_INF("DSA slave port %d Link up", ctx->dsa_port_idx);
144 result = SWT_SetEthPortMII(&prv->swt_handle, ctx->dsa_port_idx,
145 PHY_TO_NETC_SPEED(state->speed),
146 PHY_TO_NETC_DUPLEX_MODE(state->speed));
147 if (result != kStatus_Success) {
148 LOG_ERR("DSA slave port %d failed to set MAC up", ctx->dsa_port_idx);
149 }
150 net_eth_carrier_on(iface);
151 } else {
152 LOG_INF("DSA slave port %d Link down", ctx->dsa_port_idx);
153 net_eth_carrier_off(iface);
154 }
155 }
156
dsa_netc_iface_init(struct net_if * iface)157 static void dsa_netc_iface_init(struct net_if *iface)
158 {
159 const struct device *dev = net_if_get_device(iface);
160 struct dsa_netc_slave_config *cfg = (struct dsa_netc_slave_config *)dev->config;
161 struct dsa_context *context = dev->data;
162 struct dsa_netc_data *prv = PRV_DATA(context);
163
164 struct ethernet_context *ctx = net_if_l2_data(iface);
165 struct ethernet_context *ctx_master;
166 int i = cfg->port_idx;
167
168 /* Find master port */
169 if (context->iface_master == NULL) {
170 context->iface_master = net_if_lookup_by_dev(prv->dev_master);
171 if (context->iface_master == NULL) {
172 LOG_ERR("DSA: Master iface NOT found!");
173 return;
174 }
175
176 /*
177 * Provide pointer to DSA context to master's eth interface
178 * struct ethernet_context
179 */
180 ctx_master = net_if_l2_data(context->iface_master);
181 ctx_master->dsa_ctx = context;
182 }
183
184 if (context->iface_slave[i] == NULL) {
185 context->iface_slave[i] = iface;
186 net_if_set_link_addr(iface, cfg->mac_addr, sizeof(cfg->mac_addr),
187 NET_LINK_ETHERNET);
188 ctx->dsa_port_idx = i;
189 ctx->dsa_ctx = context;
190
191 /*
192 * Initialize ethernet context 'work' for this iface to
193 * be able to monitor the carrier status.
194 */
195 ethernet_init(iface);
196 }
197
198 /* Do not start the interface until link is up */
199 net_if_carrier_off(iface);
200
201 if (cfg->pseudo_mac) {
202 return;
203 }
204
205 if (!device_is_ready(cfg->phy_dev)) {
206 LOG_ERR("PHY device (%p) is not ready, cannot init iface", cfg->phy_dev);
207 return;
208 }
209
210 phy_link_callback_set(cfg->phy_dev, &netc_eth_phylink_callback, (void *)dev);
211 }
212
dsa_netc_get_capabilities(const struct device * dev)213 static enum ethernet_hw_caps dsa_netc_get_capabilities(const struct device *dev)
214 {
215 ARG_UNUSED(dev);
216
217 return ETHERNET_DSA_SLAVE_PORT | ETHERNET_LINK_10BASE_T | ETHERNET_LINK_100BASE_T |
218 ETHERNET_LINK_1000BASE_T;
219 }
220
dsa_netc_get_phy(const struct device * dev)221 static const struct device *dsa_netc_get_phy(const struct device *dev)
222 {
223 const struct dsa_netc_slave_config *cfg = dev->config;
224
225 return cfg->phy_dev;
226 }
227
dsa_netc_tx(const struct device * dev,struct net_pkt * pkt)228 static int dsa_netc_tx(const struct device *dev, struct net_pkt *pkt)
229 {
230 /* TODO: frame tagging not supported on old NETC versions. Need implementation here. */
231 return 0;
232 }
233
234 const struct ethernet_api dsa_netc_eth_api = {
235 .iface_api.init = dsa_netc_iface_init,
236 .get_capabilities = dsa_netc_get_capabilities,
237 .get_phy = dsa_netc_get_phy,
238 .send = dsa_netc_tx,
239 };
240
241 static struct dsa_api dsa_netc_api = {
242 .switch_read = dsa_netc_sw_read_reg,
243 .switch_write = dsa_netc_sw_write_reg,
244 .switch_set_mac_table_entry = dsa_netc_set_mac_table_entry,
245 .switch_get_mac_table_entry = dsa_netc_get_mac_table_entry,
246 };
247
248 #define DSA_NETC_SLAVE_DEVICE_INIT_INSTANCE(slave, n) \
249 PINCTRL_DT_DEFINE(slave); \
250 const struct dsa_netc_slave_config dsa_netc_##n##_slave_##slave##_config = { \
251 .mac_addr = DT_PROP_OR(slave, local_mac_address, {0}), \
252 .phy_dev = (COND_CODE_1(DT_NODE_HAS_PROP(slave, phy_handle), \
253 DEVICE_DT_GET(DT_PHANDLE_BY_IDX(slave, phy_handle, 0)), \
254 NULL)), \
255 .phy_mode = NETC_PHY_MODE(slave), \
256 .pseudo_mac = DT_ENUM_HAS_VALUE(slave, phy_connection_type, internal), \
257 .pincfg = PINCTRL_DT_DEV_CONFIG_GET(slave), \
258 .port_idx = DT_REG_ADDR_BY_IDX(slave, 0), \
259 .ethernet_connection = (COND_CODE_1(DT_NODE_HAS_PROP(slave, ethernet), \
260 (DEVICE_DT_GET(DT_PHANDLE(slave, ethernet))), NULL)), \
261 }; \
262 NET_DEVICE_INIT_INSTANCE( \
263 CONCAT(dsa_slave_port_, slave), \
264 "switch_port" STRINGIFY(n), n, dsa_netc_port_init, NULL, &dsa_netc_context_##n, \
265 &dsa_netc_##n##_slave_##slave##_config, \
266 CONFIG_ETH_INIT_PRIORITY, &dsa_netc_eth_api, ETHERNET_L2, \
267 NET_L2_GET_CTX_TYPE(ETHERNET_L2), NET_ETH_MTU);
268
269 #define DSA_NETC_DEVICE(n) \
270 AT_NONCACHEABLE_SECTION_ALIGN(static netc_cmd_bd_t dsa_netc_##n##_cmd_bd[8], \
271 NETC_BD_ALIGN); \
272 static struct dsa_netc_data dsa_netc_data_prv_##n = { \
273 .port_num = DT_INST_CHILD_NUM_STATUS_OKAY(n), \
274 .port_init_count = 0, \
275 .cmd_bd = dsa_netc_##n##_cmd_bd, \
276 }; \
277 static struct dsa_context dsa_netc_context_##n = { \
278 .num_slave_ports = DT_INST_CHILD_NUM(n), \
279 .dapi = &dsa_netc_api, \
280 .prv_data = (void *)&dsa_netc_data_prv_##n, \
281 }; \
282 DT_INST_FOREACH_CHILD_STATUS_OKAY_VARGS(n, DSA_NETC_SLAVE_DEVICE_INIT_INSTANCE, n);
283
284 DT_INST_FOREACH_STATUS_OKAY(DSA_NETC_DEVICE);
285