From 211c28bfe61f0154967efb3876cc36e02c555288 Mon Sep 17 00:00:00 2001 From: Qiang Zhao Date: Tue, 16 Sep 2025 16:30:24 +0800 Subject: [PATCH 1/5] net: dsa: add set/get_config support Supported set/get_config API. Signed-off-by: Qiang Zhao --- include/zephyr/net/dsa_core.h | 10 ++++++++++ subsys/net/l2/ethernet/dsa/dsa_port.c | 26 ++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/include/zephyr/net/dsa_core.h b/include/zephyr/net/dsa_core.h index 7e10e84d6c400..3e437dcf1a702 100644 --- a/include/zephyr/net/dsa_core.h +++ b/include/zephyr/net/dsa_core.h @@ -129,6 +129,16 @@ struct dsa_api { /** Connect the switch to the tag protocol */ int (*connect_tag_protocol)(struct dsa_switch_context *dsa_switch_ctx, int tag_proto); + + /** Set specific hardware configuration */ + int (*set_config)(const struct device *dev, + enum ethernet_config_type type, + const struct ethernet_config *config); + + /** Get hardware specific configuration */ + int (*get_config)(const struct device *dev, + enum ethernet_config_type type, + struct ethernet_config *config); }; /** diff --git a/subsys/net/l2/ethernet/dsa/dsa_port.c b/subsys/net/l2/ethernet/dsa/dsa_port.c index 3d9d95cbff266..df7c3324d00a2 100644 --- a/subsys/net/l2/ethernet/dsa/dsa_port.c +++ b/subsys/net/l2/ethernet/dsa/dsa_port.c @@ -143,6 +143,30 @@ enum ethernet_hw_caps dsa_port_get_capabilities(const struct device *dev) return caps; } +static int dsa_set_config(const struct device *dev, enum ethernet_config_type type, + const struct ethernet_config *config) +{ + struct dsa_switch_context *dsa_switch_ctx = dev->data; + + if (!dsa_switch_ctx->dapi->set_config) { + return -ENOTSUP; + } + + return dsa_switch_ctx->dapi->set_config(dev, type, config); +} + +static int dsa_get_config(const struct device *dev, enum ethernet_config_type type, + struct ethernet_config *config) +{ + struct dsa_switch_context *dsa_switch_ctx = dev->data; + + if (!dsa_switch_ctx->dapi->get_config) { + return -ENOTSUP; + } + + return dsa_switch_ctx->dapi->get_config(dev, type, config); +} + const struct ethernet_api dsa_eth_api = { .iface_api.init = dsa_port_iface_init, .get_phy = dsa_port_get_phy, @@ -151,4 +175,6 @@ const struct ethernet_api dsa_eth_api = { .get_ptp_clock = dsa_port_get_ptp_clock, #endif .get_capabilities = dsa_port_get_capabilities, + .set_config = dsa_set_config, + .get_config = dsa_get_config, }; From 6ec6b037a0d44332d3f0cc41a142620b3d5af974 Mon Sep 17 00:00:00 2001 From: Qiang Zhao Date: Wed, 10 Sep 2025 14:09:49 +0530 Subject: [PATCH 2/5] net: ethernet: add NET_QBV Kconfig Add NET_QBV in Kconfig, Qbv is Enhancements for Scheduled Traffic (EST), one feature of TSN. The PTP clock provides the time reference for Qbv Signed-off-by: Qiang Zhao --- subsys/net/l2/ethernet/Kconfig | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/subsys/net/l2/ethernet/Kconfig b/subsys/net/l2/ethernet/Kconfig index 0c21e6f3c7a8e..026fbcfb3b05f 100644 --- a/subsys/net/l2/ethernet/Kconfig +++ b/subsys/net/l2/ethernet/Kconfig @@ -174,4 +174,14 @@ config NET_ETHERNET_FORWARD_UNRECOGNISED_ETHERTYPE it does not recognize the EtherType in the header. By default, such frames are dropped at the L2 processing. +config NET_QBV + bool "Qbv support" + depends on PTP_CLOCK + help + Enable Qbv support, Qbv is Enhancements for Scheduled Traffic (EST), one + feature of TSN. It provides time "slots" for specific classes of traffic + in a manner similar to TDM. Host (beyond standard) can select time/time + gate during which a packet will be sent, granular control by software of + packet transmission. + endif # NET_L2_ETHERNET From f6f144f61f0870241391c787ad0b563b280c578a Mon Sep 17 00:00:00 2001 From: Qiang Zhao Date: Wed, 10 Sep 2025 14:09:49 +0530 Subject: [PATCH 3/5] drivers: dsa_nxp_imx_netc: add DSA Qbv support Add DSA Qbv support, add set_config/get_config to set and get Qbv configuration. support enable/disable, set/get times, set/get list length and set/get gate control list. Signed-off-by: Qiang Zhao --- drivers/ethernet/dsa/Kconfig | 9 ++ drivers/ethernet/dsa/dsa_nxp_imx_netc.c | 182 ++++++++++++++++++++++++ 2 files changed, 191 insertions(+) diff --git a/drivers/ethernet/dsa/Kconfig b/drivers/ethernet/dsa/Kconfig index 5094622caff1c..5c9de6aad948e 100644 --- a/drivers/ethernet/dsa/Kconfig +++ b/drivers/ethernet/dsa/Kconfig @@ -75,3 +75,12 @@ config DSA_NXP_IMX_NETC Add support for NXP i.MX NETC DSA device driver. endif # DSA_DRIVERS + +config DSA_NXP_NETC_GCL_LEN + int "Gate control list length for i.MX NETC DSA" + default 64 + range 1 256 + depends on DSA_NXP_IMX_NETC && NET_QBV + help + Amount of Gate control list to use, reduce to save RAM. + The Max of the value can be 64,128,256. diff --git a/drivers/ethernet/dsa/dsa_nxp_imx_netc.c b/drivers/ethernet/dsa/dsa_nxp_imx_netc.c index a29c4a16a212c..ea16d0cf7ba4a 100644 --- a/drivers/ethernet/dsa/dsa_nxp_imx_netc.c +++ b/drivers/ethernet/dsa/dsa_nxp_imx_netc.c @@ -12,6 +12,7 @@ LOG_MODULE_REGISTER(dsa_netc, CONFIG_ETHERNET_LOG_LEVEL); #include #include #include +#include #include "../eth.h" #include "fsl_netc_switch.h" @@ -32,6 +33,13 @@ struct dsa_netc_config { DEVICE_MMIO_NAMED_ROM(pfconfig); }; +#ifdef CONFIG_NET_QBV +struct netc_qbv_config { + netc_tb_tgs_gcl_t tgs_config; + netc_tgs_gate_entry_t gcList[CONFIG_DSA_NXP_NETC_GCL_LEN]; +}; +#endif + struct dsa_netc_data { DEVICE_MMIO_NAMED_RAM(base); DEVICE_MMIO_NAMED_RAM(pfconfig); @@ -42,6 +50,10 @@ struct dsa_netc_data { uint8_t cpu_port_idx; struct k_fifo tx_ts_queue; #endif +#ifdef CONFIG_NET_QBV + struct netc_qbv_config qbv_config[DSA_PORT_MAX_COUNT]; +#endif + }; static int dsa_netc_port_init(const struct device *dev) @@ -86,6 +98,14 @@ static int dsa_netc_port_init(const struct device *dev) } #endif +#ifdef CONFIG_NET_QBV + memset(&(prv->qbv_config[cfg->port_idx].tgs_config), 0, sizeof(netc_tb_tgs_gcl_t)); + memset(prv->qbv_config[cfg->port_idx].gcList, 0, + sizeof(netc_tgs_gate_entry_t) * CONFIG_DSA_NXP_NETC_GCL_LEN); + prv->qbv_config[cfg->port_idx].tgs_config.entryID = cfg->port_idx; + prv->qbv_config[cfg->port_idx].tgs_config.gcList = prv->qbv_config[cfg->port_idx].gcList; +#endif + return 0; } @@ -235,6 +255,166 @@ static int dsa_netc_switch_init(const struct device *dev) return 0; } +#ifdef CONFIG_NET_QBV +static int dsa_netc_set_qbv(const struct device *dev, const struct ethernet_config *config) +{ + struct dsa_switch_context *dsa_switch_ctx = dev->data; + struct dsa_netc_data *prv = PRV_DATA(dsa_switch_ctx); + struct dsa_port_config *cfg = (struct dsa_port_config *)dev->config; + status_t result; + uint32_t gate_num; + int i; + uint16_t row; + int ret = 0; + + switch (config->qbv_param.type) { + case ETHERNET_QBV_PARAM_TYPE_STATUS: + result = SWT_TxPortTGSEnable(&prv->swt_handle, cfg->port_idx, + config->qbv_param.enabled); + if (result != kStatus_Success) { + LOG_ERR("Couldn't enable/disable QBV"); + ret = -ENOTSUP; + } + break; + case ETHERNET_QBV_PARAM_TYPE_TIME: + prv->qbv_config[cfg->port_idx].tgs_config.baseTime = + config->qbv_param.base_time.second * NSEC_PER_SEC + + (config->qbv_param.base_time.fract_nsecond >> 16); + prv->qbv_config[cfg->port_idx].tgs_config.cycleTime = + (uint32_t)(config->qbv_param.cycle_time.second * NSEC_PER_SEC + + config->qbv_param.cycle_time.nanosecond); + prv->qbv_config[cfg->port_idx].tgs_config.extTime = + config->qbv_param.extension_time; + break; + case ETHERNET_QBV_PARAM_TYPE_GATE_CONTROL_LIST: + row = config->qbv_param.gate_control.row; + gate_num = ((CONFIG_NET_TC_TX_COUNT) < 8 ? (CONFIG_NET_TC_TX_COUNT) : 8); + if (row > CONFIG_DSA_NXP_NETC_GCL_LEN) { + LOG_ERR("The gate control list length exceeds the limit"); + ret = -ENOTSUP; + } + prv->qbv_config[cfg->port_idx].gcList[row].interval = + config->qbv_param.gate_control.time_interval; + prv->qbv_config[cfg->port_idx].gcList[row].tcGateState = 0; + for (i = 0; i < gate_num; i++) { + prv->qbv_config[cfg->port_idx].gcList[row].tcGateState + |= config->qbv_param.gate_control.gate_status[i] << i; + } + if (prv->qbv_config[cfg->port_idx].tgs_config.numEntries > 0 && + (row + 1) == prv->qbv_config[cfg->port_idx].tgs_config.numEntries) { + result = SWT_TxTGSConfigAdminGcl(&prv->swt_handle, + &(prv->qbv_config[cfg->port_idx].tgs_config)); + if (result != kStatus_Success) { + LOG_ERR("Fail to set gate control list, err code: 0x%x", result); + ret = -ENOTSUP; + } + } + break; + case ETHERNET_QBV_PARAM_TYPE_GATE_CONTROL_LIST_LEN: + prv->qbv_config[cfg->port_idx].tgs_config.numEntries = + config->qbv_param.gate_control_list_len; + break; + default: + /* No validation needed */ + break; + } + + return ret; +} + +static int dsa_netc_get_qbv(const struct device *dev, struct ethernet_config *config) +{ + struct dsa_switch_context *dsa_switch_ctx = dev->data; + struct dsa_netc_data *prv = PRV_DATA(dsa_switch_ctx); + struct dsa_port_config *cfg = (struct dsa_port_config *)dev->config; + uint32_t gate_num; + int i; + uint16_t row; + int ret = 0; + + switch (config->qbv_param.type) { + case ETHERNET_QBV_PARAM_TYPE_STATUS: + config->qbv_param.enabled = ((prv->swt_handle.hw.ports[cfg->port_idx].port->PTGSCR + & NETC_PORT_PTGSCR_TGE_MASK) != 0); + break; + case ETHERNET_QBV_PARAM_TYPE_TIME: + config->qbv_param.base_time.second = + prv->qbv_config[cfg->port_idx].tgs_config.baseTime / NSEC_PER_SEC; + config->qbv_param.base_time.fract_nsecond = + prv->qbv_config[cfg->port_idx].tgs_config.baseTime % NSEC_PER_SEC << 16; + config->qbv_param.cycle_time.second = (uint64_t) + (prv->qbv_config[cfg->port_idx].tgs_config.cycleTime / NSEC_PER_SEC); + config->qbv_param.cycle_time.nanosecond = + prv->qbv_config[cfg->port_idx].tgs_config.cycleTime % NSEC_PER_SEC; + config->qbv_param.extension_time = + prv->qbv_config[cfg->port_idx].tgs_config.extTime; + break; + case ETHERNET_QBV_PARAM_TYPE_GATE_CONTROL_LIST: + row = config->qbv_param.gate_control.row; + gate_num = ((CONFIG_NET_TC_TX_COUNT) < 8 ? (CONFIG_NET_TC_TX_COUNT) : 8); + if (row > CONFIG_DSA_NXP_NETC_GCL_LEN) { + LOG_ERR("The gate control list length exceeds the limit"); + ret = -ENOTSUP; + } + config->qbv_param.gate_control.time_interval = + prv->qbv_config[cfg->port_idx].gcList[row].interval; + for (i = 0; i < gate_num; i++) { + config->qbv_param.gate_control.gate_status[i] = + ((prv->qbv_config[cfg->port_idx].gcList[row].tcGateState + & BIT(i)) != 0); + } + break; + case ETHERNET_QBV_PARAM_TYPE_GATE_CONTROL_LIST_LEN: + config->qbv_param.gate_control_list_len = + prv->qbv_config[cfg->port_idx].tgs_config.numEntries; + break; + default: + /* No validation needed */ + break; + } + + return ret; +} +#endif + +static int dsa_netc_set_config(const struct device *dev, enum ethernet_config_type type, + const struct ethernet_config *config) +{ + int ret = 0; + + switch (type) { +#ifdef CONFIG_NET_QBV + case ETHERNET_CONFIG_TYPE_QBV_PARAM: + ret = dsa_netc_set_qbv(dev, config); + break; +#endif + default: + ret = -ENOTSUP; + break; + } + + return ret; +} + +static int dsa_netc_get_config(const struct device *dev, enum ethernet_config_type type, + struct ethernet_config *config) +{ + int ret = 0; + + switch (type) { +#ifdef CONFIG_NET_QBV + case ETHERNET_CONFIG_TYPE_QBV_PARAM: + ret = dsa_netc_get_qbv(dev, config); + break; +#endif + default: + ret = -ENOTSUP; + break; + } + + return ret; +} + static struct dsa_api dsa_netc_api = { .port_init = dsa_netc_port_init, .port_generate_random_mac = dsa_netc_port_generate_random_mac, @@ -244,6 +424,8 @@ static struct dsa_api dsa_netc_api = { .port_txtstamp = dsa_netc_port_txtstamp, #endif .connect_tag_protocol = dsa_netc_connect_tag_protocol, + .set_config = dsa_netc_set_config, + .get_config = dsa_netc_get_config, }; #define DSA_NETC_PORT_INST_INIT(port, n) \ From 6cb1c21d9211d57438557b920f4fe3bfe775a0ca Mon Sep 17 00:00:00 2001 From: Qiang Zhao Date: Wed, 10 Sep 2025 13:25:38 +0530 Subject: [PATCH 4/5] net: shell: Add Qbv shell Added Qbv shell subcommand to net command. Supported enable, set_config, set_time and get_info functions. Signed-off-by: Qiang Zhao --- subsys/net/lib/shell/CMakeLists.txt | 1 + subsys/net/lib/shell/Kconfig | 8 + subsys/net/lib/shell/qbv_shell.c | 337 ++++++++++++++++++++++++++++ 3 files changed, 346 insertions(+) create mode 100644 subsys/net/lib/shell/qbv_shell.c diff --git a/subsys/net/lib/shell/CMakeLists.txt b/subsys/net/lib/shell/CMakeLists.txt index db06c84d8bb65..a027ff434ef4e 100644 --- a/subsys/net/lib/shell/CMakeLists.txt +++ b/subsys/net/lib/shell/CMakeLists.txt @@ -37,3 +37,4 @@ zephyr_library_sources_ifdef(CONFIG_NET_SHELL_UDP_SUPPORTED udp.c) zephyr_library_sources_ifdef(CONFIG_NET_SHELL_VIRTUAL_SUPPORTED virtual.c) zephyr_library_sources_ifdef(CONFIG_NET_SHELL_VLAN_SUPPORTED vlan.c) zephyr_library_sources_ifdef(CONFIG_NET_SHELL_WEBSOCKET_SUPPORTED websocket.c) +zephyr_library_sources_ifdef(CONFIG_NET_SHELL_QBV_SUPPORTED qbv_shell.c) diff --git a/subsys/net/lib/shell/Kconfig b/subsys/net/lib/shell/Kconfig index c8d7c3ed2af4b..c050afd1ff612 100644 --- a/subsys/net/lib/shell/Kconfig +++ b/subsys/net/lib/shell/Kconfig @@ -150,6 +150,14 @@ config NET_SHELL_WEBSOCKET_SUPPORTED default y depends on NET_SHELL_SHOW_DISABLED_COMMANDS || WEBSOCKET_CLIENT +config NET_SHELL_QBV_SUPPORTED + bool "Qbv Shell" + default y + depends on NET_SHELL_SHOW_DISABLED_COMMANDS || (NET_QBV && NET_L2_ETHERNET_MGMT) + help + Enable Qbv Shell. + The Qbv shell currently supports set/enable operations. + config NET_SHELL_DYN_CMD_COMPLETION bool "Network shell dynamic command completion" default y diff --git a/subsys/net/lib/shell/qbv_shell.c b/subsys/net/lib/shell/qbv_shell.c new file mode 100644 index 0000000000000..7f74b50e53986 --- /dev/null +++ b/subsys/net/lib/shell/qbv_shell.c @@ -0,0 +1,337 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +LOG_MODULE_DECLARE(net_shell); + +#include +#include +#include +#include +#include +#include +#include + +#include "net_shell_private.h" + +#if defined(CONFIG_NET_QBV) && defined(CONFIG_NET_L2_ETHERNET_MGMT) +static struct net_if *get_iface_from_shell(const struct shell *sh, size_t argc, char **argv) +{ + int idx; + struct net_if *iface; + + idx = get_iface_idx(sh, argv[1]); + if (idx < 0) { + return NULL; + } + + iface = net_if_get_by_index(idx); + if (net_if_l2(iface) != &NET_L2_GET_NAME(ETHERNET)) { + PR_WARNING("No such interface in index %d\n", idx); + return NULL; + } + + return iface; +} +#endif + +/* qbv enable */ +static int cmd_net_qbv(const struct shell *sh, size_t argc, char **argv) +{ + ARG_UNUSED(argc); + ARG_UNUSED(argv); + +#if defined(CONFIG_NET_QBV) && defined(CONFIG_NET_L2_ETHERNET_MGMT) + shell_print(sh, "To set Qbv config:"); + shell_print(sh, " 1. Run enable to on"); + shell_print(sh, " 2. Run set_config to set base_time/cycle_time/cycle_time_ext/list_len"); + shell_print(sh, " 3. Run set_gc to set gate control"); + shell_print(sh, "For example:"); + shell_print(sh, " 1. net qbv enable 1 on"); + shell_print(sh, " 2. net qbv set_config 1 200 0 0 10000000 0 2"); + shell_print(sh, " 3. qbv set_gc 1 0 0x1 5000000"); + shell_print(sh, " 4. qbv set_gc 1 0 0x2 5000000"); +#else + shell_print(sh, "Set %s to enable %s support.\n", "CONFIG_NET_QBV", "qbv"); + shell_print(sh, "Set %s to enable %s support.\n", "CONFIG_NET_L2_ETHERNET_MGMT", + "Ethernet network management interface"); +#endif + + return 0; +} + +/* qbv enable */ +static int cmd_qbv_enable(const struct shell *sh, size_t argc, char **argv) +{ +#if defined(CONFIG_NET_QBV) && defined(CONFIG_NET_L2_ETHERNET_MGMT) + struct net_if *iface; + struct ethernet_req_params params; + int ret; + bool enable; + + iface = get_iface_from_shell(sh, argc, argv); + if (!iface) { + return -ENOEXEC; + } + + enable = shell_strtobool(argv[2], 0, &ret); + if (ret < 0) { + return ret; + } + + params.qbv_param.type = ETHERNET_QBV_PARAM_TYPE_STATUS; + params.qbv_param.state = ETHERNET_QBV_STATE_TYPE_ADMIN; + params.qbv_param.enabled = enable; + + ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QBV_PARAM, + iface, + ¶ms, sizeof(struct ethernet_req_params)); + if (ret < 0) { + shell_error(sh, "failed to set %s", argv[1]); + return ret; + } +#else + ARG_UNUSED(argc); + ARG_UNUSED(argv); + shell_print(sh, "Set %s to enable %s support.\n", "CONFIG_NET_QBV", "qbv"); + shell_print(sh, "Set %s to enable %s support.\n", "CONFIG_NET_L2_ETHERNET_MGMT", + "Ethernet network management interface"); +#endif + + return 0; +} + +/* + * qbv set_config + * + */ +static int cmd_qbv_set_config(const struct shell *sh, size_t argc, char **argv) +{ +#if defined(CONFIG_NET_QBV) && defined(CONFIG_NET_L2_ETHERNET_MGMT) + struct net_if *iface; + struct ethernet_req_params params; + uint32_t list_len; + int ret; + + iface = get_iface_from_shell(sh, argc, argv); + if (!iface) { + return -ENOEXEC; + } + + params.qbv_param.type = ETHERNET_QBV_PARAM_TYPE_TIME; + params.qbv_param.state = ETHERNET_QBV_STATE_TYPE_ADMIN; + + params.qbv_param.base_time.second = shell_strtoull(argv[2], 10, &ret); + if (ret < 0) { + return ret; + } + + params.qbv_param.base_time.fract_nsecond = shell_strtoull(argv[3], 10, &ret); + if (ret < 0) { + return ret; + } + + params.qbv_param.cycle_time.second = shell_strtoull(argv[4], 10, &ret); + if (ret < 0) { + return ret; + } + + params.qbv_param.cycle_time.nanosecond = shell_strtoul(argv[5], 10, &ret); + if (ret < 0) { + return ret; + } + + params.qbv_param.extension_time = shell_strtoul(argv[6], 10, &ret); + if (ret < 0) { + return ret; + } + + ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QBV_PARAM, + iface, + ¶ms, sizeof(struct ethernet_req_params)); + + list_len = shell_strtol(argv[7], 10, &ret); + if (ret < 0) { + shell_print(sh, "failed to set times"); + return ret; + } + + params.qbv_param.type = + ETHERNET_QBV_PARAM_TYPE_GATE_CONTROL_LIST_LEN; + params.qbv_param.gate_control_list_len = list_len; + ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QBV_PARAM, + iface, + ¶ms, sizeof(struct ethernet_req_params)); + if (ret < 0) { + shell_print(sh, "failed to set list length"); + return ret; + } +#else + ARG_UNUSED(argc); + ARG_UNUSED(argv); + shell_print(sh, "Set %s to enable %s support.\n", "CONFIG_NET_QBV", "qbv"); + shell_print(sh, "Set %s to enable %s support.\n", "CONFIG_NET_L2_ETHERNET_MGMT", + "Ethernet network management interface"); +#endif + + return 0; +} + +/* qbv set_config */ +static int cmd_qbv_set_gc(const struct shell *sh, size_t argc, char **argv) +{ +#if defined(CONFIG_NET_QBV) && defined(CONFIG_NET_L2_ETHERNET_MGMT) + struct net_if *iface; + struct ethernet_req_params params; + uint32_t row; + uint32_t interval; + uint32_t gc; + int ret; + + iface = get_iface_from_shell(sh, argc, argv); + if (!iface) { + return -ENOEXEC; + } + + row = shell_strtoul(argv[2], 10, &ret); + if (ret < 0) { + return ret; + } + + gc = shell_strtoul(argv[3], 16, &ret); + if (ret < 0) { + return ret; + } + + interval = shell_strtoul(argv[4], 10, &ret); + if (ret < 0) { + return ret; + } + params.qbv_param.type = ETHERNET_QBV_PARAM_TYPE_GATE_CONTROL_LIST; + params.qbv_param.state = ETHERNET_QBV_STATE_TYPE_ADMIN; + + params.qbv_param.gate_control.time_interval = interval; + params.qbv_param.gate_control.row = row; + for (int i = 0; i < CONFIG_NET_TC_TX_COUNT; i++) { + params.qbv_param.gate_control.gate_status[i] = ((gc & BIT(i)) != 0); + } + + ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QBV_PARAM, + iface, + ¶ms, sizeof(struct ethernet_req_params)); + if (ret < 0) { + return ret; + } +#else + ARG_UNUSED(argc); + ARG_UNUSED(argv); + shell_print(sh, "Set %s to enable %s support.\n", "CONFIG_NET_QBV", "qbv"); + shell_print(sh, "Set %s to enable %s support.\n", "CONFIG_NET_L2_ETHERNET_MGMT", + "Ethernet network management interface"); +#endif + + return 0; +} + +/* qbv get_info */ +static int cmd_qbv_get_info(const struct shell *sh, size_t argc, char **argv) +{ +#if defined(CONFIG_NET_QBV) && defined(CONFIG_NET_L2_ETHERNET_MGMT) + struct net_if *iface; + struct ethernet_req_params params; + int ret; + uint32_t list_len; + uint32_t gate_status; + + iface = get_iface_from_shell(sh, argc, argv); + if (!iface) { + return -ENOEXEC; + } + + params.qbv_param.type = ETHERNET_QBV_PARAM_TYPE_STATUS; + params.qbv_param.state = ETHERNET_QBV_STATE_TYPE_ADMIN; + + ret = net_mgmt(NET_REQUEST_ETHERNET_GET_QBV_PARAM, + iface, + ¶ms, sizeof(struct ethernet_req_params)); + if (ret < 0) { + shell_error(sh, "failed to get %s status", argv[1]); + return ret; + } + shell_print(sh, "status: %s", (params.qbv_param.enabled ? "on" : "off")); + + params.qbv_param.type = ETHERNET_QBV_PARAM_TYPE_TIME; + ret = net_mgmt(NET_REQUEST_ETHERNET_GET_QBV_PARAM, + iface, + ¶ms, sizeof(struct ethernet_req_params)); + if (ret < 0) { + shell_error(sh, "failed to get %s time", argv[1]); + return ret; + } + shell_print(sh, "base_time(s): %"PRIu64, params.qbv_param.base_time.second); + shell_print(sh, "base_time(fract_ns): %"PRIu64, params.qbv_param.base_time.fract_nsecond); + shell_print(sh, "cycle_time(s): %"PRIu64, params.qbv_param.cycle_time.second); + shell_print(sh, "cycle_time(ns): %"PRIu32, params.qbv_param.cycle_time.nanosecond); + shell_print(sh, "extension_time(ns): %"PRIu32, params.qbv_param.extension_time); + + params.qbv_param.type = ETHERNET_QBV_PARAM_TYPE_GATE_CONTROL_LIST_LEN; + ret = net_mgmt(NET_REQUEST_ETHERNET_GET_QBV_PARAM, + iface, + ¶ms, sizeof(struct ethernet_req_params)); + if (ret < 0) { + shell_error(sh, "failed to get %s list length", argv[1]); + return ret; + } + shell_print(sh, "list len: %"PRIu32, params.qbv_param.gate_control_list_len); + + list_len = params.qbv_param.gate_control_list_len; + params.qbv_param.type = ETHERNET_QBV_PARAM_TYPE_GATE_CONTROL_LIST; + for (uint16_t i = 0; i < list_len; i++) { + params.qbv_param.gate_control.row = i; + ret = net_mgmt(NET_REQUEST_ETHERNET_GET_QBV_PARAM, + iface, + ¶ms, sizeof(struct ethernet_req_params)); + if (ret < 0) { + shell_error(sh, "failed to get %s gate control", argv[1]); + return ret; + } + gate_status = 0; + for (int j = 0; j < CONFIG_NET_TC_TX_COUNT; j++) { + gate_status |= params.qbv_param.gate_control.gate_status[j] << j; + } + shell_print(sh, "row: %"PRIu16" interval: %"PRIu32" gate_status: 0x%x", + i, params.qbv_param.gate_control.time_interval, gate_status); + } +#else + ARG_UNUSED(argc); + ARG_UNUSED(argv); + shell_print(sh, "Set %s to enable %s support.\n", "CONFIG_NET_QBV", "qbv"); + shell_print(sh, "Set %s to enable %s support.\n", "CONFIG_NET_L2_ETHERNET_MGMT", + "Ethernet network management interface"); +#endif + + return 0; +} + +SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_qbv, + SHELL_CMD_ARG(enable, NULL, + "Enable: enable ", + cmd_qbv_enable, 3, 0), + SHELL_CMD_ARG(set_config, NULL, + "Set config: set ", + cmd_qbv_set_config, 8, 0), + SHELL_CMD_ARG(set_gc, NULL, + "Set gate control: set ", + cmd_qbv_set_gc, 5, 0), + SHELL_CMD_ARG(get_info, NULL, + "Get info: get_info ", + cmd_qbv_get_info, 2, 0), + SHELL_SUBCMD_SET_END /* Array terminated. */ +); + +SHELL_SUBCMD_ADD((net), qbv, &net_cmd_qbv, + "Qbv commands", + cmd_net_qbv, 1, 0); From 068677687d1d9664c892ea0a585141c1fb025587 Mon Sep 17 00:00:00 2001 From: Qiang Zhao Date: Wed, 17 Sep 2025 10:16:13 +0800 Subject: [PATCH 5/5] drivers: dsa_nxp_imx_netc: add Qbv capability add Qbv capability for dsa_nxp_imx_netc Signed-off-by: Qiang Zhao --- drivers/ethernet/dsa/dsa_nxp_imx_netc.c | 14 ++++++++++++++ include/zephyr/net/dsa_core.h | 3 +++ subsys/net/l2/ethernet/dsa/dsa_port.c | 6 ++++++ 3 files changed, 23 insertions(+) diff --git a/drivers/ethernet/dsa/dsa_nxp_imx_netc.c b/drivers/ethernet/dsa/dsa_nxp_imx_netc.c index ea16d0cf7ba4a..b5a03e5d36cda 100644 --- a/drivers/ethernet/dsa/dsa_nxp_imx_netc.c +++ b/drivers/ethernet/dsa/dsa_nxp_imx_netc.c @@ -415,6 +415,19 @@ static int dsa_netc_get_config(const struct device *dev, enum ethernet_config_ty return ret; } +static enum ethernet_hw_caps dsa_port_get_capabilities(const struct device *dev) +{ + uint32_t caps = 0; + + ARG_UNUSED(dev); + +#ifdef CONFIG_NET_QBV + caps |= ETHERNET_QBV; +#endif + + return caps; +} + static struct dsa_api dsa_netc_api = { .port_init = dsa_netc_port_init, .port_generate_random_mac = dsa_netc_port_generate_random_mac, @@ -424,6 +437,7 @@ static struct dsa_api dsa_netc_api = { .port_txtstamp = dsa_netc_port_txtstamp, #endif .connect_tag_protocol = dsa_netc_connect_tag_protocol, + .get_capabilities = dsa_port_get_capabilities, .set_config = dsa_netc_set_config, .get_config = dsa_netc_get_config, }; diff --git a/include/zephyr/net/dsa_core.h b/include/zephyr/net/dsa_core.h index 3e437dcf1a702..cf5b7a7b413b6 100644 --- a/include/zephyr/net/dsa_core.h +++ b/include/zephyr/net/dsa_core.h @@ -130,6 +130,9 @@ struct dsa_api { /** Connect the switch to the tag protocol */ int (*connect_tag_protocol)(struct dsa_switch_context *dsa_switch_ctx, int tag_proto); + /** Get the device capabilities */ + enum ethernet_hw_caps (*get_capabilities)(const struct device *dev); + /** Set specific hardware configuration */ int (*set_config)(const struct device *dev, enum ethernet_config_type type, diff --git a/subsys/net/l2/ethernet/dsa/dsa_port.c b/subsys/net/l2/ethernet/dsa/dsa_port.c index df7c3324d00a2..828961c113c84 100644 --- a/subsys/net/l2/ethernet/dsa/dsa_port.c +++ b/subsys/net/l2/ethernet/dsa/dsa_port.c @@ -133,6 +133,7 @@ const struct device *dsa_port_get_ptp_clock(const struct device *dev) enum ethernet_hw_caps dsa_port_get_capabilities(const struct device *dev) { + struct dsa_switch_context *dsa_switch_ctx = dev->data; uint32_t caps = 0; #ifdef CONFIG_NET_L2_PTP @@ -140,6 +141,11 @@ enum ethernet_hw_caps dsa_port_get_capabilities(const struct device *dev) caps |= ETHERNET_PTP; } #endif + + if (dsa_switch_ctx->dapi->get_capabilities) { + caps |= dsa_switch_ctx->dapi->get_capabilities(dev); + } + return caps; }