Devicetree
 help / color / mirror / Atom feed
* [PATCH net-next v5 11/15] net: phy: ncn26000: Support for loopback
From: Selvamani Rajagopal via B4 Relay @ 2026-06-14 17:00 UTC (permalink / raw)
  To: Andrew Lunn, Piergiorgio Beruto, Heiner Kallweit, Russell King,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Andrew Lunn, Parthiban Veerasooran, Selva Rajagopal,
	Richard Cochran, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Simon Horman, Jonathan Corbet, Shuah Khan
  Cc: netdev, linux-kernel, devicetree, linux-doc, Jerry Ray,
	Selvamani Rajagopal
In-Reply-To: <20260614-s2500-mac-phy-support-v5-0-89874b72f725@onsemi.com>

From: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>

Adding loopback support for S2500 internal PHY to help running
loopback test through ethtool.

This instance requires only manipulating the loopback bit in
the PHY. nothing else. The existing genphy interface
implementation for loopback, does more than flipping the
loopback bit. This is the reason to add a new API.

Signed-off-by: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>

---
changes in v5
  - No change
changes in v4
  - Moved the onsemi implementation to a new, genphy API to
    make it vendor agnostic.
changes in v3
  - Addd loopback functionality for onsemi's PHY driver
  - First patch
---
 drivers/net/phy/ncn26000.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/net/phy/ncn26000.c b/drivers/net/phy/ncn26000.c
index c3a34b2c524d..afafa81dc22b 100644
--- a/drivers/net/phy/ncn26000.c
+++ b/drivers/net/phy/ncn26000.c
@@ -178,6 +178,7 @@ static struct phy_driver ncn26000_driver[] = {
 		.config_aneg           = ncn26000_config_aneg,
 		.read_status           = ncn26000_read_status,
 		.handle_interrupt      = ncn26000_handle_interrupt,
+		.set_loopback          = genphy_loopback_fixed_speed,
 		.set_plca_cfg          = ncn26000_c45_plca_set_cfg,
 		.get_plca_cfg          = genphy_c45_plca_get_cfg,
 		.get_plca_status       = genphy_c45_plca_get_status,

-- 
2.43.0



^ permalink raw reply related

* [PATCH net-next v5 13/15] onsemi: s2500: Added selftest support to onsemi's S2500 driver
From: Selvamani Rajagopal via B4 Relay @ 2026-06-14 17:00 UTC (permalink / raw)
  To: Andrew Lunn, Piergiorgio Beruto, Heiner Kallweit, Russell King,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Andrew Lunn, Parthiban Veerasooran, Selva Rajagopal,
	Richard Cochran, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Simon Horman, Jonathan Corbet, Shuah Khan
  Cc: netdev, linux-kernel, devicetree, linux-doc, Jerry Ray,
	Selvamani Rajagopal
In-Reply-To: <20260614-s2500-mac-phy-support-v5-0-89874b72f725@onsemi.com>

From: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>

Adds selftest support for onsemi S2500 MAC-PHY. Added as a separate
patch for the ease of review.

---
changes in v5
  - No change
changes in v4:
  - Added a selftest as a separate patch
  - First patch

Signed-off-by: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>
---
 drivers/net/ethernet/onsemi/s2500/Kconfig         | 1 +
 drivers/net/ethernet/onsemi/s2500/s2500_ethtool.c | 7 +++++++
 2 files changed, 8 insertions(+)

diff --git a/drivers/net/ethernet/onsemi/s2500/Kconfig b/drivers/net/ethernet/onsemi/s2500/Kconfig
index f2e8d5d1429d..7bb4b413d5f9 100644
--- a/drivers/net/ethernet/onsemi/s2500/Kconfig
+++ b/drivers/net/ethernet/onsemi/s2500/Kconfig
@@ -8,6 +8,7 @@ if NET_VENDOR_ONSEMI
 config S2500_MACPHY
 	tristate "S2500 support"
 	depends on SPI
+	imply NET_SELFTESTS
 	select NCN26000_PHY
 	select OA_TC6
 	help
diff --git a/drivers/net/ethernet/onsemi/s2500/s2500_ethtool.c b/drivers/net/ethernet/onsemi/s2500/s2500_ethtool.c
index 85bd8ea50dd8..334fac71ddea 100644
--- a/drivers/net/ethernet/onsemi/s2500/s2500_ethtool.c
+++ b/drivers/net/ethernet/onsemi/s2500/s2500_ethtool.c
@@ -5,6 +5,7 @@
  */
 
 #include <linux/ethtool.h>
+#include <net/selftests.h>
 #include <linux/phy.h>
 
 #include "s2500_hw_def.h"
@@ -229,6 +230,8 @@ static int s2500_get_sset_count(struct net_device *ndev, int sset)
 	switch (sset) {
 	case ETH_SS_STATS:
 		return S2500_MAC_STATS_LEN;
+	case ETH_SS_TEST:
+		return net_selftest_get_count();
 	default:
 		return -EOPNOTSUPP;
 	}
@@ -242,6 +245,9 @@ static void s2500_get_strings(struct net_device *ndev, u32 stringset,
 		memcpy(buf, s2500_mac_stat_strings,
 		       S2500_MAC_STATS_LEN * ETH_GSTRING_LEN);
 		break;
+	case ETH_SS_TEST:
+		net_selftest_get_strings(buf);
+		break;
 	}
 }
 
@@ -343,5 +349,6 @@ const struct ethtool_ops s2500_ethtool_ops = {
 	.get_ts_info        = s2500_get_ts_info,
 	.get_regs_len       = s2500_get_regs_len,
 	.get_regs           = s2500_get_regs,
+	.self_test          = net_selftest,
 };
 

-- 
2.43.0



^ permalink raw reply related

* [PATCH net-next v5 12/15] onsemi: s2500: Add driver support for TS2500 MAC-PHY
From: Selvamani Rajagopal via B4 Relay @ 2026-06-14 17:00 UTC (permalink / raw)
  To: Andrew Lunn, Piergiorgio Beruto, Heiner Kallweit, Russell King,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Andrew Lunn, Parthiban Veerasooran, Selva Rajagopal,
	Richard Cochran, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Simon Horman, Jonathan Corbet, Shuah Khan
  Cc: netdev, linux-kernel, devicetree, linux-doc, Jerry Ray,
	Selvamani Rajagopal
In-Reply-To: <20260614-s2500-mac-phy-support-v5-0-89874b72f725@onsemi.com>

From: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>

Support for onsemi's S2500, 802.3 cg compliant Ethernet
transceiver with integrated MAC-PHY. Works with
Open Alliance TC6 framework.

adjtime callback is implemented using adjfine. If time
delta is too big, bigger than 1 second, using adjtime
would take long to reduce the delta. In those cases,
settime callback is used to reduce the delta. Once delta
becomes less than a second, it uses adjfine to reduce
the drift further.

Driver has ethtool support for printing some device
registers, traffic stats, rmon stats and hardware
timestamp realated settings and traffic counters.

Signed-off-by: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>

---
changes in v5
  - integrated the onsemi's selftest support to onsemi's driver
    as a single patch as number of patches exceeded the limit
  - Fixed the formatting issue in Kconfig file
  - Fixed commenting issue
changes in v4
  - MAC/PHY Loopback implemented by onsemi replaced by Linux
    standard net_selftest APIs
  - onsemi defined MMS values are replaced by OA TC definitions.
  - removed only model information as it has practical use
  - replaced all read/write APIs that use non-zero MMS to
    use the new APIs that takes mms as one of its parameters.
  - some bit manipulations are changed to use FIELD_GET macro
changes in v3
  - split rmon stats and interface states separately.
  - moved get_ts_info to OA TC6 framework as this isn't vendor
    specific
  - removed few static inline code
  - moved ptp register code OA TC6 framework
changes in v2
  - Removed the support for onsemi's NCN26010 which is legacy
    MAC-PHY to keep only onsemi's S2500 MAC-PHY
  - Renamed all the functions to start with S2500
changes in v1
  - Added onsemi MAC-PHYs NCN26010 and S2500 support
---
 MAINTAINERS                                       |   7 +
 drivers/net/ethernet/oa_tc6/oa_tc6_std_def.h      |   2 +-
 drivers/net/ethernet/oa_tc6/oa_tc6_tstamp.c       |   3 +-
 drivers/net/ethernet/onsemi/Kconfig               |  21 +
 drivers/net/ethernet/onsemi/Makefile              |   7 +
 drivers/net/ethernet/onsemi/s2500/Kconfig         |  21 +
 drivers/net/ethernet/onsemi/s2500/Makefile        |   7 +
 drivers/net/ethernet/onsemi/s2500/s2500_ethtool.c | 347 ++++++++++++
 drivers/net/ethernet/onsemi/s2500/s2500_hw_def.h  | 225 ++++++++
 drivers/net/ethernet/onsemi/s2500/s2500_main.c    | 632 ++++++++++++++++++++++
 drivers/net/ethernet/onsemi/s2500/s2500_ptp.c     | 233 ++++++++
 11 files changed, 1502 insertions(+), 3 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 54dc01628081..ccaf27e6073e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19978,6 +19978,13 @@ S:	Supported
 W:	http://www.onsemi.com
 F:	drivers/net/phy/ncn*
 
+ONSEMI S2500 10BASE-T1S MACPHY ETHERNET DRIVER
+M:	Selva Rajagopal <selvamani.rajagopal@onsemi.com>
+L:	netdev@vger.kernel.org
+S:	Supported
+F:	Documentation/devicetree/bindings/net/onnn,s2500.yaml
+F:	drivers/net/ethernet/onsemi/s2500/s2500_*
+
 OP-TEE DRIVER
 M:	Jens Wiklander <jens.wiklander@linaro.org>
 L:	op-tee@lists.trustedfirmware.org (moderated for non-subscribers)
diff --git a/drivers/net/ethernet/oa_tc6/oa_tc6_std_def.h b/drivers/net/ethernet/oa_tc6/oa_tc6_std_def.h
index 3a12b3228f30..d7780df07543 100644
--- a/drivers/net/ethernet/oa_tc6/oa_tc6_std_def.h
+++ b/drivers/net/ethernet/oa_tc6/oa_tc6_std_def.h
@@ -64,7 +64,7 @@
 #define OA_TC6_PHY_STD_REG_ADDR_MASK		0x1F
 
 /* Tx timestamp capture register A (high) */
-#define OA_TC6_REG_TTSCA_HIGH			(0x1010)
+#define OA_TC6_REG_TTSCA_HIGH			(0x10)
 
 /* Control command header */
 #define OA_TC6_CTRL_HEADER_DATA_NOT_CTRL	BIT(31)
diff --git a/drivers/net/ethernet/oa_tc6/oa_tc6_tstamp.c b/drivers/net/ethernet/oa_tc6/oa_tc6_tstamp.c
index 272701a4081d..a2b1a1b5690e 100644
--- a/drivers/net/ethernet/oa_tc6/oa_tc6_tstamp.c
+++ b/drivers/net/ethernet/oa_tc6/oa_tc6_tstamp.c
@@ -137,7 +137,7 @@ EXPORT_SYMBOL_GPL(oa_tc6_hwtstamp_set);
 /**
  * oa_tc6_get_ts_stats - Provides timestamping stats
  * @tc6: oa_tc6 struct.
- * @ts_stats: ethtool data structure to fill in
+ * @stats: ethtool data structure to fill in
  */
 void oa_tc6_get_ts_stats(struct oa_tc6 *tc6,
 			 struct ethtool_ts_stats *stats)
@@ -179,7 +179,6 @@ int oa_tc6_tstamp_ioctl(struct oa_tc6 *tc6, struct ifreq *rq, int cmd)
  * oa_tc6_get_ts_info - Provides timestamp info for ethtool
  * @tc6: oa_tc6 struct.
  * @info: ethtool timestamping info structure
- * @ts_stats: ethtool data structure to fill in
  */
 int oa_tc6_get_ts_info(struct oa_tc6 *tc6,
 		       struct kernel_ethtool_ts_info *info)
diff --git a/drivers/net/ethernet/onsemi/Kconfig b/drivers/net/ethernet/onsemi/Kconfig
new file mode 100644
index 000000000000..8d72194151ea
--- /dev/null
+++ b/drivers/net/ethernet/onsemi/Kconfig
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# onsemi network device configuration
+#
+
+config NET_VENDOR_ONSEMI
+	bool "onsemi network devices"
+	help
+	  If you have a network card belonging to this class, say Y.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all
+	  the questions about onsemi ethernet devices. If you say Y, you
+	  will be asked for your specific card in the following questions.
+
+if NET_VENDOR_ONSEMI
+
+source "drivers/net/ethernet/onsemi/s2500/Kconfig"
+
+endif # NET_VENDOR_ONSEMI
+
diff --git a/drivers/net/ethernet/onsemi/Makefile b/drivers/net/ethernet/onsemi/Makefile
new file mode 100644
index 000000000000..f3d4eb154313
--- /dev/null
+++ b/drivers/net/ethernet/onsemi/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for the onsemi network device drivers.
+#
+
+obj-$(CONFIG_S2500_MACPHY) += s2500/
+
diff --git a/drivers/net/ethernet/onsemi/s2500/Kconfig b/drivers/net/ethernet/onsemi/s2500/Kconfig
new file mode 100644
index 000000000000..f2e8d5d1429d
--- /dev/null
+++ b/drivers/net/ethernet/onsemi/s2500/Kconfig
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# onsemi S2500 Driver Support
+#
+
+if NET_VENDOR_ONSEMI
+
+config S2500_MACPHY
+	tristate "S2500 support"
+	depends on SPI
+	select NCN26000_PHY
+	select OA_TC6
+	help
+	  Support for the onsemi TS2500 MACPHY Ethernet chip.
+	  It works under the framework that conform to OPEN Alliance
+	  10BASE-T1x Serial Interface specification.
+
+	  To compile this driver as a module, choose M here. The module will be
+	  called s2500.
+
+endif # NET_VENDOR_ONSEMI
diff --git a/drivers/net/ethernet/onsemi/s2500/Makefile b/drivers/net/ethernet/onsemi/s2500/Makefile
new file mode 100644
index 000000000000..61ec705cdf9f
--- /dev/null
+++ b/drivers/net/ethernet/onsemi/s2500/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for the onsemi network device drivers.
+#
+obj-$(CONFIG_S2500_MACPHY) := s2500.o
+s2500-objs := s2500_main.o s2500_ethtool.o s2500_ptp.o
+
diff --git a/drivers/net/ethernet/onsemi/s2500/s2500_ethtool.c b/drivers/net/ethernet/onsemi/s2500/s2500_ethtool.c
new file mode 100644
index 000000000000..85bd8ea50dd8
--- /dev/null
+++ b/drivers/net/ethernet/onsemi/s2500/s2500_ethtool.c
@@ -0,0 +1,347 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2026 Semiconductor Components Industries, LLC ("onsemi").
+ * onsemi's S2500 10BASE-T1S MAC-PHY driver
+ */
+
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include "s2500_hw_def.h"
+
+#define S2500_NUM_REGS			38
+#define S2500_REGDUMP_LEN		(sizeof(u32) * (S2500_NUM_REGS * 2))
+
+#define S2500_NUM_MAC_STATS		21
+#define S2500_NUM_RMON_STATS		12
+
+struct s2500_reg_map {
+	u16 reg;
+	u16 mms;
+};
+
+static const struct s2500_reg_map s2500_reg_map[S2500_NUM_REGS] = {
+	{ S2500_REG_VS_ONIPVER,      OA_TC6_PHY_C45_VS_PLCA_MMS4 },
+	{ S2500_REG_VS_TWEAKS1,      OA_TC6_PHY_C45_VS_PLCA_MMS4 },
+	{ S2500_REG_VS_PLCAEXT,      OA_TC6_PHY_C45_VS_PLCA_MMS4 },
+	{ S2500_REG_VS_PMATUNE0,     OA_TC6_PHY_C45_VS_PLCA_MMS4 },
+	{ S2500_REG_VS_PMATUNE1,     OA_TC6_PHY_C45_VS_PLCA_MMS4 },
+	{ S2500_REG_VS_T1SWUPTUNE,   OA_TC6_PHY_C45_VS_PLCA_MMS4 },
+	{ S2500_REG_VS_HDD,          OA_TC6_PHY_C45_VS_PLCA_MMS4 },
+	{ S2500_REG_VS_DCQ_TOID,     OA_TC6_PHY_C45_VS_PLCA_MMS4 },
+	{ S2500_REG_VS_DCQ_SQI,      OA_TC6_PHY_C45_VS_PLCA_MMS4 },
+	{ S2500_REG_VS_DCQ_SQI_PLUS, OA_TC6_PHY_C45_VS_PLCA_MMS4 },
+	{ S2500_REG_VS_HDD_TUNE1,    OA_TC6_PHY_C45_VS_PLCA_MMS4 },
+	{ S2500_REG_VS_HDD_TUNE2,    OA_TC6_PHY_C45_VS_PLCA_MMS4 },
+	{ S2500_REG_VS_HDD_TUNE3,    OA_TC6_PHY_C45_VS_PLCA_MMS4 },
+	{ S2500_REG_VS_WS_STATUS_0,  OA_TC6_PHY_C45_VS_PLCA_MMS4 },
+	{ S2500_REG_VS_WS_CTRL_0,    OA_TC6_PHY_C45_VS_PLCA_MMS4 },
+	{ S2500_REG_VS_WS_STATUS_1,  OA_TC6_PHY_C45_VS_PLCA_MMS4 },
+	{ S2500_REG_VS_WS_CTRL_1,    OA_TC6_PHY_C45_VS_PLCA_MMS4 },
+	{ S2500_REG_VS_MIIMIRQE,     OA_TC6_PHY_C45_VS_MMS12 },
+	{ S2500_REG_VS_MIIMIRQS,     OA_TC6_PHY_C45_VS_MMS12 },
+	{ S2500_REG_VS_DIOCFG0,      OA_TC6_PHY_C45_VS_MMS12 },
+	{ S2500_REG_VS_CHIPID,       OA_TC6_PHY_C45_VS_MMS12 },
+	{ S2500_REG_VS_TWEAKS2,      OA_TC6_PHY_C45_VS_MMS12 },
+	{ S2500_REG_VS_MACID0,       OA_TC6_PHY_C45_VS_MMS12 },
+	{ S2500_REG_VS_MACID1,       OA_TC6_PHY_C45_VS_MMS12 },
+	{ S2500_REG_VS_NVM_HEALTH,   OA_TC6_PHY_C45_VS_MMS12 },
+	{ S2500_REG_VS_PTP_SEC,      OA_TC6_PHY_C45_VS_MMS12 },
+	{ S2500_REG_VS_PTPNSEC,      OA_TC6_PHY_C45_VS_MMS12 },
+	{ S2500_REG_VS_PTP_SETSEC,   OA_TC6_PHY_C45_VS_MMS12 },
+	{ S2500_REG_VS_SETNSEC,      OA_TC6_PHY_C45_VS_MMS12 },
+	{ S2500_REG_VS_PTP_ADJ,      OA_TC6_PHY_C45_VS_MMS12 },
+	{ S2500_REG_VS_CMPCTL,       OA_TC6_PHY_C45_VS_MMS12 },
+	{ S2500_REG_VS_CMPSEC,       OA_TC6_PHY_C45_VS_MMS12 },
+	{ S2500_REG_VS_CMPNSEC,      OA_TC6_PHY_C45_VS_MMS12 },
+	{ S2500_REG_VS_CMPPER,       OA_TC6_PHY_C45_VS_MMS12 },
+	{ S2500_REG_VS_CAPCTL,       OA_TC6_PHY_C45_VS_MMS12 },
+	{ S2500_REG_VS_CAPSEC,       OA_TC6_PHY_C45_VS_MMS12 },
+	{ S2500_REG_VS_CAPNSEC,      OA_TC6_PHY_C45_VS_MMS12 },
+	{ S2500_REG_VS_BCNCNT,       OA_TC6_PHY_C45_VS_MMS12 },
+};
+
+enum s2500_stat_idx {
+	S2500_MSTOCTECTSTX_IDX = 0,
+	S2500_MSTFRAMESTXOK_IDX,
+	S2500_MSTBCASTTXOK_IDX,
+	S2500_MSTMCASTTXOK_IDX,
+	S2500_MSTFRAMESTX64_IDX,
+	S2500_MSTFRAMESTX65_IDX,
+	S2500_MSTFRAMESTX128_IDX,
+	S2500_MSTFRAMESTX256_IDX,
+	S2500_MSTFRAMESTX512_IDX,
+	S2500_MSTFRAMESTX1024_IDX,
+	S2500_MSTTXUNDEFLOW_IDX,
+	S2500_MSTSINGLECOL_IDX,
+	S2500_MSTMULTICOL_IDX,
+	S2500_MSTEXCESSCOL_IDX,
+	S2500_MSTDEFERREDTX_IDX,
+	S2500_MSTCRSERR_IDX,
+	S2500_MSTOCTECTSRX_IDX,
+	S2500_MSTFRAMESRXOK_IDX,
+	S2500_MSTBCASTRXOK_IDX,
+	S2500_MSTMCASTRXOK_IDX,
+	S2500_MSTFRAMESRX64_IDX,
+	S2500_MSTFRAMESRX65_IDX,
+	S2500_MSTFRAMESRX128_IDX,
+	S2500_MSTFRAMESRX256_IDX,
+	S2500_MSTFRAMESRX512_IDX,
+	S2500_MSTFRAMESRX1024_IDX,
+	S2500_MSTRUNTSERR_IDX,
+	S2500_MSTRXTOOLONG_IDX,
+	S2500_MSTFCSERRS_IDX,
+	S2500_MSTSYMBOLERRS_IDX,
+	S2500_MSTALIGNERRS_IDX,
+	S2500_MSTRXOVERFLOW_IDX,
+	S2500_MSTRXDROPPED_IDX,
+};
+
+static const char s2500_mac_stat_strings[][ETH_GSTRING_LEN] = {
+	"tx_bytes",
+	"tx_frames",
+	"tx_broadcast_frames",
+	"tx_multicast_frames",
+	"tx_underflow_errors",
+	"tx_single_collisions",
+	"tx_multiple_collisions",
+	"tx_excessive_collisions",
+	"tx_deferred_frames",
+	"tx_carrier_sense_errors",
+	"rx_bytes",
+	"rx_frames",
+	"rx_broadcast_frames",
+	"rx_multicast_frames",
+	"rx_runts",
+	"rx_oversize_frames",
+	"rx_crc_errors",
+	"rx_symbol_errors",
+	"rx_alignment_errors",
+	"rx_busy_drops",
+	"rx_mismatch_drops",
+};
+
+static const u8 s2500_mac_stat_map[S2500_NUM_MAC_STATS] = {
+	S2500_MSTOCTECTSTX_IDX,
+	S2500_MSTFRAMESTXOK_IDX,
+	S2500_MSTBCASTTXOK_IDX,
+	S2500_MSTMCASTTXOK_IDX,
+	S2500_MSTTXUNDEFLOW_IDX,
+	S2500_MSTSINGLECOL_IDX,
+	S2500_MSTMULTICOL_IDX,
+	S2500_MSTEXCESSCOL_IDX,
+	S2500_MSTDEFERREDTX_IDX,
+	S2500_MSTCRSERR_IDX,
+	S2500_MSTOCTECTSRX_IDX,
+	S2500_MSTFRAMESRXOK_IDX,
+	S2500_MSTBCASTRXOK_IDX,
+	S2500_MSTMCASTRXOK_IDX,
+	S2500_MSTRUNTSERR_IDX,
+	S2500_MSTRXTOOLONG_IDX,
+	S2500_MSTFCSERRS_IDX,
+	S2500_MSTSYMBOLERRS_IDX,
+	S2500_MSTALIGNERRS_IDX,
+	S2500_MSTRXOVERFLOW_IDX,
+	S2500_MSTRXDROPPED_IDX,
+};
+
+static const u8 s2500_rmon_stat_map[S2500_NUM_RMON_STATS] = {
+	S2500_MSTFRAMESTX64_IDX,
+	S2500_MSTFRAMESTX65_IDX,
+	S2500_MSTFRAMESTX128_IDX,
+	S2500_MSTFRAMESTX256_IDX,
+	S2500_MSTFRAMESTX512_IDX,
+	S2500_MSTFRAMESTX1024_IDX,
+	S2500_MSTFRAMESRX64_IDX,
+	S2500_MSTFRAMESRX65_IDX,
+	S2500_MSTFRAMESRX128_IDX,
+	S2500_MSTFRAMESRX256_IDX,
+	S2500_MSTFRAMESRX512_IDX,
+	S2500_MSTFRAMESRX1024_IDX,
+};
+
+static const struct ethtool_rmon_hist_range s2500_rmon_hist_ranges[] = {
+	{ 64, 64 },
+	{ 65, 127 },
+	{ 128, 255 },
+	{ 256, 511 },
+	{ 512, 1023 },
+	{ 1024, 2000 },
+	{ },
+};
+
+#define S2500_MAC_STATS_LEN ARRAY_SIZE(s2500_mac_stat_strings)
+static_assert(S2500_MAC_STATS_LEN == S2500_NUM_MAC_STATS);
+static_assert(ARRAY_SIZE(s2500_mac_stat_map) == S2500_NUM_MAC_STATS);
+static_assert(ARRAY_SIZE(s2500_rmon_stat_map) == S2500_NUM_RMON_STATS);
+
+#define STAT_REG_OFFSET(x) ((S2500_REG_MAC_ST##x) - \
+			   S2500_REG_MAC_FIRST_STAT)
+
+static void s2500_update_stats(struct s2500_info *priv)
+{
+	u64 *data = priv->stats_data;
+	u32 *regs, *rptr;
+	int ret;
+
+	regs = kmalloc_array(S2500_NUM_STAT_REGS, sizeof(u32), GFP_KERNEL);
+	if (!regs)
+		return;
+
+	ret = oa_tc6_read_registers_mms(priv->tc6, S2500_REG_MAC_STOCTECTSTXL,
+					OA_TC6_PHY_C45_MAC_MMS1, regs,
+					S2500_NUM_STAT_REGS);
+	if (ret)
+		goto out;
+
+	rptr = regs;
+
+	/* TX bytes is a 64-bit register that spans over two 32-bit regs
+	 * note: HW does auto-freeze when reading LSB and un-freeze on MSB
+	 */
+	*(data++) += ((u64)*rptr) | (((u64)*(rptr + 1)) << 32);
+
+	/* run until the next 64-bit register */
+	for (rptr += 2; (rptr - regs) < STAT_REG_OFFSET(OCTECTSRXL); ++rptr)
+		*(data++) += *rptr;
+
+	/* RX bytes is a 64-bit register that spans over two 32-bit regs
+	 * note: HW does auto-freeze when reading LSB and un-freeze on MSB
+	 */
+	*(data++) += ((u64)*rptr) | (((u64)*(rptr + 1)) << 32);
+
+	for (rptr += 2; (rptr - regs) < S2500_NUM_STAT_REGS; ++rptr)
+		*(data++) += *rptr;
+out:
+	kfree(regs);
+}
+
+static void s2500_get_drvinfo(struct net_device *ndev,
+			      struct ethtool_drvinfo *info)
+{
+	strscpy(info->driver, DRV_NAME, sizeof(info->driver));
+	strscpy(info->bus_info, dev_name(&ndev->dev),
+		sizeof(info->bus_info));
+}
+
+static int s2500_get_sset_count(struct net_device *ndev, int sset)
+{
+	switch (sset) {
+	case ETH_SS_STATS:
+		return S2500_MAC_STATS_LEN;
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static void s2500_get_strings(struct net_device *ndev, u32 stringset,
+			      u8 *buf)
+{
+	switch (stringset) {
+	case ETH_SS_STATS:
+		memcpy(buf, s2500_mac_stat_strings,
+		       S2500_MAC_STATS_LEN * ETH_GSTRING_LEN);
+		break;
+	}
+}
+
+static void s2500_get_ethtool_stats(struct net_device *ndev,
+				    struct ethtool_stats *stats, u64 *data)
+{
+	struct s2500_info *priv = netdev_priv(ndev);
+	int i;
+
+	s2500_update_stats(priv);
+	for (i = 0; i < S2500_NUM_MAC_STATS; i++)
+		data[i] = priv->stats_data[s2500_mac_stat_map[i]];
+}
+
+static void s2500_get_rmon_stats(struct net_device *ndev,
+				 struct ethtool_rmon_stats *rmon_stats,
+				 const struct ethtool_rmon_hist_range **ranges)
+{
+	struct s2500_info *priv = netdev_priv(ndev);
+	int i;
+
+	s2500_update_stats(priv);
+
+	memset(rmon_stats, 0, sizeof(*rmon_stats));
+	rmon_stats->src = ETHTOOL_MAC_STATS_SRC_AGGREGATE;
+	rmon_stats->undersize_pkts = priv->stats_data[S2500_MSTRUNTSERR_IDX];
+	rmon_stats->oversize_pkts = priv->stats_data[S2500_MSTRXTOOLONG_IDX];
+	rmon_stats->fragments = priv->stats_data[S2500_MSTFCSERRS_IDX];
+	rmon_stats->jabbers = priv->stats_data[S2500_MSTALIGNERRS_IDX];
+
+	for (i = 0; i < 6; i++)
+		rmon_stats->hist_tx[i] = priv->stats_data[s2500_rmon_stat_map[i]];
+
+	for (i = 0; i < 6; i++)
+		rmon_stats->hist[i] = priv->stats_data[s2500_rmon_stat_map[i + 6]];
+
+	*ranges = s2500_rmon_hist_ranges;
+}
+
+static int s2500_get_ts_info(struct net_device *ndev,
+			     struct kernel_ethtool_ts_info *ts_info)
+{
+	struct s2500_info *priv = netdev_priv(ndev);
+
+	return oa_tc6_get_ts_info(priv->tc6, ts_info);
+}
+
+static int s2500_get_regs_len(struct net_device *dev)
+{
+	return S2500_REGDUMP_LEN;
+}
+
+static void s2500_get_regs(struct net_device *ndev,
+			   struct ethtool_regs *regs, void *p)
+{
+	struct s2500_info *priv = netdev_priv(ndev);
+	u32 *pbuff = (u32 *)p;
+	u16 reg, mms;
+	int ret = 0;
+	u32 val;
+	int i;
+
+	regs->version = 0;
+	memset(p, 0, S2500_REGDUMP_LEN);
+
+	if (!netif_running(ndev))
+		return;
+
+	for (i = 0; i < S2500_NUM_REGS; i++) {
+		val = 0;
+		reg = s2500_reg_map[i].reg;
+		mms = s2500_reg_map[i].mms;
+		ret = oa_tc6_read_register_mms(priv->tc6, reg, mms, &val);
+		if (ret)
+			continue;
+		*pbuff++ = cpu_to_be32(((u32)mms << 16) | reg);
+		*pbuff++ = cpu_to_be32(val);
+	}
+}
+
+static void s2500_get_ts_stats(struct net_device *ndev,
+			       struct ethtool_ts_stats *ts_stats)
+{
+	struct s2500_info *priv = netdev_priv(ndev);
+
+	oa_tc6_get_ts_stats(priv->tc6, ts_stats);
+}
+
+const struct ethtool_ops s2500_ethtool_ops = {
+	.get_drvinfo        = s2500_get_drvinfo,
+	.get_link           = ethtool_op_get_link,
+	.get_link_ksettings = phy_ethtool_get_link_ksettings,
+	.set_link_ksettings = phy_ethtool_set_link_ksettings,
+	.get_ts_stats       = s2500_get_ts_stats,
+	.get_sset_count     = s2500_get_sset_count,
+	.get_strings        = s2500_get_strings,
+	.get_ethtool_stats  = s2500_get_ethtool_stats,
+	.get_rmon_stats     = s2500_get_rmon_stats,
+	.get_ts_info        = s2500_get_ts_info,
+	.get_regs_len       = s2500_get_regs_len,
+	.get_regs           = s2500_get_regs,
+};
+
diff --git a/drivers/net/ethernet/onsemi/s2500/s2500_hw_def.h b/drivers/net/ethernet/onsemi/s2500/s2500_hw_def.h
new file mode 100644
index 000000000000..285fd87ccf92
--- /dev/null
+++ b/drivers/net/ethernet/onsemi/s2500/s2500_hw_def.h
@@ -0,0 +1,225 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright 2026 Semiconductor Components Industries, LLC ("onsemi").
+ * onsemi's S2500 10BASE-T1S MAC-PHY driver
+ */
+
+#ifndef S2500_HW_DEF_H
+#define S2500_HW_DEF_H
+
+#include <linux/hrtimer.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/phylink.h>
+#include <linux/spi/spi.h>
+#include <linux/oa_tc6.h>
+#include <linux/net_tstamp.h>
+#include <linux/ptp_clock_kernel.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/ktime.h>
+#include <linux/errno.h>
+
+#define DRV_NAME			"s2500"
+
+#define S2500_N_MCAST_FILTERS		3
+
+/* SPI OID and model register */
+#define S2500_REG_SPI_PHYID		0x1
+
+#define S2500_SPI_PHYID_OUI_SHIFT	10
+#define S2500_SPI_PHYID_OUI_MASK	GENMASK(31, S2500_SPI_PHYID_OUI_SHIFT)
+#define S2500_SPI_PHYID_OUI_BYTE0_MASK	GENMASK(21, 16)
+#define S2500_SPI_PHYID_OUI_BYTE1_MASK	GENMASK(15, 8)
+#define S2500_SPI_PHYID_OUI_BYTE2_MASK	GENMASK(7, 0)
+/* SPI configuration register #0 */
+#define S2500_REG_SPI_CFG0		0x4
+
+#define S2500_SPI_CFG0_SYNC_BIT		BIT(15)
+#define S2500_SPI_CFG0_TXCTHRESH_SHIFT	10
+#define S2500_SPI_CFG0_RXCTE_BIT	BIT(8)
+#define S2500_SPI_CFG0_FTSS_64_BIT	BIT(6)
+#define S2500_SPI_CFG0_CPS_SHIFT	0
+
+#define S2500_TXCTHRESH_8		0x2
+
+#define S2500_CPS_64			0x6
+
+/* SPI status register #0 */
+#define S2500_REG_SPI_ST0		0x8
+
+#define S2500_SPI_ST0_CDPE_BIT		BIT(12)
+#define S2500_SPI_ST0_TXFCSE_BIT	BIT(11)
+#define S2500_SPI_ST0_TTSCAC_BIT	BIT(10)
+#define S2500_SPI_ST0_TTSCAB_BIT	BIT(9)
+#define S2500_SPI_ST0_TTSCAA_BIT	BIT(8)
+#define S2500_SPI_ST0_RESETC_BIT	BIT(6)
+#define S2500_SPI_ST0_HDRE_BIT		BIT(5)
+#define S2500_SPI_ST0_LOFE_BIT		BIT(4)
+#define S2500_SPI_ST0_RXBOE_BIT		BIT(3)
+#define S2500_SPI_ST0_TXBUE_BIT		BIT(2)
+#define S2500_SPI_ST0_TXBOE_BIT		BIT(1)
+#define S2500_SPI_ST0_TXPE_BIT		BIT(0)
+
+/* SPI IRQ enable register #0 (use the S2500_SPI_ST0_*_BIT constants) */
+#define S2500_REG_SPI_IRQM0		0xc
+
+/* SPI buffer status register */
+#define S2500_REG_SPI_BUFST		0xb
+
+#define S2500_REG_MAC_CTRL		0x0
+
+#define S2500_MAC_CTRL_MCSF_BIT		BIT(18)
+#define S2500_MAC_CTRL_ADRF_BIT		BIT(16)
+#define S2500_MAC_CTRL_FCSA_BIT		BIT(8)
+#define S2500_MAC_CTRL_TXEN_BIT		BIT(1)
+#define S2500_MAC_CTRL_RXEN_BIT		BIT(0)
+
+/* MAC address filter registers */
+#define S2500_REG_MAC_ADDRFILTL(n)	(16 + 2 * (n))
+#define S2500_REG_MAC_ADDRFILTH(n)	(17 + 2 * (n))
+#define S2500_REG_MAC_ADDRMASKL(n)	(32 + 2 * (n))
+#define S2500_REG_MAC_ADDRMASKH(n)	(33 + 2 * (n))
+
+#define S2500_MAC_ADDRFILT_EN_BIT	BIT(31)
+
+/* MAC statistic registers */
+#define S2500_REG_MAC_STOCTECTSTXL	48
+#define S2500_REG_MAC_STOCTECTSTXH	49
+#define S2500_REG_MAC_STFRAMESTXOK	50
+#define S2500_REG_MAC_STBCASTTXOK	51
+#define S2500_REG_MAC_STMCASTTXOK	52
+#define S2500_REG_MAC_STFRAMESTX64	53
+#define S2500_REG_MAC_STFRAMESTX65	54
+#define S2500_REG_MAC_STFRAMESTX128	55
+#define S2500_REG_MAC_STFRAMESTX256	56
+#define S2500_REG_MAC_STFRAMESTX512	57
+#define S2500_REG_MAC_STFRAMESTX1024	58
+#define S2500_REG_MAC_STTXUNDEFLOW	59
+#define S2500_REG_MAC_STSINGLECOL	60
+#define S2500_REG_MAC_STMULTICOL	61
+#define S2500_REG_MAC_STEXCESSCOL	62
+#define S2500_REG_MAC_STDEFERREDTX	63
+#define S2500_REG_MAC_STCRSERR		64
+#define S2500_REG_MAC_STOCTECTSRXL	65
+#define S2500_REG_MAC_STOCTECTSRXH	66
+#define S2500_REG_MAC_STFRAMESRXOK	67
+#define S2500_REG_MAC_STBCASTRXOK	68
+#define S2500_REG_MAC_STMCASTRXOK	69
+#define S2500_REG_MAC_STFRAMESRX64	70
+#define S2500_REG_MAC_STFRAMESRX65	71
+#define S2500_REG_MAC_STFRAMESRX128	72
+#define S2500_REG_MAC_STFRAMESRX256	73
+#define S2500_REG_MAC_STFRAMESRX512	74
+#define S2500_REG_MAC_STFRAMESRX1024	75
+#define S2500_REG_MAC_STRUNTSERR	76
+#define S2500_REG_MAC_STRXTOOLONG	77
+#define S2500_REG_MAC_STFCSERRS		78
+#define S2500_REG_MAC_STSYMBOLERRS	79
+#define S2500_REG_MAC_STALIGNERRS	80
+#define S2500_REG_MAC_STRXOVERFLOW	81
+#define S2500_REG_MAC_STRXDROPPED	82
+
+/* First/last statistic register for sequential access */
+#define S2500_REG_MAC_FIRST_STAT	S2500_REG_MAC_STOCTECTSTXL
+#define S2500_REG_MAC_LAST_STAT		S2500_REG_MAC_STRXDROPPED
+
+#define S2500_NUM_STAT_REGS \
+	(S2500_REG_MAC_LAST_STAT - S2500_REG_MAC_FIRST_STAT + 1)
+#define S2500_NUM_STAT_VARS		(S2500_NUM_STAT_REGS - 2)
+
+/* Vendor specific MMS4 registers */
+#define S2500_REG_VS_ONIPVER		0x8000
+#define S2500_REG_VS_TWEAKS1		0x8001
+#define S2500_REG_VS_PLCAEXT		0x8002
+#define S2500_REG_VS_PMATUNE0		0x8003
+#define S2500_REG_VS_PMATUNE1		0x8004
+#define S2500_REG_VS_T1SWUPTUNE		0x8007
+#define S2500_REG_VS_HDD		0xCC01
+#define S2500_REG_VS_DCQ_TOID		0xCC02
+#define S2500_REG_VS_DCQ_SQI		0xCC03
+#define S2500_REG_VS_DCQ_SQI_PLUS	0xCC04
+#define S2500_REG_VS_HDD_TUNE1		0xCD00
+#define S2500_REG_VS_HDD_TUNE2		0xCD01
+#define S2500_REG_VS_HDD_TUNE3		0xCD02
+#define S2500_REG_VS_WS_STATUS_0	0xD000
+#define S2500_REG_VS_WS_CTRL_0		0xD001
+#define S2500_REG_VS_WS_STATUS_1	0xD100
+#define S2500_REG_VS_WS_CTRL_1		0xD101
+
+/* Vendor specific MMS12 registers */
+#define S2500_REG_VS_MIIMIRQE		0x10
+
+/* MIIM IRQ status register */
+#define S2500_REG_VS_MIIMIRQS		0x11
+#define MIIM_IRQ_STATUS_RSTS_SHIFT	15
+#define MIIM_IRQ_STATUS_RSTS		BIT(MIIM_IRQ_STATUS_RSTS_SHIFT)
+
+#define S2500_REG_VS_DIOCFG0		0x12
+
+#define S2500_REG_VS_CHIPID		0x1000
+#define S2500_REG_VS_TWEAKS2		0x1001
+
+/* Permanent MAC address register */
+#define S2500_REG_VS_MACID0		0x1002
+#define S2500_VS_MACID0_BYTE4_MASK	GENMASK(15, 8)
+#define S2500_VS_MACID0_BYTE5_MASK	GENMASK(7, 0)
+
+#define S2500_REG_VS_MACID1		0x1003
+#define S2500_VS_MACID1_BYTE3_MASK	GENMASK(7, 0)
+#define S2500_REG_VS_NVM_HEALTH		0x1005
+
+/* PTP registers */
+#define S2500_REG_VS_PTP_SEC		0x1010
+#define S2500_REG_VS_PTP_SETSEC		0x1012
+#define S2500_REG_VS_PTP_ADJ		0x1014
+#define S2500_REG_VS_PTPNSEC		0x1011
+#define S2500_REG_VS_SETNSEC		0x1013
+#define S2500_REG_VS_CMPCTL		0x1015
+#define S2500_REG_VS_CMPSEC		0x1016
+#define S2500_REG_VS_CMPNSEC		0x1017
+#define S2500_REG_VS_CMPPER		0x1018
+#define S2500_REG_VS_CAPCTL		0x1019
+#define S2500_REG_VS_CAPSEC		0x101A
+#define S2500_REG_VS_CAPNSEC		0x101B
+#define S2500_REG_VS_BCNCNT		0x101C
+
+/* prototypes / forward declarations */
+extern const struct ethtool_ops s2500_ethtool_ops;
+
+struct s2500_info;
+
+struct s2500_info {
+	struct device *dev;
+	struct net_device *ndev;
+
+	/* To have atomic set_rx_mode operation */
+	spinlock_t lock;
+
+	/* To have atomic operation when time is adjusted */
+	struct mutex ptp_adj_lock;
+	struct task_struct *thread;
+
+	/* global state variables */
+	bool event_pending;
+	unsigned int ndev_flags;
+	bool rx_flags_upd;
+
+	signed long poll_jiff;
+
+	struct spi_device *spi;
+
+	/* statistic counters variables */
+	u64 stats_data[S2500_NUM_STAT_VARS];
+
+	/* PTP related variables */
+	struct ptp_clock_info ptp_clock_info;
+	void *tc6;
+};
+
+int s2500_ptp_register(struct s2500_info *priv);
+
+#endif /* S2500_HW_DEF_H */
+
diff --git a/drivers/net/ethernet/onsemi/s2500/s2500_main.c b/drivers/net/ethernet/onsemi/s2500/s2500_main.c
new file mode 100644
index 000000000000..7aa3267db39b
--- /dev/null
+++ b/drivers/net/ethernet/onsemi/s2500/s2500_main.c
@@ -0,0 +1,632 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2026 Semiconductor Components Industries, LLC ("onsemi").
+ * onsemi's S2500 10BASE-T1S MAC-PHY driver
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/if_ether.h>
+#include <linux/irqchip.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/bitrev.h>
+#include <linux/phy.h>
+
+#include "s2500_hw_def.h"
+
+/* S2500 functions & definitions */
+
+#define S2500_STATUS0_MASK	(S2500_SPI_ST0_CDPE_BIT | \
+				S2500_SPI_ST0_TXFCSE_BIT | \
+				S2500_SPI_ST0_TTSCAC_BIT | \
+				S2500_SPI_ST0_TTSCAB_BIT | \
+				S2500_SPI_ST0_TTSCAA_BIT | \
+				S2500_SPI_ST0_RESETC_BIT | \
+				S2500_SPI_ST0_HDRE_BIT | \
+				S2500_SPI_ST0_LOFE_BIT | \
+				S2500_SPI_ST0_RXBOE_BIT | \
+				S2500_SPI_ST0_TXBUE_BIT | \
+				S2500_SPI_ST0_TXBOE_BIT | \
+				S2500_SPI_ST0_TXPE_BIT)
+
+/* Initializes the net device MAC address by reading the UID stored
+ * into the device internal non-volatile memory.
+ */
+static int s2500_read_mac_from_nvmem(struct s2500_info *priv)
+{
+	u8 addr[ETH_ALEN];
+	u32 mac1 = 0;
+	u32 mac0 = 0;
+	u32 val;
+	int ret;
+
+	ret = oa_tc6_read_register_mms(priv->tc6, S2500_REG_VS_MACID1,
+				       OA_TC6_PHY_C45_VS_MMS12, &mac1);
+	if (ret)
+		return ret;
+
+	ret = oa_tc6_read_register_mms(priv->tc6, S2500_REG_VS_MACID0,
+				       OA_TC6_PHY_C45_VS_MMS12, &mac0);
+	if (ret)
+		return ret;
+
+	/* Pre-production parts may have 0 */
+	if (mac0 == 0 && mac1 == 0)
+		return -ENXIO;
+
+	ret = oa_tc6_read_register(priv->tc6, S2500_REG_SPI_PHYID, &val);
+	if (ret)
+		return ret;
+
+	val = (val & S2500_SPI_PHYID_OUI_MASK) >> S2500_SPI_PHYID_OUI_SHIFT;
+
+	/* Mapping for bits from PHY ID register to OUI as give below.
+	 * - PHY-ID[10:17] provides OUI[16:23]
+	 * - PHY-ID[18:25] provides OUI[8:15]
+	 * - PHY-ID[26:31] provides OUI[2:7]
+	 *
+	 * Hardware presents OUI such a way that this requires not just
+	 * byte level swap, bit level swap is needed as well.
+	 */
+	addr[0] = bitrev8(FIELD_GET(S2500_SPI_PHYID_OUI_BYTE0_MASK, val));
+	addr[1] = bitrev8(FIELD_GET(S2500_SPI_PHYID_OUI_BYTE1_MASK, val));
+	addr[2] = bitrev8(FIELD_GET(S2500_SPI_PHYID_OUI_BYTE2_MASK, val));
+
+	addr[3] = FIELD_GET(S2500_VS_MACID1_BYTE3_MASK, mac1);
+	addr[4] = FIELD_GET(S2500_VS_MACID0_BYTE4_MASK, mac0);
+	addr[5] = FIELD_GET(S2500_VS_MACID0_BYTE5_MASK, mac0);
+
+	__dev_addr_set(priv->ndev, addr, ETH_ALEN);
+	priv->ndev->addr_assign_type = NET_ADDR_PERM;
+	return ret;
+}
+
+/* Writes MAC address to macphy registers */
+static int s2500_set_mac_filter(struct net_device *ndev, const u8 *mac)
+{
+	struct s2500_info *priv = netdev_priv(ndev);
+	u32 val;
+	int ret;
+
+	/* Set unicast address filter */
+	ret = oa_tc6_write_register_mms(priv->tc6, S2500_REG_MAC_ADDRMASKL(0),
+					OA_TC6_PHY_C45_MAC_MMS1, 0xffffffff);
+	if (ret)
+		return ret;
+
+	ret = oa_tc6_write_register_mms(priv->tc6, S2500_REG_MAC_ADDRMASKH(0),
+					OA_TC6_PHY_C45_MAC_MMS1, 0xffff);
+	if (ret)
+		return ret;
+
+	val = get_unaligned_be32(&mac[2]);
+
+	ret = oa_tc6_write_register_mms(priv->tc6, S2500_REG_MAC_ADDRFILTL(0),
+					OA_TC6_PHY_C45_MAC_MMS1, val);
+	if (ret)
+		return ret;
+
+	val = S2500_MAC_ADDRFILT_EN_BIT | get_unaligned_be16(mac);
+
+	return oa_tc6_write_register_mms(priv->tc6, S2500_REG_MAC_ADDRFILTH(0),
+					 OA_TC6_PHY_C45_MAC_MMS1, val);
+}
+
+static int s2500_mac_ctrl_modify_bits(struct s2500_info *priv,
+				      u32 in_bits, bool clr)
+{
+	u32 reg = S2500_REG_MAC_CTRL;
+	u32 rval = 0;
+	int ret;
+
+	ret = oa_tc6_read_register_mms(priv->tc6, reg,
+				       OA_TC6_PHY_C45_MAC_MMS1, &rval);
+	if (!ret) {
+		u32 wval = 0;
+
+		if (clr)
+			wval = rval & ~in_bits;
+		else
+			wval = rval | in_bits;
+		if (rval != wval)
+			ret = oa_tc6_write_register_mms(priv->tc6, reg,
+							OA_TC6_PHY_C45_MAC_MMS1, wval);
+	}
+	return ret;
+}
+
+static int s2500_init(struct s2500_info *priv)
+{
+	u32 val;
+	int ret;
+
+	/* Configure the SPI protocol + 64 bit timestamp */
+	val = S2500_SPI_CFG0_SYNC_BIT | S2500_SPI_CFG0_RXCTE_BIT |
+	      (S2500_TXCTHRESH_8 << S2500_SPI_CFG0_TXCTHRESH_SHIFT) |
+	      (S2500_CPS_64 << S2500_SPI_CFG0_CPS_SHIFT) |
+	      S2500_SPI_CFG0_FTSS_64_BIT;
+
+	ret = oa_tc6_write_register(priv->tc6, S2500_REG_SPI_CFG0, val);
+	if (ret)
+		return ret;
+
+	val = (u32)~(S2500_SPI_ST0_RESETC_BIT |
+		     S2500_SPI_ST0_HDRE_BIT | S2500_SPI_ST0_LOFE_BIT |
+		     S2500_SPI_ST0_RXBOE_BIT | S2500_SPI_ST0_TXBOE_BIT |
+		     S2500_SPI_ST0_TXPE_BIT);
+
+	ret = oa_tc6_write_register(priv->tc6, S2500_REG_SPI_IRQM0, val);
+	if (ret)
+		return ret;
+
+	/* Read the initial value of TX credits */
+	ret = oa_tc6_read_register(priv->tc6, S2500_REG_SPI_BUFST, &val);
+	if (ret)
+		return ret;
+
+	/* Program the source MAC address into the device */
+	ret = s2500_set_mac_filter(priv->ndev, priv->ndev->dev_addr);
+
+	val = S2500_MAC_CTRL_ADRF_BIT | S2500_MAC_CTRL_FCSA_BIT;
+
+	return s2500_mac_ctrl_modify_bits(priv, val, false);
+}
+
+static void s2500_shutdown(struct s2500_info *priv)
+{
+	u32 val = S2500_MAC_CTRL_TXEN_BIT | S2500_MAC_CTRL_RXEN_BIT;
+	struct net_device *ndev = priv->ndev;
+
+	netif_stop_queue(ndev);
+	phy_stop(ndev->phydev);
+
+	s2500_mac_ctrl_modify_bits(priv, val, true);
+}
+
+static int s2500_set_promiscuous_mode(struct s2500_info *priv,
+				      unsigned int rx_flags)
+{
+	u32 val = S2500_MAC_CTRL_ADRF_BIT;
+	bool clr = false;
+
+	if (rx_flags & IFF_PROMISC)
+		clr = true;
+	return s2500_mac_ctrl_modify_bits(priv, val, clr);
+}
+
+static int s2500_set_multicast_mode(struct s2500_info *priv,
+				    unsigned int rx_flags)
+{
+	int i, ret = 0;
+	u32 val;
+
+	if ((rx_flags & IFF_ALLMULTI) ||
+	    (netdev_mc_count(priv->ndev) > S2500_N_MCAST_FILTERS)) {
+		/* Disable multicast filter */
+		ret = s2500_mac_ctrl_modify_bits(priv,
+						 S2500_MAC_CTRL_MCSF_BIT,
+						 true);
+		if (ret)
+			return ret;
+
+		/* Accept all multicasts */
+		ret = oa_tc6_write_register_mms(priv->tc6, S2500_REG_MAC_ADDRMASKL(1),
+						OA_TC6_PHY_C45_MAC_MMS1, 0);
+		if (ret)
+			return ret;
+
+		ret = oa_tc6_write_register_mms(priv->tc6, S2500_REG_MAC_ADDRMASKH(1),
+						OA_TC6_PHY_C45_MAC_MMS1, 0x100);
+		if (ret)
+			return ret;
+
+		ret = oa_tc6_write_register_mms(priv->tc6, S2500_REG_MAC_ADDRFILTL(1),
+						OA_TC6_PHY_C45_MAC_MMS1, 0);
+		if (ret)
+			return ret;
+
+		val = S2500_MAC_ADDRFILT_EN_BIT | 0x00000100;
+		ret = oa_tc6_write_register_mms(priv->tc6, S2500_REG_MAC_ADDRFILTH(1),
+						OA_TC6_PHY_C45_MAC_MMS1, val);
+	} else if (netdev_mc_count(priv->ndev) == 0) {
+		/* Enable multicast filter */
+		ret = s2500_mac_ctrl_modify_bits(priv,
+						 S2500_MAC_CTRL_MCSF_BIT,
+						 false);
+		if (ret)
+			return ret;
+
+		/* Disable filters */
+		for (i = 1; i <= S2500_N_MCAST_FILTERS; i++) {
+			ret = oa_tc6_write_register_mms(priv->tc6,
+							S2500_REG_MAC_ADDRFILTH(i),
+							OA_TC6_PHY_C45_MAC_MMS1, 0);
+			if (ret)
+				return ret;
+		}
+	} else {
+		struct netdev_hw_addr *ha;
+		u32 addrh, addrl;
+
+		/* Disable multicast filter */
+		ret = s2500_mac_ctrl_modify_bits(priv,
+						 S2500_MAC_CTRL_MCSF_BIT,
+						 true);
+		if (ret)
+			return ret;
+
+		/* Disable filters */
+		for (i = 1; i <= S2500_N_MCAST_FILTERS; i++) {
+			ret = oa_tc6_write_register_mms(priv->tc6,
+							S2500_REG_MAC_ADDRFILTH(i),
+							OA_TC6_PHY_C45_MAC_MMS1, 0);
+			if (ret)
+				return ret;
+		}
+
+		i = 1;
+		netdev_for_each_mc_addr(ha, priv->ndev) {
+			if (i > S2500_N_MCAST_FILTERS)
+				break;
+
+			addrh = S2500_MAC_ADDRFILT_EN_BIT |
+				 get_unaligned_be16(ha->addr);
+			addrl = get_unaligned_be32(&ha->addr[2]);
+
+			ret = oa_tc6_write_register_mms(priv->tc6,
+							S2500_REG_MAC_ADDRFILTH(i),
+							OA_TC6_PHY_C45_MAC_MMS1, addrh);
+			if (ret)
+				return ret;
+
+			ret = oa_tc6_write_register_mms(priv->tc6,
+							S2500_REG_MAC_ADDRFILTL(i),
+							OA_TC6_PHY_C45_MAC_MMS1, addrl);
+			if (ret)
+				return ret;
+
+			ret = oa_tc6_write_register_mms(priv->tc6,
+							S2500_REG_MAC_ADDRMASKL(i),
+							OA_TC6_PHY_C45_MAC_MMS1, 0xffffffff);
+			if (ret)
+				return ret;
+
+			ret = oa_tc6_write_register_mms(priv->tc6,
+							S2500_REG_MAC_ADDRMASKH(i),
+							OA_TC6_PHY_C45_MAC_MMS1, 0xffff);
+			if (ret)
+				return ret;
+			i++;
+		}
+	}
+	return ret;
+}
+
+/* Deferred function for applying RX mode flags in non-atomic context */
+static int s2500_rx_mode_update(struct s2500_info *priv)
+{
+	unsigned int rx_flags;
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	rx_flags = priv->ndev_flags;
+	priv->rx_flags_upd = false;
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	ret = s2500_set_promiscuous_mode(priv, rx_flags);
+	if (ret)
+		goto out;
+
+	ret = s2500_set_multicast_mode(priv, rx_flags);
+out:
+	return ret;
+}
+
+static void s2500_set_rx_mode(struct net_device *ndev)
+{
+	struct s2500_info *priv = netdev_priv(ndev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	priv->rx_flags_upd = true;
+	priv->ndev_flags = ndev->flags;
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	if (priv->thread)
+		wake_up_process(priv->thread);
+}
+
+static int s2500_set_mac_address(struct net_device *ndev, void *p)
+{
+	struct sockaddr *addr = p;
+
+	if (!is_valid_ether_addr(addr->sa_data))
+		return -EADDRNOTAVAIL;
+
+	eth_hw_addr_set(ndev, addr->sa_data);
+	return s2500_set_mac_filter(ndev, addr->sa_data);
+}
+
+static netdev_tx_t s2500_start_xmit(struct sk_buff *skb,
+				    struct net_device *ndev)
+{
+	struct s2500_info *priv = netdev_priv(ndev);
+
+	return oa_tc6_start_xmit(priv->tc6, skb);
+}
+
+static int s2500_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
+{
+	struct s2500_info *priv = netdev_priv(ndev);
+
+	return oa_tc6_ioctl(priv->tc6, rq, cmd);
+}
+
+static void s2500_process_events(struct s2500_info *priv)
+{
+	u32 val;
+	int ret;
+
+	if (!priv->event_pending)
+		return;
+
+	priv->event_pending = false;
+
+	ret = oa_tc6_read_register(priv->tc6, S2500_REG_SPI_ST0, &val);
+	if (ret) {
+		dev_err(&priv->spi->dev, "Error reading ST0 register");
+		return;
+	}
+}
+
+static int s2500_thread_fun(void *data)
+{
+	struct s2500_info *priv = data;
+	bool update_rx_mode = false;
+	unsigned long flags;
+	signed long tout;
+	int ret = 0;
+
+	tout = priv->poll_jiff;
+
+	do {
+		if (update_rx_mode) {
+			ret = s2500_rx_mode_update(priv);
+			if (unlikely(ret)) {
+				dev_err(&priv->spi->dev, "Failed to set new RX mode");
+				break;
+			}
+		}
+
+		if (tout == 0) {
+			tout = priv->poll_jiff;
+
+			/* Force checking the status register */
+			priv->event_pending = true;
+		}
+
+		s2500_process_events(priv);
+
+		spin_lock_irqsave(&priv->lock, flags);
+		__set_current_state(TASK_INTERRUPTIBLE);
+
+		update_rx_mode = priv->rx_flags_upd;
+		ret = update_rx_mode;
+
+		spin_unlock_irqrestore(&priv->lock, flags);
+
+		if (!ret)
+			tout = schedule_timeout(tout);
+		else
+			set_current_state(TASK_RUNNING);
+	} while (!kthread_should_stop());
+	return 0;
+}
+
+static int s2500_open(struct net_device *ndev)
+{
+	struct s2500_info *priv = netdev_priv(ndev);
+	int ret = 0;
+	u32 val;
+
+	dev_dbg(&ndev->dev, "%s", "s2500_open");
+	phy_start(priv->ndev->phydev);
+
+	priv->thread = kthread_run(s2500_thread_fun, priv, DRV_NAME "/%s:%d",
+				   dev_name(&priv->spi->dev),
+				   spi_get_chipselect(priv->spi, 0));
+
+	if (IS_ERR(priv->thread)) {
+		ret = PTR_ERR(priv->thread);
+	} else {
+		val = S2500_MAC_CTRL_TXEN_BIT | S2500_MAC_CTRL_RXEN_BIT;
+		ret = s2500_mac_ctrl_modify_bits(priv, val, false);
+
+		netif_start_queue(priv->ndev);
+	}
+	return ret;
+}
+
+static int s2500_stop(struct net_device *ndev)
+{
+	struct s2500_info *priv = netdev_priv(ndev);
+
+	dev_dbg(&ndev->dev, "%s", "s2500_stop");
+
+	s2500_shutdown(priv);
+
+	kthread_stop(priv->thread);
+	priv->thread = NULL;
+
+	return 0;
+}
+
+static int s2500_hwtstamp_get(struct net_device *ndev,
+			      struct kernel_hwtstamp_config *k_cfg)
+{
+	struct s2500_info *priv = netdev_priv(ndev);
+
+	oa_tc6_hwtstamp_get(priv->tc6, k_cfg);
+	return 0;
+}
+
+static int s2500_hwtstamp_set(struct net_device *ndev,
+			      struct kernel_hwtstamp_config *cfg,
+			      struct netlink_ext_ack *extack)
+{
+	struct s2500_info *priv = netdev_priv(ndev);
+
+	return oa_tc6_hwtstamp_set(priv->tc6, cfg);
+}
+
+static const struct net_device_ops s2500_netdev_ops = {
+	.ndo_open            = s2500_open,
+	.ndo_stop            = s2500_stop,
+	.ndo_start_xmit      = s2500_start_xmit,
+	.ndo_set_mac_address = s2500_set_mac_address,
+	.ndo_set_rx_mode     = s2500_set_rx_mode,
+	.ndo_eth_ioctl       = s2500_ioctl,
+	.ndo_hwtstamp_get    = s2500_hwtstamp_get,
+	.ndo_hwtstamp_set    = s2500_hwtstamp_set,
+};
+
+static int s2500_probe(struct spi_device *spi)
+{
+	struct device *dev = &spi->dev;
+	struct net_device *ndev;
+	struct s2500_info *priv;
+	u32 val;
+	int ret;
+
+	if (spi->irq < 0)
+		return -ENODEV;
+
+	ndev = devm_alloc_etherdev(dev, sizeof(struct s2500_info));
+	if (!ndev)
+		return -ENOMEM;
+
+	priv = netdev_priv(ndev);
+	priv->ndev = ndev;
+	priv->spi = spi;
+	priv->dev = dev;
+
+	SET_NETDEV_DEV(ndev, dev);
+
+	spin_lock_init(&priv->lock);
+	mutex_init(&priv->ptp_adj_lock);
+	ndev->irq = spi->irq;
+
+	spi->dev.platform_data = priv;
+	spi_set_drvdata(spi, priv);
+
+	ndev->netdev_ops = &s2500_netdev_ops;
+	ndev->ethtool_ops = &s2500_ethtool_ops;
+	ndev->if_port = IF_PORT_10BASET;
+	ndev->priv_flags |= IFF_UNICAST_FLT;
+	ndev->hw_features = NETIF_F_RXALL;
+
+	priv->poll_jiff = HZ * 5; /* Poll interval */
+
+	priv->tc6 = oa_tc6_init(spi, ndev);
+	if (!priv->tc6) {
+		dev_err(&spi->dev, "OA TC6 init failed");
+		return -ENODEV;
+	}
+	oa_tc6_set_vend1_mms(priv->tc6, OA_TC6_PHY_C45_VS_MMS12);
+
+	/* Clear RSTS, if set */
+	oa_tc6_read_register_mms(priv->tc6, S2500_REG_VS_MIIMIRQS,
+				 OA_TC6_PHY_C45_VS_MMS12, &val);
+	val &= MIIM_IRQ_STATUS_RSTS;
+	if (val != 0)
+		oa_tc6_write_register_mms(priv->tc6, S2500_REG_VS_MIIMIRQS,
+					  OA_TC6_PHY_C45_VS_MMS12,
+					  MIIM_IRQ_STATUS_RSTS);
+
+	/* Acknowledge all IRQ status bits */
+	ret = oa_tc6_read_register(priv->tc6, S2500_REG_SPI_ST0, &val);
+	if (!ret) {
+		u32 mask = S2500_STATUS0_MASK;
+
+		val &= mask;
+		oa_tc6_write_register(priv->tc6, S2500_REG_SPI_ST0, val);
+	}
+
+	ret = device_get_ethdev_address(priv->dev, ndev);
+	if (ret)
+		ret = s2500_read_mac_from_nvmem(priv);
+
+	if (ret) {
+		eth_hw_addr_random(ndev);
+		dev_warn(&spi->dev, "Using random MAC address %pM", ndev->dev_addr);
+	}
+
+	ret = s2500_init(priv);
+	if (unlikely(ret)) {
+		dev_err(&spi->dev, "failed to s2500_init the device");
+		goto err_reg_read;
+	}
+
+	/* Configure PTP if the model supports it */
+	ret = s2500_ptp_register(priv);
+	if (unlikely(ret))
+		goto err_reg_read;
+
+	ret = register_netdev(ndev);
+	if (ret) {
+		dev_err(&spi->dev, "failed to register the S2500 device\n");
+		ret = -ENODEV;
+
+		goto err_reg_read;
+	}
+	return 0;
+
+err_reg_read:
+	dev_err(&spi->dev, "could not initialize macphy");
+	return ret;
+}
+
+static void s2500_remove(struct spi_device *spi)
+{
+	struct s2500_info *priv = spi->dev.platform_data;
+
+	dev_dbg(&priv->ndev->dev, "%s", "s2500_remove");
+
+	oa_tc6_ptp_unregister(priv->tc6);
+	unregister_netdev(priv->ndev);
+	oa_tc6_exit(priv->tc6);
+}
+
+static const struct of_device_id s2500_of_match[] = {
+	{ .compatible = "onnn,s2500" },
+	{}
+};
+
+static const struct spi_device_id s2500_ids[] = {
+	{ "s2500" },
+	{}
+};
+
+MODULE_DEVICE_TABLE(spi, s2500_ids);
+
+static struct spi_driver s2500_driver = {
+	.driver = {
+		.name	= DRV_NAME,
+		.of_match_table = s2500_of_match,
+	},
+	.probe		= s2500_probe,
+	.remove		= s2500_remove,
+	.id_table	= s2500_ids,
+};
+
+module_spi_driver(s2500_driver);
+
+MODULE_AUTHOR("Piergiorgio Beruto <pier.beruto@onsemi.com>");
+MODULE_AUTHOR("Selva Rajagopal <selvamani.rajagopal@onsemi.com>");
+MODULE_DESCRIPTION("onsemi MACPHY ethernet driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/onsemi/s2500/s2500_ptp.c b/drivers/net/ethernet/onsemi/s2500/s2500_ptp.c
new file mode 100644
index 000000000000..9b2be0302078
--- /dev/null
+++ b/drivers/net/ethernet/onsemi/s2500/s2500_ptp.c
@@ -0,0 +1,233 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2026 Semiconductor Components Industries, LLC ("onsemi").
+ * onsemi's S2500 10BASE-T1S MAC-PHY driver
+ */
+
+#include "s2500_hw_def.h"
+
+static int s2500_ptp_get_time64(struct ptp_clock_info *ptp,
+				struct timespec64 *ts,
+				struct ptp_system_timestamp *ptp_sts)
+{
+	struct s2500_info *priv = container_of(ptp, struct s2500_info,
+					       ptp_clock_info);
+	u32 data[2];
+	int ret;
+
+	ptp_read_system_prets(ptp_sts);
+	ret = oa_tc6_read_registers_mms(priv->tc6, S2500_REG_VS_PTP_SEC,
+					OA_TC6_PHY_C45_VS_MMS12, &data[0], 2);
+	ptp_read_system_postts(ptp_sts);
+
+	if (!ret) {
+		ts->tv_sec = data[0];
+		ts->tv_nsec = data[1];
+	}
+
+	return ret;
+}
+
+static int s2500_ptp_set_time64(struct ptp_clock_info *ptp,
+				const struct timespec64 *ts)
+{
+	struct s2500_info *priv = container_of(ptp, struct s2500_info,
+					       ptp_clock_info);
+	u32 data[2];
+
+	if (ts->tv_sec >= (1ULL << 32))
+		return -ERANGE;
+
+	data[0] = (u32)ts->tv_sec;
+	data[1] = ts->tv_nsec | BIT(31); /* bit 31 = execute set command */
+
+	return oa_tc6_write_registers_mms(priv->tc6, S2500_REG_VS_PTP_SETSEC,
+					  OA_TC6_PHY_C45_VS_MMS12, &data[0], 2);
+}
+
+static int s2500_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
+{
+	struct s2500_info *priv = container_of(ptp, struct s2500_info,
+					       ptp_clock_info);
+	u32 sign_bit = 0;
+	long adj;
+	u32 val;
+	u64 ppm;
+
+	if (scaled_ppm < 0) {
+		/* split sign / mod */
+		sign_bit = 1U << 31;
+		scaled_ppm = ~scaled_ppm + 1;
+	}
+
+	/*
+	 * Convert unsigned scaled_ppm to atto-seconds per clock cycles.
+	 * The scaled_ppm format is Qx.16 --> 1 lsb = 1/65536 ppm.
+	 * The clock period of the S2500 is 8ns (125 MHz), so 1 lsb of
+	 * adj register LSB is 1 atto-sec / 8ns = 0.000125 ppm.
+	 * Represented in Qx.16 format, this is 0.000125 * 2^16 = 8(.192)
+	 * To convert scaled_ppm into a register value we need to divide
+	 * it by the LSB value, hence adj = (scaled_ppm * 1000) / 8192 to
+	 * minimize the precision loss due to the integer arithmetic.
+	 * That further reduces to (scaled_ppm * 125) / 1024.
+	 */
+	ppm = (u64)scaled_ppm * 125;
+	do_div(ppm, 1024);
+	adj = (long)ppm;
+
+	/* check overflow */
+	if (adj >= (1L << 28))
+		return -ERANGE;
+
+	val = (u32)adj | sign_bit;
+	return oa_tc6_write_register_mms(priv->tc6, S2500_REG_VS_PTP_ADJ,
+					 OA_TC6_PHY_C45_VS_MMS12, val);
+}
+
+static int s2500_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+	struct s2500_info *priv = container_of(ptp, struct s2500_info,
+					       ptp_clock_info);
+	struct ptp_system_timestamp sts;
+	struct timespec64 target;
+	unsigned int period_ms;
+	struct timespec64 now;
+	int max_iters = 3;
+	s64 scaled_ppm;
+	s64 remaining;
+	s64 target_ns;
+	int ret = 0;
+	s64 now_ns;
+	s64 num;
+	s64 den;
+
+	if (!ptp)
+		return -EINVAL;
+
+	/* Nothing to do */
+	if (delta == 0)
+		return 0;
+
+	if (mutex_lock_interruptible(&priv->ptp_adj_lock))
+		return -EINTR;
+
+	/* Try to slew the clock using adjfine for better accuracy. For large
+	 * adjustments fall back to setting time directly.
+	 */
+	remaining = delta;
+
+	while (remaining != 0 && max_iters--) {
+		s64 abs_delta = remaining > 0 ? remaining : -remaining;
+
+		/* If the adjustment is very large, more than 1 second,
+		 * use settime to avoid very long slewing periods or
+		 * excessive frequency offsets.
+		 */
+		if (abs_delta > 1000000000LL) {
+			memset(&sts, 0, sizeof(sts));
+			ret = ptp->gettimex64(ptp, &now, &sts);
+			if (!ret) {
+				struct timespec64 delta_ts;
+
+				if (remaining >= 0) {
+					delta_ts = ns_to_timespec64(remaining);
+					target = timespec64_add(now, delta_ts);
+				} else {
+					delta_ts = ns_to_timespec64(-remaining);
+					target = timespec64_sub(now, delta_ts);
+				}
+			}
+
+			if (target.tv_sec < 0 || target.tv_sec >= (1ULL << 32))
+				ret = -ERANGE;
+			else
+				ret = ptp->settime64(ptp, &target);
+
+			remaining = 0;
+			break;
+		}
+
+		/* Choose a slewing period depending on magnitude */
+		if (abs_delta <= 1000000LL) /* <= 1ms */
+			period_ms = 1000; /* 1 s */
+		else if (abs_delta <= 100000000LL) /* <= 100ms */
+			period_ms = 10000; /* 10 s */
+		else
+			period_ms = 60000; /* 60 s */
+
+		/* compute current time and fixed target for this iteration */
+		memset(&sts, 0, sizeof(sts));
+		ret = ptp->gettimex64(ptp, &now, &sts);
+		if (ret)
+			break;
+
+		if (remaining >= 0)
+			target = timespec64_add(now, ns_to_timespec64(remaining));
+		else
+			target = timespec64_sub(now, ns_to_timespec64(-remaining));
+
+		/* Compute scaled_ppm (Qx.16). scaled_ppm = ppm * 2^16
+		 * ppm = (delta_seconds / period_seconds) * 1e6
+		 * => scaled_ppm = delta_ns * 65536 / (period_ms * 1000)
+		 */
+		num = remaining * 65536LL;
+		den = (s64)period_ms * 1000LL;
+
+		/* Integer division rounds toward zero; keep sign in numerator */
+		scaled_ppm = div_s64(num, den);
+
+		/* Apply frequency adjustment */
+		ret = ptp->adjfine(ptp, (long)scaled_ppm);
+		if (ret)
+			break;
+
+		/* Sleep for the slew period (interruptible). If interrupted, clear
+		 * the adjfine and return with -EINTR.
+		 */
+		if (msleep_interruptible(period_ms)) {
+			/* Clear adjfine */
+			ptp->adjfine(ptp, 0);
+			ret = -EINTR;
+			break;
+		}
+
+		/* Clear adjfine and measure remaining offset */
+		ptp->adjfine(ptp, 0);
+
+		memset(&sts, 0, sizeof(sts));
+		ret = ptp->gettimex64(ptp, &now, &sts);
+		if (ret)
+			break;
+
+		/* remaining = target - now (in ns) */
+		target_ns = timespec64_to_ns(&target);
+		now_ns = timespec64_to_ns(&now);
+		remaining = target_ns - now_ns;
+
+		/* If remaining is small (< 1us), finish */
+		if (remaining > -1000 && remaining < 1000)
+			remaining = 0;
+	}
+
+	mutex_unlock(&priv->ptp_adj_lock);
+	return ret;
+}
+
+int s2500_ptp_register(struct s2500_info *priv)
+{
+	struct ptp_clock_info *info = &priv->ptp_clock_info;
+	int ret;
+
+	info->max_adj = 100000000;
+	info->owner = THIS_MODULE;
+	info->adjfine = s2500_ptp_adjfine;
+	info->gettimex64 = s2500_ptp_get_time64;
+	info->settime64 = s2500_ptp_set_time64;
+	info->adjtime = s2500_ptp_adjtime;
+
+	ret = oa_tc6_ptp_register(priv->tc6, info);
+	if (ret)
+		dev_err(&priv->spi->dev, "PTP registration failed");
+	return ret;
+}
+

-- 
2.43.0



^ permalink raw reply related

* [PATCH net-next v5 14/15] dt-bindings: net: add onsemi's S2500
From: Selvamani Rajagopal via B4 Relay @ 2026-06-14 17:00 UTC (permalink / raw)
  To: Andrew Lunn, Piergiorgio Beruto, Heiner Kallweit, Russell King,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Andrew Lunn, Parthiban Veerasooran, Selva Rajagopal,
	Richard Cochran, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Simon Horman, Jonathan Corbet, Shuah Khan
  Cc: netdev, linux-kernel, devicetree, linux-doc, Jerry Ray,
	Selvamani Rajagopal
In-Reply-To: <20260614-s2500-mac-phy-support-v5-0-89874b72f725@onsemi.com>

From: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>

Add YAML device tree binding for the onsemi S2500 IEEE 802.3cg
compliant Ethernet transceiver device.

We use IRQF_TRIGGER_FALLING, though OPEN Alliance 10BASE-T1x
Serial Interface specification calls for IRQF_TRIGGER_LOW.

This is to match IRQF_TRIGGER_FALLING used by OA TC6 framework code.
This bug fix requires changes to the stable branch. At that time,
this will be changed to IRQF_TRIGGER_LOW.

---
changes in v5
  - no changes
changes in v4:
  - added spi-max-frequency as suggested by AI review
  - changed interrupt to IRQ_TYPE_EDGE_FALLING as it is
    being taken care in net (stable) branch
changes in v3
  - Removed URL link that failed verification
changes in v2
  - removed spi-max-frequency entry
  - changed the compatible string to s2500
changes in v1
  - Added the first version of YAML file for onsemi MAC-PHY

Signed-off-by: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>
---
 .../devicetree/bindings/net/onnn,s2500.yaml        | 67 ++++++++++++++++++++++
 1 file changed, 67 insertions(+)

diff --git a/Documentation/devicetree/bindings/net/onnn,s2500.yaml b/Documentation/devicetree/bindings/net/onnn,s2500.yaml
new file mode 100644
index 000000000000..11edf10508d9
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/onnn,s2500.yaml
@@ -0,0 +1,67 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/onnn,s2500.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: onsemi S2500 10BASE-T1S MACPHY Ethernet Controllers
+
+maintainers:
+  - Piergiorgio Beruto <pier.beruto@onsemi.com>
+  - Selva Rajagopal <Selvamani.Rajagopal@onsemi.com>
+
+description:
+  The S2500 combines a Media Access Controller (MAC) and an
+  Ethernet PHY to enable 10BASE‑T1S networks. The Ethernet Media Access
+  Controller (MAC) module implements a 10 Mbps half duplex Ethernet MAC,
+  compatible with the IEEE 802.3 standard and a 10BASE-T1S physical layer
+  transceiver integrated into the S2500. The communication between
+  the host and the MAC-PHY is specified in the OPEN Alliance 10BASE-T1x
+  MACPHY Serial Interface (TC6).
+
+allOf:
+  - $ref: /schemas/net/ethernet-controller.yaml#
+  - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+properties:
+  compatible:
+    const: onnn,s2500
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    description:
+      Interrupt from MAC-PHY asserted in the event of Receive Chunks
+      Available, Transmit Chunk Credits Available and Extended Status
+      Event.
+    maxItems: 1
+
+  spi-max-frequency:
+    maximum: 25000000
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - spi-max-frequency
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    spi {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      ethernet@0 {
+        compatible = "onnn,s2500";
+        reg = <0>;
+        pinctrl-names = "default";
+        pinctrl-0 = <&eth0_pins>;
+        interrupt-parent = <&gpio>;
+        interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
+        spi-max-frequency = <15000000>;
+      };
+    };

-- 
2.43.0



^ permalink raw reply related

* [PATCH net-next v5 15/15] Documentation: networking: Add timestamp related APIs to OA TC6 framework
From: Selvamani Rajagopal via B4 Relay @ 2026-06-14 17:00 UTC (permalink / raw)
  To: Andrew Lunn, Piergiorgio Beruto, Heiner Kallweit, Russell King,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Andrew Lunn, Parthiban Veerasooran, Selva Rajagopal,
	Richard Cochran, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Simon Horman, Jonathan Corbet, Shuah Khan
  Cc: netdev, linux-kernel, devicetree, linux-doc, Jerry Ray,
	Selvamani Rajagopal
In-Reply-To: <20260614-s2500-mac-phy-support-v5-0-89874b72f725@onsemi.com>

From: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>

Added new APIs to support hardware timestamp feature as defined in
OPEN Alliance 10BASE-T1x MAC-PHY serial interface specification.

Expanded read/write APIs to take new mms parameter to avoid
bit manipulation before calling regular read/write APIs

Signed-off-by: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>

---
changes in v5
  - Fixed "no blank line" issues in four places
changes in v4
  - Added information for new APIs in OA TC6 framework
changes in v3
  - Added more information for documentation
changes in v2
  - Removed the changes to the existing API
  - Updated information about newly added APIs
changes in v1
  - Added changes to an existing API
  - Added documentation to new hardware timestamp related APIs
---
 Documentation/networking/oa-tc6-framework.rst | 80 +++++++++++++++++++++++++++
 1 file changed, 80 insertions(+)

diff --git a/Documentation/networking/oa-tc6-framework.rst b/Documentation/networking/oa-tc6-framework.rst
index fe2aabde923a..325d299d1a7d 100644
--- a/Documentation/networking/oa-tc6-framework.rst
+++ b/Documentation/networking/oa-tc6-framework.rst
@@ -153,6 +153,10 @@ OPEN Alliance TC6 Framework
 - Forwards the received Ethernet frame from 10Base-T1x MAC-PHY to n/w
   subsystem.
 
+- If supported by the hardware and enabled, updates hardware timestamp
+  in skb, when indicated by one of the three timestamp capture registers
+  through TSC fields of the header.
+
 Data Transaction
 ~~~~~~~~~~~~~~~~
 
@@ -495,3 +499,79 @@ the MAC-PHY.
 Zero align receive frame feature can be enabled to align all receive ethernet
 frames data to start at the beginning of any receive data chunk payload with a
 start word offset (SWO) of zero.
+
+.. c:function:: int oa_tc6_ptp_register(struct oa_tc6 *tc6, \
+                                        struct ptp_clock_info *info);
+
+Registers the PTP hardware clock related functions with the kernel.
+This API simply registers. Initialization of the fields in the
+ptp_clock_info structure are left to the vendor as programming hardware
+timer is expected to be vendor dependent. The fields max_adj, owner,
+and all the functions for the clock operations, like adjfine, gettimex64,
+settime64, adjtime are expected to be initialized in the structure before
+calling the registering the hardware clock.
+
+.. c:function:: void oa_tc6_ptp_unregister(struct oa_tc6 *tc6);
+
+Unregisters the PTP hardware clock related callbacks.
+
+.. c:function:: int oa_tc6_ioctl(struct oa_tc6 *tc6, struct ifreq *rq, \
+                                 int cmd);
+
+ioctl interface to handle hardware timestamp and PHY related commands.
+
+.. c:function:: int oa_tc6_get_ts_info(struct oa_tc6 *tc6, \
+                                       struct kernel_ethtool_ts_info *info);
+
+Provides timestamp related settings that are supported to ethtool.
+
+.. c:function:: void oa_tc6_hwtstamp_get(struct oa_tc6 *tc6, \
+                                         struct kernel_hwtstamp_config *cfg);
+
+Returns hardware timestamp configuration. Part of net_device_ops callbacks.
+
+.. c:function:: void oa_tc6_get_ts_stats(struct oa_tc6 *tc6, \
+                                         struct ethtool_ts_stats *ts_stats);
+
+Provides hardware timestamp related traffic statistics for ethtool.
+
+.. c:function:: int oa_tc6_hwtstamp_set(struct oa_tc6 *tc6, \
+                                         struct kernel_hwtstamp_config *cfg);
+
+Helper to set hardware timestamp configuration. Part of net_device_ops
+callbacks.
+
+.. c:function:: void oa_tc6_set_vend1_mms(struct oa_tc6 *tc6, int mms);
+
+Helper to map MDIO_MMD_VEND1 command to vendor specific Memory Map Select
+(MMS) value. This function offers flexibility for vendors that may have
+used any MMS value between 10 and 15 as allowed by the specification.
+MDIO_MMD_VEND2 is already mapped to MMS4 in the OA TC6 frame work code.
+
+.. c:function:: int oa_tc6_write_registers_mms(struct oa_tc6 *tc6, \
+                                               u16 address, u16 mms, \
+                                               u32 value[], u8 length);
+
+Writing multiple consecutive registers starting from @address for the
+given @mms memory map selector in the MAC-PHY. Maximum of 128 consecutive
+registers can be written starting at @address.
+
+.. c:function:: int oa_tc6_write_register_mms(struct oa_tc6 *tc6, \
+                                              u16 address, u16 mms, \
+                                              u32 value);
+
+Write a single register addressed by @address and @mms in the MAC-PHY.
+
+.. c:function:: int oa_tc6_read_registers_mms(struct oa_tc6 *tc6, \
+                                              u16 address, u16 mms, \
+                                              u32 value[], u8 length);
+
+Reading multiple consecutive registers starting from @address for the
+given @mms memory map selector value, in the MAC-PHY. Maximum of 128
+consecutive registers can be read starting at @address.
+
+.. c:function:: int oa_tc6_read_register_mms(struct oa_tc6 *tc6, \
+                                             u16 address, u16 mms, \
+                                             u32 *value);
+
+Read a single register addressed by @address and @mms in the MAC-PHY.

-- 
2.43.0



^ permalink raw reply related

* Re: [PATCH v4 2/2] iio: dac: Add AD5529R DAC driver support
From: Jonathan Cameron @ 2026-06-14 17:09 UTC (permalink / raw)
  To: Janani Sunil
  Cc: Lars-Peter Clausen, Michael Hennerich, David Lechner,
	Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Philipp Zabel, Jonathan Corbet, Shuah Khan,
	linux-iio, devicetree, linux-kernel, linux-doc, Janani Sunil
In-Reply-To: <20260609-ad5529r-driver-v4-2-2e4c02234a1a@analog.com>

On Tue, 9 Jun 2026 17:00:21 +0200
Janani Sunil <janani.sunil@analog.com> wrote:

> Add support for AD5529R 16-channel, 12/16 bit Digital to Analog Converter
> 
> Signed-off-by: Janani Sunil <janani.sunil@analog.com>

A couple of minor things inline.

The sashiko thing about not relying on how gpio resets work and so 
adding an explicit assert before deassert is best practice so make sure
to tidy that up as well.

> diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
> index 003431798498..f35e060b3643 100644
> --- a/drivers/iio/dac/Makefile
> +++ b/drivers/iio/dac/Makefile
> @@ -18,6 +18,7 @@ obj-$(CONFIG_AD5446) += ad5446.o
>  obj-$(CONFIG_AD5446_SPI) += ad5446-spi.o
>  obj-$(CONFIG_AD5446_I2C) += ad5446-i2c.o
>  obj-$(CONFIG_AD5449) += ad5449.o
> +obj-$(CONFIG_AD5529R) += ad5529r.o
>  obj-$(CONFIG_AD5592R_BASE) += ad5592r-base.o
>  obj-$(CONFIG_AD5592R) += ad5592r.o
>  obj-$(CONFIG_AD5593R) += ad5593r.o
> diff --git a/drivers/iio/dac/ad5529r.c b/drivers/iio/dac/ad5529r.c
> new file mode 100644
> index 000000000000..d2d0287d0f95
> --- /dev/null
> +++ b/drivers/iio/dac/ad5529r.c
> @@ -0,0 +1,517 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * AD5529R Digital-to-Analog Converter Driver
> + * 16-Channel, 12/16-Bit, 40V High Voltage Precision DAC
> + *
> + * Copyright 2026 Analog Devices Inc.
> + * Author: Janani Sunil <janani.sunil@analog.com>
> + */
> +
> +#include <linux/array_size.h>
> +#include <linux/bits.h>
> +#include <linux/delay.h>
> +#include <linux/dev_printk.h>
> +#include <linux/err.h>
> +#include <linux/errno.h>
> +#include <linux/iio/iio.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include <linux/property.h>
> +#include <linux/regmap.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/reset.h>
> +#include <linux/spi/spi.h>
> +
> +#define AD5529R_REG_INTERFACE_CONFIG_A		0x00
> +#define AD5529R_REG_DEVICE_CONFIG		0x02
> +#define AD5529R_REG_CHIP_GRADE			0x06
> +#define AD5529R_REG_SCRATCH_PAD			0x0A
> +#define AD5529R_REG_SPI_REVISION		0x0B
> +#define AD5529R_REG_VENDOR_H			0x0D
> +#define AD5529R_REG_STREAM_MODE			0x0E
> +#define AD5529R_REG_INTERFACE_STATUS_A		0x11
> +#define AD5529R_REG_MULTI_DAC_CH_SEL		0x14
> +#define AD5529R_REG_OUT_RANGE_BASE		0x3C
> +#define AD5529R_REG_OUT_RANGE(ch)		(AD5529R_REG_OUT_RANGE_BASE + (ch) * 2)
> +#define AD5529R_REG_DAC_INPUT_A_BASE		0x148
> +#define AD5529R_REG_DAC_INPUT_A(ch)		(AD5529R_REG_DAC_INPUT_A_BASE + (ch) * 2)
> +#define AD5529R_REG_DAC_DATA_READBACK_BASE	0x16A
> +#define AD5529R_REG_TSENS_ALERT_FLAG		0x18C
> +#define AD5529R_REG_TSENS_SHTD_FLAG		0x18E
> +#define AD5529R_REG_FUNC_BUSY			0x1A0
> +#define AD5529R_REG_REF_SEL			0x1A2
> +#define AD5529R_REG_INIT_CRC_ERR_STAT		0x1A4
> +#define AD5529R_REG_MULTI_DAC_HOTPATH_SW_LDAC	0x1A8
> +
> +#define   AD5529R_INTERFACE_CONFIG_A_SW_RESET	(BIT(7) | BIT(0))
> +#define   AD5529R_INTERFACE_CONFIG_A_ADDR_ASCENSION	BIT(5)
> +#define   AD5529R_INTERFACE_CONFIG_A_SDO_ENABLE	BIT(4)
> +#define   AD5529R_REF_SEL_INTERNAL_REF		BIT(0)

This extra indent thing only makes sense if they are next to the register
address definitions - the intent is to make the visually different.

e.g.

#define AD5529R_REG_INTERFACE_CONFIG_A		0x00
#define   AD5529R_INTERFACE_CONFIG_A_SW_RESET	(BIT(7) | BIT(0))
#define   AD5529R_INTERFACE_CONFIG_A_ADDR_ASCENSION	BIT(5)
#define   AD5529R_INTERFACE_CONFIG_A_SDO_ENABLE	BIT(4)
#define AD5529R_REG_DEVICE_CONFIG		0x02
#define AD5529R_REG_CHIP_GRADE			0x06
#define AD5529R_REG_SCRATCH_PAD			0x0A
#define AD5529R_REG_SPI_REVISION		0x0B
#define AD5529R_REG_VENDOR_H			0x0D
#define AD5529R_REG_STREAM_MODE			0x0E
#define AD5529R_REG_INTERFACE_STATUS_A		0x11
#define AD5529R_REG_MULTI_DAC_CH_SEL		0x14
#define AD5529R_REG_OUT_RANGE_BASE		0x3C
#define AD5529R_REG_OUT_RANGE(ch)		(AD5529R_REG_OUT_RANGE_BASE + (ch) * 2)
#define AD5529R_REG_DAC_INPUT_A_BASE		0x148
#define AD5529R_REG_DAC_INPUT_A(ch)		(AD5529R_REG_DAC_INPUT_A_BASE + (ch) * 2)
#define AD5529R_REG_DAC_DATA_READBACK_BASE	0x16A
#define AD5529R_REG_TSENS_ALERT_FLAG		0x18C
#define AD5529R_REG_TSENS_SHTD_FLAG		0x18E
#define AD5529R_REG_FUNC_BUSY			0x1A0
#define AD5529R_REG_REF_SEL			0x1A2
#define   AD5529R_REF_SEL_INTERNAL_REF		BIT(0)


> +#define   AD5529R_MAX_REGISTER			0x232
> +#define   AD5529R_8BIT_REG_MAX			0x13
> +#define   AD5529R_SPI_READ_FLAG			0x80
There is no reason to do the extra indent for these 3.


> +static int ad5529r_parse_channel_ranges(struct device *dev,
> +					struct ad5529r_state *st)
> +{
> +	int ret, range_idx;
> +	u32 ch;
> +	s32 vals[2];
> +
> +	device_for_each_child_node_scoped(dev, child) {
> +		range_idx = AD5529R_RANGE_0V_5V;
> +
> +		ret = fwnode_property_read_u32(child, "reg", &ch);
> +		if (ret)
> +			return dev_err_probe(dev, ret,
> +					     "Missing reg property in channel node\n");
> +
> +		if (ch >= 16)
> +			return dev_err_probe(dev, -EINVAL,
> +					     "Invalid channel number: %u\n", ch);
> +
> +		/* Read u32 property into s32 to handle negative voltage ranges */

> +		if (!fwnode_property_read_u32_array(child,
> +						    "adi,output-range-microvolt",
> +						    (u32 *)vals, ARRAY_SIZE(vals))) {
> +			range_idx = ad5529r_find_output_range(vals);
> +			if (range_idx < 0)
> +				return dev_err_probe(dev, range_idx,
> +						     "Invalid range [%d %d] for ch %u\n",
> +						     vals[0], vals[1], ch);
> +		}

The indent is a little larger than ideal, but slight preference for doing explicit
checking for optional properties so as to separate buggy ones from deliberately not
there.

		if (fwnode_property_present("adi,output-range-microvolt")) {
			/* Read u32 property into s32 to handle negative voltage ranges */
			ret = fwnode_property_read_u32_array(child,
							    "adi,output-range-microvolt",
							    (u32 *)vals, ARRAY_SIZE(vals));
			if (ret < 0) // strictly if (ret) but the return value for this one is complex.
				return dev_err_probe();
			range_idx = ad5529r_find_output_range(vals);
			if (range_idx < 0)
				return dev_err_probe(dev, range_idx,
						     "Invalid range [%d %d] for ch %u\n",
						     vals[0], vals[1], ch);
		} else {
			range_idx = AD5529R_RANGE_0V_5V;
		}
> +
> +		st->output_range_idx[ch] = range_idx;
> +		ret = regmap_write(st->regmap_16bit,
> +				   AD5529R_REG_OUT_RANGE(ch), range_idx);
> +		if (ret)
> +			return dev_err_probe(dev, ret,
> +					     "Failed to configure range for ch %u\n",
> +					     ch);
> +	}
> +
> +	return 0;
> +}



^ permalink raw reply

* Re: [PATCH v2 2/4] iio: adc: mt6323-auxadc: add mt6323 PMIC AUXADC driver
From: Jonathan Cameron @ 2026-06-14 17:11 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Roman Vivchar, David Lechner, Nuno Sá, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
	AngeloGioacchino Del Regno, Lee Jones, linux-iio, devicetree,
	linux-kernel, linux-arm-kernel, linux-mediatek, Ben Grisdale
In-Reply-To: <aihqeLKHC3PP436t@black.igk.intel.com>

On Tue, 9 Jun 2026 21:33:12 +0200
Andy Shevchenko <andriy.shevchenko@intel.com> wrote:

> On Tue, Jun 09, 2026 at 07:15:42PM +0000, Roman Vivchar wrote:
> > On Tuesday, June 9th, 2026 at 9:30 PM, Andy Shevchenko <andriy.shevchenko@intel.com> wrote:  
> > > On Tue, Jun 09, 2026 at 04:31:59PM +0300, Roman Vivchar via B4 Relay wrote:  
> 
> ...
> 
> > > > +	case IIO_CHAN_INFO_RAW:
> > > > +		scoped_guard(mutex, &auxadc->lock) {  
> > >
> > > I'm wondering why we haven't moved to guard()() here  
> > 
> > The compiler would complain about 'cannot jump from switch statement'
> > due to default case.  

See the brackets Andy suggested.  That error is what you get without specifically
defining the scope.


> 
> I am not sure I follow. See the examples in the existing drivers. They are
> warning clean in that sense.
> 


^ permalink raw reply

* Re: [PATCH v2 2/4] iio: adc: mt6323-auxadc: add mt6323 PMIC AUXADC driver
From: Jonathan Cameron @ 2026-06-14 17:22 UTC (permalink / raw)
  To: Roman Vivchar via B4 Relay
  Cc: rva333, David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
	AngeloGioacchino Del Regno, Lee Jones, linux-iio, devicetree,
	linux-kernel, linux-arm-kernel, linux-mediatek, Ben Grisdale
In-Reply-To: <20260609-mt6323-adc-v2-2-aa93a22309f9@protonmail.com>

On Tue, 09 Jun 2026 16:31:59 +0300
Roman Vivchar via B4 Relay <devnull+rva333.protonmail.com@kernel.org> wrote:

> From: Roman Vivchar <rva333@protonmail.com>
> 
> The mt6323 AUXADC is a 15-bit ADC used for system monitoring. This driver
> provides support for reading various channels including battery and
> charger voltages, battery and chip temperature, current sensing and
> accessory detection.
> 
> Add a driver for the AUXADC found in the MediaTek mt6323 PMIC.
> 
> Tested-by: Ben Grisdale <bengris32@protonmail.ch> # Amazon Echo Dot (2nd Generation)
> Signed-off-by: Roman Vivchar <rva333@protonmail.com>
Make sure to take a look at:
https://sashiko.dev/#/patchset/20260609-mt6323-adc-v2-0-aa93a22309f9%40protonmail.com

A few minor other comments inline.

> diff --git a/drivers/iio/adc/mt6323-auxadc.c b/drivers/iio/adc/mt6323-auxadc.c
> new file mode 100644
> index 000000000000..f2cef989d3ce
> --- /dev/null
> +++ b/drivers/iio/adc/mt6323-auxadc.c

> +
> +#define MTK_PMIC_IIO_CHAN(_name, _chan, _addr)                  \
> +{                                                               \
> +	.type = IIO_VOLTAGE,                                    \
> +	.indexed = 1,                                           \
> +	.channel = _chan,                                       \
> +	.address = _addr,                                       \
> +	.datasheet_name = __stringify(_name),                   \
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |          \
> +			      BIT(IIO_CHAN_INFO_SCALE),         \
> +}
> +
> +static const struct iio_chan_spec mt6323_auxadc_channels[] = {
> +	MTK_PMIC_IIO_CHAN(baton2,    MT6323_AUXADC_BATON2,    MT6323_AUXADC_ADC6),
> +	MTK_PMIC_IIO_CHAN(ch6,       MT6323_AUXADC_CH6,       MT6323_AUXADC_ADC11),
> +	MTK_PMIC_IIO_CHAN(bat_temp,  MT6323_AUXADC_BAT_TEMP,  MT6323_AUXADC_ADC5),

Reasonable query from Sashiko on why temperature channels are presented as voltages.
If for some reason that is the right choice, then maybe a comment here.


> +	MTK_PMIC_IIO_CHAN(chip_temp, MT6323_AUXADC_CHIP_TEMP, MT6323_AUXADC_ADC4),
> +	MTK_PMIC_IIO_CHAN(vcdt,      MT6323_AUXADC_VCDT,      MT6323_AUXADC_ADC2),
> +	MTK_PMIC_IIO_CHAN(baton1,    MT6323_AUXADC_BATON1,    MT6323_AUXADC_ADC3),
> +	MTK_PMIC_IIO_CHAN(isense,    MT6323_AUXADC_ISENSE,    MT6323_AUXADC_ADC1),
> +	MTK_PMIC_IIO_CHAN(batsns,    MT6323_AUXADC_BATSNS,    MT6323_AUXADC_ADC0),
> +	MTK_PMIC_IIO_CHAN(accdet,    MT6323_AUXADC_ACCDET,    MT6323_AUXADC_ADC7),
> +};
> +
> +/*
> + * The MediaTek MT6323 (as well as a lot of other PMICs) has the following hierarchy:
> + * PMIC AUXADC <- PMIC MFD <- SoC PWRAP (wrapper for PWRAP FSM)
> + *
> + * Therefore, PWRAP regmap should be obtained using dev->parent->parent.
> + */
> +struct mt6323_auxadc {
> +	struct regmap *regmap;
> +	struct mutex lock;
Locks should always have a comment on what data they are protecting.
I think this one is about protecting the state of a device during a channel read
by serializing those reads.

> +};
>
> +
> +static int mt6323_auxadc_request(struct mt6323_auxadc *auxadc,
> +				 unsigned long channel)
> +{
> +	struct regmap *map = auxadc->regmap;
> +	int ret;
> +
> +	ret = regmap_set_bits(map, MT6323_AUXADC_CON11, AUXADC_CON11_VBUF_EN);
> +	if (ret)
> +		return ret;
> +
> +	return regmap_set_bits(map, MT6323_AUXADC_CON22, BIT(channel));

I'm not sure whether the sashiko question on this is valid or not. Make sure to take
a look.

https://sashiko.dev/#/patchset/20260609-mt6323-adc-v2-0-aa93a22309f9%40protonmail.com
You may have carefully selected the numbering so the channel numbering matches
the bits in this register.  If so, it is probably worth a comment in the header
to provide a cross reference.  No idea if Sashiko will notice that, but at least
humans should!

> +}
> +

> +
> +static int mt6323_auxadc_read_raw(struct iio_dev *indio_dev,
> +				  const struct iio_chan_spec *chan,
> +				  int *val, int *val2, long mask)
> +{
> +	struct mt6323_auxadc *auxadc = iio_priv(indio_dev);
> +	int ret, mult;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SCALE:
> +		if (chan->channel == MT6323_AUXADC_ISENSE ||
> +		    chan->channel == MT6323_AUXADC_BATSNS)
> +			mult = 4;
> +		else
> +			mult = 1;
> +
> +		/* 1800mV full range with 15-bit resolution. */
> +		*val = mult * 1800;
> +		*val2 = 15;
> +
> +		return IIO_VAL_FRACTIONAL_LOG2;
> +	case IIO_CHAN_INFO_RAW:

What Andy suggested here is the preferred path in IIO at least.
Mainly because it reduced indent without hurting readability.
Just be careful to define the scope with { }


> +		scoped_guard(mutex, &auxadc->lock) {
> +			ret = mt6323_auxadc_prepare_channel(auxadc);
> +			if (ret)
> +				return ret;
> +
> +			ret = mt6323_auxadc_request(auxadc, chan->channel);
> +			if (ret)
> +				return ret;
> +
> +			/* Hardware limitation: the AUXADC needs a delay to become ready. */
> +			fsleep(300);
> +
> +			ret = mt6323_auxadc_read(auxadc, chan, val);
> +
> +			if (mt6323_auxadc_release(auxadc, chan->channel))
> +				dev_err(&indio_dev->dev,
> +					"failed to release channel %d\n", chan->channel);
> +
> +			if (ret)
> +				return ret;
> +		}
> +		return IIO_VAL_INT;
> +	default:
> +		return -EINVAL;
> +	}
> +}


^ permalink raw reply

* Re: [PATCH v2 02/12] dt-bindings: iio: dac: ad5696: rework on power supplies
From: Jonathan Cameron @ 2026-06-14 17:24 UTC (permalink / raw)
  To: Conor Dooley
  Cc: Rodrigo Alencar, rodrigo.alencar, Michael Auchter, linux,
	linux-iio, devicetree, linux-kernel, linux-hardening,
	Michael Hennerich, David Lechner, Andy Shevchenko, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Philipp Zabel, Kees Cook,
	Gustavo A. R. Silva, Conor Dooley
In-Reply-To: <20260609-manhunt-napkin-04b11606ea49@spud>

On Tue, 9 Jun 2026 17:12:29 +0100
Conor Dooley <conor@kernel.org> wrote:

> On Tue, Jun 09, 2026 at 12:40:08PM +0100, Rodrigo Alencar wrote:
> > On 09/06/26 10:28, sashiko-bot@kernel.org wrote:  
> > > Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
> > > - [High] Adding `vdd-supply`, `vlogic-supply`, and unconditionally requiring `vref-supply` breaks backward compatibility for existing device trees.
> > > - [Medium] Making `vlogic-supply` unconditionally required forces device trees to document a non-existent hardware connection for single-channel devices.
> > > --
> > > 
> > > commit 8357cd3efa5f819678f24f0cbf0a39208f4078bc
> > > Author: Rodrigo Alencar <rodrigo.alencar@analog.com>
> > > Subject: dt-bindings: iio: dac: ad5696: rework on power supplies
> > > 
> > > This commit reworks the power supply bindings for the ad5696 DAC series. It
> > > adds vdd-supply, vlogic-supply, and vref-supply, deprecates vcc-supply, and
> > > updates the required properties.
> > >   
> > > > diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml
> > > > index 2dcc049f30e91..e5fbaec4adf71 100644
> > > > --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml
> > > > +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml  
> > > 
> > > [ ... ]
> > >   
> > > > @@ -55,8 +65,21 @@ properties:
> > > >  required:
> > > >    - compatible
> > > >    - reg
> > > > +  - vdd-supply
> > > > +  - vlogic-supply  
> > > 
> > > [Severity: High]
> > > Does adding vdd-supply and vlogic-supply to the global required list break
> > > backward compatibility for existing device trees? Older device trees that
> > > omitted these will now fail dtbs_check.  
> > 
> > Yes, for correctness, it breaks the dtbs_check. In the driver side
> > we would rely on the dummy/stub regulator in case those are not declared.  
> 
> Ye I wouldn't be overly worried here. Sashiko has particular difficulty
> with these kinds of changes.
> 
> >   
> > > [Severity: Medium]
> > > Does making vlogic-supply unconditionally required force device trees to
> > > document a non-existent hardware connection? Single-channel devices in this
> > > family (such as adi,ad5693, adi,ad5693r, and adi,ad5311r) physically do not
> > > have a dedicated VLOGIC pin.  
> > 
> > VLOGIC does exist for those parts. However, as mentioned before, for some
> > tiny packages it is internally connected to VDD:
> > 
> > - 10-Lead MSOP package for AD5693R/AD5691R exposes the VLOGIC pin
> > - 8-Lead LFCSP package for AD5693R/AD5692R/AD5691R/AD5693 does NOT expose the VLOGIC pin
> > 
> > I think we agreed to make it required and user can just repeat the vdd-supply
> > value or not have it at all. Driver would rely on the dummy/stub regulator.  
> 
> Yeah, I dunno. I think this is just a judgement call, and maybe worth
> having a comment in the binding for to explain it.

Yes. A breadcrumb type of comment that just says that it may be internally wired
in which case set it to same as the other one.

> 
> >    
> > > >  
> > > >  allOf:
> > > > +  - if:
> > > > +      properties:
> > > > +        compatible:
> > > > +          contains:
> > > > +            anyOf:
> > > > +              - const: adi,ad5693
> > > > +              - const: adi,ad5694
> > > > +              - const: adi,ad5696
> > > > +    then:
> > > > +      required:
> > > > +        - vref-supply  
> > > 
> > > [Severity: High]
> > > Does unconditionally requiring vref-supply break older device trees that
> > > correctly utilized the formerly valid vcc-supply?
> > > 
> > > To preserve DT ABI compatibility, should the schema allow either vcc-supply or
> > > vref-supply to satisfy the reference voltage requirement?  
> > 
> > Same here. For correctness, it breaks DT ABI.
> >   
> > > -- 
> > > Sashiko AI review · https://sashiko.dev/#/patchset/20260609-ad5686-new-features-v2-0-70b423f5c76d@analog.com?part=2  
> > 
> > -- 
> > Kind regards,
> > 
> > Rodrigo Alencar  


^ permalink raw reply

* Re: [PATCH v2 05/12] iio: dac: ad5686: add support for missing power supplies
From: Jonathan Cameron @ 2026-06-14 17:29 UTC (permalink / raw)
  To: Rodrigo Alencar via B4 Relay
  Cc: rodrigo.alencar, Michael Auchter, linux, linux-iio, devicetree,
	linux-kernel, linux-hardening, Michael Hennerich, David Lechner,
	Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Philipp Zabel, Kees Cook, Gustavo A. R. Silva
In-Reply-To: <20260609-ad5686-new-features-v2-5-70b423f5c76d@analog.com>

On Tue, 09 Jun 2026 11:13:00 +0100
Rodrigo Alencar via B4 Relay <devnull+rodrigo.alencar.analog.com@kernel.org> wrote:

> From: Rodrigo Alencar <rodrigo.alencar@analog.com>
> 
> Get and enable regulators for vdd, vlogic and vref input power pins. Vdd
> is the input power supply, while vlogic powers the digital side. vref is
> replacing vcc, which is being deprecated, but still supported. The value
> of vref_mv is checked so that a device without internal voltage reference
> cannot proceed without an explicit supply. For correct operation, vdd and
> vlogic are required, then devm_regulator_get_enable() is used so the
> driver can still work without them by using the stub/dummy regulators.
> Error report uses dev_err_probe(), which helps debugging an init issue.
> 
> Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
Possibly the comment below falls into the bikeshed colour category.
I'm not really that fussed either way.

> ---
>  drivers/iio/dac/ad5686.c | 21 +++++++++++++++++++--
>  1 file changed, 19 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
> index 5840fda4b011..fc3863274b29 100644
> --- a/drivers/iio/dac/ad5686.c
> +++ b/drivers/iio/dac/ad5686.c
> @@ -8,6 +8,8 @@
>  #include <linux/array_size.h>
>  #include <linux/bitfield.h>
>  #include <linux/bitops.h>
> +#include <linux/delay.h>
> +#include <linux/dev_printk.h>
>  #include <linux/errno.h>
>  #include <linux/export.h>
>  #include <linux/kstrtox.h>
> @@ -484,12 +486,27 @@ int ad5686_probe(struct device *dev,
>  	st->ops = ops;
>  	st->chip_info = chip_info;
>  
> -	ret = devm_regulator_get_enable_read_voltage(dev, "vcc");
> +	ret = devm_regulator_get_enable(dev, "vdd");
> +	if (ret)
> +		return dev_err_probe(dev, ret, "failed to enable vdd supply\n");
> +
> +	ret = devm_regulator_get_enable(dev, "vlogic");
> +	if (ret)
> +		return dev_err_probe(dev, ret, "failed to enable vlogic supply\n");
> +
> +	ret = devm_regulator_get_enable_read_voltage(dev, "vref");
> +	if (ret == -ENODEV) /* vcc-supply is deprecated, but supported still */
> +		ret = devm_regulator_get_enable_read_voltage(dev, "vcc");
>  	if (ret < 0 && ret != -ENODEV)
> -		return ret;
> +		return dev_err_probe(dev, ret, "failed to read vref voltage\n");
>  
>  	st->use_internal_vref = ret == -ENODEV;
I think I'd slightly prefer this as
	ret = devm_regulator_get_enable_read_voltage(dev, "vref");
	if (ret == -ENODEV) /* vcc-supply is deprecated, but supported still */
		ret = devm_regulator_get_enable_read_voltage(dev, "vcc");
	if (ret == -ENODEV)
		st->use_internal_vref = true;
	else if (ret < 0)
		return dev_err_probe()...
(which I think is functionally the same).

But if you strongly prefer yours I guess it is readable enough.




>  	st->vref_mv = st->use_internal_vref ? st->chip_info->int_vref_mv : ret / 1000;
> +	if (!st->vref_mv)
> +		return dev_err_probe(dev, -EINVAL,
> +				     "invalid or not provided vref voltage\n");
> +
> +	fsleep(5); /* power-up time */
>  
>  	/* Initialize masks to all ones */
>  	st->pwr_down_mask = ~0;
> 


^ permalink raw reply

* Re: [PATCH v2 06/12] iio: dac: ad5686: consume optional reset signal
From: Jonathan Cameron @ 2026-06-14 17:33 UTC (permalink / raw)
  To: Rodrigo Alencar
  Cc: rodrigo.alencar, Michael Auchter, linux, linux-iio, devicetree,
	linux-kernel, linux-hardening, Michael Hennerich, David Lechner,
	Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Philipp Zabel, Kees Cook, Gustavo A. R. Silva
In-Reply-To: <ltafhuqwbdnj7qadn2p5f7exwcpz4fvr5jaizj7q4peqgt2x4b@z23ss3usjskv>

On Tue, 9 Jun 2026 12:17:01 +0100
Rodrigo Alencar <455.rodrigo.alencar@gmail.com> wrote:

> On 09/06/26 10:29, sashiko-bot@kernel.org wrote:
> > Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> > - [Medium] Premature reset line deassertion during power ramp-up due to the use of '_deasserted' combined with a manual pulse.
> > --
> > 
> > commit 61717352bc99739a9f89195c3320a934e033ad38
> > Author: Rodrigo Alencar <rodrigo.alencar@analog.com>
> > 
> > iio: dac: ad5686: consume optional reset signal
> > 
> > Add RESET pin GPIO support through an optional reset control, which is local
> > to the probe function, and include a delay for the required reset pulse width.
> >   
> > > diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
> > > index fc3863274b291..700798b80f044 100644
> > > --- a/drivers/iio/dac/ad5686.c
> > > +++ b/drivers/iio/dac/ad5686.c  
> > [ ... ]  
> > > @@ -506,7 +508,15 @@ int ad5686_probe(struct device *dev,
> > >  		return dev_err_probe(dev, -EINVAL,
> > >  				     "invalid or not provided vref voltage\n");
> > >  
> > > +	rstc = devm_reset_control_get_optional_exclusive_deasserted(dev, NULL);  
> > 
> > [Severity: Medium]
> > Does this prematurely deassert the reset line during power ramp-up?
> > 
> > Because devm_reset_control_get_optional_exclusive_deasserted() automatically
> > and immediately deasserts the reset line upon acquisition, the reset line is
> > driven high while the power supply is still stabilizing, before the 5 us
> > power-up delay has elapsed.
> > 
> > This creates an unwanted rising edge on the reset pin during power ramp-up,
> > which could cause the device to enter an unknown state or latch up.
> > 
> > Should this use devm_reset_control_get_optional_exclusive() instead, which
> > does not modify the line state during acquisition, allowing the manual toggle
> > sequence below to safely reset the device after power is stable?  
> 
> It has been discussed that it is better to get deasserted and manually create
> the reset pulse. Will move this down (after the fsleep(5) power-up time). Or
> better move this up (before the regulator enable)...

I don't think we need to ensure a pulse. Just that the line is asserted before
deassert.  So to my mind sashiko is correct with it's suggestion.
Just get it in unknown state, ensure that it is asserted (may already be),
then deassert.


> 
> > > +	if (IS_ERR(rstc))
> > > +		return dev_err_probe(dev, PTR_ERR(rstc),
> > > +				     "Failed to get reset control\n");
> > > +
> > >  	fsleep(5); /* power-up time */
> > > +	reset_control_assert(rstc);
> > > +	fsleep(1); /* reset pulse: comfortably bigger than the spec */
> > > +	reset_control_deassert(rstc);
> > >  
> > >  	/* Initialize masks to all ones */
> > >  	st->pwr_down_mask = ~0;  
> > 
> > -- 
> > Sashiko AI review · https://sashiko.dev/#/patchset/20260609-ad5686-new-features-v2-0-70b423f5c76d@analog.com?part=6  
> 


^ permalink raw reply

* Re: [PATCH v2 11/12] iio: dac: ad5686: write_raw: use guard(mutex)()
From: Jonathan Cameron @ 2026-06-14 17:38 UTC (permalink / raw)
  To: Maxwell Doose
  Cc: Rodrigo Alencar via B4 Relay, rodrigo.alencar, Michael Auchter,
	linux, linux-iio, devicetree, linux-kernel, linux-hardening,
	Michael Hennerich, David Lechner, Andy Shevchenko, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Philipp Zabel, Kees Cook,
	Gustavo A. R. Silva
In-Reply-To: <20260609171758.2077ebd5@linuxescape>

On Tue, 9 Jun 2026 17:17:58 -0500
Maxwell Doose <m32285159@gmail.com> wrote:

> On Tue, 9 Jun 2026 17:13:02 -0500
> Maxwell Doose <m32285159@gmail.com> wrote:
> 
> > On Tue, 09 Jun 2026 11:13:06 +0100
> > Rodrigo Alencar via B4 Relay
> > <devnull+rodrigo.alencar.analog.com@kernel.org> wrote:
> >   
> > > From: Rodrigo Alencar <rodrigo.alencar@analog.com>
> > > 
> > > Use guarded mutex lock to facilitate code review when adding new
> > > attributes. This will allow for early returns, avoiding error-prone
> > > locking and unlocking in error paths. Gain-control support will add
> > > the scale attribute.
> > > 
> > > Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
> > > ---
> > >  drivers/iio/dac/ad5686.c | 14 +++++---------
> > >  1 file changed, 5 insertions(+), 9 deletions(-)
> > >     
> > 
> > I agree with Joshua on this, linux/cleanup.h should definitely be
> > included. However I don't think sending a v3 is worth the trouble
> > (Jonathan may tweak while applying but I wouldn't count on it), so with
> > or without:
> > 
> > Reviewed-by: Maxwell Doose <m32285159@gmail.com>
> >   
> 
> Although I will note that it appears that you haven't converted all of
> the locks to guard(mutex)(). While its only in two spots you should
> probably convert all of them. Either way, patch is obviously correct
> (besides IWYU), so I still stand by my rb.
> 

As far as I'm concerned it is fine to mix and match between guard() and
non guard() if there are places where it is too messy to use guard()

Sometimes it feels nice from a completeness point of view but I'm
definitely not insisting on it!

Jonathan

^ permalink raw reply

* Re: [PATCH v2 12/12] iio: dac: ad5686: add gain control support
From: Jonathan Cameron @ 2026-06-14 17:49 UTC (permalink / raw)
  To: Rodrigo Alencar
  Cc: rodrigo.alencar, Michael Auchter, linux, linux-iio, devicetree,
	linux-kernel, linux-hardening, Michael Hennerich, David Lechner,
	Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Philipp Zabel, Kees Cook, Gustavo A. R. Silva
In-Reply-To: <luo2bvidr3h5gz5qb3ettl2rzzgjsdi6invgzgmm6dx7rki7gn@6faoaxv44vxr>

On Tue, 9 Jun 2026 12:10:02 +0100
Rodrigo Alencar <455.rodrigo.alencar@gmail.com> wrote:

> On 09/06/26 10:37, sashiko-bot@kernel.org wrote:
> > Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
> > - [Medium] ABI Violation: Exposing a writable sysfs attribute to fix a static hardware configuration.
> > - [Medium] State Inconsistency: `st->double_scale` is not rolled back if hardware synchronization fails.
> > - [Low] Data Race: Lockless read of `st->double_scale` can race with concurrent writes.
> > --
> > 
> > commit 7fefd2fd08838a6ee5a81a3a819607e02af9d85d
> > Author: Rodrigo Alencar <rodrigo.alencar@analog.com>
> > 
> > iio: dac: ad5686: add gain control support
> > 
> > This commit adds support for a 2x gain multiplier applied to the output
> > voltage. This provides a gain control through a bit field in the control
> > register or a dedicated GAIN pin, exposing the scale property as configurable.
> >   
> > > diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
> > > index d9022c6a32016..d49390af71f90 100644
> > > --- a/drivers/iio/dac/ad5686.c
> > > +++ b/drivers/iio/dac/ad5686.c  
> > 
> > [ ... ]
> >   
> > > @@ -192,9 +196,14 @@ static int ad5686_read_raw(struct iio_dev *indio_dev,
> > >  			GENMASK(chan->scan_type.realbits - 1, 0);
> > >  		return IIO_VAL_INT;
> > >  	case IIO_CHAN_INFO_SCALE:
> > > -		*val = st->vref_mv;
> > > -		*val2 = chan->scan_type.realbits;
> > > -		return IIO_VAL_FRACTIONAL_LOG2;
> > > +		if (st->double_scale) {
> > > +			*val = st->scale_avail[2];
> > > +			*val2 = st->scale_avail[3];
> > > +		} else {
> > > +			*val = st->scale_avail[0];
> > > +			*val2 = st->scale_avail[1];
> > > +		}
> > > +		return IIO_VAL_INT_PLUS_NANO;  
> > 
> > [Severity: Low]
> > Is it possible for a data race to occur here?
> > 
> > In ad5686_read_raw(), the mutable software state st->double_scale is read
> > to determine the returned scale values without holding the st->lock mutex.
> > 
> > Because st->double_scale can be concurrently modified by ad5686_write_raw()
> > (which properly acquires st->lock), this can lead to reading a prematurely
> > updated state before the hardware configuration has actually completed.  
> 
> Valid concern, will add guarded mutex lock in read_raw to the previous patch.

I don't mind the locking being added as it makes it easier to reason about
but other than risk of a write tear or other silliness, I don't think the
report is valid.  The upshot of this race is that the read might report a
value just ahead of it being valid.  Given there is no roll back on error
(maybe there should be?) in write_raw() we are dealing with same visible
result as the we will get with the lock. One or other of read_raw / write_raw
gets the lock and that dictates what is read.

Chances are for the read we aren't going to use the scale immediately so
it might be stale anyway if we really have multiple threads messing around
in sysfs for a device.

Perhaps I'm missing something?
>  
> > >  	}
> > >  	return -EINVAL;
> > >  }  
> > 
> > [ ... ]
> >   
> > > @@ -216,6 +225,63 @@ static int ad5686_write_raw(struct iio_dev *indio_dev,
> > >  
> > >  		return ad5686_write(st, AD5686_CMD_WRITE_INPUT_N_UPDATE_N,
> > >  				    chan->address, val << chan->scan_type.shift);
> > > +	case IIO_CHAN_INFO_SCALE:
> > > +		if (val == st->scale_avail[0] && val2 == st->scale_avail[1])
> > > +			st->double_scale = false;
> > > +		else if (val == st->scale_avail[2] && val2 == st->scale_avail[3])
> > > +			st->double_scale = true;
> > > +		else
> > > +			return -EINVAL;
> > > +
> > > +		switch (st->chip_info->regmap_type) {
> > > +		case AD5310_REGMAP:
> > > +			return ad5310_control_sync(st);
> > > +		case AD5683_REGMAP:
> > > +			return ad5683_control_sync(st);  
> > 
> > [Severity: Medium]
> > Should st->double_scale be restored to its previous value if the hardware
> > synchronization fails?
> > 
> > In ad5686_write_raw(), st->double_scale is updated before the driver attempts
> > to push the change to the DAC via ad5310_control_sync(st) or
> > ad5683_control_sync(st). 
> > 
> > If the underlying SPI/I2C write fails, the function immediately returns the
> > error code without rolling back st->double_scale.
> > 
> > This could cause the driver's cached state to diverge from the actual
> > hardware configuration, causing future reads of the scale to return the
> > incorrect updated value.  
> 
> Will ignore that and assume that SPI write error is a platform problem.
> At another attempt the cached value can take effect.

It's fine given you don't gate on the double_scale when repeating (so that
answers the above question I had on whether to roll back).

> 
> >   
> > > +		case AD5686_REGMAP:
> > > +			/*
> > > +			 * Even if the gain pin is hardwired on the board, the
> > > +			 * user is able to control the scale such that it
> > > +			 * matches the actual gain setting.
> > > +			 */
> > > +			gpiod_set_value_cansleep(st->gain_gpio,
> > > +						 st->double_scale ? 1 : 0);
> > > +			return 0;  
> > 
> > [Severity: Medium]
> > Does this expose a writable sysfs attribute to fix a static hardware
> > configuration?
> > 
> > The commit introduces a writable scale sysfs attribute to allow users to manually
> > match the software scale to a hardwired gain pin. Static hardware properties
> > should typically be described via firmware properties (e.g., Device Tree) so
> > the driver can expose the correct, immutable scale at initialization.
> > 
> > If the GAIN GPIO is absent, writing to the scale attribute returns success but
> > does not change the physical hardware state, leading to a silent desynchronization
> > between the reported scale and the actual physical DAC output.
> > 
> > Could this mislead generic IIO clients by presenting multiple available scales
> > that they believe they can dynamically toggle?  
> 
> No sure if this is a big concern (assuming that the user has knowledge of the hardware).
> An option is to add another dt property to indicate that the gain is doubled in hardware.
> Such property cannot be defined alongside the ldac-gpios property. 

hmm. I'm not bothered by fixed values on that gpio (we solve that with binding changes
on the odd occasion that actually turns up on real hardware).  If you want to support
that then great - there are a few examples doing that in tree.

However, if the thing is optional (or that new property you mention is set) and we don't
have it we should not be exposing an interface that tries to set the value of the gpio.
So no gpio means:
- scale_avail should be based on assumption that pin is in some default (document that
  ideally in dt binding).
- write_raw() should return an error if a value other than that default is requested.

Adjust those if the property lets us know which way the pin is wired (high or low).

Jonathan



>  
> > -- 
> > Sashiko AI review · https://sashiko.dev/#/patchset/20260609-ad5686-new-features-v2-0-70b423f5c76d@analog.com?part=12  
> 


^ permalink raw reply

* [PATCH] dt-bindings: spi: microchip,pic32: Convert to DT schema
From: Udaya Kiran Challa @ 2026-06-14 17:50 UTC (permalink / raw)
  To: tsbogend, robh, krzk+dt, conor+dt
  Cc: skhan, me, linux-rtc, devicetree, linux-kernel,
	Udaya Kiran Challa

Convert Microchip PIC32 SPI controller devicetree binding
from legacy text format to DT schema.

Signed-off-by: Udaya Kiran Challa <challauday369@gmail.com>
---
 .../bindings/spi/microchip,pic32-spi.yaml     | 78 +++++++++++++++++++
 .../bindings/spi/microchip,spi-pic32.txt      | 34 --------
 2 files changed, 78 insertions(+), 34 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/spi/microchip,pic32-spi.yaml
 delete mode 100644 Documentation/devicetree/bindings/spi/microchip,spi-pic32.txt

diff --git a/Documentation/devicetree/bindings/spi/microchip,pic32-spi.yaml b/Documentation/devicetree/bindings/spi/microchip,pic32-spi.yaml
new file mode 100644
index 000000000000..97a381b2065f
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/microchip,pic32-spi.yaml
@@ -0,0 +1,78 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/spi/microchip,pic32-spi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Microchip PIC32MZDA SPI Controller
+
+maintainers:
+  - Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+
+allOf:
+  - $ref: spi-controller.yaml#
+
+properties:
+  compatible:
+    const: microchip,pic32mzda-spi
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    items:
+      - description: Fault interrupt
+      - description: Receive interrupt
+      - description: Transmit interrupt
+
+  interrupt-names:
+    items:
+      - const: fault
+      - const: rx
+      - const: tx
+
+  clocks:
+    maxItems: 1
+
+  clock-names:
+    items:
+      - const: mck0
+
+  dmas:
+    items:
+      - description: RX DMA channel
+      - description: TX DMA channel
+
+  dma-names:
+    items:
+      - const: spi-rx
+      - const: spi-tx
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - interrupt-names
+  - clocks
+  - clock-names
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/gpio/gpio.h>
+
+    spi@1f821000 {
+        compatible = "microchip,pic32mzda-spi";
+        reg = <0x1f821000 0x200>;
+        interrupts = <109 IRQ_TYPE_LEVEL_HIGH>,
+                     <110 IRQ_TYPE_LEVEL_HIGH>,
+                     <111 IRQ_TYPE_LEVEL_HIGH>;
+        interrupt-names = "fault", "rx", "tx";
+        clocks = <&PBCLK2>;
+        clock-names = "mck0";
+        cs-gpios = <&gpio3 4 GPIO_ACTIVE_LOW>;
+        dmas = <&dma 134>, <&dma 135>;
+        dma-names = "spi-rx", "spi-tx";
+    };
diff --git a/Documentation/devicetree/bindings/spi/microchip,spi-pic32.txt b/Documentation/devicetree/bindings/spi/microchip,spi-pic32.txt
deleted file mode 100644
index 79de379f4dc0..000000000000
--- a/Documentation/devicetree/bindings/spi/microchip,spi-pic32.txt
+++ /dev/null
@@ -1,34 +0,0 @@
-Microchip PIC32 SPI Master controller
-
-Required properties:
-- compatible: Should be "microchip,pic32mzda-spi".
-- reg: Address and length of register space for the device.
-- interrupts: Should contain all three spi interrupts in sequence
-              of <fault-irq>, <receive-irq>, <transmit-irq>.
-- interrupt-names: Should be "fault", "rx", "tx" in order.
-- clocks: Phandle of the clock generating SPI clock on the bus.
-- clock-names: Should be "mck0".
-- cs-gpios: Specifies the gpio pins to be used for chipselects.
-            See: Documentation/devicetree/bindings/spi/spi-bus.txt
-
-Optional properties:
-- dmas: Two or more DMA channel specifiers following the convention outlined
-        in Documentation/devicetree/bindings/dma/dma.txt
-- dma-names: Names for the dma channels. There must be at least one channel
-             named "spi-tx" for transmit and named "spi-rx" for receive.
-
-Example:
-
-spi1: spi@1f821000 {
-        compatible = "microchip,pic32mzda-spi";
-        reg = <0x1f821000 0x200>;
-        interrupts = <109 IRQ_TYPE_LEVEL_HIGH>,
-                     <110 IRQ_TYPE_LEVEL_HIGH>,
-                     <111 IRQ_TYPE_LEVEL_HIGH>;
-        interrupt-names = "fault", "rx", "tx";
-        clocks = <&PBCLK2>;
-        clock-names = "mck0";
-        cs-gpios = <&gpio3 4 GPIO_ACTIVE_LOW>;
-        dmas = <&dma 134>, <&dma 135>;
-        dma-names = "spi-rx", "spi-tx";
-};
-- 
2.34.1


^ permalink raw reply related

* Re: [PATCH RFC v4 1/6] dt-bindings: iio: add Open Sensor Fusion device
From: Jonathan Cameron @ 2026-06-14 17:55 UTC (permalink / raw)
  To: Kim Jinseob
  Cc: Conor Dooley, linux-iio, David Lechner, Nuno Sá,
	Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jonathan Corbet, Shuah Khan, devicetree, linux-kernel, linux-doc
In-Reply-To: <CALMSew+cL0_kG6W15RapxLtE+Fw2_DYdrYoRFSjENQktWw2H4Q@mail.gmail.com>

On Wed, 10 Jun 2026 18:33:54 +0900
Kim Jinseob <kimjinseob88@gmail.com> wrote:

> > Do you think it makes sense to permit a regulator here, so that the
> > "host" OS can power on/off the board running the osf stack?  
> 
> From the OSF hardware side, yes, that makes sense.
> 
> The current prototype used for testing is powered independently, but an OSF
> device may also be integrated as a host-powered UART peripheral. In that case
> allowing the host to control the board supply through an optional regulator
> would be useful.
> 
> Unless the IIO side prefers otherwise, I will add an optional supply property
> to the binding and matching optional regulator handling in the driver in the
> next revision.

When a device needs power, the regulator is required, not optional from a binding
point of view.  If it is always one people can use a fixed regulator to represent it.

Now from a driver point of view, the regulator framework in linux provides stub regulators
for missing ones - on assumption they are always on.  So we can just request the
regulators in the driver.  Keep it simple for now and use a
devm_regulator_get_enable() in probe so we have power on for all the time
the driver is loaded. Can do fancy stuff later when you have a board where the
power is controlled.

> 
> Jinseob
> 
> 
> 2026년 6월 10일 (수) 오전 1:19, Conor Dooley <conor@kernel.org>님이 작성:
> >
> > Jonathan/IIO folks,
> >
> > On Mon, Jun 08, 2026 at 08:43:38AM +0900, Jinseob Kim wrote:
> >  
> > > diff --git a/Documentation/devicetree/bindings/iio/opensensorfusion,osf.yaml b/Documentation/devicetree/bindings/iio/opensensorfusion,osf.yaml
> > > new file mode 100644
> > > index 000000000..a4049715a
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/iio/opensensorfusion,osf.yaml
> > > @@ -0,0 +1,43 @@
> > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > +%YAML 1.2
> > > +---
> > > +$id: http://devicetree.org/schemas/iio/opensensorfusion,osf.yaml#
> > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > +
> > > +title: Open Sensor Fusion Sensor Aggregation Hub
> > > +
> > > +maintainers:
> > > +  - Jinseob Kim <kimjinseob88@gmail.com>
> > > +
> > > +description: |
> > > +  Open Sensor Fusion is a sensor aggregation hub. The hub exposes an OSF
> > > +  protocol data stream over its host interface and may report capabilities and
> > > +  samples for multiple sensor classes. The Linux driver discovers the actual
> > > +  sensor channels from OSF capability reports instead of describing those
> > > +  sensors in Device Tree.
> > > +
> > > +  Open Sensor Fusion is not a generic industry standard. Public project
> > > +  documentation is available at:
> > > +
> > > +    https://github.com/opensensorfusion
> > > +
> > > +allOf:
> > > +  - $ref: /schemas/serial/serial-peripheral-props.yaml#
> > > +
> > > +properties:
> > > +  compatible:
> > > +    const: opensensorfusion,osf
> > > +
> > > +required:
> > > +  - compatible  
> >
> > Do you think it makes sense to permit a regulator here, so that the
> > "host" OS can power on/off the board running the osf stack?
> >  
> > > +
> > > +unevaluatedProperties: false
> > > +
> > > +examples:
> > > +  - |
> > > +    serial {
> > > +        sensor {
> > > +            compatible = "opensensorfusion,osf";
> > > +        };
> > > +    };
> > > +...  


^ permalink raw reply

* Re: [PATCH RFC v4 1/6] dt-bindings: iio: add Open Sensor Fusion device
From: Jonathan Cameron @ 2026-06-14 17:57 UTC (permalink / raw)
  To: Jinseob Kim
  Cc: linux-iio, David Lechner, Nuno Sá, Andy Shevchenko,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
	Shuah Khan, devicetree, linux-kernel, linux-doc
In-Reply-To: <20260607234343.22109-2-kimjinseob88@gmail.com>

On Mon,  8 Jun 2026 08:43:38 +0900
Jinseob Kim <kimjinseob88@gmail.com> wrote:

> Describe the OSF sensor aggregation hub.
> 
> Use the generic opensensorfusion,osf compatible.
> 
> Signed-off-by: Jinseob Kim <kimjinseob88@gmail.com>

This seems to be a patch against an earlier version of your series.
Make sure this applies directly on the upstream kernel (release or rc1
typically depending on which is most recent).

Jonathan

> ---
>  .../iio/imu/opensensorfusion,osf-green.yaml   | 43 -------------------
>  .../bindings/iio/opensensorfusion,osf.yaml    | 43 +++++++++++++++++++
>  MAINTAINERS                                   | 27 ++++++------
>  3 files changed, 57 insertions(+), 56 deletions(-)
>  delete mode 100644 Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.yaml
>  create mode 100644 Documentation/devicetree/bindings/iio/opensensorfusion,osf.yaml
> 
> diff --git a/Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.yaml b/Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.yaml
> deleted file mode 100644
> index 626b41fb0..000000000
> --- a/Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.yaml
> +++ /dev/null
> @@ -1,43 +0,0 @@
> -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> -%YAML 1.2
> ----
> -$id: http://devicetree.org/schemas/iio/imu/opensensorfusion,osf-green.yaml#
> -$schema: http://devicetree.org/meta-schemas/core.yaml#
As above. There shouldn't be anything to remove!

> diff --git a/MAINTAINERS b/MAINTAINERS
> index 56181470d..e227b9aff 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -19977,19 +19977,6 @@ F:	Documentation/networking/oa-tc6-framework.rst
>  F:	drivers/net/ethernet/oa_tc6.c
>  F:	include/linux/oa_tc6.h
>  
> -OPEN SENSOR FUSION IIO DRIVER
> -M:	Jinseob Kim <kimjinseob88@gmail.com>
> -S:	Maintained
> -F:	Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.yaml
> -F:	Documentation/iio/open-sensor-fusion-protocol-v0.rst
> -F:	drivers/iio/opensensorfusion/Kconfig
> -F:	drivers/iio/opensensorfusion/Makefile
> -F:	drivers/iio/opensensorfusion/osf_core.*
> -F:	drivers/iio/opensensorfusion/osf_iio.*
> -F:	drivers/iio/opensensorfusion/osf_protocol.*
> -F:	drivers/iio/opensensorfusion/osf_serdev.c
> -F:	drivers/iio/opensensorfusion/osf_stream.*
> -
>  OPEN FIRMWARE AND FLATTENED DEVICE TREE
>  M:	Rob Herring <robh@kernel.org>
>  M:	Saravana Kannan <saravanak@kernel.org>
> @@ -20024,6 +20011,20 @@ F:	Documentation/devicetree/
>  F:	arch/*/boot/dts/
>  F:	include/dt-bindings/
>  
> +OPEN SENSOR FUSION IIO DRIVER
> +M:	Jinseob Kim <kimjinseob88@gmail.com>
> +S:	Maintained
> +F:	Documentation/devicetree/bindings/iio/opensensorfusion,osf.yaml
> +F:	Documentation/iio/open-sensor-fusion-protocol-v0.rst
> +F:	drivers/iio/opensensorfusion/Kconfig
> +F:	drivers/iio/opensensorfusion/Makefile
> +F:	drivers/iio/opensensorfusion/osf_core.*
> +F:	drivers/iio/opensensorfusion/osf_iio.*
> +F:	drivers/iio/opensensorfusion/osf_protocol.*
> +F:	drivers/iio/opensensorfusion/osf_serdev.c
> +F:	drivers/iio/opensensorfusion/osf_stream.*
> +
One blank line.

> +
>  OPENCOMPUTE PTP CLOCK DRIVER
>  M:	Vadim Fedorenko <vadim.fedorenko@linux.dev>
>  L:	netdev@vger.kernel.org


^ permalink raw reply

* Re: [PATCH RFC v4 2/6] Documentation: iio: add Open Sensor Fusion driver overview
From: Jonathan Cameron @ 2026-06-14 17:59 UTC (permalink / raw)
  To: Jinseob Kim
  Cc: linux-iio, David Lechner, Nuno Sá, Andy Shevchenko,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
	Shuah Khan, devicetree, linux-kernel, linux-doc
In-Reply-To: <20260607234343.22109-3-kimjinseob88@gmail.com>

On Mon,  8 Jun 2026 08:43:39 +0900
Jinseob Kim <kimjinseob88@gmail.com> wrote:

> Add a short driver-facing overview.
> 
> Leave full protocol details to project docs.
> 
> Signed-off-by: Jinseob Kim <kimjinseob88@gmail.com>
Same issue on needing this series to be against a suitable
tree, not on top of your previous version.

> ---
>  Documentation/iio/index.rst                   |   1 +
>  .../iio/open-sensor-fusion-protocol-v0.rst    | 308 ------------------
>  Documentation/iio/open-sensor-fusion.rst      |  62 ++++
>  MAINTAINERS                                   |   3 +-
>  4 files changed, 64 insertions(+), 310 deletions(-)
>  delete mode 100644 Documentation/iio/open-sensor-fusion-protocol-v0.rst
>  create mode 100644 Documentation/iio/open-sensor-fusion.rst
> 
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index e227b9aff..2ddefc42d 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -20015,7 +20015,7 @@ OPEN SENSOR FUSION IIO DRIVER
>  M:	Jinseob Kim <kimjinseob88@gmail.com>
>  S:	Maintained
>  F:	Documentation/devicetree/bindings/iio/opensensorfusion,osf.yaml
> -F:	Documentation/iio/open-sensor-fusion-protocol-v0.rst
> +F:	Documentation/iio/open-sensor-fusion.rst
>  F:	drivers/iio/opensensorfusion/Kconfig
>  F:	drivers/iio/opensensorfusion/Makefile
>  F:	drivers/iio/opensensorfusion/osf_core.*
> @@ -20024,7 +20024,6 @@ F:	drivers/iio/opensensorfusion/osf_protocol.*
>  F:	drivers/iio/opensensorfusion/osf_serdev.c
>  F:	drivers/iio/opensensorfusion/osf_stream.*
>  
> -
Check your series for things like this right before sending (we all make 
mistakes like this when building up series!)  It clearly belongs in the
previous patch.

>  OPENCOMPUTE PTP CLOCK DRIVER
>  M:	Vadim Fedorenko <vadim.fedorenko@linux.dev>
>  L:	netdev@vger.kernel.org


^ permalink raw reply

* Re: [PATCH net-next v5 2/4] net: phy: mediatek: add phy driver for MT7628 built-in Fast Ethernet PHYs
From: sashiko-bot @ 2026-06-14 18:20 UTC (permalink / raw)
  To: Joris Vaisvila; +Cc: conor+dt, robh, devicetree
In-Reply-To: <20260613181845.111877-3-joey@tinyisr.com>

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The `mtk_soc_fe_phy_driver` lacks `.suspend` and `.resume` callbacks (typically assigned to `genphy_suspend` and `genphy_resume`). This causes a severe power management regression and breaks hardware initialization after system resume. Without a `.suspend` callback, `phy_suspend()` returns 0 without powering down the PHY (`BMCR_PDOWN`). Furthermore, `mdio_bus_phy_may_suspend()` returns false, which prevents `phydev->suspended_by_mdio_bus` from being set to 1 during system suspend. During system resume, `mdio_bus_phy_resume()` checks this flag and, seeing it is false, completely skips calling `phy_init_hw()`. Because `phy_init_hw()` is bypassed, the driver's `.config_init` callback (`mt7628_phy_config_init`) is never executed. Since this callback sets a critical undocumented bit required for 100 Mbps links, the PHY will silently fail to establish correct links if it loses power or state during suspend.
--

commit 6c0828ab418aa7d550c1020af637c9ed3e4c1236
Author: Joris Vaisvila <joey@tinyisr.com>

net: phy: mediatek: add phy driver for MT7628 built-in Fast Ethernet PHYs

This commit adds support for the Fast Ethernet PHYs in the MT7628 SoCs.
It adds the MEDIATEK_FE_SOC_PHY Kconfig option and the corresponding
mtk-fe-soc.c driver to set an undocumented bit required for 100mbps links.

> diff --git a/drivers/net/phy/mediatek/mtk-fe-soc.c b/drivers/net/phy/mediatek/mtk-fe-soc.c
> new file mode 100644
> index 0000000000000..9eb4960bcaade
> --- /dev/null
> +++ b/drivers/net/phy/mediatek/mtk-fe-soc.c

[ ... ]

> +static struct phy_driver mtk_soc_fe_phy_driver[] = {
> +	{
> +		PHY_ID_MATCH_EXACT(MTK_FPHY_ID_MT7628),
> +		.name = "MediaTek MT7628 PHY",
> +		.config_init = mt7628_phy_config_init,
> +		.read_page = mt7628_phy_read_page,
> +		.write_page = mt7628_phy_write_page,
> +	},
> +};

[Severity: High]
Does this driver need .suspend and .resume callbacks (like genphy_suspend
and genphy_resume) to properly handle system power management?

Without a .suspend callback, mdio_bus_phy_may_suspend() returns false,
which prevents phydev->suspended_by_mdio_bus from being set during system
suspend.

During resume, mdio_bus_phy_resume() checks this flag and skips calling
phy_init_hw() entirely. Because phy_init_hw() is bypassed, the
mt7628_phy_config_init() callback is never executed after a resume.

Will this cause the PHY to silently fail to establish 100 Mbps links after
waking up from suspend because the undocumented bit is lost?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260613181845.111877-1-joey@tinyisr.com?part=2

^ permalink raw reply

* Re: [PATCH net-next v5 4/4] net: dsa: initial support for MT7628 embedded switch
From: sashiko-bot @ 2026-06-14 18:20 UTC (permalink / raw)
  To: Joris Vaisvila; +Cc: robh, conor+dt, devicetree
In-Reply-To: <20260613181845.111877-5-joey@tinyisr.com>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Using devres (devm_) memory allocation and registration inside the DSA .setup callback causes a memory leak and duplicate sysfs name failure upon dynamic DSA tree teardown and setup.
- [Medium] The Kconfig entry fails to select REGMAP_MMIO and depends on HAS_IOMEM, leading to runtime failures.
--

commit 43985d7959e86c4777c7631d696ad0d94372a424
Author: Joris Vaisvila <joey@tinyisr.com>

net: dsa: initial support for MT7628 embedded switch

Add support for the MT7628 embedded switch. The switch has 5 built-in 100Mbps
user ports and one 1Gbps CPU port. It uses a 16 entry VLAN table for
forwarding.

> diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
> index 4ab567c5bbafc..daa1d3d4ba609 100644
> --- a/drivers/net/dsa/Kconfig
> +++ b/drivers/net/dsa/Kconfig
> @@ -63,6 +63,14 @@ config NET_DSA_MT7530_MMIO
>  	  are directly mapped into the SoCs register space rather than being
>  	  accessible via MDIO.
>  
> +config NET_DSA_MT7628
> +	tristate "MediaTek MT7628 Embedded Ethernet switch support"
> +	select NET_DSA_TAG_MT7628
> +	select MEDIATEK_FE_SOC_PHY

[Severity: Medium]
Does this Kconfig entry need to select REGMAP_MMIO and depend on HAS_IOMEM?

The driver uses devm_regmap_init_mmio(), which requires CONFIG_REGMAP_MMIO
to be enabled. If it is disabled, the stub function will return -EINVAL and
cause the driver probe to fail unconditionally.

> +	help
> +	  This enables support for the built-in Ethernet switch found
> +	  in the MT7628 SoC.
> +

[ ... ]

> diff --git a/drivers/net/dsa/mt7628.c b/drivers/net/dsa/mt7628.c
> new file mode 100644
> index 0000000000000..cedf063ad749c
> --- /dev/null
> +++ b/drivers/net/dsa/mt7628.c

[ ... ]

> +static int mt7628_setup_internal_mdio(struct dsa_switch *ds)
> +{
> +	struct mt7628_esw *esw = ds->priv;
> +	struct device *dev = ds->dev;
> +	struct mii_bus *bus;
> +
> +	bus = devm_mdiobus_alloc(dev);
> +	if (!bus)
> +		return -ENOMEM;
> +
> +	bus->name = "MT7628 internal MDIO bus";
> +	snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(dev));
> +	bus->priv = esw;
> +	bus->read = mt7628_mii_read;
> +	bus->write = mt7628_mii_write;
> +	bus->parent = dev;
> +
> +	ds->user_mii_bus = bus;
> +	bus->phy_mask = ~ds->phys_mii_mask;
> +
> +	return devm_mdiobus_register(dev, bus);
> +}

[Severity: High]
Can this memory leak and fail on dynamic DSA tree setups?

Since mt7628_setup_internal_mdio() is called from the DSA .setup callback
via mt7628_setup(), it can be invoked multiple times during the device's
lifetime (for example, if the conduit or master network device is unbound
and later rebound).

Because the MDIO bus is allocated and registered using the devm_ variants
tied to the physical device, won't the bus remain allocated after .teardown()
runs? If .setup() is called again, this would leak the previous allocation
and cause devm_mdiobus_register() to fail with a sysfs name conflict.

Should the MDIO bus be allocated in the probe function instead, or use the
non-devm variants here with explicit cleanup in .teardown()?

[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260613181845.111877-1-joey@tinyisr.com?part=4

^ permalink raw reply

* Re: [PATCH 1/3] iio: common: st_sensors: honour channel endianness in read_axis_data
From: Jonathan Cameron @ 2026-06-14 18:39 UTC (permalink / raw)
  To: Herman van Hazendonk
  Cc: linusw, denis.ciocca, robh, krzk+dt, conor+dt, dlechner, nuno.sa,
	andy, sanjayembeddedse, maudspierings, linux-iio, devicetree,
	linux-kernel
In-Reply-To: <0dac8e8e2872dc180b138923b5cd4fd18eee047b.1780652883.git.github.com@herrie.org>

On Fri,  5 Jun 2026 12:08:41 +0200
Herman van Hazendonk <github.com@herrie.org> wrote:

> st_sensors_read_axis_data() unconditionally decoded multi-byte
> results with get_unaligned_le16() / get_unaligned_le24() regardless
> of the channel's declared scan_type.endianness.
> 
> For every ST sensor that has used this helper since it was introduced
> this happened to be fine because the ST IMU/accel/gyro/pressure
> families publish their data registers as little-endian and the
> channel specs in those drivers declare IIO_LE accordingly.
> 
> The LSM303DLH magnetometer however publishes its X/Y/Z output as a
> pair of big-endian bytes (the H register sits at the lower address,
> 0x03/0x05/0x07, and the L register immediately after), and its
> channel specs in st_magn_core.c correctly declare IIO_BE -- but
> read_axis_data() ignored that and decoded as little-endian, swapping
> the high and low bytes of every magnetometer sample.
> 
> The bug is most visible on a stationary chip: in earth's field the
> true X reading is small and the high byte sits at 0x00, so swapping
> the bytes pins sysfs X at exactly the low byte's pattern (e.g. 0x00F0
> = 240). Y and Z still appear "to vary" because their magnitudes are
> larger and the noise in the low byte produces big swings in the
> swapped high byte:
> 
>   before (chip flat, sysfs in_magn_*_raw):
>       X=240 (stuck), Y= 12032..23296, Z=-16128..-9728
> 
>   after (direct i2c-dev big-endian decode, same chip same orientation):
>       X≈-4096, Y≈210, Z≈80     (sensible values reflecting earth's
>                                 ambient field at low gauss range)
> 
> Fix read_axis_data() to dispatch on ch->scan_type.endianness and
> call get_unaligned_be16() / get_unaligned_be24() when the channel
> declares IIO_BE. Existing IIO_LE consumers (st_accel, st_gyro,
> st_pressure, st_lsm6dsx and others) are unaffected because their
> channel specs already declare IIO_LE and the LE path is unchanged.
> 
Hi Herman,

Fixes tag please.   We want to know how far to backport and provide
info to those trying to work out if the bug affects their kernel
trees.

Thanks

Jonathan

> Signed-off-by: Herman van Hazendonk <github.com@herrie.org>


^ permalink raw reply

* Re: [PATCH 2/3] dt-bindings: iio: st,st-sensors: add st,fullscale-mg
From: Jonathan Cameron @ 2026-06-14 18:44 UTC (permalink / raw)
  To: Herman van Hazendonk
  Cc: linusw, denis.ciocca, robh, krzk+dt, conor+dt, dlechner, nuno.sa,
	andy, sanjayembeddedse, maudspierings, linux-iio, devicetree,
	linux-kernel
In-Reply-To: <e49aba850b1f48ea00089e7142c00584541001eb.1780652883.git.github.com@herrie.org>

On Fri,  5 Jun 2026 12:08:42 +0200
Herman van Hazendonk <github.com@herrie.org> wrote:

> Add an optional st,fullscale-mg property that selects the initial
> full-scale range of an ST MEMS sensor at probe time, expressed in
> milligauss for magnetometers (and analogous engineering units for
> other ST sensor families that may grow this property in the future).
> 
> The property is purely additive: if absent, drivers fall back to
> their existing chip default, and if present but unsupported by the
> specific sensor the driver warns and falls back. No existing in-tree
> DTS is affected.
> 
> The motivating case is the LSM303DLH magnetometer on the HP TouchPad
> (apq8060 / tenderloin) where the kernel's chip-default +/-1.3 G range
> saturates the X axis to the chip's 0xF000 overflow sentinel out of
> probe, because the chip is mounted close to surrounding power planes
> and picks up enough DC bias to exceed the smallest range. The driver
> core hardcodes fs_avl[0] as the starting range, so userspace cannot
> recover without racing the driver to write the in_magn_x_scale sysfs
> attribute after probe. st,fullscale-mg lets the device tree declare
> a wider initial range up-front and avoids the race entirely.

I'm trying to understand what you mean here by racing.

If we get this overflow condition the chip is wedged until reset, or
userspace simply has to change the range to recover?

I'm wondering if a UDEV rule is sufficient in theory to fix this.

I'm not necessarily against having the range in DT as it is effectively
hardware dependent but just want to make sure I fully understand the issue.

Jonathan



> 
> Signed-off-by: Herman van Hazendonk <github.com@herrie.org>
> ---
>  .../devicetree/bindings/iio/st,st-sensors.yaml | 18 ++++++++++++++++++
>  1 file changed, 18 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/iio/st,st-sensors.yaml b/Documentation/devicetree/bindings/iio/st,st-sensors.yaml
> index a1a958215cdb..335f38e9f78f 100644
> --- a/Documentation/devicetree/bindings/iio/st,st-sensors.yaml
> +++ b/Documentation/devicetree/bindings/iio/st,st-sensors.yaml
> @@ -126,6 +126,24 @@ properties:
>    mount-matrix:
>      description: an optional 3x3 mounting rotation matrix.
>  
> +  st,fullscale-mg:
> +    description: |
> +      Selects the initial sensor full-scale at probe time, expressed in
> +      milligauss for magnetometers (or analogous engineering units for
> +      other sensor families that may grow this property in the future).
> +      The value must match one of the sensor-specific full-scale ranges
> +      supported by the chip; if the chip does not support the requested
> +      range the driver falls back to its built-in default.
> +
> +      This is intended for boards where the magnetometer chip picks up
> +      enough DC bias from nearby PCB structures (power planes, ferrous
> +      shields, etc.) that the kernel's chip-default highest-sensitivity
> +      range saturates one or more axes to the chip's overflow sentinel,
> +      and userspace observes that axis as permanently stuck. Declaring
> +      a wider initial range avoids the saturation at the cost of a
> +      slightly coarser quantisation.
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +
>  allOf:
>    - if:
>        properties:


^ permalink raw reply

* Re: [PATCH v3 1/2] dt-bindings: iio: dac: Add AD5529R
From: Jonathan Cameron @ 2026-06-14 19:44 UTC (permalink / raw)
  To: Janani Sunil
  Cc: Rodrigo Alencar, Janani Sunil, Lars-Peter Clausen,
	Michael Hennerich, David Lechner, Nuno Sá, Andy Shevchenko,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Philipp Zabel,
	Jonathan Corbet, Shuah Khan, linux-iio, devicetree, linux-kernel,
	linux-doc, Mark Brown
In-Reply-To: <603473ac-30e6-45e5-8a3b-c9902715cc9e@gmail.com>

On Tue, 9 Jun 2026 16:47:23 +0200
Janani Sunil <jan.sun97@gmail.com> wrote:

> On 5/26/26 15:11, Rodrigo Alencar wrote:
> > On 26/05/19 05:42PM, Janani Sunil wrote:  
> >> Devicetree bindings for AD5529R 16 channel 12/16 bit high voltage,
> >> buffered voltage output digital-to-analog converter (DAC) with an
> >> integrated precision reference.  
> > ...
> > Probably others may comment on that, but...
> >
> > This parent node may support device addressing for multi-device support through
> > those ID pins. I suppose that each device may have its own power supplies or
> > other resources like the toggle pins or reset and enable.
> >
> > That way I suppose that an example would look like...
> >  
> >> +
> >> +patternProperties:
> >> +  "^channel@([0-9]|1[0-5])$":
> >> +    type: object
> >> +    description: Child nodes for individual channel configuration
> >> +
> >> +    properties:
> >> +      reg:
> >> +        description: Channel number.
> >> +        minimum: 0
> >> +        maximum: 15
> >> +
> >> +      adi,output-range-microvolt:
> >> +        description: |
> >> +          Output voltage range for this channel as [min, max] in microvolts.
> >> +          If not specified, defaults to 0V to 5V range.
> >> +        oneOf:
> >> +          - items:
> >> +              - const: 0
> >> +              - enum: [5000000, 10000000, 20000000, 40000000]
> >> +          - items:
> >> +              - const: -5000000
> >> +              - const: 5000000
> >> +          - items:
> >> +              - const: -10000000
> >> +              - const: 10000000
> >> +          - items:
> >> +              - const: -15000000
> >> +              - const: 15000000
> >> +          - items:
> >> +              - const: -20000000
> >> +              - const: 20000000
> >> +
> >> +    required:
> >> +      - reg
> >> +
> >> +    additionalProperties: false
> >> +
> >> +required:
> >> +  - compatible
> >> +  - reg
> >> +  - vdd-supply
> >> +  - avdd-supply
> >> +  - hvdd-supply
> >> +
> >> +dependencies:
> >> +  spi-cpha: [ spi-cpol ]
> >> +  spi-cpol: [ spi-cpha ]
> >> +
> >> +allOf:
> >> +  - $ref: /schemas/spi/spi-peripheral-props.yaml#
> >> +
> >> +unevaluatedProperties: false
> >> +
> >> +examples:
> >> +  - |
> >> +    #include <dt-bindings/gpio/gpio.h>
> >> +
> >> +    spi {
> >> +        #address-cells = <1>;
> >> +        #size-cells = <0>;
> >> +
> >> +        dac@0 {
> >> +            compatible = "adi,ad5529r-16";
> >> +            reg = <0>;
> >> +            spi-max-frequency = <25000000>;
> >> +
> >> +            vdd-supply = <&vdd_regulator>;
> >> +            avdd-supply = <&avdd_regulator>;
> >> +            hvdd-supply = <&hvdd_regulator>;
> >> +            hvss-supply = <&hvss_regulator>;
> >> +
> >> +            reset-gpios = <&gpio0 87 GPIO_ACTIVE_LOW>;
> >> +
> >> +            #address-cells = <1>;
> >> +            #size-cells = <0>;
> >> +
> >> +            channel@0 {
> >> +                reg = <0>;
> >> +                adi,output-range-microvolt = <0 5000000>;
> >> +            };
> >> +
> >> +            channel@1 {
> >> +                reg = <1>;
> >> +                adi,output-range-microvolt = <(-10000000) 10000000>;
> >> +            };
> >> +
> >> +            channel@2 {
> >> +                reg = <2>;
> >> +                adi,output-range-microvolt = <0 40000000>;
> >> +            };
> >> +        };
> >> +    };  
> > ...
> >
> > 	spi {
> > 		#address-cells = <1>;
> > 		#size-cells = <0>;
> >
> > 		multi-dac@0 {
> > 			compatible = "adi,ad5529r-16";
> > 			reg = <0>;
> > 			spi-max-frequency = <25000000>;
> >
> > 			#address-cells = <1>;
> > 			#size-cells = <0>;
> >
> > 			dac@0 {
> > 				reg = <0>;
> > 				vdd-supply = <&vdd_regulator>;
> > 				avdd-supply = <&avdd_regulator>;
> > 				hvdd-supply = <&hvdd_regulator>;
> > 				hvss-supply = <&hvss_regulator>;
> >
> > 				reset-gpios = <&gpio0 87 GPIO_ACTIVE_LOW>;
> >
> > 				#address-cells = <1>;
> > 				#size-cells = <0>;
> >
> > 				channel@0 {
> > 					reg = <0>;
> > 					adi,output-range-microvolt = <0 5000000>;
> > 				};
> >
> > 				channel@1 {
> > 					reg = <1>;
> > 					adi,output-range-microvolt = <(-10000000) 10000000>;
> > 				};
> >
> > 				channel@2 {
> > 					reg = <2>;
> > 					adi,output-range-microvolt = <0 40000000>;
> > 				};
> > 			}
> >
> > 			dac@1 {
> > 				reg = <1>;
> > 				vdd-supply = <&vdd_regulator>;
> > 				avdd-supply = <&avdd_regulator>;
> > 				hvdd-supply = <&hvdd_regulator>;
> > 				hvss-supply = <&hvss_regulator>;
> >
> > 				reset-gpios = <&gpio0 88 GPIO_ACTIVE_LOW>;
> >
> > 				#address-cells = <1>;
> > 				#size-cells = <0>;
> >
> > 				channel@0 {
> > 					reg = <0>;
> > 					adi,output-range-microvolt = <0 5000000>;
> > 				};
> >
> > 				channel@1 {
> > 					reg = <1>;
> > 					adi,output-range-microvolt = <(-10000000) 10000000>;
> > 				};
> > 			}
> > 		};
> > 	};
> >
> > then you might need something like:
> >
> > 	patternProperties:
> > 		"^dac@[0-3]$":
> >
> > and put most of the things under this node pattern.
> >
> > So the main driver that you're putting together might need to handle up to four instances.
> > Even if your current driver cannot handle this, the dt-bindings might need cover that.
> >
> > Need to double check if each dac node needs a separate compatible, so you would maybe populate
> > a platform data to be shared with the child nodes, which would be a separate driver.
> > (not sure if it would make sense to mix and match ad5529r-16 and ad5529r-12).  
> 
> Hi Rodrigo,
> 
> Thank you for looking at this.
> 
> For now, I would prefer to keep the binding scoped to a single AD5529R device instance. The current
> hardware/use case we have only needs one device node and the driver is written around that model as well.
> While the device addressing pins could allow multi-device topology, we do not have an actual platform using
> that configuration at the moment, so I would prefer not to introduce an extra parent/child binding structure
> speculatively without a validating use case.

Interesting feature - kind of similar to address control on a typical i2c bus device, or
looking at it another way a kind of distributed SPI mux.

Challenge of a binding is we need to anticipate the future.  So I think we do need something
like Rodrigo is suggesting even if we only (for now) support a single instance in the driver.
That would leave the path open to supporting the addressing at a later date.
An alternative might be to look at it like a chained device setup. In those we pretend there
is just one device with a lot of channels etc.  The snag is that here things are more loosely
coupled whereas for those devices it tends to be you have to read / write the same register
in all devices in the chain as one big SPI message.

+CC Mark Brown as he may know of some precedence for this feature. For his reference..
- Each of these device has 2 ID pins.  The SPI transfers have to contain the 2 bit
value that matches that or they are ignored.  Thus a single bus + 1 chip select can
be used to talk to 4 devices.  Question is what that looks like in device tree + I guess
longer term how to support it cleanly in SPI.

Jonathan


> 
> Best Regards,
> Janani Sunil
> 
> 


^ permalink raw reply

* Re: [PATCH v3 2/3] dt-bindings: iio: light: isl29018: add isil,cover-comp-gain
From: Jonathan Cameron @ 2026-06-14 19:51 UTC (permalink / raw)
  To: Conor Dooley
  Cc: github.com, me, dlechner, nuno.sa, andy, robh, krzk+dt, conor+dt,
	tomasborquez13, masneyb, linux-iio, devicetree, linux-kernel
In-Reply-To: <20260608-raft-auction-6305f9f324cd@spud>

On Mon, 8 Jun 2026 18:49:13 +0100
Conor Dooley <conor@kernel.org> wrote:

> On Fri, Jun 05, 2026 at 09:28:03PM +0200, me@herrie.org wrote:
> > On 2026-06-05 15:18, me@herrie.org wrote:  
> > > On 2026-06-05 15:04, Jonathan Cameron wrote:  
> > > > On Thu, 4 Jun 2026 18:01:08 +0100
> > > > Conor Dooley <conor@kernel.org> wrote:
> > > >   
> > > > > On Thu, Jun 04, 2026 at 12:06:16PM +0200, Herman van Hazendonk wrote:  
> > > > > > Document the new optional property that seeds the ISL29018 calibration
> > > > > > scale factor at boot from firmware, allowing boards with tinted cover
> > > > > > glass to ship with correct luminance readings without a userspace helper.
> > > > > >
> > > > > > The value is a positive integer (minimum 1, maximum 65535) that is
> > > > > > multiplied with the raw lux reading.  Userspace can still override it
> > > > > > at runtime through in_illuminance0_calibscale.
> > > > > >
> > > > > > Signed-off-by: Herman van Hazendonk <github.com@herrie.org>
> > > > > > ---
> > > > > >  .../devicetree/bindings/iio/light/isl29018.yaml     | 13 +++++++++++++
> > > > > >  1 file changed, 13 insertions(+)
> > > > > >
> > > > > > diff --git a/Documentation/devicetree/bindings/iio/light/isl29018.yaml b/Documentation/devicetree/bindings/iio/light/isl29018.yaml
> > > > > > index 0ea278b07d1c..92ea2742bbd3 100644
> > > > > > --- a/Documentation/devicetree/bindings/iio/light/isl29018.yaml
> > > > > > +++ b/Documentation/devicetree/bindings/iio/light/isl29018.yaml
> > > > > > @@ -34,6 +34,19 @@ properties:
> > > > > >    vcc-supply:
> > > > > >      description: Regulator that provides power to the sensor
> > > > > >
> > > > > > +  isil,cover-comp-gain:
> > > > > > +    description: |
> > > > > > +      Multiplier applied to the ambient-light reading at startup to
> > > > > > +      compensate for optical loss in the board's cover glass. Boards
> > > > > > +      that mount the sensor under a tinted or coated window typically
> > > > > > +      need a value between a few and a few hundred.  
> > > > 
> > > > Is it useful to support decimal points on these values?  The
> > > > userspace interface
> > > > does and you mention the 'right' answer might be only a few which
> > > > means precision
> > > > at that range will be terrible - less of an issue if 100s!
> > > > 
> > > > Thanks
> > > > 
> > > > Jonathan
> > > >   
> > > Hard to say, my old HP TouchPad needs 100 as a value here (taken from
> > > legacy 2.6.35
> > > kernel and binaries). So we probably don't need precision, but I have no
> > > other
> > > references to substantiate.
> > > 
> > > Thanks
> > > Herman  
> > Scratch that. Did some more research. Proof is in the legacy webOS binaries:
> > 
> > What legacy webOS actually does here:
> > 
> > 1. Per-device calibration via factory token reads an ALSCal token from
> > system storage
> > containing JSON with calibration points at lux_0, lux_50, lux_100, lux_400
> > (measured
> > ADC counts at known reference illuminance levels).
> > 
> > 2. Computes a floating-point ratio AlsToLux_Ratio_WhiteLED = average of
> > expected_lux /
> > measured_count across the JSON calibration points. This is a real number,
> > not an integer.
> > 
> > 3. Adjusts for light source spectrum at runtime detects illuminant type from
> > ALS:IR
> > ratio, then applies a fractional spectrum correction:
> > Fluorescent above 100 counts: ratio x 0.4652
> > Incandescent above 700 counts: ratio x 0.4
> > Incandescent 50-100 counts:    ratio x 0.9
> > Fluorescent < 50 counts:       ratio x (-0.000724·N + 0.7463)
> > 
> > 4. Final lux = ALSCount / spectrum_corrected_ratio - a true floating-point
> > division.
> > 
> > Implications
> > 
> > - The "right" cover-comp value is per-device factory-measured, not
> > per-board. Different
> > units off the same production line have different optical transmission due
> > to coating
> > tolerance.
> > - The values are fractional by nature. Examples from the legacy code:
> > 0.4652, 0.7463,
> > 0.8333. None are integers.
> > 
> > The ISL29023 datasheet itself says nothing about cover compensation - it's
> > strictly
> > board-level optical correction. So there's no "right" answer from the chip
> > side;
> > it's whatever the board's cover glass + coating attenuates.
> > 
> > ALSCal values found on the particular device:
> > 
> > {"lux_50":{"c":31}, "lux_100":{"c":58}, "lux_400":{"c":164}}
> > 
> > This is device specific TouchPad's factory calibration:
> >   - At 50 lux, ADC reads 31 counts  lux/count = 1.613
> >   - At 100 lux, ADC reads 58 counts lux/count = 1.724
> >   - At 400 lux, ADC reads 164 counts  lux/count = 2.439
> > 
> > Note the ratio isn't constant - the response is mildly non-linear, but per
> > the legacy
> > code the driver computes the average ratio as the calibration:
> > 
> >   ratio = (1.6129 + 1.7241 + 2.4390) / 3 = 1.9253 lux/count
> > 
> > Independent verification:
> >   - At calibscale=34: lux = 1295
> >   - Implied raw count: 1295 / (34 x 0.015258) = 2496 counts
> >   - Applying legacy formula: 2496 / 1.9253 = 1296.4 = 1295
> > 
> > The factory-calibrated value for this specific TouchPad is 34.04 (not 100).
> > Per-point
> > calibscale values from the ALSCal JSON:
> > 
> > Cal point‚ lux/count ratio‚ Equivalent calibscale
> > lux_50    ‚ 1.6129          ‚ 40.64
> > lux_100   ‚ 1.7241          ‚ 38.01
> > lux_400   ‚ 2.4390          ‚ 26.87
> > average   ‚ 1.9253          ‚ 34.04
> > 
> > What this means concretely
> > 
> > 1. Decimal precision is necessary, not nice-to-have. Real per-device factory
> > values
> > span 26.9 - 40.6 across the chip's response curve. A single scalar
> > approximation costs
> > precision; restricting to integer compounds it.
> > 
> > 2. Updated v5 plan: switch to two-cell <int micro> for fractional values,
> > and change
> > the tenderloin DTS default from <100> to <34 040000> (or close to that).
> > 
> > Thoughts on this?  
> 
> Instead of going 2-cell, it might be worth moving the property to be
> based in some fractional unit to begin with. I just don't know what to
> call that, milligain? I dunno.
> 
> 
> Jonathan's call here I think

If this is varying that a lot per device, then, unless you plan to
have a bootloader inject it into DT, are we better off making this
a userspace problem?  There is a hybrid we've done before, which is
to make it a firmware loading problem but that requires some way to
generate a suitable file to load and typically a calibration sequence
that goes with it.

If it is 'fairly constant' for a given device then fine to be in DT
- it will just be a case of picking the average of a bunch of measured
devices and assuming it is good enough.

If it does end up in dt, then a fractional type like Conor suggests
is fine.

Jonathan



^ permalink raw reply

* Re: [PATCH v16 04/14] lib: kstrtox: add initial value to _parse_integer_limit()
From: Jonathan Cameron @ 2026-06-14 20:00 UTC (permalink / raw)
  To: Rodrigo Alencar
  Cc: rodrigo.alencar, linux-kernel, linux-iio, devicetree, linux-doc,
	linux, David Lechner, Andy Shevchenko, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jonathan Corbet, Andrew Morton, Petr Mladek, Steven Rostedt,
	Andy Shevchenko, Rasmus Villemoes, Sergey Senozhatsky, Shuah Khan
In-Reply-To: <rvx36i2ydmwhbbkdgbvh26uqchyoptzuu7tleuyarqn2skzkxz@owzwp62mzkdo>

On Thu, 4 Jun 2026 11:09:33 +0100
Rodrigo Alencar <455.rodrigo.alencar@gmail.com> wrote:

> On 26/06/04 10:58AM, Rodrigo Alencar via B4 Relay wrote:
> > From: Rodrigo Alencar <rodrigo.alencar@analog.com>
> > 
> > Add init parameter to _parse_integer_limit() that defines an initial
> > value for the accumulated result when parsing an 64-bit integer. The
> > new function prototype is adjusted so that the _parse_integer() macros
> > stay consistent allowing for one more argument, which defaults to 0.  
> 
> ...
> 
> >  noinline
> >  unsigned int _parse_integer_limit(const char *s, unsigned int base, unsigned long long *p,
> > -				  size_t max_chars)
> > +				  size_t max_chars, unsigned long long init)
> >  {
> >  	unsigned long long res;
> >  	unsigned int rv;
> >  
> > -	res = 0;
> > +	res = init;  
> 
> This might generate conflict, as the code around have changed in linux-next.
> It is an easy fix though.
> 
Thanks for the heads up. Hopefully that will all fall out when I rebase testing
on rc1 once that is out.

Jonathan

> >  	rv = 0;
> >  	while (max_chars--) {
> >  		unsigned int c = *s;  
> 


^ permalink raw reply

* [PATCH v2] ARM: dts: exynos: Add bluetooth support to manta
From: Lukas Timmermann @ 2026-06-14 20:16 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Alim Akhtar
  Cc: devicetree, linux-arm-kernel, linux-samsung-soc, linux-kernel,
	Alexandre Marquet, Lukas Timmermann

Enable the bcm4330-bt device for manta boards on serial0.
Also adds the necessary pin definitions and interrupt handling for
wakeup.

Co-developed-by: Alexandre Marquet <tb@a-marquet.fr>
Signed-off-by: Alexandre Marquet <tb@a-marquet.fr>
Co-developed-by: Lukas Timmermann <linux@timmermann.space>
Signed-off-by: Lukas Timmermann <linux@timmermann.space>
---
Changes in v2:
- Sorted DCO chain (@krzk)
- Link to v1: https://patch.msgid.link/20260408-manta-bluetooth-v1-1-b7658e78359a@timmermann.space

To: Rob Herring <robh@kernel.org>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Conor Dooley <conor+dt@kernel.org>
To: Alim Akhtar <alim.akhtar@samsung.com>
Cc: devicetree@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-samsung-soc@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
---
 arch/arm/boot/dts/samsung/exynos5250-manta.dts | 41 +++++++++++++++++++++++++-
 1 file changed, 40 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/samsung/exynos5250-manta.dts b/arch/arm/boot/dts/samsung/exynos5250-manta.dts
index 24a27b342227..76d3657eb22f 100644
--- a/arch/arm/boot/dts/samsung/exynos5250-manta.dts
+++ b/arch/arm/boot/dts/samsung/exynos5250-manta.dts
@@ -461,6 +461,13 @@ acc_int: acc-int-pins {
 		samsung,pin-pud = <EXYNOS_PIN_PULL_UP>;
 	};
 
+	bt_host_wakeup: bt-host-wakeup-pins {
+		samsung,pins = "gpx2-6";
+		samsung,pin-function = <EXYNOS_PIN_FUNC_INPUT>;
+		samsung,pin-pud = <EXYNOS_PIN_PULL_NONE>;
+		samsung,pin-drv = <EXYNOS4_PIN_DRV_LV1>;
+	};
+
 	max77686_irq: max77686-irq-pins {
 		samsung,pins = "gpx0-2";
 		samsung,pin-function = <EXYNOS_PIN_FUNC_F>;
@@ -488,6 +495,20 @@ bh1721fvc_reset: bh1721fvc-reset-pins {
 		samsung,pin-pud = <EXYNOS_PIN_PULL_NONE>;
 	};
 
+	bt_reg_on: bt-reg-on-pins {
+		samsung,pins = "gph0-0";
+		samsung,pin-function = <EXYNOS_PIN_FUNC_OUTPUT>;
+		samsung,pin-con-pdn = <EXYNOS_PIN_PDN_PREV>;
+		samsung,pin-pud-pdn = <EXYNOS_PIN_PULL_NONE>;
+	};
+
+	bt_wake: bt-wake-pins {
+		samsung,pins = "gph1-3";
+		samsung,pin-function = <EXYNOS_PIN_FUNC_OUTPUT>;
+		samsung,pin-con-pdn = <EXYNOS_PIN_PDN_PREV>;
+		samsung,pin-pud-pdn = <EXYNOS_PIN_PULL_NONE>;
+	};
+
 	msense_reset: msense-reset-pins {
 		samsung,pins = "gpg2-0";
 		samsung,pin-function = <EXYNOS_PIN_FUNC_OUTPUT>;
@@ -536,7 +557,25 @@ &sd1_cmd {
 
 /* Bluetooth */
 &serial_0 {
-	status = "disabled";
+	pinctrl-0 = <&uart0_data &uart0_fctl>;
+	pinctrl-names = "default";
+
+	bluetooth {
+		compatible = "brcm,bcm4330-bt";
+
+		pinctrl-0 = <&bt_reg_on &bt_wake &bt_host_wakeup>;
+		pinctrl-names = "default";
+
+		shutdown-gpios = <&gph0 0 GPIO_ACTIVE_HIGH>;
+		device-wakeup-gpios = <&gph1 3 GPIO_ACTIVE_HIGH>;
+
+		interrupt-parent = <&gpx2>;
+		interrupts = <6 IRQ_TYPE_EDGE_FALLING>;
+		interrupt-names = "host-wakeup";
+
+		clocks = <&max77686 MAX77686_CLK_PMIC>;
+		clock-names = "lpo";
+	};
 };
 
 /* GPS */

---
base-commit: e5f7e05a699f41275d6380c497293446034bc8af
change-id: 20260404-manta-bluetooth-836133028bb6

Best regards,
--  
Lukas Timmermann <linux@timmermann.space>


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox