mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v2 0/3] hwmon: (fs1603) Add driver for TDK FS160x and FS1006 uPOL converters
@ 2026-10-20 16:48 Kevin Jia
  2026-10-20 16:48 ` [PATCH v2 1/3] dt-bindings: vendor-prefixes: Add TDK Kevin Jia
                   ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: Kevin Jia @ 2026-10-20 16:48 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Guenter Roeck,
	Jonathan Corbet, Shuah Khan, Randy Dunlap
  Cc: devicetree, linux-kernel, linux-hwmon, linux-doc, Kevin Jia,
	Abdurrahman Hussain

The TDK FS1603, FS1604, FS1606 and FS1006 are uPOL DC-DC converters with
an integrated inductor. They report input and output voltage, output
current and temperature over a proprietary I2C register map. They are not
PMBus devices, and their register numbers collide with PMBus command codes.

Patch 1 adds the TDK vendor prefix, patch 2 documents the parts as trivial
devices, and patch 3 adds a read-only hwmon driver. The model cannot be
detected from the part, so it is declared through the I2C device name or
the devicetree compatible. The FS1606-0600 and FS1606-2500 factory presets
scale telemetry differently from the base FS1606, so they have their own
compatibles.

Tested on FS1006-3300, FS1006-5000 and FS1606-0600 parts with the driver
backported to 6.12.

Signed-off-by: Kevin Jia <kjia@nexthop.ai>
---
Changes in v2:
- hwmon: Scale in1_crit by the feedback divider ratio measured at probe.
  Further testing after v1 found that on boards that raise the rail with a
  feedback divider, as the FS1606-0600 is used, VOUT_SET holds the 0.6 V
  feedback target while VOUT reports the rail, so v1 reported the
  feedback-pin trip point (about 0.72 V) as in1_crit on every such rail.
- hwmon: Use named initializers in fs1603_id[] (Sashiko).
- Link to v1: https://patch.msgid.link/20261019-fs1603-v1-0-8adf972386c1@nexthop.ai

---
Kevin Jia (3):
      dt-bindings: vendor-prefixes: Add TDK
      dt-bindings: trivial-devices: Add TDK FS160x and FS1006
      hwmon: (fs1603) Add driver for TDK FS160x and FS1006 uPOL converters

 .../devicetree/bindings/trivial-devices.yaml       |  14 +
 .../devicetree/bindings/vendor-prefixes.yaml       |   2 +
 Documentation/hwmon/fs1603.rst                     |  78 +++
 Documentation/hwmon/index.rst                      |   1 +
 MAINTAINERS                                        |   7 +
 drivers/hwmon/Kconfig                              |  11 +
 drivers/hwmon/Makefile                             |   1 +
 drivers/hwmon/fs1603.c                             | 540 +++++++++++++++++++++
 8 files changed, 654 insertions(+)
---
base-commit: 400be69515c327ced60082b27ede902a8d93cbdb
change-id: 20261016-fs1603-25483c5edc57

Best regards,
--  
Kevin Jia <kjia@nexthop.ai>


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v2 1/3] dt-bindings: vendor-prefixes: Add TDK
  2026-10-20 16:48 [PATCH v2 0/3] hwmon: (fs1603) Add driver for TDK FS160x and FS1006 uPOL converters Kevin Jia
@ 2026-10-20 16:48 ` Kevin Jia
  2026-10-26 22:06   ` Guenter Roeck
  2026-10-20 16:48 ` [PATCH v2 2/3] dt-bindings: trivial-devices: Add TDK FS160x and FS1006 Kevin Jia
  2026-10-20 16:48 ` [PATCH v2 3/3] hwmon: (fs1603) Add driver for TDK FS160x and FS1006 uPOL converters Kevin Jia
  2 siblings, 1 reply; 10+ messages in thread
From: Kevin Jia @ 2026-10-20 16:48 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Guenter Roeck,
	Jonathan Corbet, Shuah Khan, Randy Dunlap
  Cc: devicetree, linux-kernel, linux-hwmon, linux-doc, Kevin Jia

Add a vendor prefix for TDK Corporation.

Link: https://www.tdk.com/
Signed-off-by: Kevin Jia <kjia@nexthop.ai>
---
 Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 4a5cf436eb14..b82311759c47 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -1682,6 +1682,8 @@ patternProperties:
     description: Shenzhen City Tang Cheng Technology Co., Ltd.
   "^tcu,.*":
     description: TC Unterhaltungselektronik AG
+  "^tdk,.*":
+    description: TDK Corporation
   "^tdo,.*":
     description: Shangai Top Display Optoelectronics Co., Ltd
   "^team-source-display,.*":

-- 
2.43.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v2 2/3] dt-bindings: trivial-devices: Add TDK FS160x and FS1006
  2026-10-20 16:48 [PATCH v2 0/3] hwmon: (fs1603) Add driver for TDK FS160x and FS1006 uPOL converters Kevin Jia
  2026-10-20 16:48 ` [PATCH v2 1/3] dt-bindings: vendor-prefixes: Add TDK Kevin Jia
@ 2026-10-20 16:48 ` Kevin Jia
  2026-10-23  8:57   ` Krzysztof Kozlowski
  2026-10-20 16:48 ` [PATCH v2 3/3] hwmon: (fs1603) Add driver for TDK FS160x and FS1006 uPOL converters Kevin Jia
  2 siblings, 1 reply; 10+ messages in thread
From: Kevin Jia @ 2026-10-20 16:48 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Guenter Roeck,
	Jonathan Corbet, Shuah Khan, Randy Dunlap
  Cc: devicetree, linux-kernel, linux-hwmon, linux-doc, Kevin Jia

Add the TDK FS1603, FS1604, FS1606 and FS1006 uPOL converters. The
FS1606-0600 and FS1606-2500 presets scale telemetry differently, so they
get their own compatibles.

Signed-off-by: Kevin Jia <kjia@nexthop.ai>
---
 Documentation/devicetree/bindings/trivial-devices.yaml | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml
index 42527c7ee9c8..73bc8c130802 100644
--- a/Documentation/devicetree/bindings/trivial-devices.yaml
+++ b/Documentation/devicetree/bindings/trivial-devices.yaml
@@ -466,6 +466,20 @@ properties:
           - synaptics,synaptics_i2c
             # Ambient Light Sensor with SMBUS/Two Wire Serial Interface
           - taos,tsl2550
+            # TDK FS1006-3300 uPOL DC-DC converter with integrated inductor
+          - tdk,fs1006-3300
+            # TDK FS1006-5000 uPOL DC-DC converter with integrated inductor
+          - tdk,fs1006-5000
+            # TDK FS1603 uPOL DC-DC converter with integrated inductor
+          - tdk,fs1603
+            # TDK FS1604 uPOL DC-DC converter with integrated inductor
+          - tdk,fs1604
+            # TDK FS1606 uPOL DC-DC converter with integrated inductor
+          - tdk,fs1606
+            # TDK FS1606-0600 uPOL DC-DC converter with integrated inductor
+          - tdk,fs1606-0600
+            # TDK FS1606-2500 uPOL DC-DC converter with integrated inductor
+          - tdk,fs1606-2500
             # Digital PWM System Controller PMBus
           - ti,cd9200
             # Digital PWM System Controller PMBus

-- 
2.43.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v2 3/3] hwmon: (fs1603) Add driver for TDK FS160x and FS1006 uPOL converters
  2026-10-20 16:48 [PATCH v2 0/3] hwmon: (fs1603) Add driver for TDK FS160x and FS1006 uPOL converters Kevin Jia
  2026-10-20 16:48 ` [PATCH v2 1/3] dt-bindings: vendor-prefixes: Add TDK Kevin Jia
  2026-10-20 16:48 ` [PATCH v2 2/3] dt-bindings: trivial-devices: Add TDK FS160x and FS1006 Kevin Jia
@ 2026-10-20 16:48 ` Kevin Jia
  2026-10-26 22:04   ` Guenter Roeck
  2 siblings, 1 reply; 10+ messages in thread
From: Kevin Jia @ 2026-10-20 16:48 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Guenter Roeck,
	Jonathan Corbet, Shuah Khan, Randy Dunlap
  Cc: devicetree, linux-kernel, linux-hwmon, linux-doc, Kevin Jia,
	Abdurrahman Hussain

The TDK FS1603, FS1604, FS1606 and FS1006 are uPOL DC-DC converters with
an integrated inductor. They report input voltage, output voltage, output
current and temperature, plus over-voltage, over-current and
over-temperature status, over a proprietary I2C register map. They are
not PMBus devices, and their register numbers collide with PMBus command
codes: 0x21 is a status byte here where PMBus defines VOUT_COMMAND.

The parts have no ID register, and the FS1606-0600 and FS1606-2500
presets change how telemetry is encoded: the -2500 uses the 20mV steps
of the higher voltage parts, and the -0600 needs a current correction
derived from the IOUT_GAIN register and the output voltage. The model is
therefore taken from the device name or compatible. OCSet[2:0], the
programmable over-current protection level, ships with a different
default on each family member, so probe warns if it does not match the
declared part, which usually catches a wrong device name.

The over-voltage and over-temperature protection thresholds are
reported as in1_crit and temp1_crit. The over-voltage threshold is a
percentage of the VOUT_SET target, so the driver reads VOUT_SET but
never writes it: VOUT_SET is a two-byte pair, and a partial write leaves
the part silently ignoring all register writes until its input power is
cycled. The driver is read-only.

Power good is not exposed. It also deasserts while the output is
disabled or soft-starting, so it would report a deliberately disabled
output as a fault.

On boards that raise the rail with a feedback divider, VOUT_SET holds
the feedback-pin target while the output voltage telemetry reports the
rail. The driver measures the divider ratio at probe from the output
voltage and the Fb register and scales the over-voltage threshold by
it, so in1_crit is in the same domain as in1_input. A part whose Fb
reads within four steps of the output keeps a ratio of one.

Co-developed-by: Abdurrahman Hussain <abdurrahman@nexthop.ai>
Signed-off-by: Abdurrahman Hussain <abdurrahman@nexthop.ai>
Signed-off-by: Kevin Jia <kjia@nexthop.ai>
---
 Documentation/hwmon/fs1603.rst |  78 ++++++
 Documentation/hwmon/index.rst  |   1 +
 MAINTAINERS                    |   7 +
 drivers/hwmon/Kconfig          |  11 +
 drivers/hwmon/Makefile         |   1 +
 drivers/hwmon/fs1603.c         | 540 +++++++++++++++++++++++++++++++++++++++++
 6 files changed, 638 insertions(+)

diff --git a/Documentation/hwmon/fs1603.rst b/Documentation/hwmon/fs1603.rst
new file mode 100644
index 000000000000..ba0038e0d9b5
--- /dev/null
+++ b/Documentation/hwmon/fs1603.rst
@@ -0,0 +1,78 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+Kernel driver fs1603
+====================
+
+Supported chips:
+
+  * TDK FS1603
+
+    Prefix: 'fs1603'
+
+    Datasheet: Publicly available at the TDK website
+
+  * TDK FS1604
+
+    Prefix: 'fs1604'
+
+    Datasheet: Publicly available at the TDK website
+
+  * TDK FS1606, FS1606-0600, FS1606-2500
+
+    Prefixes: 'fs1606', 'fs1606_0600', 'fs1606_2500'
+
+    Datasheet: Publicly available at the TDK website
+
+  * TDK FS1006-3300, FS1006-5000
+
+    Prefixes: 'fs1006_3300', 'fs1006_5000'
+
+    Datasheet: Publicly available at the TDK website
+
+Author:
+
+  * Kevin Jia <kjia@nexthop.ai>
+
+Description
+-----------
+
+The TDK FS160x and FS1006 are uPOL DC-DC converters with an integrated
+inductor. They report input voltage, output voltage, output current and
+temperature over a proprietary I2C register map. They are not PMBus devices,
+and their register numbers collide with PMBus command codes, so a PMBus driver
+must not be bound to them.
+
+The model is not auto-detected. It must be declared through the I2C device
+name or the devicetree compatible. The FS1606-0600 and FS1606-2500 presets
+scale telemetry differently from the base FS1606, so they must be declared
+by their own names. At probe time the driver compares the OCSet register,
+the programmable over-current protection level, with the factory default of
+the declared family member and logs a warning on a mismatch. That usually
+means the wrong model was declared, but it is expected on boards that
+reprogram the over-current protection level.
+
+The driver is read-only. On boards that use a feedback divider to reach a
+higher rail voltage, the output voltage is still reported for the rail, and the
+over-voltage threshold is scaled to the rail by the divider ratio, measured at
+probe from the output voltage and the feedback pin. TDK notes that current
+reporting does not apply in that configuration.
+
+Sysfs attributes
+----------------
+
+=================== == ========================================================
+in0_input           RO Input voltage in millivolts.
+in0_label           RO "vin"
+in1_input           RO Output voltage in millivolts.
+in1_label           RO "vout"
+in1_crit            RO Output over-voltage protection threshold in millivolts:
+                       the OV_Threshold percentage of the VOUT_SET target,
+                       scaled to the rail on boards with a feedback divider.
+in1_alarm           RO Output over-voltage status.
+curr1_input         RO Output current in milliamperes.
+curr1_alarm         RO Over-current status.
+temp1_input         RO Temperature in millidegrees Celsius.
+temp1_crit          RO Over-temperature protection threshold in millidegrees
+                       Celsius.
+temp1_alarm         RO Over-temperature status.
+=================== == ========================================================
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index 199ca79621e9..7d79a7aadaa6 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -81,6 +81,7 @@ Hardware Monitoring Kernel Drivers
    f71805f
    f71882fg
    fam15h_power
+   fs1603
    fsp-3y
    ftsteutates
    g760a
diff --git a/MAINTAINERS b/MAINTAINERS
index 5ad12c57ca5f..24073ee3ede2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -26799,6 +26799,13 @@ W:	https://linuxtv.org
 T:	git git://linuxtv.org/media.git
 F:	drivers/media/i2c/tda9840*
 
+TDK FS1603 HARDWARE MONITOR DRIVER
+M:	Kevin Jia <kjia@nexthop.ai>
+L:	linux-hwmon@vger.kernel.org
+S:	Maintained
+F:	Documentation/hwmon/fs1603.rst
+F:	drivers/hwmon/fs1603.c
+
 TEA5761 TUNER DRIVER
 M:	Mauro Carvalho Chehab <mchehab@kernel.org>
 L:	linux-media@vger.kernel.org
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 18c682e1a475..047a3050946b 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -728,6 +728,17 @@ config SENSORS_MC33XS2410
 	  This driver can also be built as a module. If so, the module
 	  will be called mc33xs2410_hwmon.
 
+config SENSORS_FS1603
+	tristate "TDK FS1603 and compatible uPOL DC-DC converters"
+	depends on I2C
+	select REGMAP_I2C
+	help
+	  If you say yes here you get hardware monitoring support for the
+	  TDK FS1603, FS1604, FS1606 and FS1006 uPOL DC-DC converters.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called fs1603.
+
 config SENSORS_FSCHMD
 	tristate "Fujitsu Siemens Computers sensor chips"
 	depends on (X86 || COMPILE_TEST) && I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index d4c345ed7261..23a5f262eecf 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -85,6 +85,7 @@ obj-$(CONFIG_SENSORS_F71805F)	+= f71805f.o
 obj-$(CONFIG_SENSORS_F71882FG)	+= f71882fg.o
 obj-$(CONFIG_SENSORS_F75375S)	+= f75375s.o
 obj-$(CONFIG_SENSORS_FAM15H_POWER) += fam15h_power.o
+obj-$(CONFIG_SENSORS_FS1603)	+= fs1603.o
 obj-$(CONFIG_SENSORS_FSCHMD)	+= fschmd.o
 obj-$(CONFIG_SENSORS_FTSTEUTATES) += ftsteutates.o
 obj-$(CONFIG_SENSORS_G760A)	+= g760a.o
diff --git a/drivers/hwmon/fs1603.c b/drivers/hwmon/fs1603.c
new file mode 100644
index 000000000000..afe75657ce39
--- /dev/null
+++ b/drivers/hwmon/fs1603.c
@@ -0,0 +1,540 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/*
+ * Copyright (C) 2026 Nexthop Systems Inc.
+ *
+ * Driver for the TDK FS160x family of uPOL DC-DC converters with integrated
+ * inductor. Supported models: FS1603, FS1604, FS1606, FS1006.
+ *
+ * The parts expose voltage, current and temperature telemetry over a
+ * proprietary I2C register map. They are not PMBus devices, and their
+ * register numbers actively collide with PMBus command codes -- 0x21 is a
+ * status byte here where PMBus defines VOUT_COMMAND -- so a generic PMBus
+ * driver must never be bound to them.
+ *
+ * Read-only. VOUT_SET (0x12/0x13) is an atomic write-protected pair: writing
+ * the high byte arms an update that takes effect only once the low byte is
+ * written. A partial write leaves the part armed, after which it acknowledges
+ * but silently ignores every register write until its input power is cycled,
+ * so this driver only reads it, to scale the over-voltage threshold.
+ *
+ * On a board that raises the rail with a feedback divider, VOUT still reports
+ * the rail but VOUT_SET holds the feedback-pin target, so the over-voltage
+ * threshold is scaled by the divider ratio, measured at probe from VOUT and
+ * the Fb register (0x22). TDK notes that current reporting does not apply in
+ * that configuration.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#define FS1603_REG_PVIN			0x0C
+#define FS1603_REG_VOUT			0x0D
+#define FS1603_REG_IOUT			0x0E
+#define FS1603_REG_TEMP			0x0F
+#define FS1603_REG_VOUT_SET_HI		0x12
+#define FS1603_REG_VOUT_SET_LO		0x13
+#define FS1603_REG_OCSET		0x15
+#define FS1603_REG_OV_THRESHOLD		0x17
+#define FS1603_REG_OT_THRESHOLD		0x19
+#define FS1603_REG_IOUT_GAIN		0x1A
+#define FS1603_REG_STATUS		0x21
+#define FS1603_REG_FB			0x22
+
+#define FS1603_VOUT_SET_HI_MASK		BIT(0)
+#define FS1603_OV_THRESHOLD_MASK	GENMASK(1, 0)
+#define FS1603_OT_THRESHOLD_MASK	GENMASK(1, 0)
+#define FS1603_IOUT_GAIN_MASK		GENMASK(7, 2)
+
+/*
+ * OCSet[2:0] is the user-programmable over-current protection trip level,
+ * not an ID. Each family member ships with a different default, so a
+ * mismatch at probe usually means the wrong model was declared.
+ */
+#define FS1603_OCSET_LEVEL		GENMASK(2, 0)
+#define FS1603_OCSET_DEFAULT_FS1603	0
+#define FS1603_OCSET_DEFAULT_FS1604	1
+#define FS1603_OCSET_DEFAULT_FS1606	2
+#define FS1603_OCSET_DEFAULT_FS1006	3
+
+/* Bits in FS1603_REG_STATUS. */
+#define FS1603_STATUS_OVER_VOLTAGE	BIT(6)
+#define FS1603_STATUS_OVER_CURRENT	BIT(5)
+#define FS1603_STATUS_OVER_TEMP		BIT(4)
+
+/* OV_Threshold[1:0] in percent of programmed VOUT; OT_Threshold[1:0] in degC. */
+static const unsigned int fs1603_ov_threshold_pct[] = { 105, 110, 115, 120 };
+static const unsigned int fs1603_ot_threshold_c[] = { 75, 85, 125, 145 };
+
+/*
+ * Voltage telemetry is a linear step and base, both fixed per variant.
+ * Current is raw/32 A except on the FS1606-0600, which applies a calibration
+ * correction derived from FS1603_REG_IOUT_GAIN and the output voltage.
+ */
+struct fs1603_info {
+	unsigned int vout_step_mv;
+	unsigned int vout_base_mv;
+	u8 ocset_default;
+	bool iout_gain_corrected;
+};
+
+enum fs1603_model {
+	fs1603,
+	fs1604,
+	fs1606,
+	fs1606_0600,
+	fs1606_2500,
+	fs1006,
+};
+
+/*
+ * The FS1606 reports at 10mV/300mV except for the -2500 preset, which uses the
+ * same 20mV/600mV encoding as the higher-voltage family members.
+ */
+static const struct fs1603_info fs1603_models[] = {
+	[fs1603]      = { .vout_step_mv = 20, .vout_base_mv = 600,
+			  .ocset_default = FS1603_OCSET_DEFAULT_FS1603 },
+	[fs1604]      = { .vout_step_mv = 20, .vout_base_mv = 600,
+			  .ocset_default = FS1603_OCSET_DEFAULT_FS1604 },
+	[fs1606]      = { .vout_step_mv = 10, .vout_base_mv = 300,
+			  .ocset_default = FS1603_OCSET_DEFAULT_FS1606 },
+	[fs1606_0600] = { .vout_step_mv = 10, .vout_base_mv = 300,
+			  .ocset_default = FS1603_OCSET_DEFAULT_FS1606,
+			  .iout_gain_corrected = true },
+	[fs1606_2500] = { .vout_step_mv = 20, .vout_base_mv = 600,
+			  .ocset_default = FS1603_OCSET_DEFAULT_FS1606 },
+	[fs1006]      = { .vout_step_mv = 20, .vout_base_mv = 600,
+			  .ocset_default = FS1603_OCSET_DEFAULT_FS1006 },
+};
+
+/*
+ * Every register is a byte, and every read is live telemetry, so nothing is
+ * cached. regmap is used for the uniform accessors and the debugfs dump.
+ */
+static const struct regmap_config fs1603_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = FS1603_REG_FB,
+	.cache_type = REGCACHE_NONE,
+};
+
+struct fs1603_data {
+	struct regmap *regmap;
+	const struct fs1603_info *info;
+	u32 fb_ratio_num;
+	u32 fb_ratio_den;
+};
+
+static int fs1603_read_vout_mv(struct fs1603_data *data, long *val)
+{
+	unsigned int raw;
+	int err;
+
+	err = regmap_read(data->regmap, FS1603_REG_VOUT, &raw);
+	if (err)
+		return err;
+
+	*val = raw * data->info->vout_step_mv + data->info->vout_base_mv;
+	return 0;
+}
+
+/*
+ * Output current in mA.
+ *
+ * Every variant but the FS1606-0600 reports raw/32 A directly. The -0600
+ * additionally applies, with G taken from FS1603_REG_IOUT_GAIN bits [7:2]:
+ *
+ *   I = raw/32 - (9.05 - 0.24 * G) * Vout - 0.356 * G + 13.1
+ *
+ * Scaled to milliamps and millivolts so it stays in integer arithmetic:
+ *
+ *   I_mA = raw * 125 / 4
+ *          - ((9050 - 240 * G) * Vout_mV) / 1000
+ *          - 356 * G
+ *          + 13100
+ */
+static int fs1603_read_iout_ma(struct fs1603_data *data, long *val)
+{
+	unsigned int raw, gain;
+	long vout_mv;
+	int err;
+
+	err = regmap_read(data->regmap, FS1603_REG_IOUT, &raw);
+	if (err)
+		return err;
+
+	*val = raw * 125 / 4;
+
+	if (!data->info->iout_gain_corrected)
+		return 0;
+
+	err = regmap_read(data->regmap, FS1603_REG_IOUT_GAIN, &gain);
+	if (err)
+		return err;
+	gain = FIELD_GET(FS1603_IOUT_GAIN_MASK, gain);
+
+	err = fs1603_read_vout_mv(data, &vout_mv);
+	if (err)
+		return err;
+
+	*val -= ((9050 - 240 * (long)gain) * vout_mv) / 1000;
+	*val -= 356 * (long)gain;
+	*val += 13100;
+
+	if (*val < 0)
+		*val = 0;
+
+	return 0;
+}
+
+/*
+ * The 9-bit VOUT_SET code steps at half the telemetry resolution from a base
+ * of 80 steps.
+ */
+static int fs1603_read_vout_set_mv(struct fs1603_data *data, long *val)
+{
+	unsigned int hi, lo;
+	int err;
+
+	err = regmap_read(data->regmap, FS1603_REG_VOUT_SET_HI, &hi);
+	if (err)
+		return err;
+	err = regmap_read(data->regmap, FS1603_REG_VOUT_SET_LO, &lo);
+	if (err)
+		return err;
+
+	*val = ((FIELD_GET(FS1603_VOUT_SET_HI_MASK, hi) << 8 | lo) + 80) *
+	       data->info->vout_step_mv / 2;
+	return 0;
+}
+
+/* Fb has no documented encoding; it decodes with the VOUT step and base. */
+static int fs1603_read_fb_mv(struct fs1603_data *data, long *val)
+{
+	unsigned int raw;
+	int err;
+
+	err = regmap_read(data->regmap, FS1603_REG_FB, &raw);
+	if (err)
+		return err;
+
+	*val = raw * data->info->vout_step_mv + data->info->vout_base_mv;
+	return 0;
+}
+
+/*
+ * A board that raises the rail with a feedback divider leaves VOUT_SET at the
+ * feedback target, and Fb then reads well below VOUT. The divider is fixed in
+ * hardware, so measure its ratio once. Without a divider Fb reads the output
+ * itself, and the ratio stays one so the limit does not follow ADC noise.
+ */
+static int fs1603_measure_fb_ratio(struct fs1603_data *data)
+{
+	long vout_mv, fb_mv;
+	int err;
+
+	data->fb_ratio_num = 1;
+	data->fb_ratio_den = 1;
+
+	err = fs1603_read_vout_mv(data, &vout_mv);
+	if (err)
+		return err;
+	err = fs1603_read_fb_mv(data, &fb_mv);
+	if (err)
+		return err;
+
+	if (fb_mv > 0 && vout_mv > fb_mv + 4 * data->info->vout_step_mv) {
+		data->fb_ratio_num = vout_mv;
+		data->fb_ratio_den = fb_mv;
+	}
+	return 0;
+}
+
+/*
+ * OVP trips at OV_Threshold percent of the VOUT_SET target, measured at the
+ * feedback pin. Scale it by the feedback divider ratio to report it in the
+ * same rail domain as in1_input.
+ */
+static int fs1603_read_vout_crit_mv(struct fs1603_data *data, long *val)
+{
+	unsigned int raw;
+	long vout_set_mv;
+	int err;
+
+	err = regmap_read(data->regmap, FS1603_REG_OV_THRESHOLD, &raw);
+	if (err)
+		return err;
+	err = fs1603_read_vout_set_mv(data, &vout_set_mv);
+	if (err)
+		return err;
+
+	raw = FIELD_GET(FS1603_OV_THRESHOLD_MASK, raw);
+	*val = DIV_ROUND_CLOSEST_ULL((u64)fs1603_ov_threshold_pct[raw] *
+				     vout_set_mv * data->fb_ratio_num,
+				     100 * data->fb_ratio_den);
+	return 0;
+}
+
+static int fs1603_read_status_bit(struct fs1603_data *data, u8 mask,
+				  long *val)
+{
+	unsigned int status;
+	int err;
+
+	err = regmap_read(data->regmap, FS1603_REG_STATUS, &status);
+	if (err)
+		return err;
+
+	*val = !!(status & mask);
+	return 0;
+}
+
+static int fs1603_read_in(struct fs1603_data *data, u32 attr, int channel,
+			  long *val)
+{
+	unsigned int raw;
+	int err;
+
+	switch (attr) {
+	case hwmon_in_input:
+		if (channel == 0) {
+			err = regmap_read(data->regmap, FS1603_REG_PVIN, &raw);
+			if (err)
+				return err;
+			/* PVIN reports in 1/16 V steps. */
+			*val = raw * 1000 / 16;
+			return 0;
+		}
+		return fs1603_read_vout_mv(data, val);
+	case hwmon_in_crit:
+		return fs1603_read_vout_crit_mv(data, val);
+	case hwmon_in_alarm:
+		return fs1603_read_status_bit(data, FS1603_STATUS_OVER_VOLTAGE,
+					      val);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static int fs1603_read_curr(struct fs1603_data *data, u32 attr, long *val)
+{
+	switch (attr) {
+	case hwmon_curr_input:
+		return fs1603_read_iout_ma(data, val);
+	case hwmon_curr_alarm:
+		return fs1603_read_status_bit(data, FS1603_STATUS_OVER_CURRENT,
+					      val);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static int fs1603_read_temp_crit_mc(struct fs1603_data *data, long *val)
+{
+	unsigned int raw;
+	int err;
+
+	err = regmap_read(data->regmap, FS1603_REG_OT_THRESHOLD, &raw);
+	if (err)
+		return err;
+
+	*val = fs1603_ot_threshold_c[FIELD_GET(FS1603_OT_THRESHOLD_MASK, raw)] * 1000;
+	return 0;
+}
+
+static int fs1603_read_temp(struct fs1603_data *data, u32 attr, long *val)
+{
+	unsigned int raw;
+	int err;
+
+	switch (attr) {
+	case hwmon_temp_input:
+		err = regmap_read(data->regmap, FS1603_REG_TEMP, &raw);
+		if (err)
+			return err;
+		/* TEMP_REPORT is degC directly: 1 degC per LSB, no offset. */
+		*val = raw * 1000;
+		return 0;
+	case hwmon_temp_crit:
+		return fs1603_read_temp_crit_mc(data, val);
+	case hwmon_temp_alarm:
+		return fs1603_read_status_bit(data, FS1603_STATUS_OVER_TEMP,
+					      val);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static int fs1603_read(struct device *dev, enum hwmon_sensor_types type,
+		       u32 attr, int channel, long *val)
+{
+	struct fs1603_data *data = dev_get_drvdata(dev);
+
+	switch (type) {
+	case hwmon_in:
+		return fs1603_read_in(data, attr, channel, val);
+	case hwmon_curr:
+		return fs1603_read_curr(data, attr, val);
+	case hwmon_temp:
+		return fs1603_read_temp(data, attr, val);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static int fs1603_read_string(struct device *dev, enum hwmon_sensor_types type,
+			      u32 attr, int channel, const char **str)
+{
+	if (type != hwmon_in || attr != hwmon_in_label)
+		return -EOPNOTSUPP;
+
+	*str = channel == 0 ? "vin" : "vout";
+	return 0;
+}
+
+static umode_t fs1603_is_visible(const void *data, enum hwmon_sensor_types type,
+				 u32 attr, int channel)
+{
+	switch (type) {
+	case hwmon_in:
+		switch (attr) {
+		case hwmon_in_input:
+		case hwmon_in_label:
+			return 0444;
+		case hwmon_in_crit:
+		case hwmon_in_alarm:
+			/* OVP and its alarm describe the regulated output only. */
+			return channel == 1 ? 0444 : 0;
+		default:
+			return 0;
+		}
+	case hwmon_curr:
+	case hwmon_temp:
+		return 0444;
+	default:
+		return 0;
+	}
+}
+
+static const struct hwmon_ops fs1603_ops = {
+	.is_visible = fs1603_is_visible,
+	.read = fs1603_read,
+	.read_string = fs1603_read_string,
+};
+
+static const struct hwmon_channel_info *fs1603_channel_info[] = {
+	HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LABEL,
+			   HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_CRIT |
+			   HWMON_I_ALARM),
+	HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT | HWMON_C_ALARM),
+	HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_ALARM),
+	NULL
+};
+
+static const struct hwmon_chip_info fs1603_chip_info = {
+	.ops = &fs1603_ops,
+	.info = fs1603_channel_info,
+};
+
+static int fs1603_probe(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	char *name;
+	struct fs1603_data *data;
+	struct device *hwmon;
+	unsigned int ocset;
+	int err;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		return dev_err_probe(dev, -ENODEV,
+				     "SMBus byte data not supported\n");
+
+	name = devm_hwmon_sanitize_name(dev, client->name);
+	if (IS_ERR(name))
+		return PTR_ERR(name);
+
+	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->regmap = devm_regmap_init_i2c(client, &fs1603_regmap_config);
+	if (IS_ERR(data->regmap))
+		return dev_err_probe(dev, PTR_ERR(data->regmap),
+				     "failed to allocate regmap\n");
+
+	data->info = i2c_get_match_data(client);
+	if (!data->info)
+		return dev_err_probe(dev, -ENODEV, "no match data for %s\n",
+				     client->name);
+
+	err = regmap_read(data->regmap, FS1603_REG_OCSET, &ocset);
+	if (err)
+		return dev_err_probe(dev, err, "failed to read OCSet\n");
+
+	ocset = FIELD_GET(FS1603_OCSET_LEVEL, ocset);
+	if (ocset != data->info->ocset_default)
+		dev_warn(dev, "OCSet %u differs from the %s default %u\n",
+			 ocset, client->name, data->info->ocset_default);
+
+	err = fs1603_measure_fb_ratio(data);
+	if (err)
+		return dev_err_probe(dev, err,
+				     "failed to read the feedback ratio\n");
+
+	hwmon = devm_hwmon_device_register_with_info(dev, name, data,
+						     &fs1603_chip_info, NULL);
+	return PTR_ERR_OR_ZERO(hwmon);
+}
+
+static const struct i2c_device_id fs1603_id[] = {
+	{ .name = "fs1603",
+	  .driver_data = (kernel_ulong_t)&fs1603_models[fs1603] },
+	{ .name = "fs1604",
+	  .driver_data = (kernel_ulong_t)&fs1603_models[fs1604] },
+	{ .name = "fs1606",
+	  .driver_data = (kernel_ulong_t)&fs1603_models[fs1606] },
+	{ .name = "fs1606_0600",
+	  .driver_data = (kernel_ulong_t)&fs1603_models[fs1606_0600] },
+	{ .name = "fs1606_2500",
+	  .driver_data = (kernel_ulong_t)&fs1603_models[fs1606_2500] },
+	{ .name = "fs1006_5000",
+	  .driver_data = (kernel_ulong_t)&fs1603_models[fs1006] },
+	{ .name = "fs1006_3300",
+	  .driver_data = (kernel_ulong_t)&fs1603_models[fs1006] },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, fs1603_id);
+
+static const struct of_device_id fs1603_of_match[] = {
+	{ .compatible = "tdk,fs1603", .data = &fs1603_models[fs1603] },
+	{ .compatible = "tdk,fs1604", .data = &fs1603_models[fs1604] },
+	{ .compatible = "tdk,fs1606", .data = &fs1603_models[fs1606] },
+	{ .compatible = "tdk,fs1606-0600",
+	  .data = &fs1603_models[fs1606_0600] },
+	{ .compatible = "tdk,fs1606-2500",
+	  .data = &fs1603_models[fs1606_2500] },
+	{ .compatible = "tdk,fs1006-5000",
+	  .data = &fs1603_models[fs1006] },
+	{ .compatible = "tdk,fs1006-3300",
+	  .data = &fs1603_models[fs1006] },
+	{}
+};
+MODULE_DEVICE_TABLE(of, fs1603_of_match);
+
+static struct i2c_driver fs1603_driver = {
+	.driver = {
+		.name	= "fs1603",
+		.of_match_table = fs1603_of_match,
+	},
+	.probe = fs1603_probe,
+	.id_table = fs1603_id,
+};
+module_i2c_driver(fs1603_driver);
+
+MODULE_AUTHOR("Kevin Jia <kjia@nexthop.ai>");
+MODULE_DESCRIPTION("TDK FS160x uPOL DC-DC converter telemetry driver");
+MODULE_LICENSE("GPL");

-- 
2.43.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 2/3] dt-bindings: trivial-devices: Add TDK FS160x and FS1006
  2026-10-20 16:48 ` [PATCH v2 2/3] dt-bindings: trivial-devices: Add TDK FS160x and FS1006 Kevin Jia
@ 2026-10-23  8:57   ` Krzysztof Kozlowski
  2026-10-26 16:08     ` Guenter Roeck
  0 siblings, 1 reply; 10+ messages in thread
From: Krzysztof Kozlowski @ 2026-10-23  8:57 UTC (permalink / raw)
  To: Kevin Jia
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Guenter Roeck,
	Jonathan Corbet, Shuah Khan, Randy Dunlap, devicetree,
	linux-kernel, linux-hwmon, linux-doc

On Tue, Oct 20, 2026 at 04:48:15PM +0000, Kevin Jia wrote:
> Add the TDK FS1603, FS1604, FS1606 and FS1006 uPOL converters. The
> FS1606-0600 and FS1606-2500 presets scale telemetry differently, so they
> get their own compatibles.

And why tdk,fs1006 has multiple variants? Are they actually different?

> 
> Signed-off-by: Kevin Jia <kjia@nexthop.ai>
> ---
>  Documentation/devicetree/bindings/trivial-devices.yaml | 14 ++++++++++++++
>  1 file changed, 14 insertions(+)

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 2/3] dt-bindings: trivial-devices: Add TDK FS160x and FS1006
  2026-10-23  8:57   ` Krzysztof Kozlowski
@ 2026-10-26 16:08     ` Guenter Roeck
  0 siblings, 0 replies; 10+ messages in thread
From: Guenter Roeck @ 2026-10-26 16:08 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Kevin Jia
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
	Shuah Khan, Randy Dunlap, devicetree, linux-kernel, linux-hwmon,
	linux-doc

On 10/23/26 01:57, Krzysztof Kozlowski wrote:
> On Tue, Oct 20, 2026 at 04:48:15PM +0000, Kevin Jia wrote:
>> Add the TDK FS1603, FS1604, FS1606 and FS1006 uPOL converters. The
>> FS1606-0600 and FS1606-2500 presets scale telemetry differently, so they
>> get their own compatibles.
> 
> And why tdk,fs1006 has multiple variants? Are they actually different?
> 

I checked the datasheet. Unfortunately, they _are_ different, and there seems
to be no way to determine the chip ID by reading chip registers. This is an
odd chip :-(.

Guenter

>>
>> Signed-off-by: Kevin Jia <kjia@nexthop.ai>
>> ---
>>   Documentation/devicetree/bindings/trivial-devices.yaml | 14 ++++++++++++++
>>   1 file changed, 14 insertions(+)
> 
> Best regards,
> Krzysztof


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 3/3] hwmon: (fs1603) Add driver for TDK FS160x and FS1006 uPOL converters
  2026-10-20 16:48 ` [PATCH v2 3/3] hwmon: (fs1603) Add driver for TDK FS160x and FS1006 uPOL converters Kevin Jia
@ 2026-10-26 22:04   ` Guenter Roeck
  2026-10-27 23:21     ` Kevin Jia
  0 siblings, 1 reply; 10+ messages in thread
From: Guenter Roeck @ 2026-10-26 22:04 UTC (permalink / raw)
  To: Kevin Jia
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
	Shuah Khan, Randy Dunlap, devicetree, linux-kernel, linux-hwmon,
	linux-doc, Abdurrahman Hussain

On Tue, Oct 20, 2026 at 04:48:16PM +0000, Kevin Jia wrote:
> The TDK FS1603, FS1604, FS1606 and FS1006 are uPOL DC-DC converters with
> an integrated inductor. They report input voltage, output voltage, output
> current and temperature, plus over-voltage, over-current and
> over-temperature status, over a proprietary I2C register map. They are
> not PMBus devices, and their register numbers collide with PMBus command
> codes: 0x21 is a status byte here where PMBus defines VOUT_COMMAND.
> 
> The parts have no ID register, and the FS1606-0600 and FS1606-2500
> presets change how telemetry is encoded: the -2500 uses the 20mV steps
> of the higher voltage parts, and the -0600 needs a current correction
> derived from the IOUT_GAIN register and the output voltage. The model is
> therefore taken from the device name or compatible. OCSet[2:0], the
> programmable over-current protection level, ships with a different
> default on each family member, so probe warns if it does not match the
> declared part, which usually catches a wrong device name.
> 
> The over-voltage and over-temperature protection thresholds are
> reported as in1_crit and temp1_crit. The over-voltage threshold is a
> percentage of the VOUT_SET target, so the driver reads VOUT_SET but
> never writes it: VOUT_SET is a two-byte pair, and a partial write leaves
> the part silently ignoring all register writes until its input power is
> cycled. The driver is read-only.
> 
> Power good is not exposed. It also deasserts while the output is
> disabled or soft-starting, so it would report a deliberately disabled
> output as a fault.
> 
> On boards that raise the rail with a feedback divider, VOUT_SET holds
> the feedback-pin target while the output voltage telemetry reports the
> rail. The driver measures the divider ratio at probe from the output
> voltage and the Fb register and scales the over-voltage threshold by
> it, so in1_crit is in the same domain as in1_input. A part whose Fb
> reads within four steps of the output keeps a ratio of one.
> 
> Co-developed-by: Abdurrahman Hussain <abdurrahman@nexthop.ai>
> Signed-off-by: Abdurrahman Hussain <abdurrahman@nexthop.ai>
> Signed-off-by: Kevin Jia <kjia@nexthop.ai>
> ---
>  Documentation/hwmon/fs1603.rst |  78 ++++++
>  Documentation/hwmon/index.rst  |   1 +
>  MAINTAINERS                    |   7 +
>  drivers/hwmon/Kconfig          |  11 +
>  drivers/hwmon/Makefile         |   1 +
>  drivers/hwmon/fs1603.c         | 540 +++++++++++++++++++++++++++++++++++++++++
>  6 files changed, 638 insertions(+)
> 
> diff --git a/Documentation/hwmon/fs1603.rst b/Documentation/hwmon/fs1603.rst
> new file mode 100644
> index 000000000000..ba0038e0d9b5
> --- /dev/null
> +++ b/Documentation/hwmon/fs1603.rst
> @@ -0,0 +1,78 @@
> +.. SPDX-License-Identifier: GPL-2.0-or-later
> +
> +Kernel driver fs1603
> +====================
> +
> +Supported chips:
> +
> +  * TDK FS1603
> +
> +    Prefix: 'fs1603'
> +
> +    Datasheet: Publicly available at the TDK website
> +
> +  * TDK FS1604
> +
> +    Prefix: 'fs1604'
> +
> +    Datasheet: Publicly available at the TDK website
> +
> +  * TDK FS1606, FS1606-0600, FS1606-2500
> +
> +    Prefixes: 'fs1606', 'fs1606_0600', 'fs1606_2500'
> +
> +    Datasheet: Publicly available at the TDK website
> +
> +  * TDK FS1006-3300, FS1006-5000
> +
> +    Prefixes: 'fs1006_3300', 'fs1006_5000'
> +
> +    Datasheet: Publicly available at the TDK website
> +
> +Author:
> +
> +  * Kevin Jia <kjia@nexthop.ai>
> +
> +Description
> +-----------
> +
> +The TDK FS160x and FS1006 are uPOL DC-DC converters with an integrated
> +inductor. They report input voltage, output voltage, output current and
> +temperature over a proprietary I2C register map. They are not PMBus devices,
> +and their register numbers collide with PMBus command codes, so a PMBus driver
> +must not be bound to them.
> +
> +The model is not auto-detected. It must be declared through the I2C device
> +name or the devicetree compatible. The FS1606-0600 and FS1606-2500 presets
> +scale telemetry differently from the base FS1606, so they must be declared
> +by their own names. At probe time the driver compares the OCSet register,
> +the programmable over-current protection level, with the factory default of
> +the declared family member and logs a warning on a mismatch. That usually
> +means the wrong model was declared, but it is expected on boards that
> +reprogram the over-current protection level.
> +
> +The driver is read-only. On boards that use a feedback divider to reach a
> +higher rail voltage, the output voltage is still reported for the rail, and the
> +over-voltage threshold is scaled to the rail by the divider ratio, measured at
> +probe from the output voltage and the feedback pin. TDK notes that current
> +reporting does not apply in that configuration.
> +
> +Sysfs attributes
> +----------------
> +
> +=================== == ========================================================
> +in0_input           RO Input voltage in millivolts.
> +in0_label           RO "vin"
> +in1_input           RO Output voltage in millivolts.
> +in1_label           RO "vout"
> +in1_crit            RO Output over-voltage protection threshold in millivolts:
> +                       the OV_Threshold percentage of the VOUT_SET target,
> +                       scaled to the rail on boards with a feedback divider.
> +in1_alarm           RO Output over-voltage status.
> +curr1_input         RO Output current in milliamperes.
> +curr1_alarm         RO Over-current status.
> +temp1_input         RO Temperature in millidegrees Celsius.
> +temp1_crit          RO Over-temperature protection threshold in millidegrees
> +                       Celsius.
> +temp1_alarm         RO Over-temperature status.
> +=================== == ========================================================
> diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
> index 199ca79621e9..7d79a7aadaa6 100644
> --- a/Documentation/hwmon/index.rst
> +++ b/Documentation/hwmon/index.rst
> @@ -81,6 +81,7 @@ Hardware Monitoring Kernel Drivers
>     f71805f
>     f71882fg
>     fam15h_power
> +   fs1603
>     fsp-3y
>     ftsteutates
>     g760a
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 5ad12c57ca5f..24073ee3ede2 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -26799,6 +26799,13 @@ W:	https://linuxtv.org
>  T:	git git://linuxtv.org/media.git
>  F:	drivers/media/i2c/tda9840*
>  
> +TDK FS1603 HARDWARE MONITOR DRIVER
> +M:	Kevin Jia <kjia@nexthop.ai>
> +L:	linux-hwmon@vger.kernel.org
> +S:	Maintained
> +F:	Documentation/hwmon/fs1603.rst
> +F:	drivers/hwmon/fs1603.c
> +
>  TEA5761 TUNER DRIVER
>  M:	Mauro Carvalho Chehab <mchehab@kernel.org>
>  L:	linux-media@vger.kernel.org
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 18c682e1a475..047a3050946b 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -728,6 +728,17 @@ config SENSORS_MC33XS2410
>  	  This driver can also be built as a module. If so, the module
>  	  will be called mc33xs2410_hwmon.
>  
> +config SENSORS_FS1603
> +	tristate "TDK FS1603 and compatible uPOL DC-DC converters"
> +	depends on I2C
> +	select REGMAP_I2C
> +	help
> +	  If you say yes here you get hardware monitoring support for the
> +	  TDK FS1603, FS1604, FS1606 and FS1006 uPOL DC-DC converters.
> +
> +	  This driver can also be built as a module. If so, the module
> +	  will be called fs1603.
> +
>  config SENSORS_FSCHMD
>  	tristate "Fujitsu Siemens Computers sensor chips"
>  	depends on (X86 || COMPILE_TEST) && I2C
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index d4c345ed7261..23a5f262eecf 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -85,6 +85,7 @@ obj-$(CONFIG_SENSORS_F71805F)	+= f71805f.o
>  obj-$(CONFIG_SENSORS_F71882FG)	+= f71882fg.o
>  obj-$(CONFIG_SENSORS_F75375S)	+= f75375s.o
>  obj-$(CONFIG_SENSORS_FAM15H_POWER) += fam15h_power.o
> +obj-$(CONFIG_SENSORS_FS1603)	+= fs1603.o
>  obj-$(CONFIG_SENSORS_FSCHMD)	+= fschmd.o
>  obj-$(CONFIG_SENSORS_FTSTEUTATES) += ftsteutates.o
>  obj-$(CONFIG_SENSORS_G760A)	+= g760a.o
> diff --git a/drivers/hwmon/fs1603.c b/drivers/hwmon/fs1603.c
> new file mode 100644
> index 000000000000..afe75657ce39
> --- /dev/null
> +++ b/drivers/hwmon/fs1603.c
> @@ -0,0 +1,540 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +
> +/*
> + * Copyright (C) 2026 Nexthop Systems Inc.
> + *
> + * Driver for the TDK FS160x family of uPOL DC-DC converters with integrated
> + * inductor. Supported models: FS1603, FS1604, FS1606, FS1006.
> + *
> + * The parts expose voltage, current and temperature telemetry over a
> + * proprietary I2C register map. They are not PMBus devices, and their
> + * register numbers actively collide with PMBus command codes -- 0x21 is a
> + * status byte here where PMBus defines VOUT_COMMAND -- so a generic PMBus
> + * driver must never be bound to them.
> + *
> + * Read-only. VOUT_SET (0x12/0x13) is an atomic write-protected pair: writing
> + * the high byte arms an update that takes effect only once the low byte is
> + * written. A partial write leaves the part armed, after which it acknowledges
> + * but silently ignores every register write until its input power is cycled,
> + * so this driver only reads it, to scale the over-voltage threshold.
> + *
> + * On a board that raises the rail with a feedback divider, VOUT still reports
> + * the rail but VOUT_SET holds the feedback-pin target, so the over-voltage
> + * threshold is scaled by the divider ratio, measured at probe from VOUT and
> + * the Fb register (0x22). TDK notes that current reporting does not apply in
> + * that configuration.
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/err.h>
> +#include <linux/hwmon.h>
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +
> +#define FS1603_REG_PVIN			0x0C
> +#define FS1603_REG_VOUT			0x0D
> +#define FS1603_REG_IOUT			0x0E
> +#define FS1603_REG_TEMP			0x0F
> +#define FS1603_REG_VOUT_SET_HI		0x12
> +#define FS1603_REG_VOUT_SET_LO		0x13
> +#define FS1603_REG_OCSET		0x15
> +#define FS1603_REG_OV_THRESHOLD		0x17
> +#define FS1603_REG_OT_THRESHOLD		0x19
> +#define FS1603_REG_IOUT_GAIN		0x1A
> +#define FS1603_REG_STATUS		0x21
> +#define FS1603_REG_FB			0x22
> +
> +#define FS1603_VOUT_SET_HI_MASK		BIT(0)
> +#define FS1603_OV_THRESHOLD_MASK	GENMASK(1, 0)
> +#define FS1603_OT_THRESHOLD_MASK	GENMASK(1, 0)
> +#define FS1603_IOUT_GAIN_MASK		GENMASK(7, 2)
> +
> +/*
> + * OCSet[2:0] is the user-programmable over-current protection trip level,
> + * not an ID. Each family member ships with a different default, so a
> + * mismatch at probe usually means the wrong model was declared.
> + */
> +#define FS1603_OCSET_LEVEL		GENMASK(2, 0)
> +#define FS1603_OCSET_DEFAULT_FS1603	0
> +#define FS1603_OCSET_DEFAULT_FS1604	1
> +#define FS1603_OCSET_DEFAULT_FS1606	2
> +#define FS1603_OCSET_DEFAULT_FS1006	3
> +
> +/* Bits in FS1603_REG_STATUS. */
> +#define FS1603_STATUS_OVER_VOLTAGE	BIT(6)
> +#define FS1603_STATUS_OVER_CURRENT	BIT(5)
> +#define FS1603_STATUS_OVER_TEMP		BIT(4)
> +
> +/* OV_Threshold[1:0] in percent of programmed VOUT; OT_Threshold[1:0] in degC. */
> +static const unsigned int fs1603_ov_threshold_pct[] = { 105, 110, 115, 120 };
> +static const unsigned int fs1603_ot_threshold_c[] = { 75, 85, 125, 145 };
> +
> +/*
> + * Voltage telemetry is a linear step and base, both fixed per variant.
> + * Current is raw/32 A except on the FS1606-0600, which applies a calibration
> + * correction derived from FS1603_REG_IOUT_GAIN and the output voltage.
> + */
> +struct fs1603_info {
> +	unsigned int vout_step_mv;
> +	unsigned int vout_base_mv;
> +	u8 ocset_default;
> +	bool iout_gain_corrected;
> +};
> +
> +enum fs1603_model {
> +	fs1603,
> +	fs1604,
> +	fs1606,
> +	fs1606_0600,
> +	fs1606_2500,
> +	fs1006,
> +};
> +
> +/*
> + * The FS1606 reports at 10mV/300mV except for the -2500 preset, which uses the
> + * same 20mV/600mV encoding as the higher-voltage family members.
> + */
> +static const struct fs1603_info fs1603_models[] = {
> +	[fs1603]      = { .vout_step_mv = 20, .vout_base_mv = 600,
> +			  .ocset_default = FS1603_OCSET_DEFAULT_FS1603 },
> +	[fs1604]      = { .vout_step_mv = 20, .vout_base_mv = 600,
> +			  .ocset_default = FS1603_OCSET_DEFAULT_FS1604 },
> +	[fs1606]      = { .vout_step_mv = 10, .vout_base_mv = 300,
> +			  .ocset_default = FS1603_OCSET_DEFAULT_FS1606 },
> +	[fs1606_0600] = { .vout_step_mv = 10, .vout_base_mv = 300,
> +			  .ocset_default = FS1603_OCSET_DEFAULT_FS1606,
> +			  .iout_gain_corrected = true },
> +	[fs1606_2500] = { .vout_step_mv = 20, .vout_base_mv = 600,
> +			  .ocset_default = FS1603_OCSET_DEFAULT_FS1606 },
> +	[fs1006]      = { .vout_step_mv = 20, .vout_base_mv = 600,
> +			  .ocset_default = FS1603_OCSET_DEFAULT_FS1006 },
> +};
> +
> +/*
> + * Every register is a byte, and every read is live telemetry, so nothing is
> + * cached. regmap is used for the uniform accessors and the debugfs dump.
> + */
> +static const struct regmap_config fs1603_regmap_config = {
> +	.reg_bits = 8,
> +	.val_bits = 8,
> +	.max_register = FS1603_REG_FB,
> +	.cache_type = REGCACHE_NONE,
> +};
> +
> +struct fs1603_data {
> +	struct regmap *regmap;
> +	const struct fs1603_info *info;
> +	u32 fb_ratio_num;
> +	u32 fb_ratio_den;
> +};
> +
> +static int fs1603_read_vout_mv(struct fs1603_data *data, long *val)
> +{
> +	unsigned int raw;
> +	int err;
> +
> +	err = regmap_read(data->regmap, FS1603_REG_VOUT, &raw);
> +	if (err)
> +		return err;
> +
> +	*val = raw * data->info->vout_step_mv + data->info->vout_base_mv;
> +	return 0;
> +}
> +
> +/*
> + * Output current in mA.
> + *
> + * Every variant but the FS1606-0600 reports raw/32 A directly. The -0600
> + * additionally applies, with G taken from FS1603_REG_IOUT_GAIN bits [7:2]:
> + *
> + *   I = raw/32 - (9.05 - 0.24 * G) * Vout - 0.356 * G + 13.1
> + *
> + * Scaled to milliamps and millivolts so it stays in integer arithmetic:
> + *
> + *   I_mA = raw * 125 / 4
> + *          - ((9050 - 240 * G) * Vout_mV) / 1000
> + *          - 356 * G
> + *          + 13100
> + */
> +static int fs1603_read_iout_ma(struct fs1603_data *data, long *val)
> +{
> +	unsigned int raw, gain;
> +	long vout_mv;
> +	int err;
> +
> +	err = regmap_read(data->regmap, FS1603_REG_IOUT, &raw);
> +	if (err)
> +		return err;
> +
> +	*val = raw * 125 / 4;
> +
> +	if (!data->info->iout_gain_corrected)
> +		return 0;
> +
> +	err = regmap_read(data->regmap, FS1603_REG_IOUT_GAIN, &gain);
> +	if (err)
> +		return err;
> +	gain = FIELD_GET(FS1603_IOUT_GAIN_MASK, gain);
> +
> +	err = fs1603_read_vout_mv(data, &vout_mv);
> +	if (err)
> +		return err;
> +
> +	*val -= ((9050 - 240 * (long)gain) * vout_mv) / 1000;
> +	*val -= 356 * (long)gain;
> +	*val += 13100;
> +
> +	if (*val < 0)
> +		*val = 0;
> +
> +	return 0;
> +}
> +
> +/*
> + * The 9-bit VOUT_SET code steps at half the telemetry resolution from a base
> + * of 80 steps.
> + */
> +static int fs1603_read_vout_set_mv(struct fs1603_data *data, long *val)
> +{
> +	unsigned int hi, lo;
> +	int err;
> +
> +	err = regmap_read(data->regmap, FS1603_REG_VOUT_SET_HI, &hi);
> +	if (err)
> +		return err;
> +	err = regmap_read(data->regmap, FS1603_REG_VOUT_SET_LO, &lo);
> +	if (err)
> +		return err;
> +
> +	*val = ((FIELD_GET(FS1603_VOUT_SET_HI_MASK, hi) << 8 | lo) + 80) *
> +	       data->info->vout_step_mv / 2;
> +	return 0;
> +}
> +
> +/* Fb has no documented encoding; it decodes with the VOUT step and base. */
> +static int fs1603_read_fb_mv(struct fs1603_data *data, long *val)
> +{
> +	unsigned int raw;
> +	int err;
> +
> +	err = regmap_read(data->regmap, FS1603_REG_FB, &raw);
> +	if (err)
> +		return err;
> +
> +	*val = raw * data->info->vout_step_mv + data->info->vout_base_mv;
> +	return 0;
> +}
> +
> +/*
> + * A board that raises the rail with a feedback divider leaves VOUT_SET at the
> + * feedback target, and Fb then reads well below VOUT. The divider is fixed in
> + * hardware, so measure its ratio once. Without a divider Fb reads the output
> + * itself, and the ratio stays one so the limit does not follow ADC noise.
> + */
> +static int fs1603_measure_fb_ratio(struct fs1603_data *data)
> +{
> +	long vout_mv, fb_mv;
> +	int err;
> +
> +	data->fb_ratio_num = 1;
> +	data->fb_ratio_den = 1;
> +
> +	err = fs1603_read_vout_mv(data, &vout_mv);
> +	if (err)
> +		return err;
> +	err = fs1603_read_fb_mv(data, &fb_mv);
> +	if (err)
> +		return err;
> +
> +	if (fb_mv > 0 && vout_mv > fb_mv + 4 * data->info->vout_step_mv) {
> +		data->fb_ratio_num = vout_mv;
> +		data->fb_ratio_den = fb_mv;
> +	}
> +	return 0;
> +}
> +
> +/*
> + * OVP trips at OV_Threshold percent of the VOUT_SET target, measured at the
> + * feedback pin. Scale it by the feedback divider ratio to report it in the
> + * same rail domain as in1_input.
> + */
> +static int fs1603_read_vout_crit_mv(struct fs1603_data *data, long *val)
> +{
> +	unsigned int raw;
> +	long vout_set_mv;
> +	int err;
> +
> +	err = regmap_read(data->regmap, FS1603_REG_OV_THRESHOLD, &raw);
> +	if (err)
> +		return err;
> +	err = fs1603_read_vout_set_mv(data, &vout_set_mv);
> +	if (err)
> +		return err;
> +
> +	raw = FIELD_GET(FS1603_OV_THRESHOLD_MASK, raw);
> +	*val = DIV_ROUND_CLOSEST_ULL((u64)fs1603_ov_threshold_pct[raw] *
> +				     vout_set_mv * data->fb_ratio_num,
> +				     100 * data->fb_ratio_den);
> +	return 0;
> +}
> +
> +static int fs1603_read_status_bit(struct fs1603_data *data, u8 mask,
> +				  long *val)
> +{
> +	unsigned int status;
> +	int err;
> +
> +	err = regmap_read(data->regmap, FS1603_REG_STATUS, &status);
> +	if (err)
> +		return err;
> +
> +	*val = !!(status & mask);
> +	return 0;
> +}
> +
> +static int fs1603_read_in(struct fs1603_data *data, u32 attr, int channel,
> +			  long *val)
> +{
> +	unsigned int raw;
> +	int err;
> +
> +	switch (attr) {
> +	case hwmon_in_input:
> +		if (channel == 0) {
> +			err = regmap_read(data->regmap, FS1603_REG_PVIN, &raw);
> +			if (err)
> +				return err;
> +			/* PVIN reports in 1/16 V steps. */
> +			*val = raw * 1000 / 16;
> +			return 0;
> +		}
> +		return fs1603_read_vout_mv(data, val);
> +	case hwmon_in_crit:
> +		return fs1603_read_vout_crit_mv(data, val);
> +	case hwmon_in_alarm:
> +		return fs1603_read_status_bit(data, FS1603_STATUS_OVER_VOLTAGE,
> +					      val);
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +}
> +
> +static int fs1603_read_curr(struct fs1603_data *data, u32 attr, long *val)
> +{
> +	switch (attr) {
> +	case hwmon_curr_input:
> +		return fs1603_read_iout_ma(data, val);
> +	case hwmon_curr_alarm:
> +		return fs1603_read_status_bit(data, FS1603_STATUS_OVER_CURRENT,
> +					      val);
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +}
> +
> +static int fs1603_read_temp_crit_mc(struct fs1603_data *data, long *val)
> +{
> +	unsigned int raw;
> +	int err;
> +
> +	err = regmap_read(data->regmap, FS1603_REG_OT_THRESHOLD, &raw);
> +	if (err)
> +		return err;
> +
> +	*val = fs1603_ot_threshold_c[FIELD_GET(FS1603_OT_THRESHOLD_MASK, raw)] * 1000;
> +	return 0;
> +}
> +
> +static int fs1603_read_temp(struct fs1603_data *data, u32 attr, long *val)
> +{
> +	unsigned int raw;
> +	int err;
> +
> +	switch (attr) {
> +	case hwmon_temp_input:
> +		err = regmap_read(data->regmap, FS1603_REG_TEMP, &raw);
> +		if (err)
> +			return err;
> +		/* TEMP_REPORT is degC directly: 1 degC per LSB, no offset. */
> +		*val = raw * 1000;
> +		return 0;
> +	case hwmon_temp_crit:
> +		return fs1603_read_temp_crit_mc(data, val);
> +	case hwmon_temp_alarm:
> +		return fs1603_read_status_bit(data, FS1603_STATUS_OVER_TEMP,
> +					      val);
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +}
> +
> +static int fs1603_read(struct device *dev, enum hwmon_sensor_types type,
> +		       u32 attr, int channel, long *val)
> +{
> +	struct fs1603_data *data = dev_get_drvdata(dev);
> +
> +	switch (type) {
> +	case hwmon_in:
> +		return fs1603_read_in(data, attr, channel, val);
> +	case hwmon_curr:
> +		return fs1603_read_curr(data, attr, val);
> +	case hwmon_temp:
> +		return fs1603_read_temp(data, attr, val);
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +}
> +
> +static int fs1603_read_string(struct device *dev, enum hwmon_sensor_types type,
> +			      u32 attr, int channel, const char **str)
> +{
> +	if (type != hwmon_in || attr != hwmon_in_label)
> +		return -EOPNOTSUPP;
> +
> +	*str = channel == 0 ? "vin" : "vout";
> +	return 0;
> +}
> +
> +static umode_t fs1603_is_visible(const void *data, enum hwmon_sensor_types type,
> +				 u32 attr, int channel)
> +{
> +	switch (type) {
> +	case hwmon_in:
> +		switch (attr) {
> +		case hwmon_in_input:
> +		case hwmon_in_label:
> +			return 0444;
> +		case hwmon_in_crit:
> +		case hwmon_in_alarm:
> +			/* OVP and its alarm describe the regulated output only. */
> +			return channel == 1 ? 0444 : 0;
> +		default:
> +			return 0;
> +		}
> +	case hwmon_curr:
> +	case hwmon_temp:
> +		return 0444;
> +	default:
> +		return 0;
> +	}
> +}
> +
> +static const struct hwmon_ops fs1603_ops = {
> +	.is_visible = fs1603_is_visible,
> +	.read = fs1603_read,
> +	.read_string = fs1603_read_string,
> +};
> +
> +static const struct hwmon_channel_info *fs1603_channel_info[] = {
> +	HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LABEL,
> +			   HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_CRIT |
> +			   HWMON_I_ALARM),
> +	HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT | HWMON_C_ALARM),
> +	HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_ALARM),
> +	NULL
> +};
> +
> +static const struct hwmon_chip_info fs1603_chip_info = {
> +	.ops = &fs1603_ops,
> +	.info = fs1603_channel_info,
> +};
> +
> +static int fs1603_probe(struct i2c_client *client)
> +{
> +	struct device *dev = &client->dev;
> +	char *name;
> +	struct fs1603_data *data;
> +	struct device *hwmon;
> +	unsigned int ocset;
> +	int err;
> +
> +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
> +		return dev_err_probe(dev, -ENODEV,
> +				     "SMBus byte data not supported\n");
> +
> +	name = devm_hwmon_sanitize_name(dev, client->name);
> +	if (IS_ERR(name))
> +		return PTR_ERR(name);
> +
> +	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +
> +	data->regmap = devm_regmap_init_i2c(client, &fs1603_regmap_config);
> +	if (IS_ERR(data->regmap))
> +		return dev_err_probe(dev, PTR_ERR(data->regmap),
> +				     "failed to allocate regmap\n");
> +
> +	data->info = i2c_get_match_data(client);
> +	if (!data->info)
> +		return dev_err_probe(dev, -ENODEV, "no match data for %s\n",
> +				     client->name);
> +
> +	err = regmap_read(data->regmap, FS1603_REG_OCSET, &ocset);
> +	if (err)
> +		return dev_err_probe(dev, err, "failed to read OCSet\n");
> +
> +	ocset = FIELD_GET(FS1603_OCSET_LEVEL, ocset);
> +	if (ocset != data->info->ocset_default)
> +		dev_warn(dev, "OCSet %u differs from the %s default %u\n",
> +			 ocset, client->name, data->info->ocset_default);
> +
> +	err = fs1603_measure_fb_ratio(data);
> +	if (err)
> +		return dev_err_probe(dev, err,
> +				     "failed to read the feedback ratio\n");
> +
> +	hwmon = devm_hwmon_device_register_with_info(dev, name, data,
> +						     &fs1603_chip_info, NULL);
> +	return PTR_ERR_OR_ZERO(hwmon);
> +}
> +
> +static const struct i2c_device_id fs1603_id[] = {
> +	{ .name = "fs1603",
> +	  .driver_data = (kernel_ulong_t)&fs1603_models[fs1603] },
> +	{ .name = "fs1604",
> +	  .driver_data = (kernel_ulong_t)&fs1603_models[fs1604] },
> +	{ .name = "fs1606",
> +	  .driver_data = (kernel_ulong_t)&fs1603_models[fs1606] },
> +	{ .name = "fs1606_0600",
> +	  .driver_data = (kernel_ulong_t)&fs1603_models[fs1606_0600] },
> +	{ .name = "fs1606_2500",
> +	  .driver_data = (kernel_ulong_t)&fs1603_models[fs1606_2500] },
> +	{ .name = "fs1006_5000",
> +	  .driver_data = (kernel_ulong_t)&fs1603_models[fs1006] },
> +	{ .name = "fs1006_3300",
> +	  .driver_data = (kernel_ulong_t)&fs1603_models[fs1006] },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(i2c, fs1603_id);
> +
> +static const struct of_device_id fs1603_of_match[] = {
> +	{ .compatible = "tdk,fs1603", .data = &fs1603_models[fs1603] },
> +	{ .compatible = "tdk,fs1604", .data = &fs1603_models[fs1604] },
> +	{ .compatible = "tdk,fs1606", .data = &fs1603_models[fs1606] },
> +	{ .compatible = "tdk,fs1606-0600",
> +	  .data = &fs1603_models[fs1606_0600] },
> +	{ .compatible = "tdk,fs1606-2500",
> +	  .data = &fs1603_models[fs1606_2500] },
> +	{ .compatible = "tdk,fs1006-5000",
> +	  .data = &fs1603_models[fs1006] },
> +	{ .compatible = "tdk,fs1006-3300",
> +	  .data = &fs1603_models[fs1006] },

Why those extra compatibles ? I can see that -0600 and -2500 are
needed, but the others could just use tdk,fs1606.

Thanks,
Guenter

> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, fs1603_of_match);
> +
> +static struct i2c_driver fs1603_driver = {
> +	.driver = {
> +		.name	= "fs1603",
> +		.of_match_table = fs1603_of_match,
> +	},
> +	.probe = fs1603_probe,
> +	.id_table = fs1603_id,
> +};
> +module_i2c_driver(fs1603_driver);
> +
> +MODULE_AUTHOR("Kevin Jia <kjia@nexthop.ai>");
> +MODULE_DESCRIPTION("TDK FS160x uPOL DC-DC converter telemetry driver");
> +MODULE_LICENSE("GPL");

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 1/3] dt-bindings: vendor-prefixes: Add TDK
  2026-10-20 16:48 ` [PATCH v2 1/3] dt-bindings: vendor-prefixes: Add TDK Kevin Jia
@ 2026-10-26 22:06   ` Guenter Roeck
  0 siblings, 0 replies; 10+ messages in thread
From: Guenter Roeck @ 2026-10-26 22:06 UTC (permalink / raw)
  To: Kevin Jia
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
	Shuah Khan, Randy Dunlap, devicetree, linux-kernel, linux-hwmon,
	linux-doc

Hi,

On Tue, Oct 20, 2026 at 04:48:14PM +0000, Kevin Jia wrote:
> Add a vendor prefix for TDK Corporation.
> 
> Link: https://www.tdk.com/
> Signed-off-by: Kevin Jia <kjia@nexthop.ai>

This patch won't be needed - it is already added with
https://patchwork.kernel.org/project/linux-hwmon/patch/20261020-hwmon-pmbus-fs1412-v4-1-27ece4e43d04@nexthop.ai/

Thanks,
Guenter

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 3/3] hwmon: (fs1603) Add driver for TDK FS160x and FS1006 uPOL converters
  2026-10-26 22:04   ` Guenter Roeck
@ 2026-10-27 23:21     ` Kevin Jia
  2026-10-27 23:53       ` Guenter Roeck
  0 siblings, 1 reply; 10+ messages in thread
From: Kevin Jia @ 2026-10-27 23:21 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
	Shuah Khan, Randy Dunlap, Abdurrahman Hussain, devicetree,
	linux-kernel, linux-hwmon, linux-doc

On Mon, Oct 26, 2026 at 03:04:45PM -0700, Guenter Roeck wrote:
> > +static const struct of_device_id fs1603_of_match[] = {
> > +	{ .compatible = "tdk,fs1603", .data = &fs1603_models[fs1603] },
> > +	{ .compatible = "tdk,fs1604", .data = &fs1603_models[fs1604] },
> > +	{ .compatible = "tdk,fs1606", .data = &fs1603_models[fs1606] },
> > +	{ .compatible = "tdk,fs1606-0600",
> > +	  .data = &fs1603_models[fs1606_0600] },
> > +	{ .compatible = "tdk,fs1606-2500",
> > +	  .data = &fs1603_models[fs1606_2500] },
> > +	{ .compatible = "tdk,fs1006-5000",
> > +	  .data = &fs1603_models[fs1006] },
> > +	{ .compatible = "tdk,fs1006-3300",
> > +	  .data = &fs1603_models[fs1006] },
>
> Why those extra compatibles ? I can see that -0600 and -2500 are
> needed, but the others could just use tdk,fs1606.

The FS1606 reports in 10 mV steps from a 300 mV base, while the FS1603,
FS1604 and FS1006 report in 20 mV steps from a 600 mV base, so they can't
use tdk,fs1606. They do encode telemetry the same way as each other, as
does the FS1606-2500.

So for v3, I'd like to move the bindings to a dedicated tdk,fs1603.yaml,
where the FS1604, FS1006-3300, FS1006-5000 and FS1606-2500 list
tdk,fs1603 as a fallback. The driver would then only match tdk,fs1603,
tdk,fs1606 and tdk,fs1606-0600, with three sets of model data. I'd also
drop the OCSet check and patch 1/3.

How does that sound? And is there anything else you'd like me to change
before I send v3?

Thanks,
Kevin

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 3/3] hwmon: (fs1603) Add driver for TDK FS160x and FS1006 uPOL converters
  2026-10-27 23:21     ` Kevin Jia
@ 2026-10-27 23:53       ` Guenter Roeck
  0 siblings, 0 replies; 10+ messages in thread
From: Guenter Roeck @ 2026-10-27 23:53 UTC (permalink / raw)
  To: Kevin Jia
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
	Shuah Khan, Randy Dunlap, Abdurrahman Hussain, devicetree,
	linux-kernel, linux-hwmon, linux-doc

Hi,

On 10/27/26 16:21, Kevin Jia wrote:
> On Mon, Oct 26, 2026 at 03:04:45PM -0700, Guenter Roeck wrote:
>>> +static const struct of_device_id fs1603_of_match[] = {
>>> +	{ .compatible = "tdk,fs1603", .data = &fs1603_models[fs1603] },
>>> +	{ .compatible = "tdk,fs1604", .data = &fs1603_models[fs1604] },
>>> +	{ .compatible = "tdk,fs1606", .data = &fs1603_models[fs1606] },
>>> +	{ .compatible = "tdk,fs1606-0600",
>>> +	  .data = &fs1603_models[fs1606_0600] },
>>> +	{ .compatible = "tdk,fs1606-2500",
>>> +	  .data = &fs1603_models[fs1606_2500] },
>>> +	{ .compatible = "tdk,fs1006-5000",
>>> +	  .data = &fs1603_models[fs1006] },
>>> +	{ .compatible = "tdk,fs1006-3300",
>>> +	  .data = &fs1603_models[fs1006] },
>>
>> Why those extra compatibles ? I can see that -0600 and -2500 are
>> needed, but the others could just use tdk,fs1606.
> 
> The FS1606 reports in 10 mV steps from a 300 mV base, while the FS1603,
> FS1604 and FS1006 report in 20 mV steps from a 600 mV base, so they can't
> use tdk,fs1606. They do encode telemetry the same way as each other, as
> does the FS1606-2500.

I referred to "tdk,fs1006-5000" and "tdk,fs1006-3300, both of which reference
the fs1006 configuration. Sorry for the misunderstanding.

> 
> So for v3, I'd like to move the bindings to a dedicated tdk,fs1603.yaml,
> where the FS1604, FS1006-3300, FS1006-5000 and FS1606-2500 list
> tdk,fs1603 as a fallback. The driver would then only match tdk,fs1603,
> tdk,fs1606 and tdk,fs1606-0600, with three sets of model data. I'd also
> drop the OCSet check and patch 1/3.
> 
> How does that sound? And is there anything else you'd like me to change
> before I send v3?
> 

Sounds good to me. I don't have any further suggestions.

Thanks,
Guenter


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-10-27 23:53 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-20 16:48 [PATCH v2 0/3] hwmon: (fs1603) Add driver for TDK FS160x and FS1006 uPOL converters Kevin Jia
2026-10-20 16:48 ` [PATCH v2 1/3] dt-bindings: vendor-prefixes: Add TDK Kevin Jia
2026-10-26 22:06   ` Guenter Roeck
2026-10-20 16:48 ` [PATCH v2 2/3] dt-bindings: trivial-devices: Add TDK FS160x and FS1006 Kevin Jia
2026-10-23  8:57   ` Krzysztof Kozlowski
2026-10-26 16:08     ` Guenter Roeck
2026-10-20 16:48 ` [PATCH v2 3/3] hwmon: (fs1603) Add driver for TDK FS160x and FS1006 uPOL converters Kevin Jia
2026-10-26 22:04   ` Guenter Roeck
2026-10-27 23:21     ` Kevin Jia
2026-10-27 23:53       ` Guenter Roeck

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

all inboxes | Powered by JetHome®