mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Guenter Roeck <linux@roeck-us.net>
To: Abdurrahman Hussain <abdurrahman@nexthop.ai>
Cc: Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>,
	Jonathan Corbet <corbet@lwn.net>,
	Shuah Khan <skhan@linuxfoundation.org>,
	Randy Dunlap <rdunlap@infradead.org>,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-hwmon@vger.kernel.org, linux-doc@vger.kernel.org
Subject: Re: [PATCH 3/3] hwmon: (pmbus/fs1412) Add TDK FS1412 driver
Date: Mon, 19 Oct 2026 13:25:01 -0700	[thread overview]
Message-ID: <a3ccc85b-aab6-44b4-9695-ecd78401102c@roeck-us.net> (raw)
In-Reply-To: <20261008-hwmon-pmbus-fs1412-v1-3-0ae7e5ee4e41@nexthop.ai>

On Thu, Oct 08, 2026 at 05:56:04PM -0700, Abdurrahman Hussain wrote:
> The TDK FS1412 is a 12A uPOL regulator with PMBus telemetry for input
> voltage, output voltage and temperature, plus the matching status
> registers. It has over-current and over-temperature protection with
> programmable fault limits but no current telemetry: READ_IOUT is not in
> its command table. Its MFR_ID reads "KDT", which the driver checks
> before binding.
> 
> VOUT_MODE reads 0x98: LINEAR mode with exponent -8 and bit 7 set. The
> datasheet defines the output voltage limits relative to the output
> voltage with a resolution of 1/256, so 0x0133 against a 0.6V command is
> a 120% over-voltage limit. The PMBus core takes bits [7:5] as the mode
> and rejects the device, so the driver reports VOUT_MODE with bit 7
> cleared and converts the four VOUT limits to and from absolute voltages
> against VOUT_COMMAND.
> 
> OT_WARN_LIMIT is not in the command table either. The register answers,
> but the part has no warning threshold and reports OT_WARNING at room
> temperature, so the driver hides the register from the core and only
> the OT_FAULT_LIMIT based attributes are provided.
> 
> READ_VOUT reports the voltage at the feedback pin, so boards that set
> the output with a resistor divider scale in2_input in user space.
> 
> Signed-off-by: Abdurrahman Hussain <abdurrahman@nexthop.ai>
> ---
>  Documentation/hwmon/fs1412.rst |  87 ++++++++++++++++++++
>  Documentation/hwmon/index.rst  |   1 +
>  MAINTAINERS                    |   7 ++
>  drivers/hwmon/pmbus/Kconfig    |   9 +++
>  drivers/hwmon/pmbus/Makefile   |   1 +
>  drivers/hwmon/pmbus/fs1412.c   | 179 +++++++++++++++++++++++++++++++++++++++++
>  6 files changed, 284 insertions(+)
> 
> diff --git a/Documentation/hwmon/fs1412.rst b/Documentation/hwmon/fs1412.rst
> new file mode 100644
> index 000000000000..b9b12fcbc5d9
> --- /dev/null
> +++ b/Documentation/hwmon/fs1412.rst
> @@ -0,0 +1,87 @@
> +.. SPDX-License-Identifier: GPL-2.0-or-later
> +
> +Kernel driver fs1412
> +====================
> +
> +Supported chips:
> +
> +  * TDK FS1412
> +
> +    Prefix: 'fs1412'
> +
> +    Addresses scanned: -
> +
> +    Datasheet: https://product.tdk.com/system/files/dam/doc/product/power/switching-power/micro-pol/data_sheet/fs1412_datasheet.pdf
> +
> +Authors:
> +    Abdurrahman Hussain <abdurrahman@nexthop.ai>
> +
> +
> +Description
> +-----------
> +
> +This driver implements support for the TDK FS1412, a 12A uPOL DC/DC
> +regulator with PMBus monitoring of its input voltage, output voltage and
> +temperature. The part has over-current and over-temperature protection with
> +programmable fault limits but no current telemetry, so no current attributes
> +are provided.
> +
> +The device answers on two I2C addresses: a register interface at the I2C
> +base address (0x08 by default) and the PMBus interface at the PMBus base
> +address (0x70 by default), each plus the offset strapped on the ADDR pin.
> +This driver binds to the PMBus address.
> +
> +The driver is a client driver to the core PMBus driver.
> +Please see Documentation/hwmon/pmbus.rst for details on PMBus client drivers.
> +
> +
> +Usage Notes
> +-----------
> +
> +This driver does not auto-detect devices. You will have to instantiate the
> +devices explicitly. Please see Documentation/i2c/instantiating-devices.rst for
> +details.
> +
> +READ_VOUT reports the voltage at the feedback (VFB) pin, not the regulated
> +output. When the output is set with an external resistor divider, scale
> +in2_input and the in2 limits by the divider ratio in user space.
> +
> +The device stores its output voltage limits relative to VOUT_COMMAND
> +(VOUT_MODE bit 7 set). The driver converts them to and from absolute
> +voltages, so the in2 limits read and write in the same units as in2_input.
> +A written limit is stored as a 1/256 fraction of VOUT_COMMAND, so it may
> +read back rounded to that resolution. The over-voltage protection itself
> +engages at one of four thresholds the device rounds the fault limit to; see
> +the datasheet.
> +
> +The part reports an over-temperature warning at room temperature and has no
> +OT_WARN_LIMIT in its command table, so temp1_max and its alarm are not
> +provided; temp1_crit comes from OT_FAULT_LIMIT.
> +
> +
> +Sysfs entries
> +-------------
> +
> +======================= ======================================================
> +in1_label		"vin"
> +in1_input		Measured input voltage
> +in1_min			Minimum input voltage
> +in1_min_alarm		Input voltage low alarm
> +in1_crit		Critical high input voltage
> +in1_crit_alarm		Input voltage critical high alarm
> +
> +in2_label		"vout1"
> +in2_input		Measured voltage at the feedback pin
> +in2_min			Minimum output voltage
> +in2_min_alarm		Output voltage low alarm
> +in2_max			Maximum output voltage
> +in2_max_alarm		Output voltage high alarm
> +in2_lcrit		Critical low output voltage
> +in2_lcrit_alarm		Output voltage critical low alarm
> +in2_crit		Critical high output voltage
> +in2_crit_alarm		Output voltage critical high alarm
> +
> +temp1_input		Measured temperature
> +temp1_crit		Critical high temperature
> +temp1_crit_alarm	Temperature critical high alarm
> +======================= ======================================================
> diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
> index 9955a525436a..625833374c94 100644
> --- a/Documentation/hwmon/index.rst
> +++ b/Documentation/hwmon/index.rst
> @@ -79,6 +79,7 @@ Hardware Monitoring Kernel Drivers
>     f71805f
>     f71882fg
>     fam15h_power
> +   fs1412
>     fsp-3y
>     ftsteutates
>     g760a
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 65e8a4b5c90b..05461a380bf7 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -26778,6 +26778,13 @@ W:	https://linuxtv.org
>  T:	git git://linuxtv.org/media.git
>  F:	drivers/media/i2c/tda9840*
>  
> +TDK FS1412 MICRO POL DRIVER
> +M:	Abdurrahman Hussain <abdurrahman@nexthop.ai>
> +L:	linux-hwmon@vger.kernel.org
> +S:	Maintained
> +F:	Documentation/hwmon/fs1412.rst
> +F:	drivers/hwmon/pmbus/fs1412.c
> +
>  TEA5761 TUNER DRIVER
>  M:	Mauro Carvalho Chehab <mchehab@kernel.org>
>  L:	linux-media@vger.kernel.org
> diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
> index eb71a0ac044b..030ef72415fe 100644
> --- a/drivers/hwmon/pmbus/Kconfig
> +++ b/drivers/hwmon/pmbus/Kconfig
> @@ -132,6 +132,15 @@ config SENSORS_DELTA_AHE50DC_FAN
>  	  This driver can also be built as a module. If so, the module
>  	  will be called delta-ahe50dc-fan.
>  
> +config SENSORS_FS1412
> +	tristate "TDK FS1412"
> +	help
> +	  If you say yes here you get hardware monitoring support for the TDK
> +	  FS1412 uPOL regulator.
> +
> +	  This driver can also be built as a module. If so, the module will
> +	  be called fs1412.
> +
>  config SENSORS_FSP_3Y
>  	tristate "FSP/3Y-Power power supplies"
>  	help
> diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
> index e288fe72a437..7bbc0d02bfad 100644
> --- a/drivers/hwmon/pmbus/Makefile
> +++ b/drivers/hwmon/pmbus/Makefile
> @@ -13,6 +13,7 @@ obj-$(CONFIG_SENSORS_APS_379)	+= aps-379.o
>  obj-$(CONFIG_SENSORS_BEL_PFE)	+= bel-pfe.o
>  obj-$(CONFIG_SENSORS_BPA_RS600)	+= bpa-rs600.o
>  obj-$(CONFIG_SENSORS_DELTA_AHE50DC_FAN) += delta-ahe50dc-fan.o
> +obj-$(CONFIG_SENSORS_FS1412)	+= fs1412.o
>  obj-$(CONFIG_SENSORS_FSP_3Y)	+= fsp-3y.o
>  obj-$(CONFIG_SENSORS_HAC300S)	+= hac300s.o
>  obj-$(CONFIG_SENSORS_IBM_CFFPS)	+= ibm-cffps.o
> diff --git a/drivers/hwmon/pmbus/fs1412.c b/drivers/hwmon/pmbus/fs1412.c
> new file mode 100644
> index 000000000000..90a72b23967e
> --- /dev/null
> +++ b/drivers/hwmon/pmbus/fs1412.c
> @@ -0,0 +1,179 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Hardware monitoring driver for the TDK FS1412 uPOL regulator
> + *
> + * Copyright 2026 Nexthop Systems.
> + */
> +
> +#include <linux/bits.h>
> +#include <linux/i2c.h>
> +#include <linux/math.h>
> +#include <linux/minmax.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/pmbus.h>
> +#include <linux/string.h>
> +
> +#include "pmbus.h"
> +
> +#define FS1412_MFR_ID		"KDT"
> +#define FS1412_MFR_ID_LEN	(sizeof(FS1412_MFR_ID) - 1)
> +
> +/* VOUT_MODE: bit 7 selects relative VOUT limits, bits [4:0] the exponent. */
> +#define FS1412_VOUT_MODE_RELATIVE	BIT(7)
> +#define FS1412_VOUT_MODE_EXP_MASK	0x1f
> +#define FS1412_VOUT_EXP_SHIFT		8	/* exponent -8: 1/256 V per LSB */
> +
> +/*
> + * VOUT_MODE reads 0x98: LINEAR mode with exponent -8 and bit 7 set, which
> + * makes the VOUT limit commands relative to VOUT_COMMAND. The PMBus core
> + * treats bits [7:5] as the mode, so report the mode and exponent with bit
> + * 7 cleared and convert the relative limits in the word accessors below.
> + */
> +static int fs1412_read_byte_data(struct i2c_client *client, int page, int reg)
> +{
> +	int ret;
> +
> +	if (reg != PMBUS_VOUT_MODE)
> +		return -ENODATA;
> +
> +	ret = pmbus_read_byte_data(client, page, reg);
> +	if (ret < 0 || ret == 0xff)
> +		return ret;
> +
> +	return ret & ~FS1412_VOUT_MODE_RELATIVE;

This implementation is wrong. Support for relative mode should instead
be added to the PMBus core. Or, rather, this driver should be tested with
https://patchwork.kernel.org/project/linux-hwmon/patch/20240315151855.377627-2-lars.petter.mostad@appear.net/
in place (with appropriate fixes for that patch if necessary), and the
patches should be applied together.

Thanks,
Guenter

> +}
> +
> +static bool fs1412_vout_limit(int reg)
> +{
> +	switch (reg) {
> +	case PMBUS_VOUT_OV_FAULT_LIMIT:
> +	case PMBUS_VOUT_OV_WARN_LIMIT:
> +	case PMBUS_VOUT_UV_WARN_LIMIT:
> +	case PMBUS_VOUT_UV_FAULT_LIMIT:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +/*
> + * The VOUT limits hold the limit as a fraction of VOUT_COMMAND in the same
> + * 1/256 units, so 0x0133 on a 0.6 V command is a 120% limit. Report them
> + * as absolute voltages: limit = fraction * VOUT_COMMAND.
> + */
> +static int fs1412_read_word_data(struct i2c_client *client, int page,
> +				 int phase, int reg)
> +{
> +	int rel, cmd;
> +
> +	/*
> +	 * OT_WARN_LIMIT is not in the command table. The register answers,
> +	 * but the part has no warning threshold and reports OT_WARNING at
> +	 * room temperature, so keep the core from exposing either.
> +	 */
> +	if (reg == PMBUS_OT_WARN_LIMIT)
> +		return -ENXIO;
> +	if (!fs1412_vout_limit(reg))
> +		return -ENODATA;
> +
> +	rel = pmbus_read_word_data(client, page, phase, reg);
> +	if (rel < 0)
> +		return rel;
> +
> +	cmd = pmbus_read_word_data(client, page, 0xff, PMBUS_VOUT_COMMAND);
> +	if (cmd < 0)
> +		return cmd;
> +
> +	return min_t(u32, DIV_ROUND_CLOSEST((u32)rel * cmd,
> +					     1 << FS1412_VOUT_EXP_SHIFT), 0xffff);
> +}
> +
> +static int fs1412_write_word_data(struct i2c_client *client, int page,
> +				  int reg, u16 word)
> +{
> +	int cmd;
> +
> +	if (reg == PMBUS_OT_WARN_LIMIT)
> +		return -ENXIO;
> +	if (!fs1412_vout_limit(reg))
> +		return -ENODATA;
> +
> +	cmd = pmbus_read_word_data(client, page, 0xff, PMBUS_VOUT_COMMAND);
> +	if (cmd < 0)
> +		return cmd;
> +	if (!cmd)
> +		return -EINVAL;
> +
> +	word = clamp_val(DIV_ROUND_CLOSEST(word << FS1412_VOUT_EXP_SHIFT, cmd),
> +			 0, 0xffff);
> +
> +	return pmbus_write_word_data(client, page, reg, word);
> +}
> +
> +static struct pmbus_driver_info fs1412_info = {
> +	.pages = 1,
> +	.format[PSC_VOLTAGE_IN] = linear,
> +	.format[PSC_VOLTAGE_OUT] = linear,
> +	.format[PSC_TEMPERATURE] = linear,
> +	.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT |
> +		   PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
> +		   PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
> +	.read_byte_data = fs1412_read_byte_data,
> +	.read_word_data = fs1412_read_word_data,
> +	.write_word_data = fs1412_write_word_data,
> +};
> +
> +static int fs1412_probe(struct i2c_client *client)
> +{
> +	char buf[I2C_SMBUS_BLOCK_MAX + 1] = { 0 };
> +	struct device *dev = &client->dev;
> +	int ret;
> +
> +	ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf);
> +	if (ret < 0)
> +		return dev_err_probe(dev, ret, "Failed to read PMBUS_MFR_ID\n");
> +
> +	if (ret != FS1412_MFR_ID_LEN ||
> +	    strncmp(buf, FS1412_MFR_ID, FS1412_MFR_ID_LEN))
> +		return dev_err_probe(dev, -ENODEV,
> +				     "Unsupported Manufacturer ID '%s'\n", buf);
> +
> +	/* The relative limit conversion assumes the 1/256 V exponent. */
> +	ret = i2c_smbus_read_byte_data(client, PMBUS_VOUT_MODE);
> +	if (ret < 0)
> +		return dev_err_probe(dev, ret, "Failed to read VOUT_MODE\n");
> +	if ((ret & FS1412_VOUT_MODE_EXP_MASK) != 0x18)
> +		return dev_err_probe(dev, -ENODEV,
> +				     "Unsupported VOUT_MODE 0x%02x\n", ret);
> +
> +	return pmbus_do_probe(client, &fs1412_info);
> +}
> +
> +static const struct i2c_device_id fs1412_id[] = {
> +	{ .name = "fs1412" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, fs1412_id);
> +
> +static const struct of_device_id fs1412_of_match[] = {
> +	{ .compatible = "tdk,fs1412" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, fs1412_of_match);
> +
> +static struct i2c_driver fs1412_driver = {
> +	.driver = {
> +		.name = "fs1412",
> +		.of_match_table = fs1412_of_match,
> +	},
> +	.probe = fs1412_probe,
> +	.id_table = fs1412_id,
> +};
> +
> +module_i2c_driver(fs1412_driver);
> +
> +MODULE_AUTHOR("Abdurrahman Hussain <abdurrahman@nexthop.ai>");
> +MODULE_DESCRIPTION("PMBus driver for the TDK FS1412 uPOL regulator");
> +MODULE_LICENSE("GPL");
> +MODULE_IMPORT_NS("PMBUS");

  reply	other threads:[~2026-10-19 20:25 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-09  0:56 [PATCH 0/3] hwmon: (pmbus/fs1412) Add TDK FS1412 support Abdurrahman Hussain
2026-10-09  0:56 ` [PATCH 1/3] dt-bindings: vendor-prefixes: Add TDK Corporation Abdurrahman Hussain
2026-10-09 10:47   ` Krzysztof Kozlowski
2026-10-09  0:56 ` [PATCH 2/3] dt-bindings: trivial-devices: Add TDK FS1412 Abdurrahman Hussain
2026-10-09 10:47   ` Krzysztof Kozlowski
2026-10-09  0:56 ` [PATCH 3/3] hwmon: (pmbus/fs1412) Add TDK FS1412 driver Abdurrahman Hussain
2026-10-19 20:25   ` Guenter Roeck [this message]
2026-10-20  7:31     ` Abdurrahman Hussain
2026-10-20 15:27       ` Guenter Roeck

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=a3ccc85b-aab6-44b4-9695-ecd78401102c@roeck-us.net \
    --to=linux@roeck-us.net \
    --cc=abdurrahman@nexthop.ai \
    --cc=conor+dt@kernel.org \
    --cc=corbet@lwn.net \
    --cc=devicetree@vger.kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-hwmon@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=rdunlap@infradead.org \
    --cc=robh@kernel.org \
    --cc=skhan@linuxfoundation.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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®