* [PATCH 0/3] hwmon: (fs1603) Add driver for TDK FS160x and FS1006 uPOL converters
@ 2026-10-19 18:45 Kevin Jia
2026-10-19 18:45 ` [PATCH 1/3] dt-bindings: vendor-prefixes: Add TDK Kevin Jia
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Kevin Jia @ 2026-10-19 18:45 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
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>
---
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 | 75 ++++
Documentation/hwmon/index.rst | 1 +
MAINTAINERS | 7 +
drivers/hwmon/Kconfig | 11 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/fs1603.c | 480 +++++++++++++++++++++
8 files changed, 591 insertions(+)
---
base-commit: 400be69515c327ced60082b27ede902a8d93cbdb
change-id: 20261016-fs1603-25483c5edc57
Best regards,
--
Kevin Jia <kjia@nexthop.ai>
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/3] dt-bindings: vendor-prefixes: Add TDK
2026-10-19 18:45 [PATCH 0/3] hwmon: (fs1603) Add driver for TDK FS160x and FS1006 uPOL converters Kevin Jia
@ 2026-10-19 18:45 ` Kevin Jia
2026-10-19 18:45 ` [PATCH 2/3] dt-bindings: trivial-devices: Add TDK FS160x and FS1006 Kevin Jia
2026-10-19 18:45 ` [PATCH 3/3] hwmon: (fs1603) Add driver for TDK FS160x and FS1006 uPOL converters Kevin Jia
2 siblings, 0 replies; 5+ messages in thread
From: Kevin Jia @ 2026-10-19 18:45 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 related [flat|nested] 5+ messages in thread
* [PATCH 2/3] dt-bindings: trivial-devices: Add TDK FS160x and FS1006
2026-10-19 18:45 [PATCH 0/3] hwmon: (fs1603) Add driver for TDK FS160x and FS1006 uPOL converters Kevin Jia
2026-10-19 18:45 ` [PATCH 1/3] dt-bindings: vendor-prefixes: Add TDK Kevin Jia
@ 2026-10-19 18:45 ` Kevin Jia
2026-10-19 18:45 ` [PATCH 3/3] hwmon: (fs1603) Add driver for TDK FS160x and FS1006 uPOL converters Kevin Jia
2 siblings, 0 replies; 5+ messages in thread
From: Kevin Jia @ 2026-10-19 18:45 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 related [flat|nested] 5+ messages in thread
* [PATCH 3/3] hwmon: (fs1603) Add driver for TDK FS160x and FS1006 uPOL converters
2026-10-19 18:45 [PATCH 0/3] hwmon: (fs1603) Add driver for TDK FS160x and FS1006 uPOL converters Kevin Jia
2026-10-19 18:45 ` [PATCH 1/3] dt-bindings: vendor-prefixes: Add TDK Kevin Jia
2026-10-19 18:45 ` [PATCH 2/3] dt-bindings: trivial-devices: Add TDK FS160x and FS1006 Kevin Jia
@ 2026-10-19 18:45 ` Kevin Jia
2026-10-19 18:52 ` sashiko-bot
2 siblings, 1 reply; 5+ messages in thread
From: Kevin Jia @ 2026-10-19 18:45 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
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.
The reported output voltage is the converter's own output, so on boards
that use a feedback divider to reach a higher rail voltage, the output
voltage and current do not apply to the rail.
Signed-off-by: Kevin Jia <kjia@nexthop.ai>
---
Documentation/hwmon/fs1603.rst | 75 +++++++
Documentation/hwmon/index.rst | 1 +
MAINTAINERS | 7 +
drivers/hwmon/Kconfig | 11 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/fs1603.c | 480 +++++++++++++++++++++++++++++++++++++++++
6 files changed, 575 insertions(+)
diff --git a/Documentation/hwmon/fs1603.rst b/Documentation/hwmon/fs1603.rst
new file mode 100644
index 000000000000..e7cf79252b87
--- /dev/null
+++ b/Documentation/hwmon/fs1603.rst
@@ -0,0 +1,75 @@
+.. 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. The reported output voltage is the converter's own
+output. On boards that use a feedback divider to reach a higher rail voltage,
+the reported output voltage and current do not apply to the rail.
+
+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.
+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..ae22964d22b2
--- /dev/null
+++ b/drivers/hwmon/fs1603.c
@@ -0,0 +1,480 @@
+// 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.
+ *
+ * The reported output voltage is the converter's own output. Where a board uses
+ * a feedback divider to reach a higher rail voltage, TDK directs readers to the
+ * Fb register (0x22) scaled by target VOUT/0.6 instead, and notes that current
+ * reporting does not apply in that configuration. Neither is implemented here.
+ */
+
+#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;
+};
+
+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;
+}
+
+/*
+ * OVP trips at OV_Threshold percent of the VOUT_SET target, in the same
+ * converter-output 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 = fs1603_ov_threshold_pct[raw] * vout_set_mv / 100;
+ 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);
+
+ 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[] = {
+ { "fs1603", (kernel_ulong_t)&fs1603_models[fs1603] },
+ { "fs1604", (kernel_ulong_t)&fs1603_models[fs1604] },
+ { "fs1606", (kernel_ulong_t)&fs1603_models[fs1606] },
+ { "fs1606_0600", (kernel_ulong_t)&fs1603_models[fs1606_0600] },
+ { "fs1606_2500", (kernel_ulong_t)&fs1603_models[fs1606_2500] },
+ { "fs1006_5000", (kernel_ulong_t)&fs1603_models[fs1006] },
+ { "fs1006_3300", (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 related [flat|nested] 5+ messages in thread
* Re: [PATCH 3/3] hwmon: (fs1603) Add driver for TDK FS160x and FS1006 uPOL converters
2026-10-19 18:45 ` [PATCH 3/3] hwmon: (fs1603) Add driver for TDK FS160x and FS1006 uPOL converters Kevin Jia
@ 2026-10-19 18:52 ` sashiko-bot
0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-10-19 18:52 UTC (permalink / raw)
To: Kevin Jia; +Cc: devicetree, linux-hwmon, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] hwmon: (fs1603) positional initializers in struct i2c_device_id fs1603_id
--
commit d32400df72d7884af500586426d84640110c0f0e
Author: Kevin Jia <kjia@nexthop.ai>
hwmon: (fs1603) Add driver for TDK FS160x and FS1006 uPOL converters
This commit adds a read-only hwmon driver for the TDK FS1603, FS1604,
FS1606, and FS1006 uPOL DC-DC converters, reporting input and output
voltages, output current, temperature, and protection thresholds over a
proprietary I2C register map.
> diff --git a/drivers/hwmon/fs1603.c b/drivers/hwmon/fs1603.c
> new file mode 100644
> index 0000000000000..ae22964d22b2c
> --- /dev/null
> +++ b/drivers/hwmon/fs1603.c
> @@ -0,0 +1,480 @@
[ ... ]
> +static const struct i2c_device_id fs1603_id[] = {
> + { "fs1603", (kernel_ulong_t)&fs1603_models[fs1603] },
> + { "fs1604", (kernel_ulong_t)&fs1603_models[fs1604] },
> + { "fs1606", (kernel_ulong_t)&fs1603_models[fs1606] },
> + { "fs1606_0600", (kernel_ulong_t)&fs1603_models[fs1606_0600] },
> + { "fs1606_2500", (kernel_ulong_t)&fs1603_models[fs1606_2500] },
> + { "fs1006_5000", (kernel_ulong_t)&fs1603_models[fs1006] },
> + { "fs1006_3300", (kernel_ulong_t)&fs1603_models[fs1006] },
> + {}
> +};
> +MODULE_DEVICE_TABLE(i2c, fs1603_id);
[Severity: Low]
This isn't a bug, but should the entries in fs1603_id[] in
drivers/hwmon/fs1603.c use named initializers (.name and .driver_data)
rather than positional initializers, matching the convention for new
struct i2c_device_id tables and the style used in fs1603_of_match[] right
below?
[ ... ]
────
Touestas, feedback, or questions about this review? Email
linux-kernel-bot@google.com.
────
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261019-fs1603-v1-0-8adf972386c1@nexthop.ai?part=3
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-10-19 18:52 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-19 18:45 [PATCH 0/3] hwmon: (fs1603) Add driver for TDK FS160x and FS1006 uPOL converters Kevin Jia
2026-10-19 18:45 ` [PATCH 1/3] dt-bindings: vendor-prefixes: Add TDK Kevin Jia
2026-10-19 18:45 ` [PATCH 2/3] dt-bindings: trivial-devices: Add TDK FS160x and FS1006 Kevin Jia
2026-10-19 18:45 ` [PATCH 3/3] hwmon: (fs1603) Add driver for TDK FS160x and FS1006 uPOL converters Kevin Jia
2026-10-19 18:52 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox