* [RFC net-next 0/4] net: dsa: motorcomm: Add LED support
@ 2026-06-18 20:26 David Yang
2026-06-18 20:26 ` [RFC net-next 1/4] net: dsa: motorcomm: Move to subdirectory David Yang
` (3 more replies)
0 siblings, 4 replies; 10+ messages in thread
From: David Yang @ 2026-06-18 20:26 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel
RFC during net-next closed
David Yang (4):
net: dsa: motorcomm: Move to subdirectory
net: dsa: motorcomm: Split SMI module
net: dsa: motorcomm: Dynamically allocate port structures
net: dsa: motorcomm: Add LED support
MAINTAINERS | 2 +-
drivers/net/dsa/Kconfig | 10 +-
drivers/net/dsa/Makefile | 2 +-
drivers/net/dsa/motorcomm/Kconfig | 17 +
drivers/net/dsa/motorcomm/Makefile | 5 +
.../net/dsa/{yt921x.c => motorcomm/chip.c} | 311 +++-------
.../net/dsa/{yt921x.h => motorcomm/chip.h} | 21 +-
drivers/net/dsa/motorcomm/leds.c | 530 ++++++++++++++++++
drivers/net/dsa/motorcomm/leds.h | 104 ++++
drivers/net/dsa/motorcomm/smi.c | 155 +++++
drivers/net/dsa/motorcomm/smi.h | 88 +++
11 files changed, 1003 insertions(+), 242 deletions(-)
create mode 100644 drivers/net/dsa/motorcomm/Kconfig
create mode 100644 drivers/net/dsa/motorcomm/Makefile
rename drivers/net/dsa/{yt921x.c => motorcomm/chip.c} (95%)
rename drivers/net/dsa/{yt921x.h => motorcomm/chip.h} (99%)
create mode 100644 drivers/net/dsa/motorcomm/leds.c
create mode 100644 drivers/net/dsa/motorcomm/leds.h
create mode 100644 drivers/net/dsa/motorcomm/smi.c
create mode 100644 drivers/net/dsa/motorcomm/smi.h
--
2.53.0
^ permalink raw reply [flat|nested] 10+ messages in thread
* [RFC net-next 1/4] net: dsa: motorcomm: Move to subdirectory
2026-06-18 20:26 [RFC net-next 0/4] net: dsa: motorcomm: Add LED support David Yang
@ 2026-06-18 20:26 ` David Yang
2026-06-18 20:26 ` [RFC net-next 2/4] net: dsa: motorcomm: Split SMI module David Yang
` (2 subsequent siblings)
3 siblings, 0 replies; 10+ messages in thread
From: David Yang @ 2026-06-18 20:26 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel
yt921x is already the longest single-file DSA driver, so it's time to
split it into parts.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
MAINTAINERS | 2 +-
drivers/net/dsa/Kconfig | 10 ++--------
drivers/net/dsa/Makefile | 2 +-
drivers/net/dsa/motorcomm/Kconfig | 8 ++++++++
drivers/net/dsa/motorcomm/Makefile | 3 +++
drivers/net/dsa/{yt921x.c => motorcomm/chip.c} | 2 +-
drivers/net/dsa/{yt921x.h => motorcomm/chip.h} | 0
7 files changed, 16 insertions(+), 11 deletions(-)
create mode 100644 drivers/net/dsa/motorcomm/Kconfig
create mode 100644 drivers/net/dsa/motorcomm/Makefile
rename drivers/net/dsa/{yt921x.c => motorcomm/chip.c} (99%)
rename drivers/net/dsa/{yt921x.h => motorcomm/chip.h} (100%)
diff --git a/MAINTAINERS b/MAINTAINERS
index 06df1171f4cf..b007f20b2763 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18039,7 +18039,7 @@ M: David Yang <mmyangfl@gmail.com>
L: netdev@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml
-F: drivers/net/dsa/yt921x.*
+F: drivers/net/dsa/motorcomm/
F: net/dsa/tag_yt921x.c
MOXA SMARTIO/INDUSTIO/INTELLIO SERIAL CARD
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index 4ab567c5bbaf..98e9bbe47de7 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -72,6 +72,8 @@ config NET_DSA_MV88E6060
source "drivers/net/dsa/microchip/Kconfig"
+source "drivers/net/dsa/motorcomm/Kconfig"
+
source "drivers/net/dsa/mv88e6xxx/Kconfig"
source "drivers/net/dsa/mxl862xx/Kconfig"
@@ -158,12 +160,4 @@ config NET_DSA_VITESSE_VSC73XX_PLATFORM
This enables support for the Vitesse VSC7385, VSC7388, VSC7395
and VSC7398 SparX integrated ethernet switches, connected over
a CPU-attached address bus and work in memory-mapped I/O mode.
-
-config NET_DSA_YT921X
- tristate "Motorcomm YT9215 ethernet switch chip support"
- select NET_DSA_TAG_YT921X
- select NET_IEEE8021Q_HELPERS if DCB
- help
- This enables support for the Motorcomm YT9215 ethernet switch
- chip.
endmenu
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index d2975badffc0..138225baa4d5 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -14,11 +14,11 @@ obj-$(CONFIG_NET_DSA_SMSC_LAN9303_MDIO) += lan9303_mdio.o
obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX) += vitesse-vsc73xx-core.o
obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_PLATFORM) += vitesse-vsc73xx-platform.o
obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_SPI) += vitesse-vsc73xx-spi.o
-obj-$(CONFIG_NET_DSA_YT921X) += yt921x.o
obj-y += b53/
obj-y += hirschmann/
obj-y += lantiq/
obj-y += microchip/
+obj-y += motorcomm/
obj-y += mv88e6xxx/
obj-y += mxl862xx/
obj-y += netc/
diff --git a/drivers/net/dsa/motorcomm/Kconfig b/drivers/net/dsa/motorcomm/Kconfig
new file mode 100644
index 000000000000..64ff7d07a91b
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/Kconfig
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config NET_DSA_YT921X
+ tristate "Motorcomm YT9215 ethernet switch chip support"
+ select NET_DSA_TAG_YT921X
+ select NET_IEEE8021Q_HELPERS if DCB
+ help
+ This enables support for the Motorcomm YT9215 ethernet switch
+ chip.
diff --git a/drivers/net/dsa/motorcomm/Makefile b/drivers/net/dsa/motorcomm/Makefile
new file mode 100644
index 000000000000..bf99feb4c454
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_NET_DSA_YT921X) += yt921x.o
+yt921x-objs := chip.o
diff --git a/drivers/net/dsa/yt921x.c b/drivers/net/dsa/motorcomm/chip.c
similarity index 99%
rename from drivers/net/dsa/yt921x.c
rename to drivers/net/dsa/motorcomm/chip.c
index 159b16606f6c..f070732845eb 100644
--- a/drivers/net/dsa/yt921x.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -26,7 +26,7 @@
#include <net/ieee8021q.h>
#include <net/pkt_cls.h>
-#include "yt921x.h"
+#include "chip.h"
struct yt921x_mib_desc {
unsigned int size;
diff --git a/drivers/net/dsa/yt921x.h b/drivers/net/dsa/motorcomm/chip.h
similarity index 100%
rename from drivers/net/dsa/yt921x.h
rename to drivers/net/dsa/motorcomm/chip.h
--
2.53.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [RFC net-next 2/4] net: dsa: motorcomm: Split SMI module
2026-06-18 20:26 [RFC net-next 0/4] net: dsa: motorcomm: Add LED support David Yang
2026-06-18 20:26 ` [RFC net-next 1/4] net: dsa: motorcomm: Move to subdirectory David Yang
@ 2026-06-18 20:26 ` David Yang
2026-06-18 20:26 ` [RFC net-next 3/4] net: dsa: motorcomm: Dynamically allocate port structures David Yang
2026-06-18 20:26 ` [RFC net-next 4/4] net: dsa: motorcomm: Add LED support David Yang
3 siblings, 0 replies; 10+ messages in thread
From: David Yang @ 2026-06-18 20:26 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel
SMI operations is going to be used across different modules.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/Makefile | 1 +
drivers/net/dsa/motorcomm/chip.c | 207 +----------------------------
drivers/net/dsa/motorcomm/smi.c | 155 +++++++++++++++++++++
drivers/net/dsa/motorcomm/smi.h | 88 ++++++++++++
4 files changed, 245 insertions(+), 206 deletions(-)
create mode 100644 drivers/net/dsa/motorcomm/smi.c
create mode 100644 drivers/net/dsa/motorcomm/smi.h
diff --git a/drivers/net/dsa/motorcomm/Makefile b/drivers/net/dsa/motorcomm/Makefile
index bf99feb4c454..9fa24929007c 100644
--- a/drivers/net/dsa/motorcomm/Makefile
+++ b/drivers/net/dsa/motorcomm/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_NET_DSA_YT921X) += yt921x.o
yt921x-objs := chip.o
+yt921x-objs += smi.o
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index f070732845eb..6dee25b6754a 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -13,7 +13,6 @@
#include <linux/if_bridge.h>
#include <linux/if_hsr.h>
#include <linux/if_vlan.h>
-#include <linux/iopoll.h>
#include <linux/mdio.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -27,6 +26,7 @@
#include <net/pkt_cls.h>
#include "chip.h"
+#include "smi.h"
struct yt921x_mib_desc {
unsigned int size;
@@ -155,9 +155,6 @@ static const struct yt921x_info yt921x_infos[] = {
#define YT921X_VID_UNWARE 4095
-#define YT921X_POLL_SLEEP_US 10000
-#define YT921X_POLL_TIMEOUT_US 100000
-
/* The interval should be small enough to avoid overflow of 32bit MIBs.
*
* Until we can read MIBs from stats64 call directly (i.e. sleep
@@ -196,208 +193,6 @@ static u32 ethaddr_lo2_to_u32(const unsigned char *addr)
return (addr[4] << 8) | addr[5];
}
-static int yt921x_reg_read(struct yt921x_priv *priv, u32 reg, u32 *valp)
-{
- WARN_ON(!mutex_is_locked(&priv->reg_lock));
-
- return priv->reg_ops->read(priv->reg_ctx, reg, valp);
-}
-
-static int yt921x_reg_write(struct yt921x_priv *priv, u32 reg, u32 val)
-{
- WARN_ON(!mutex_is_locked(&priv->reg_lock));
-
- return priv->reg_ops->write(priv->reg_ctx, reg, val);
-}
-
-static int
-yt921x_reg_wait(struct yt921x_priv *priv, u32 reg, u32 mask, u32 *valp)
-{
- u32 val;
- int res;
- int ret;
-
- ret = read_poll_timeout(yt921x_reg_read, res,
- res || (val & mask) == *valp,
- YT921X_POLL_SLEEP_US, YT921X_POLL_TIMEOUT_US,
- false, priv, reg, &val);
- if (ret)
- return ret;
- if (res)
- return res;
-
- *valp = val;
- return 0;
-}
-
-static int
-yt921x_reg_update_bits(struct yt921x_priv *priv, u32 reg, u32 mask, u32 val)
-{
- int res;
- u32 v;
- u32 u;
-
- res = yt921x_reg_read(priv, reg, &v);
- if (res)
- return res;
-
- u = v;
- u &= ~mask;
- u |= val;
- if (u == v)
- return 0;
-
- return yt921x_reg_write(priv, reg, u);
-}
-
-static int yt921x_reg_set_bits(struct yt921x_priv *priv, u32 reg, u32 mask)
-{
- return yt921x_reg_update_bits(priv, reg, 0, mask);
-}
-
-static int yt921x_reg_clear_bits(struct yt921x_priv *priv, u32 reg, u32 mask)
-{
- return yt921x_reg_update_bits(priv, reg, mask, 0);
-}
-
-static int
-yt921x_reg_toggle_bits(struct yt921x_priv *priv, u32 reg, u32 mask, bool set)
-{
- return yt921x_reg_update_bits(priv, reg, mask, !set ? 0 : mask);
-}
-
-/* Some multi-word registers, like VLANn_CTRL, should be treated as a single
- * long register. More specifically, writes to parts of its words won't become
- * visible, until the last word is written.
- *
- * Here we require full read and write operations over these registers to
- * eliminate potential issues, although partial reads/writes are also possible.
- */
-
-static void update_ctrls_unaligned(u32 *lo, u32 *hi, u64 mask, u64 val)
-{
- *lo &= ~lower_32_bits(mask);
- *hi &= ~upper_32_bits(mask);
- *lo |= lower_32_bits(val);
- *hi |= upper_32_bits(val);
-}
-
-static int
-yt921x_regs_read(struct yt921x_priv *priv, u32 reg, u32 *vals,
- unsigned int num_regs)
-{
- int res;
-
- for (unsigned int i = 0; i < num_regs; i++) {
- res = yt921x_reg_read(priv, reg + 4 * i, &vals[i]);
- if (res)
- return res;
- }
-
- return 0;
-}
-
-static int
-yt921x_regs_write(struct yt921x_priv *priv, u32 reg, const u32 *vals,
- unsigned int num_regs)
-{
- int res;
-
- for (unsigned int i = 0; i < num_regs; i++) {
- res = yt921x_reg_write(priv, reg + 4 * i, vals[i]);
- if (res)
- return res;
- }
-
- return 0;
-}
-
-static int
-yt921x_regs_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,
- const u32 *vals, unsigned int num_regs)
-{
- bool changed = false;
- u32 vs[4];
- int res;
-
- BUILD_BUG_ON(num_regs > ARRAY_SIZE(vs));
-
- res = yt921x_regs_read(priv, reg, vs, num_regs);
- if (res)
- return res;
-
- for (unsigned int i = 0; i < num_regs; i++) {
- u32 u = vs[i];
-
- u &= ~masks[i];
- u |= vals[i];
- if (u != vs[i])
- changed = true;
-
- vs[i] = u;
- }
-
- if (!changed)
- return 0;
-
- return yt921x_regs_write(priv, reg, vs, num_regs);
-}
-
-static int
-yt921x_regs_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,
- unsigned int num_regs)
-{
- bool changed = false;
- u32 vs[4];
- int res;
-
- BUILD_BUG_ON(num_regs > ARRAY_SIZE(vs));
-
- res = yt921x_regs_read(priv, reg, vs, num_regs);
- if (res)
- return res;
-
- for (unsigned int i = 0; i < num_regs; i++) {
- u32 u = vs[i];
-
- u &= ~masks[i];
- if (u != vs[i])
- changed = true;
-
- vs[i] = u;
- }
-
- if (!changed)
- return 0;
-
- return yt921x_regs_write(priv, reg, vs, num_regs);
-}
-
-static int
-yt921x_reg64_write(struct yt921x_priv *priv, u32 reg, const u32 *vals)
-{
- return yt921x_regs_write(priv, reg, vals, 2);
-}
-
-static int
-yt921x_reg64_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,
- const u32 *vals)
-{
- return yt921x_regs_update_bits(priv, reg, masks, vals, 2);
-}
-
-static int
-yt921x_reg64_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks)
-{
- return yt921x_regs_clear_bits(priv, reg, masks, 2);
-}
-
-static int
-yt921x_reg96_write(struct yt921x_priv *priv, u32 reg, const u32 *vals)
-{
- return yt921x_regs_write(priv, reg, vals, 3);
-}
-
static int yt921x_reg_mdio_read(void *context, u32 reg, u32 *valp)
{
struct yt921x_reg_mdio *mdio = context;
diff --git a/drivers/net/dsa/motorcomm/smi.c b/drivers/net/dsa/motorcomm/smi.c
new file mode 100644
index 000000000000..93e6c0f7e562
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/smi.c
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 David Yang
+ */
+
+#include <linux/iopoll.h>
+
+#include "chip.h"
+#include "smi.h"
+
+#define YT921X_POLL_SLEEP_US 10000
+#define YT921X_POLL_TIMEOUT_US 100000
+
+int yt921x_reg_read(struct yt921x_priv *priv, u32 reg, u32 *valp)
+{
+ WARN_ON(!mutex_is_locked(&priv->reg_lock));
+
+ return priv->reg_ops->read(priv->reg_ctx, reg, valp);
+}
+
+int yt921x_reg_write(struct yt921x_priv *priv, u32 reg, u32 val)
+{
+ WARN_ON(!mutex_is_locked(&priv->reg_lock));
+
+ return priv->reg_ops->write(priv->reg_ctx, reg, val);
+}
+
+int yt921x_reg_wait(struct yt921x_priv *priv, u32 reg, u32 mask, u32 *valp)
+{
+ u32 val;
+ int res;
+ int ret;
+
+ ret = read_poll_timeout(yt921x_reg_read, res,
+ res || (val & mask) == *valp,
+ YT921X_POLL_SLEEP_US, YT921X_POLL_TIMEOUT_US,
+ false, priv, reg, &val);
+ if (ret)
+ return ret;
+ if (res)
+ return res;
+
+ *valp = val;
+ return 0;
+}
+
+int yt921x_reg_update_bits(struct yt921x_priv *priv, u32 reg, u32 mask, u32 val)
+{
+ int res;
+ u32 v;
+ u32 u;
+
+ res = yt921x_reg_read(priv, reg, &v);
+ if (res)
+ return res;
+
+ u = v;
+ u &= ~mask;
+ u |= val;
+ if (u == v)
+ return 0;
+
+ return yt921x_reg_write(priv, reg, u);
+}
+
+int
+yt921x_regs_read(struct yt921x_priv *priv, u32 reg, u32 *vals,
+ unsigned int num_regs)
+{
+ int res;
+
+ for (unsigned int i = 0; i < num_regs; i++) {
+ res = yt921x_reg_read(priv, reg + 4 * i, &vals[i]);
+ if (res)
+ return res;
+ }
+
+ return 0;
+}
+
+int
+yt921x_regs_write(struct yt921x_priv *priv, u32 reg, const u32 *vals,
+ unsigned int num_regs)
+{
+ int res;
+
+ for (unsigned int i = 0; i < num_regs; i++) {
+ res = yt921x_reg_write(priv, reg + 4 * i, vals[i]);
+ if (res)
+ return res;
+ }
+
+ return 0;
+}
+
+int
+yt921x_regs_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,
+ const u32 *vals, unsigned int num_regs)
+{
+ bool changed = false;
+ u32 vs[4];
+ int res;
+
+ WARN_ON_ONCE(num_regs > ARRAY_SIZE(vs));
+
+ res = yt921x_regs_read(priv, reg, vs, num_regs);
+ if (res)
+ return res;
+
+ for (unsigned int i = 0; i < num_regs; i++) {
+ u32 u = vs[i];
+
+ u &= ~masks[i];
+ u |= vals[i];
+ if (u != vs[i])
+ changed = true;
+
+ vs[i] = u;
+ }
+
+ if (!changed)
+ return 0;
+
+ return yt921x_regs_write(priv, reg, vs, num_regs);
+}
+
+int
+yt921x_regs_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,
+ unsigned int num_regs)
+{
+ bool changed = false;
+ u32 vs[4];
+ int res;
+
+ WARN_ON_ONCE(num_regs > ARRAY_SIZE(vs));
+
+ res = yt921x_regs_read(priv, reg, vs, num_regs);
+ if (res)
+ return res;
+
+ for (unsigned int i = 0; i < num_regs; i++) {
+ u32 u = vs[i];
+
+ u &= ~masks[i];
+ if (u != vs[i])
+ changed = true;
+
+ vs[i] = u;
+ }
+
+ if (!changed)
+ return 0;
+
+ return yt921x_regs_write(priv, reg, vs, num_regs);
+}
diff --git a/drivers/net/dsa/motorcomm/smi.h b/drivers/net/dsa/motorcomm/smi.h
new file mode 100644
index 000000000000..2e956065eb90
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/smi.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2026 David Yang
+ */
+
+#ifndef _YT_SMI_H
+#define _YT_SMI_H
+
+#include <linux/types.h>
+#include <linux/wordpart.h>
+
+struct yt921x_priv;
+
+int yt921x_reg_read(struct yt921x_priv *priv, u32 reg, u32 *valp);
+int yt921x_reg_write(struct yt921x_priv *priv, u32 reg, u32 val);
+int yt921x_reg_wait(struct yt921x_priv *priv, u32 reg, u32 mask, u32 *valp);
+int yt921x_reg_update_bits(struct yt921x_priv *priv, u32 reg, u32 mask,
+ u32 val);
+
+static inline int
+yt921x_reg_set_bits(struct yt921x_priv *priv, u32 reg, u32 mask)
+{
+ return yt921x_reg_update_bits(priv, reg, 0, mask);
+}
+
+static inline int
+yt921x_reg_clear_bits(struct yt921x_priv *priv, u32 reg, u32 mask)
+{
+ return yt921x_reg_update_bits(priv, reg, mask, 0);
+}
+
+static inline int
+yt921x_reg_toggle_bits(struct yt921x_priv *priv, u32 reg, u32 mask, bool set)
+{
+ return yt921x_reg_update_bits(priv, reg, mask, !set ? 0 : mask);
+}
+
+/* Some multi-word registers, like VLANn_CTRL, should be treated as a single
+ * long register. More specifically, writes to parts of its words won't become
+ * visible, until the last word is written.
+ *
+ * Here we require full read and write operations over these registers to
+ * eliminate potential issues, although partial reads/writes are also possible.
+ */
+
+static inline void update_ctrls_unaligned(u32 *lo, u32 *hi, u64 mask, u64 val)
+{
+ *lo &= ~lower_32_bits(mask);
+ *hi &= ~upper_32_bits(mask);
+ *lo |= lower_32_bits(val);
+ *hi |= upper_32_bits(val);
+}
+
+int yt921x_regs_read(struct yt921x_priv *priv, u32 reg, u32 *vals,
+ unsigned int num_regs);
+int yt921x_regs_write(struct yt921x_priv *priv, u32 reg, const u32 *vals,
+ unsigned int num_regs);
+int yt921x_regs_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,
+ const u32 *vals, unsigned int num_regs);
+int yt921x_regs_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,
+ unsigned int num_regs);
+
+static inline int
+yt921x_reg64_write(struct yt921x_priv *priv, u32 reg, const u32 *vals)
+{
+ return yt921x_regs_write(priv, reg, vals, 2);
+}
+
+static inline int
+yt921x_reg64_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,
+ const u32 *vals)
+{
+ return yt921x_regs_update_bits(priv, reg, masks, vals, 2);
+}
+
+static inline int
+yt921x_reg64_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks)
+{
+ return yt921x_regs_clear_bits(priv, reg, masks, 2);
+}
+
+static inline int
+yt921x_reg96_write(struct yt921x_priv *priv, u32 reg, const u32 *vals)
+{
+ return yt921x_regs_write(priv, reg, vals, 3);
+}
+
+#endif
--
2.53.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [RFC net-next 3/4] net: dsa: motorcomm: Dynamically allocate port structures
2026-06-18 20:26 [RFC net-next 0/4] net: dsa: motorcomm: Add LED support David Yang
2026-06-18 20:26 ` [RFC net-next 1/4] net: dsa: motorcomm: Move to subdirectory David Yang
2026-06-18 20:26 ` [RFC net-next 2/4] net: dsa: motorcomm: Split SMI module David Yang
@ 2026-06-18 20:26 ` David Yang
2026-06-19 6:06 ` Andrew Lunn
2026-06-18 20:26 ` [RFC net-next 4/4] net: dsa: motorcomm: Add LED support David Yang
3 siblings, 1 reply; 10+ messages in thread
From: David Yang @ 2026-06-18 20:26 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel
With support for LED introduced later, struct yt921x_priv will be 17k
which is not very good for a single kmalloc(). Convert the ports array
to a array of pointers to stop bloating the priv struct.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/chip.c | 95 ++++++++++++++++++++++++--------
drivers/net/dsa/motorcomm/chip.h | 3 +-
2 files changed, 75 insertions(+), 23 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index 6dee25b6754a..d44f7749de02 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -548,11 +548,14 @@ yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp)
/* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed before. */
static int yt921x_read_mib(struct yt921x_priv *priv, int port)
{
- struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_port *pp = priv->ports[port];
struct device *dev = to_device(priv);
struct yt921x_mib *mib = &pp->mib;
int res = 0;
+ if (!pp)
+ return -ENODEV;
+
/* Reading of yt921x_port::mib is not protected by a lock and it's vain
* to keep its consistency, since we have to read registers one by one
* and there is no way to make a snapshot of MIB stats.
@@ -609,9 +612,8 @@ static void yt921x_poll_mib(struct work_struct *work)
{
struct yt921x_port *pp = container_of_const(work, struct yt921x_port,
mib_read.work);
- struct yt921x_priv *priv = (void *)(pp - pp->index) -
- offsetof(struct yt921x_priv, ports);
unsigned long delay = YT921X_STATS_INTERVAL_JIFFIES;
+ struct yt921x_priv *priv = pp->priv;
int port = pp->index;
int res;
@@ -643,10 +645,13 @@ static void
yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
{
struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_port *pp = priv->ports[port];
struct yt921x_mib *mib = &pp->mib;
size_t j;
+ if (!pp)
+ return;
+
mutex_lock(&priv->reg_lock);
yt921x_read_mib(priv, port);
mutex_unlock(&priv->reg_lock);
@@ -685,9 +690,12 @@ yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,
struct ethtool_eth_mac_stats *mac_stats)
{
struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_port *pp = priv->ports[port];
struct yt921x_mib *mib = &pp->mib;
+ if (!pp)
+ return;
+
mutex_lock(&priv->reg_lock);
yt921x_read_mib(priv, port);
mutex_unlock(&priv->reg_lock);
@@ -721,9 +729,12 @@ yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
struct ethtool_eth_ctrl_stats *ctrl_stats)
{
struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_port *pp = priv->ports[port];
struct yt921x_mib *mib = &pp->mib;
+ if (!pp)
+ return;
+
mutex_lock(&priv->reg_lock);
yt921x_read_mib(priv, port);
mutex_unlock(&priv->reg_lock);
@@ -750,9 +761,12 @@ yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,
const struct ethtool_rmon_hist_range **ranges)
{
struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_port *pp = priv->ports[port];
struct yt921x_mib *mib = &pp->mib;
+ if (!pp)
+ return;
+
mutex_lock(&priv->reg_lock);
yt921x_read_mib(priv, port);
mutex_unlock(&priv->reg_lock);
@@ -786,9 +800,12 @@ yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,
struct rtnl_link_stats64 *stats)
{
struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_port *pp = priv->ports[port];
struct yt921x_mib *mib = &pp->mib;
+ if (!pp)
+ return;
+
stats->rx_length_errors = mib->rx_undersize_errors +
mib->rx_fragment_errors;
stats->rx_over_errors = mib->rx_oversize_errors;
@@ -822,9 +839,12 @@ yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port,
struct ethtool_pause_stats *pause_stats)
{
struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_port *pp = priv->ports[port];
struct yt921x_mib *mib = &pp->mib;
+ if (!pp)
+ return;
+
mutex_lock(&priv->reg_lock);
yt921x_read_mib(priv, port);
mutex_unlock(&priv->reg_lock);
@@ -3332,15 +3352,20 @@ static int yt921x_bridge(struct yt921x_priv *priv, u16 ports_mask)
isolated_mask = 0;
for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) {
- struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_port *pp = priv->ports[port];
+ if (!pp)
+ continue;
if (pp->isolated)
isolated_mask |= BIT(port);
}
/* Block from non-cpu bridge ports ... */
for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) {
- struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_port *pp = priv->ports[port];
+
+ if (!pp)
+ continue;
/* to non-bridge ports */
ctrl = ~ports_mask;
@@ -3397,11 +3422,14 @@ static int
yt921x_bridge_flags(struct yt921x_priv *priv, int port,
struct switchdev_brport_flags flags)
{
- struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_port *pp = priv->ports[port];
bool do_flush;
u32 mask;
int res;
+ if (!pp)
+ return -ENODEV;
+
if (flags.mask & BR_LEARNING) {
bool learning = flags.val & BR_LEARNING;
@@ -3954,11 +3982,16 @@ yt921x_phylink_mac_link_down(struct phylink_config *config, unsigned int mode,
{
struct dsa_port *dp = dsa_phylink_to_port(config);
struct yt921x_priv *priv = to_yt921x_priv(dp->ds);
+ struct yt921x_port *pp;
int port = dp->index;
int res;
+ pp = priv->ports[port];
+ if (!pp)
+ return;
+
/* No need to sync; port control block is hold until device remove */
- cancel_delayed_work(&priv->ports[port].mib_read);
+ cancel_delayed_work(&pp->mib_read);
mutex_lock(&priv->reg_lock);
res = yt921x_port_down(priv, port);
@@ -3977,9 +4010,14 @@ yt921x_phylink_mac_link_up(struct phylink_config *config,
{
struct dsa_port *dp = dsa_phylink_to_port(config);
struct yt921x_priv *priv = to_yt921x_priv(dp->ds);
+ struct yt921x_port *pp;
int port = dp->index;
int res;
+ pp = priv->ports[port];
+ if (!pp)
+ return;
+
mutex_lock(&priv->reg_lock);
res = yt921x_port_up(priv, port, mode, interface, speed, duplex,
tx_pause, rx_pause);
@@ -3989,7 +4027,7 @@ yt921x_phylink_mac_link_up(struct phylink_config *config,
dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring up",
port, res);
- schedule_delayed_work(&priv->ports[port].mib_read, 0);
+ schedule_delayed_work(&pp->mib_read, 0);
}
static void
@@ -4574,6 +4612,23 @@ static int yt921x_dsa_setup(struct dsa_switch *ds)
return -ENODEV;
}
+ for (int port = 0; port < YT921X_PORT_NUM; port++) {
+ struct yt921x_port *pp;
+
+ if (!(BIT(port) & (priv->info->internal_mask |
+ priv->info->external_mask)))
+ continue;
+
+ pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL);
+ if (!pp)
+ return -ENOMEM;
+ priv->ports[port] = pp;
+
+ pp->priv = priv;
+ pp->index = port;
+ INIT_DELAYED_WORK(&pp->mib_read, yt921x_poll_mib);
+ }
+
mutex_lock(&priv->reg_lock);
res = yt921x_chip_setup(priv);
mutex_unlock(&priv->reg_lock);
@@ -4682,7 +4737,10 @@ static void yt921x_mdio_remove(struct mdio_device *mdiodev)
return;
for (size_t i = ARRAY_SIZE(priv->ports); i-- > 0; ) {
- struct yt921x_port *pp = &priv->ports[i];
+ struct yt921x_port *pp = priv->ports[i];
+
+ if (!pp)
+ continue;
disable_delayed_work_sync(&pp->mib_read);
}
@@ -4730,13 +4788,6 @@ static int yt921x_mdio_probe(struct mdio_device *mdiodev)
priv->reg_ops = &yt921x_reg_ops_mdio;
priv->reg_ctx = mdio;
- for (size_t i = 0; i < ARRAY_SIZE(priv->ports); i++) {
- struct yt921x_port *pp = &priv->ports[i];
-
- pp->index = i;
- INIT_DELAYED_WORK(&pp->mib_read, yt921x_poll_mib);
- }
-
ds = &priv->ds;
ds->dev = dev;
ds->assisted_learning_on_cpu_port = true;
diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h
index 555046526669..950a5799f8b6 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -929,6 +929,7 @@ struct yt921x_acl_blk {
};
struct yt921x_port {
+ struct yt921x_priv *priv;
unsigned char index;
bool hairpin;
@@ -964,7 +965,7 @@ struct yt921x_priv {
struct mii_bus *mbus_int;
struct mii_bus *mbus_ext;
- struct yt921x_port ports[YT921X_PORT_NUM];
+ struct yt921x_port *ports[YT921X_PORT_NUM];
u16 eee_ports_mask;
--
2.53.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [RFC net-next 4/4] net: dsa: motorcomm: Add LED support
2026-06-18 20:26 [RFC net-next 0/4] net: dsa: motorcomm: Add LED support David Yang
` (2 preceding siblings ...)
2026-06-18 20:26 ` [RFC net-next 3/4] net: dsa: motorcomm: Dynamically allocate port structures David Yang
@ 2026-06-18 20:26 ` David Yang
3 siblings, 0 replies; 10+ messages in thread
From: David Yang @ 2026-06-18 20:26 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel
LEDs can be described in the device tree using the same format as qca8k.
Each port can configure up to 3 LEDs.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/Kconfig | 9 +
drivers/net/dsa/motorcomm/Makefile | 1 +
drivers/net/dsa/motorcomm/chip.c | 7 +-
drivers/net/dsa/motorcomm/chip.h | 18 +
drivers/net/dsa/motorcomm/leds.c | 530 +++++++++++++++++++++++++++++
drivers/net/dsa/motorcomm/leds.h | 104 ++++++
6 files changed, 667 insertions(+), 2 deletions(-)
create mode 100644 drivers/net/dsa/motorcomm/leds.c
create mode 100644 drivers/net/dsa/motorcomm/leds.h
diff --git a/drivers/net/dsa/motorcomm/Kconfig b/drivers/net/dsa/motorcomm/Kconfig
index 64ff7d07a91b..7c4d1eaa16c2 100644
--- a/drivers/net/dsa/motorcomm/Kconfig
+++ b/drivers/net/dsa/motorcomm/Kconfig
@@ -6,3 +6,12 @@ config NET_DSA_YT921X
help
This enables support for the Motorcomm YT9215 ethernet switch
chip.
+
+config NET_DSA_YT921X_LEDS
+ bool "LED support for Motorcomm YT9215"
+ default y
+ depends on NET_DSA_YT921X
+ depends on LEDS_CLASS=y || LEDS_CLASS=NET_DSA_YT921X
+ help
+ This enabled support for controlling the LEDs attached to the
+ Motorcomm YT9215 switch chips.
diff --git a/drivers/net/dsa/motorcomm/Makefile b/drivers/net/dsa/motorcomm/Makefile
index 9fa24929007c..6bb3adfbcc2d 100644
--- a/drivers/net/dsa/motorcomm/Makefile
+++ b/drivers/net/dsa/motorcomm/Makefile
@@ -2,3 +2,4 @@
obj-$(CONFIG_NET_DSA_YT921X) += yt921x.o
yt921x-objs := chip.o
yt921x-objs += smi.o
+yt921x-$(CONFIG_NET_DSA_YT921X_LEDS) += leds.o
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index d44f7749de02..4856db69e2ea 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -26,6 +26,7 @@
#include <net/pkt_cls.h>
#include "chip.h"
+#include "leds.h"
#include "smi.h"
struct yt921x_mib_desc {
@@ -151,8 +152,6 @@ static const struct yt921x_info yt921x_infos[] = {
{}
};
-#define YT921X_NAME "yt921x"
-
#define YT921X_VID_UNWARE 4095
/* The interval should be small enough to avoid overflow of 32bit MIBs.
@@ -4559,6 +4558,10 @@ static int yt921x_chip_setup(struct yt921x_priv *priv)
return res;
#endif
+ res = yt921x_led_setup(priv);
+ if (res)
+ return res;
+
/* Clear MIB */
ctrl = YT921X_MIB_CTRL_CLEAN | YT921X_MIB_CTRL_ALL_PORT;
res = yt921x_reg_write(priv, YT921X_MIB_CTRL, ctrl);
diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h
index 950a5799f8b6..ea889319d996 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -850,9 +850,13 @@ enum yt921x_fdb_entry_status {
#define YT921X_ACL_NUM (YT921X_ACL_BLK_NUM * YT921X_ACL_ENT_PER_BLK)
#define YT921X_UDF_NUM 8
+#define YT921X_LED_GROUP_NUM 3
+
/* 8 internal + 2 external + 1 mcu */
#define YT921X_PORT_NUM 11
+#define YT921X_NAME "yt921x"
+
#define yt921x_port_is_internal(port) ((port) < 8)
#define yt921x_port_is_external(port) (8 <= (port) && (port) < 9)
@@ -928,6 +932,14 @@ struct yt921x_acl_blk {
struct yt921x_acl_rule *rules[YT921X_ACL_ENT_PER_BLK];
};
+struct yt921x_led {
+ struct led_classdev cdev;
+ unsigned char group;
+
+ bool use_cycle;
+ bool use_duty;
+};
+
struct yt921x_port {
struct yt921x_priv *priv;
unsigned char index;
@@ -939,6 +951,12 @@ struct yt921x_port {
struct yt921x_mib mib;
u64 rx_frames;
u64 tx_frames;
+
+#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)
+ struct yt921x_led leds[YT921X_LED_GROUP_NUM];
+ unsigned int blink_cycle;
+ unsigned int blink_duty;
+#endif
};
struct yt921x_reg_ops {
diff --git a/drivers/net/dsa/motorcomm/leds.c b/drivers/net/dsa/motorcomm/leds.c
new file mode 100644
index 000000000000..49d657b38822
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/leds.c
@@ -0,0 +1,530 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 David Yang
+ */
+
+#include "chip.h"
+#include "leds.h"
+#include "smi.h"
+
+#define to_yt921x_led(led_cdev) \
+ container_of_const((led_cdev), struct yt921x_led, cdev)
+#define to_yt921x_port(led) \
+ ((void *)((led) - (led)->group) - offsetof(struct yt921x_port, leds))
+#define to_yt921x_priv(pp) ((pp)->priv)
+#define to_device(priv) ((priv)->ds.dev)
+
+static u32 yt921x_led_regaddr(struct yt921x_priv *priv, int port, int group)
+{
+ switch (group) {
+ case 0:
+ default:
+ return YT921X_LED0_PORTn(port);
+ case 1:
+ return YT921X_LED1_PORTn(port);
+ case 2:
+ return YT921X_LED2_PORTn(port);
+ }
+}
+
+static int
+yt921x_led_force_get(struct yt921x_priv *priv, int port, int group, bool *onp)
+{
+ u32 val;
+ int res;
+
+ res = yt921x_reg_read(priv, YT921X_LED2_PORTn(port), &val);
+ if (res)
+ return res;
+
+ *onp = (val & YT921X_LED2_PORT_FORCEn_M(group)) ==
+ YT921X_LED2_PORT_FORCEn_ON(group);
+ return 0;
+}
+
+static int
+yt921x_led_force_set(struct yt921x_priv *priv, int port, int group, bool on)
+{
+ struct yt921x_port *pp = priv->ports[port];
+ struct yt921x_led *led = &pp->leds[group];
+ u32 ctrl;
+ u32 mask;
+
+ if (!pp)
+ return -ENODEV;
+
+ led->use_cycle = false;
+ led->use_duty = false;
+
+ mask = YT921X_LED2_PORT_FORCEn_M(group);
+ ctrl = on ? YT921X_LED2_PORT_FORCEn_ON(group) :
+ YT921X_LED2_PORT_FORCEn_OFF(group);
+ return yt921x_reg_update_bits(priv, YT921X_LED2_PORTn(port), mask,
+ ctrl);
+}
+
+/* 2*lcm(2,3,4,6) */
+#define YT921X_LED_DUTY_DENOM 24
+#define YT921X_LED_DUTY(nom, denom) (YT921X_LED_DUTY_DENOM * (nom) / (denom))
+
+#define M_SQRT2 1.41421356237309504880
+
+static int
+yt921x_led_blink_select(const struct yt921x_priv *priv, unsigned long on,
+ unsigned long off, unsigned int *cyclep,
+ unsigned int *dutyp)
+{
+ unsigned int cycle_upper;
+ unsigned int cycle_req;
+ unsigned int cycle;
+ unsigned int duty;
+
+ if (!on && !off) {
+ *cyclep = YT921X_LED_BLINK_DEF;
+ *dutyp = YT921X_LED_DUTY(1, 2);
+ return 0;
+ }
+
+ cycle = YT921X_LED_BLINK_MAX;
+ cycle_upper = M_SQRT2 * YT921X_LED_BLINK_MAX + 1;
+ if (cycle_upper <= on + off)
+ return -EOPNOTSUPP;
+
+ cycle_req = on + off;
+ for (; cycle > YT921X_LED_BLINK_MIN; cycle_upper >>= 1, cycle >>= 1)
+ if (cycle_upper >> 1 <= cycle_req)
+ break;
+
+ duty = YT921X_LED_DUTY(on > off ? off : on, cycle_req);
+ if (duty < YT921X_LED_DUTY(5, 24))
+ duty = YT921X_LED_DUTY(1, 6);
+ else if (duty < YT921X_LED_DUTY(7, 24))
+ duty = YT921X_LED_DUTY(1, 4);
+ else if (duty < YT921X_LED_DUTY(5, 12))
+ duty = YT921X_LED_DUTY(1, 3);
+ else
+ duty = YT921X_LED_DUTY(1, 2);
+ if (on > off)
+ duty = YT921X_LED_DUTY_DENOM - duty;
+
+ *cyclep = cycle;
+ *dutyp = duty;
+ return 0;
+}
+
+static int
+yt921x_led_blink_set(struct yt921x_priv *priv, int port, int group,
+ unsigned long *onp, unsigned long *offp)
+{
+ struct yt921x_port *pp = priv->ports[port];
+ struct yt921x_led *led = &pp->leds[group];
+ unsigned int cycle;
+ unsigned int duty;
+ bool change_cycle;
+ bool change_duty;
+ bool use_cycle;
+ u32 ctrl;
+ u32 mask;
+ u32 val;
+ int res;
+
+ if (!pp)
+ return -ENODEV;
+
+ res = yt921x_led_blink_select(priv, *onp, *offp, &cycle, &duty);
+ if (res)
+ return res;
+
+ use_cycle = cycle < YT921X_LED_BLINK_DEF;
+ change_cycle = use_cycle && cycle != pp->blink_cycle;
+ change_duty = duty != pp->blink_duty;
+ if (change_cycle || change_duty)
+ for (unsigned int i = 0; i < YT921X_LED_GROUP_NUM; i++) {
+ if (i == group)
+ continue;
+ if ((change_cycle && pp->leds[i].use_cycle) ||
+ (change_duty && pp->leds[i].use_duty))
+ return -EOPNOTSUPP;
+ }
+
+ mask = YT921X_LED1_PORT_BLINK_DUTY_M | YT921X_LED1_PORT_BLINK_DUTY_COMP;
+ switch (duty >= YT921X_LED_DUTY(1, 2) ? duty :
+ YT921X_LED_DUTY_DENOM - duty) {
+ default:
+ duty = YT921X_LED_DUTY(1, 2);
+ fallthrough;
+ case YT921X_LED_DUTY(1, 2):
+ ctrl = YT921X_LED1_PORT_BLINK_DUTY_1_2;
+ break;
+ case YT921X_LED_DUTY(2, 3):
+ ctrl = YT921X_LED1_PORT_BLINK_DUTY_2_3;
+ break;
+ case YT921X_LED_DUTY(3, 4):
+ ctrl = YT921X_LED1_PORT_BLINK_DUTY_3_4;
+ break;
+ case YT921X_LED_DUTY(5, 6):
+ ctrl = YT921X_LED1_PORT_BLINK_DUTY_5_6;
+ break;
+ }
+ if (duty < YT921X_LED_DUTY(1, 2))
+ ctrl |= YT921X_LED1_PORT_BLINK_DUTY_COMP;
+ if (use_cycle) {
+ mask |= YT921X_LED1_PORT_OTHER_BLINK_M;
+ ctrl |= YT921X_LED1_PORT_OTHER_BLINK(9 - __fls(cycle));
+ }
+ res = yt921x_reg_update_bits(priv, YT921X_LED1_PORTn(port), mask, ctrl);
+ if (res)
+ return res;
+
+ res = yt921x_reg_read(priv, YT921X_LED2_PORTn(port), &val);
+ if (res)
+ return res;
+
+ /* The chip seems to jam a while if changing duty only */
+ ctrl = val & ~YT921X_LED2_PORT_FORCEn_M(group);
+ ctrl |= YT921X_LED2_PORT_FORCEn_OFF(group);
+ if (ctrl != val) {
+ res = yt921x_reg_write(priv, YT921X_LED2_PORTn(port), ctrl);
+ if (res)
+ return res;
+ }
+
+ ctrl = val & ~(YT921X_LED2_PORT_FORCEn_M(group) |
+ YT921X_LED2_PORT_FORCE_BLINKn_M(group));
+ ctrl |= YT921X_LED2_PORT_FORCEn_BLINK(group);
+ if (use_cycle)
+ ctrl |= YT921X_LED2_PORT_FORCE_BLINKn_OTHER(group);
+ else
+ ctrl |= YT921X_LED2_PORT_FORCE_BLINKn(group, __fls(cycle) - 9);
+ res = yt921x_reg_write(priv, YT921X_LED2_PORTn(port), ctrl);
+ if (res)
+ return res;
+
+ if (use_cycle) {
+ led->use_cycle = true;
+ pp->blink_cycle = cycle;
+ }
+ led->use_duty = true;
+ pp->blink_duty = duty;
+
+ *onp = duty * cycle / YT921X_LED_DUTY_DENOM;
+ *offp = cycle - *onp;
+ return 0;
+}
+
+static u32 yt921x_led_trigger_maps[__TRIGGER_NETDEV_MAX] = {
+ [TRIGGER_NETDEV_LINK] = YT921X_LEDx_PORT_ACT_ACTIVE,
+ [TRIGGER_NETDEV_LINK_10] = YT921X_LEDx_PORT_ACT_10M,
+ [TRIGGER_NETDEV_LINK_100] = YT921X_LEDx_PORT_ACT_100M,
+ [TRIGGER_NETDEV_LINK_1000] = YT921X_LEDx_PORT_ACT_1000M,
+ [TRIGGER_NETDEV_HALF_DUPLEX] = YT921X_LEDx_PORT_ACT_DUPLEX_HALF,
+ [TRIGGER_NETDEV_FULL_DUPLEX] = YT921X_LEDx_PORT_ACT_DUPLEX_FULL,
+ [TRIGGER_NETDEV_TX] = YT921X_LEDx_PORT_ACT_TX,
+ [TRIGGER_NETDEV_RX] = YT921X_LEDx_PORT_ACT_RX,
+};
+
+static bool yt921x_led_trigger_is_supported(int group, unsigned long flags)
+{
+ unsigned int i;
+
+ for_each_set_bit(i, &flags, __TRIGGER_NETDEV_MAX)
+ if (!yt921x_led_trigger_maps[i])
+ return false;
+
+ return true;
+}
+
+static int
+yt921x_led_trigger_get(struct yt921x_priv *priv, int port, int group,
+ unsigned long *flagsp)
+{
+ u32 addr = yt921x_led_regaddr(priv, port, group);
+ u32 val;
+ int res;
+
+ res = yt921x_reg_read(priv, addr, &val);
+ if (res)
+ return res;
+
+ *flagsp = 0;
+ for (unsigned int i = 0; i < __TRIGGER_NETDEV_MAX; i++)
+ if (val & yt921x_led_trigger_maps[i])
+ *flagsp |= BIT(i);
+
+ return 0;
+}
+
+static int
+yt921x_led_trigger_set(struct yt921x_priv *priv, int port, int group,
+ unsigned long flags)
+{
+ struct yt921x_port *pp = priv->ports[port];
+ struct yt921x_led *led = &pp->leds[group];
+ unsigned int i;
+ u32 addr;
+ u32 ctrl;
+ u32 mask;
+ int res;
+
+ if (!pp)
+ return -ENODEV;
+
+ ctrl = 0;
+ for_each_set_bit(i, &flags, __TRIGGER_NETDEV_MAX) {
+ if (!yt921x_led_trigger_maps[i])
+ return -EOPNOTSUPP;
+
+ ctrl |= yt921x_led_trigger_maps[i];
+ }
+
+ led->use_cycle = false;
+ led->use_duty = false;
+
+ mask = !group ? YT921X_LED0_PORT_ACT_M : YT921X_LEDx_PORT_ACT_M;
+ if (group == 2) {
+ mask |= YT921X_LED2_PORT_FORCEn_M(group);
+ ctrl |= YT921X_LED2_PORT_FORCEn_DONTCARE(group);
+ }
+ addr = yt921x_led_regaddr(priv, port, group);
+ res = yt921x_reg_update_bits(priv, addr, mask, ctrl);
+ if (res)
+ return res;
+
+ if (group != 2) {
+ mask = YT921X_LED2_PORT_FORCEn_M(group);
+ ctrl = YT921X_LED2_PORT_FORCEn_DONTCARE(group);
+ res = yt921x_reg_update_bits(priv, YT921X_LED2_PORTn(port),
+ mask, ctrl);
+ if (res)
+ return res;
+ }
+
+ return 0;
+}
+
+static enum led_brightness
+yt921x_cled_brightness_get(struct led_classdev *led_cdev)
+{
+ struct yt921x_led *led = to_yt921x_led(led_cdev);
+ struct yt921x_port *pp = to_yt921x_port(led);
+ struct yt921x_priv *priv = to_yt921x_priv(pp);
+ bool on = false;
+
+ mutex_lock(&priv->reg_lock);
+ yt921x_led_force_get(priv, pp->index, led->group, &on);
+ mutex_unlock(&priv->reg_lock);
+
+ return on ? LED_ON : LED_OFF;
+}
+
+static int
+yt921x_cled_brightness_set_blocking(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct yt921x_led *led = to_yt921x_led(led_cdev);
+ struct yt921x_port *pp = to_yt921x_port(led);
+ struct yt921x_priv *priv = to_yt921x_priv(pp);
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_led_force_set(priv, pp->index, led->group, brightness);
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static int
+yt921x_cled_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ struct yt921x_led *led = to_yt921x_led(led_cdev);
+ struct yt921x_port *pp = to_yt921x_port(led);
+ struct yt921x_priv *priv = to_yt921x_priv(pp);
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_led_blink_set(priv, pp->index, led->group, delay_on,
+ delay_off);
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static struct device * __maybe_unused
+yt921x_cled_hw_control_get_device(struct led_classdev *led_cdev)
+{
+ struct yt921x_led *led = to_yt921x_led(led_cdev);
+ struct yt921x_port *pp = to_yt921x_port(led);
+ struct yt921x_priv *priv = to_yt921x_priv(pp);
+ struct dsa_port *dp;
+
+ dp = dsa_to_port(&priv->ds, pp->index);
+ if (!dp || !dp->user)
+ return NULL;
+ return &dp->user->dev;
+}
+
+static int __maybe_unused
+yt921x_cled_hw_control_is_supported(struct led_classdev *led_cdev,
+ unsigned long flags)
+{
+ struct yt921x_led *led = to_yt921x_led(led_cdev);
+
+ return yt921x_led_trigger_is_supported(led->group, flags) ? 0 :
+ -EOPNOTSUPP;
+}
+
+static int __maybe_unused
+yt921x_cled_hw_control_get(struct led_classdev *led_cdev, unsigned long *flagsp)
+{
+ struct yt921x_led *led = to_yt921x_led(led_cdev);
+ struct yt921x_port *pp = to_yt921x_port(led);
+ struct yt921x_priv *priv = to_yt921x_priv(pp);
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_led_trigger_get(priv, pp->index, led->group, flagsp);
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static int __maybe_unused
+yt921x_cled_hw_control_set(struct led_classdev *led_cdev, unsigned long flags)
+{
+ struct yt921x_led *led = to_yt921x_led(led_cdev);
+ struct yt921x_port *pp = to_yt921x_port(led);
+ struct yt921x_priv *priv = to_yt921x_priv(pp);
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_led_trigger_set(priv, pp->index, led->group, flags);
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static int
+yt921x_led_setup_port(struct yt921x_priv *priv, int port,
+ struct fwnode_handle *fwnode, u32 *invp)
+{
+ struct yt921x_port *pp = priv->ports[port];
+ struct device *dev = to_device(priv);
+ struct led_init_data init_data = {};
+ struct led_classdev *led_cdev;
+ enum led_default_state state;
+ struct yt921x_led *led;
+ char name[64];
+ u32 group;
+ int res;
+
+ if (!pp)
+ return -ENODEV;
+
+ res = fwnode_property_read_u32(fwnode, "reg", &group);
+ if (res)
+ return res;
+
+ if (group >= YT921X_LED_GROUP_NUM) {
+ dev_warn(dev, "Invalid LED reg %d defined for port %d", group,
+ port);
+ return -EINVAL;
+ }
+
+ led = &pp->leds[group];
+ led->group = group;
+
+ led_cdev = &led->cdev;
+ state = led_init_default_state_get(fwnode);
+ switch (state) {
+ case LEDS_DEFSTATE_OFF:
+ case LEDS_DEFSTATE_ON:
+ res = yt921x_led_force_set(priv, port, group, state);
+ if (res)
+ return res;
+ led_cdev->brightness = state;
+ break;
+ case LEDS_DEFSTATE_KEEP: {
+ bool on;
+
+ res = yt921x_led_force_get(priv, port, group, &on);
+ if (res)
+ return res;
+ led_cdev->brightness = on ? LED_ON : LED_OFF;
+ break;
+ }
+ }
+ led_cdev->max_brightness = 1;
+ led_cdev->flags = LED_RETAIN_AT_SHUTDOWN;
+ led_cdev->brightness_get = yt921x_cled_brightness_get;
+ led_cdev->brightness_set_blocking = yt921x_cled_brightness_set_blocking;
+ led_cdev->blink_set = yt921x_cled_blink_set;
+#ifdef CONFIG_LEDS_TRIGGERS
+ led_cdev->hw_control_trigger = "netdev";
+ led_cdev->hw_control_get_device = yt921x_cled_hw_control_get_device;
+ led_cdev->hw_control_is_supported = yt921x_cled_hw_control_is_supported;
+ led_cdev->hw_control_get = yt921x_cled_hw_control_get;
+ led_cdev->hw_control_set = yt921x_cled_hw_control_set;
+#endif
+
+ init_data.fwnode = fwnode;
+ snprintf(name, sizeof(name), YT921X_NAME "-%d:%02d:%d", priv->ds.index,
+ port, group);
+ init_data.devicename = name;
+ init_data.devname_mandatory = true;
+
+ res = devm_led_classdev_register_ext(dev, led_cdev, &init_data);
+ if (res) {
+ dev_warn(dev, "Failed to init LED %d for port %d", group, port);
+ return res;
+ }
+
+ return 0;
+}
+
+int yt921x_led_setup(struct yt921x_priv *priv)
+{
+ struct dsa_switch *ds = &priv->ds;
+ struct dsa_port *dp;
+ u32 mask;
+ u32 ctrl;
+ int res;
+
+ mask = YT921X_LED_CTRL_MODE_M | YT921X_LED_CTRL_PORT_NUM_M |
+ YT921X_LED_CTRL_EN;
+ ctrl = YT921X_LED_CTRL_MODE_PARALLEL | YT921X_LED_CTRL_PORT_NUM_M |
+ YT921X_LED_CTRL_EN;
+ res = yt921x_reg_update_bits(priv, YT921X_LED_CTRL, mask, ctrl);
+ if (res)
+ return res;
+
+ ctrl = 0;
+ dsa_switch_for_each_port(dp, ds) {
+ struct device_node *leds_np;
+
+ if (!dp->dn)
+ continue;
+
+ leds_np = of_get_child_by_name(dp->dn, "leds");
+ if (!leds_np)
+ continue;
+
+ for_each_child_of_node_scoped(leds_np, led_np) {
+ res = yt921x_led_setup_port(priv, dp->index,
+ of_fwnode_handle(led_np),
+ &ctrl);
+ if (res)
+ break;
+ }
+
+ of_node_put(leds_np);
+ if (res)
+ return res;
+ }
+
+ res = yt921x_reg_write(priv, YT921X_LED_PAR_INV, ctrl);
+ if (res)
+ return res;
+
+ return 0;
+}
diff --git a/drivers/net/dsa/motorcomm/leds.h b/drivers/net/dsa/motorcomm/leds.h
new file mode 100644
index 000000000000..265d5ea5f04e
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/leds.h
@@ -0,0 +1,104 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2026 David Yang
+ */
+
+#ifndef _YT_LEDS_H
+#define _YT_LEDS_H
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/kconfig.h>
+
+#define YT921X_LED_CTRL 0xd0000
+#define YT921X_LED_CTRL_EN BIT(21)
+#define YT921X_LED_CTRL_LOOPDETECT_BLINK_M GENMASK(20, 19) /* cycle = 512 * x ms */
+#define YT921X_LED_CTRL_LOOPDETECT_BLINK(x) FIELD_PREP(YT921X_LED_CTRL_LOOPDETECT_BLINK_M, (x))
+#define YT921X_LED_CTRL_PORT_NUM_M GENMASK(16, 13)
+#define YT921X_LED_CTRL_PORT_NUM(x) FIELD_PREP(YT921X_LED_CTRL_PORT_NUM_M, (x))
+#define YT921X_LED_CTRL_MODE_M GENMASK(1, 0)
+#define YT921X_LED_CTRL_MODE(x) FIELD_PREP(YT921X_LED_CTRL_MODE_M, (x))
+#define YT921X_LED_CTRL_MODE_PARALLEL YT921X_LED_CTRL_MODE(0)
+#define YT921X_LED_CTRL_MODE_SERIAL YT921X_LED_CTRL_MODE(2)
+#define YT921X_LED0_PORTn(port) (0xd0004 + 4 * (port))
+#define YT921X_LED0_PORT_ACT_M GENMASK(17, 0)
+#define YT921X_LED0_PORT_ACT_LINK_TRY_DIS BIT(17)
+#define YT921X_LED0_PORT_ACT_COLLISION_BLINK BIT(16)
+#define YT921X_LED1_PORTn(port) (0xd0040 + 4 * (port))
+#define YT921X_LED1_PORT_OTHER_BLINK_M GENMASK(31, 30) /* cycle = 512 >> x ms */
+#define YT921X_LED1_PORT_OTHER_BLINK(x) FIELD_PREP(YT921X_LED1_PORT_OTHER_BLINK_M, (x))
+#define YT921X_LED1_PORT_EEE_BLINK_M GENMASK(29, 28) /* cycle = 512 >> x ms */
+#define YT921X_LED1_PORT_EEE_BLINK(x) FIELD_PREP(YT921X_LED1_PORT_EEE_BLINK_M, (x))
+#define YT921X_LED1_PORT_BLINK_DUTY_COMP BIT(27)
+#define YT921X_LED1_PORT_BLINK_DUTY_M GENMASK(26, 25)
+#define YT921X_LED1_PORT_BLINK_DUTY(x) FIELD_PREP(YT921X_LED1_PORT_BLINK_DUTY_M, (x))
+#define YT921X_LED1_PORT_BLINK_DUTY_1_2 YT921X_LED1_PORT_BLINK_DUTY(0)
+#define YT921X_LED1_PORT_BLINK_DUTY_2_3 YT921X_LED1_PORT_BLINK_DUTY(1)
+#define YT921X_LED1_PORT_BLINK_DUTY_3_4 YT921X_LED1_PORT_BLINK_DUTY(2)
+#define YT921X_LED1_PORT_BLINK_DUTY_5_6 YT921X_LED1_PORT_BLINK_DUTY(3)
+#define YT921X_LED2_PORTn(port) (0xd0080 + 4 * (port))
+#define YT921X_LED2_PORT_FORCEn_M(grp) GENMASK(4 * (grp) + 19, 4 * (grp) + 18)
+#define YT921X_LED2_PORT_FORCEn(grp, x) ((x) << (4 * (grp) + 18))
+#define YT921X_LED2_PORT_FORCEn_DONTCARE(grp) YT921X_LED2_PORT_FORCEn(grp, 0)
+#define YT921X_LED2_PORT_FORCEn_BLINK(grp) YT921X_LED2_PORT_FORCEn(grp, 1)
+#define YT921X_LED2_PORT_FORCEn_ON(grp) YT921X_LED2_PORT_FORCEn(grp, 2)
+#define YT921X_LED2_PORT_FORCEn_OFF(grp) YT921X_LED2_PORT_FORCEn(grp, 3)
+#define YT921X_LED2_PORT_FORCE_BLINKn_M(grp) GENMASK(4 * (grp) + 17, 4 * (grp) + 16) /* cycle = 512 << x ms */
+#define YT921X_LED2_PORT_FORCE_BLINKn(grp, x) ((x) << (4 * (grp) + 16))
+#define YT921X_LED2_PORT_FORCE_BLINKn_OTHER(grp) YT921X_LED2_PORT_FORCE_BLINKn(grp, 3)
+#define YT921X_LEDx_PORT_ACT_M GENMASK(16, 0)
+#define YT921X_LEDx_PORT_ACT_EEE BIT(15)
+#define YT921X_LEDx_PORT_ACT_LOOPDETECT BIT(14)
+#define YT921X_LEDx_PORT_ACT_ACTIVE BIT(13)
+#define YT921X_LEDx_PORT_ACT_DUPLEX_FULL BIT(12)
+#define YT921X_LEDx_PORT_ACT_DUPLEX_HALF BIT(11)
+#define YT921X_LEDx_PORT_ACT_TX_BLINK BIT(10)
+#define YT921X_LEDx_PORT_ACT_RX_BLINK BIT(9)
+#define YT921X_LEDx_PORT_ACT_TX BIT(8)
+#define YT921X_LEDx_PORT_ACT_RX BIT(7)
+#define YT921X_LEDx_PORT_ACT_1000M BIT(6)
+#define YT921X_LEDx_PORT_ACT_100M BIT(5)
+#define YT921X_LEDx_PORT_ACT_10M BIT(4)
+#define YT921X_LEDx_PORT_ACT_COLLISION_BLINK_EN BIT(3)
+#define YT921X_LEDx_PORT_ACT_1000M_BLINK BIT(2)
+#define YT921X_LEDx_PORT_ACT_100M_BLINK BIT(1)
+#define YT921X_LEDx_PORT_ACT_10M_BLINK BIT(0)
+#define YT921X_LED_SER_CTRL 0xd0100
+#define YT921X_LED_SER_CTRL_EN GENMASK(25, 24)
+#define YT921X_LED_SER_CTRL_ACTIVE_LOW BIT(4)
+#define YT921X_LED_SER_CTRL_LED_NUM_M GENMASK(1, 0) /* #led - 1 */
+#define YT921X_LED_SER_CTRL_LED_NUM(x) FIELD_PREP(YT921X_LED_SER_CTRL_LED_NUM_M, (x))
+#define YT921X_LED_SER_MAPnm(grp, port) (0xd0104 + 8 * (2 - (grp)) + 4 * ((port) / 5))
+#define YT921X_LED_SER_MAP_DSTn_PORT_M(port) GENMASK(6 * ((port) % 5) + 5, 6 * ((port) % 5) + 2)
+#define YT921X_LED_SER_MAP_DSTn_PORT(port, x) ((x) << (6 * ((port) % 5) + 2))
+#define YT921X_LED_SER_MAP_DSTn_LED_M(port) GENMASK(6 * ((port) % 5) + 1, 6 * ((port) % 5))
+#define YT921X_LED_SER_MAP_DSTn_LED(port, x) ((x) << (6 * ((port) % 5)))
+#define YT921X_LED_PAR_PORTS 0xd01c4
+#define YT921X_LED_PAR_INV 0xd01c8
+#define YT921X_LED_PAR_INV_INVnm(grp, port) BIT(10 * (grp) + (port))
+#define YT921X_LED_PAR_MAPn(port) (0xd01d0 + 4 * (port))
+#define YT921X_LED_PAR_MAP_DSTn_PORT_M(grp) GENMASK(6 * (grp) + 5, 6 * (grp) + 2)
+#define YT921X_LED_PAR_MAP_DSTn_PORT(grp, x) ((x) << (6 * (grp) + 2))
+#define YT921X_LED_PAR_MAP_DSTn_LED_M(grp) GENMASK(6 * (grp) + 1, 6 * (grp))
+#define YT921X_LED_PAR_MAP_DSTn_LED(grp, x) ((x) << (6 * (grp)))
+
+#define YT921X_LED_BLINK_MIN 64
+#define YT921X_LED_BLINK_DEF 512
+#define YT921X_LED_BLINK_MAX 2048
+
+struct yt921x_priv;
+
+#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)
+
+int yt921x_led_setup(struct yt921x_priv *priv);
+
+#else
+
+static inline int yt921x_led_setup(struct yt921x_priv *priv)
+{
+ return 0;
+}
+
+#endif
+
+#endif
--
2.53.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [RFC net-next 3/4] net: dsa: motorcomm: Dynamically allocate port structures
2026-06-18 20:26 ` [RFC net-next 3/4] net: dsa: motorcomm: Dynamically allocate port structures David Yang
@ 2026-06-19 6:06 ` Andrew Lunn
2026-06-19 6:46 ` David Yang
0 siblings, 1 reply; 10+ messages in thread
From: Andrew Lunn @ 2026-06-19 6:06 UTC (permalink / raw)
To: David Yang
Cc: netdev, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, linux-kernel
On Fri, Jun 19, 2026 at 04:26:31AM +0800, David Yang wrote:
> With support for LED introduced later, struct yt921x_priv will be 17k
> which is not very good for a single kmalloc(). Convert the ports array
> to a array of pointers to stop bloating the priv struct.
>
> Signed-off-by: David Yang <mmyangfl@gmail.com>
> ---
> drivers/net/dsa/motorcomm/chip.c | 95 ++++++++++++++++++++++++--------
> drivers/net/dsa/motorcomm/chip.h | 3 +-
> 2 files changed, 75 insertions(+), 23 deletions(-)
>
> diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
> index 6dee25b6754a..d44f7749de02 100644
> --- a/drivers/net/dsa/motorcomm/chip.c
> +++ b/drivers/net/dsa/motorcomm/chip.c
> @@ -548,11 +548,14 @@ yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp)
> /* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed before. */
> static int yt921x_read_mib(struct yt921x_priv *priv, int port)
> {
> - struct yt921x_port *pp = &priv->ports[port];
> + struct yt921x_port *pp = priv->ports[port];
> struct device *dev = to_device(priv);
> struct yt921x_mib *mib = &pp->mib;
> int res = 0;
>
> + if (!pp)
> + return -ENODEV;
> +
Are all these tests actually needed? If you cannot allocate the
memory, i would expect the probe to fail, so you can never get here.
Andrew
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [RFC net-next 3/4] net: dsa: motorcomm: Dynamically allocate port structures
2026-06-19 6:06 ` Andrew Lunn
@ 2026-06-19 6:46 ` David Yang
2026-06-20 8:03 ` Andrew Lunn
0 siblings, 1 reply; 10+ messages in thread
From: David Yang @ 2026-06-19 6:46 UTC (permalink / raw)
To: Andrew Lunn
Cc: netdev, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, linux-kernel
On Fri, Jun 19, 2026 at 2:06 PM Andrew Lunn <andrew@lunn.ch> wrote:
>
> On Fri, Jun 19, 2026 at 04:26:31AM +0800, David Yang wrote:
> > With support for LED introduced later, struct yt921x_priv will be 17k
> > which is not very good for a single kmalloc(). Convert the ports array
> > to a array of pointers to stop bloating the priv struct.
> >
> > Signed-off-by: David Yang <mmyangfl@gmail.com>
> > ---
> > drivers/net/dsa/motorcomm/chip.c | 95 ++++++++++++++++++++++++--------
> > drivers/net/dsa/motorcomm/chip.h | 3 +-
> > 2 files changed, 75 insertions(+), 23 deletions(-)
> >
> > diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
> > index 6dee25b6754a..d44f7749de02 100644
> > --- a/drivers/net/dsa/motorcomm/chip.c
> > +++ b/drivers/net/dsa/motorcomm/chip.c
> > @@ -548,11 +548,14 @@ yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp)
> > /* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed before. */
> > static int yt921x_read_mib(struct yt921x_priv *priv, int port)
> > {
> > - struct yt921x_port *pp = &priv->ports[port];
> > + struct yt921x_port *pp = priv->ports[port];
> > struct device *dev = to_device(priv);
> > struct yt921x_mib *mib = &pp->mib;
> > int res = 0;
> >
> > + if (!pp)
> > + return -ENODEV;
> > +
>
> Are all these tests actually needed? If you cannot allocate the
> memory, i would expect the probe to fail, so you can never get here.
>
> Andrew
Dummy ports are no longer assigned control blocks (in yt921x_dsa_setup).
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [RFC net-next 3/4] net: dsa: motorcomm: Dynamically allocate port structures
2026-06-19 6:46 ` David Yang
@ 2026-06-20 8:03 ` Andrew Lunn
2026-06-20 16:13 ` David Yang
0 siblings, 1 reply; 10+ messages in thread
From: Andrew Lunn @ 2026-06-20 8:03 UTC (permalink / raw)
To: David Yang
Cc: netdev, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, linux-kernel
On Fri, Jun 19, 2026 at 02:46:24PM +0800, David Yang wrote:
> On Fri, Jun 19, 2026 at 2:06 PM Andrew Lunn <andrew@lunn.ch> wrote:
> >
> > On Fri, Jun 19, 2026 at 04:26:31AM +0800, David Yang wrote:
> > > With support for LED introduced later, struct yt921x_priv will be 17k
> > > which is not very good for a single kmalloc(). Convert the ports array
> > > to a array of pointers to stop bloating the priv struct.
> > >
> > > Signed-off-by: David Yang <mmyangfl@gmail.com>
> > > ---
> > > drivers/net/dsa/motorcomm/chip.c | 95 ++++++++++++++++++++++++--------
> > > drivers/net/dsa/motorcomm/chip.h | 3 +-
> > > 2 files changed, 75 insertions(+), 23 deletions(-)
> > >
> > > diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
> > > index 6dee25b6754a..d44f7749de02 100644
> > > --- a/drivers/net/dsa/motorcomm/chip.c
> > > +++ b/drivers/net/dsa/motorcomm/chip.c
> > > @@ -548,11 +548,14 @@ yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp)
> > > /* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed before. */
> > > static int yt921x_read_mib(struct yt921x_priv *priv, int port)
> > > {
> > > - struct yt921x_port *pp = &priv->ports[port];
> > > + struct yt921x_port *pp = priv->ports[port];
> > > struct device *dev = to_device(priv);
> > > struct yt921x_mib *mib = &pp->mib;
> > > int res = 0;
> > >
> > > + if (!pp)
> > > + return -ENODEV;
> > > +
> >
> > Are all these tests actually needed? If you cannot allocate the
> > memory, i would expect the probe to fail, so you can never get here.
> >
> > Andrew
>
> Dummy ports are no longer assigned control blocks (in yt921x_dsa_setup).
This seems pretty error prone. A missing check will result in an
opps. At least it will be obvious. How big is each port structure? Is
the memory saving worth it?
Andrew
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [RFC net-next 3/4] net: dsa: motorcomm: Dynamically allocate port structures
2026-06-20 8:03 ` Andrew Lunn
@ 2026-06-20 16:13 ` David Yang
2026-06-21 9:06 ` Andrew Lunn
0 siblings, 1 reply; 10+ messages in thread
From: David Yang @ 2026-06-20 16:13 UTC (permalink / raw)
To: Andrew Lunn
Cc: netdev, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, linux-kernel
On Sat, Jun 20, 2026 at 4:03 PM Andrew Lunn <andrew@lunn.ch> wrote:
>
> On Fri, Jun 19, 2026 at 02:46:24PM +0800, David Yang wrote:
> > On Fri, Jun 19, 2026 at 2:06 PM Andrew Lunn <andrew@lunn.ch> wrote:
> > >
> > > On Fri, Jun 19, 2026 at 04:26:31AM +0800, David Yang wrote:
> > > > With support for LED introduced later, struct yt921x_priv will be 17k
> > > > which is not very good for a single kmalloc(). Convert the ports array
> > > > to a array of pointers to stop bloating the priv struct.
> > > >
> > > > Signed-off-by: David Yang <mmyangfl@gmail.com>
> > > > ---
> > > > drivers/net/dsa/motorcomm/chip.c | 95 ++++++++++++++++++++++++--------
> > > > drivers/net/dsa/motorcomm/chip.h | 3 +-
> > > > 2 files changed, 75 insertions(+), 23 deletions(-)
> > > >
> > > > diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
> > > > index 6dee25b6754a..d44f7749de02 100644
> > > > --- a/drivers/net/dsa/motorcomm/chip.c
> > > > +++ b/drivers/net/dsa/motorcomm/chip.c
> > > > @@ -548,11 +548,14 @@ yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp)
> > > > /* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed before. */
> > > > static int yt921x_read_mib(struct yt921x_priv *priv, int port)
> > > > {
> > > > - struct yt921x_port *pp = &priv->ports[port];
> > > > + struct yt921x_port *pp = priv->ports[port];
> > > > struct device *dev = to_device(priv);
> > > > struct yt921x_mib *mib = &pp->mib;
> > > > int res = 0;
> > > >
> > > > + if (!pp)
> > > > + return -ENODEV;
> > > > +
> > >
> > > Are all these tests actually needed? If you cannot allocate the
> > > memory, i would expect the probe to fail, so you can never get here.
> > >
> > > Andrew
> >
> > Dummy ports are no longer assigned control blocks (in yt921x_dsa_setup).
>
> This seems pretty error prone. A missing check will result in an
> opps. At least it will be obvious. How big is each port structure? Is
> the memory saving worth it?
>
> Andrew
It's about 1.4k per port for 5 dummy ports.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [RFC net-next 3/4] net: dsa: motorcomm: Dynamically allocate port structures
2026-06-20 16:13 ` David Yang
@ 2026-06-21 9:06 ` Andrew Lunn
0 siblings, 0 replies; 10+ messages in thread
From: Andrew Lunn @ 2026-06-21 9:06 UTC (permalink / raw)
To: David Yang
Cc: netdev, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, linux-kernel
> > This seems pretty error prone. A missing check will result in an
> > opps. At least it will be obvious. How big is each port structure? Is
> > the memory saving worth it?
> >
> > Andrew
>
> It's about 1.4k per port for 5 dummy ports.
That adds up, so not allocating them makes sense.
Maybe check if the functions you are adding tests to can actually be
called for dummy ports. They should not have a netdev, so that often
means there is no path to call these functions.
Andrew
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-06-21 9:06 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-18 20:26 [RFC net-next 0/4] net: dsa: motorcomm: Add LED support David Yang
2026-06-18 20:26 ` [RFC net-next 1/4] net: dsa: motorcomm: Move to subdirectory David Yang
2026-06-18 20:26 ` [RFC net-next 2/4] net: dsa: motorcomm: Split SMI module David Yang
2026-06-18 20:26 ` [RFC net-next 3/4] net: dsa: motorcomm: Dynamically allocate port structures David Yang
2026-06-19 6:06 ` Andrew Lunn
2026-06-19 6:46 ` David Yang
2026-06-20 8:03 ` Andrew Lunn
2026-06-20 16:13 ` David Yang
2026-06-21 9:06 ` Andrew Lunn
2026-06-18 20:26 ` [RFC net-next 4/4] net: dsa: motorcomm: Add LED support David Yang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox