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