* [PATCH 0/3] Support onsemi's FD5121 multiphase digital controller
@ 2026-06-23 5:55 ` Selvamani Rajagopal
0 siblings, 0 replies; 11+ messages in thread
From: Selvamani Rajagopal via B4 Relay @ 2026-06-23 5:55 UTC (permalink / raw)
To: Guenter Roeck, Jonathan Corbet, Shuah Khan, Selva Rajagopal,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-hwmon, linux-doc, linux-kernel, devicetree,
Selvamani Rajagopal
FD5121 is a dual rail, multi-phase controller designed to
power CPU, ASIC or SoC with fully configurable rails.
This driver adds support for FD5121, FD5123 and FD5125. These
controllers configurability through PMBus 1.4.1.
Added documents for these controllers.
Signed-off-by: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>
---
Selvamani Rajagopal (3):
Documentation/hwmon: Add onsemi's FD5121 controllers' documentation
dt-bindings: hwmon: pmbus: Support for onsemi's FD5121
hwmon: (pmbus/fd5121): Add support FD5121, FD5123 and FD5125
.../bindings/hwmon/pmbus/onnn,fd5121.yaml | 41 +
Documentation/hwmon/fd5121.rst | 93 ++
Documentation/hwmon/index.rst | 1 +
MAINTAINERS | 8 +
drivers/hwmon/pmbus/Kconfig | 9 +
drivers/hwmon/pmbus/Makefile | 1 +
drivers/hwmon/pmbus/fd5121.c | 1004 ++++++++++++++++++++
7 files changed, 1157 insertions(+)
---
base-commit: 1a3746ccbb0a97bed3c06ccde6b880013b1dddc1
change-id: 20260622-support-fd5121-from-onsemi-6fa9f98b5bf0
Best regards,
--
Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 0/3] Support onsemi's FD5121 multiphase digital controller
@ 2026-06-23 5:55 ` Selvamani Rajagopal
0 siblings, 0 replies; 11+ messages in thread
From: Selvamani Rajagopal @ 2026-06-23 5:55 UTC (permalink / raw)
To: Guenter Roeck, Jonathan Corbet, Shuah Khan, Selva Rajagopal,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-hwmon, linux-doc, linux-kernel, devicetree,
Selvamani Rajagopal
FD5121 is a dual rail, multi-phase controller designed to
power CPU, ASIC or SoC with fully configurable rails.
This driver adds support for FD5121, FD5123 and FD5125. These
controllers configurability through PMBus 1.4.1.
Added documents for these controllers.
Signed-off-by: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>
---
Selvamani Rajagopal (3):
Documentation/hwmon: Add onsemi's FD5121 controllers' documentation
dt-bindings: hwmon: pmbus: Support for onsemi's FD5121
hwmon: (pmbus/fd5121): Add support FD5121, FD5123 and FD5125
.../bindings/hwmon/pmbus/onnn,fd5121.yaml | 41 +
Documentation/hwmon/fd5121.rst | 93 ++
Documentation/hwmon/index.rst | 1 +
MAINTAINERS | 8 +
drivers/hwmon/pmbus/Kconfig | 9 +
drivers/hwmon/pmbus/Makefile | 1 +
drivers/hwmon/pmbus/fd5121.c | 1004 ++++++++++++++++++++
7 files changed, 1157 insertions(+)
---
base-commit: 1a3746ccbb0a97bed3c06ccde6b880013b1dddc1
change-id: 20260622-support-fd5121-from-onsemi-6fa9f98b5bf0
Best regards,
--
Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 1/3] Documentation/hwmon: Add onsemi's FD5121 controllers' documentation
2026-06-23 5:55 ` Selvamani Rajagopal
@ 2026-06-23 5:55 ` Selvamani Rajagopal
-1 siblings, 0 replies; 11+ messages in thread
From: Selvamani Rajagopal via B4 Relay @ 2026-06-23 5:55 UTC (permalink / raw)
To: Guenter Roeck, Jonathan Corbet, Shuah Khan, Selva Rajagopal,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-hwmon, linux-doc, linux-kernel, devicetree,
Selvamani Rajagopal
From: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>
Document the hardware monitoring support for the FD5121, FD5123, and
FD5125 devices.
Documentation describes the supported telemetry data exposed via
the sysfs for the hwmon subsystem, including voltage, current,
power and temperature measurements.
Signed-off-by: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>
---
Documentation/hwmon/fd5121.rst | 93 ++++++++++++++++++++++++++++++++++++++++++
Documentation/hwmon/index.rst | 1 +
2 files changed, 94 insertions(+)
diff --git a/Documentation/hwmon/fd5121.rst b/Documentation/hwmon/fd5121.rst
new file mode 100644
index 000000000000..c279db4641e4
--- /dev/null
+++ b/Documentation/hwmon/fd5121.rst
@@ -0,0 +1,93 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+Kernel driver fd5121
+=====================
+
+Supported chips:
+
+ * onsemi FD5121
+
+ Prefix: 'fd5121'
+
+ Datasheet: Datasheet is not publicly available.
+
+ * onsemi FD5123
+
+ Prefix: 'fd5121'
+
+ Datasheet: Datasheet is not publicly available.
+
+ * onsemi FD5125
+
+ Prefix: 'fd5121'
+
+ Datasheet: Datasheet is not publicly available.
+
+Author: Selva Rajagopal <selvamani.rajagopal@onsemi.com>
+
+Description
+-----------
+
+FD5121, FD5123, FD5125 are dual-rail, multi-phase controllers
+and compliant to
+
+ - AVS and AMD proprietary SVI3 protocol.
+ - PMBus rev 1.4.1 interface.
+
+Used as multi-phase voltage regulators for CPUs, high performance
+ASICs, SoCs or graphic cores.
+
+Gives full telemetry options including input/output voltage
+and current, as well as fault handling and identifications.
+
+Usage Notes
+-----------
+
+This driver does not probe for PMBus devices. You will have
+to instantiate devices explicitly.
+
+Example: the following commands will load the driver for the
+controller at address 0x50 on I2C bus #1::
+
+ # modprobe fd5121
+ # echo fd5121 0x50 > /sys/bus/i2c/devices/i2c-1/new_device
+
+It can also be instantiated by declaring in device tree
+
+Sysfs attributes
+----------------
+
+The following attributes are supported:
+
+====================== ====================================
+curr[1-2]_label "iin[1-2]"
+curr[3-4]_label "iout[1-2]"
+curr[1-2]_input Measured input current.
+curr[3-4]_input Measured output current.
+curr[1-4]_crit_alarm Output current critical high alarm.
+curr[1-4]_max_alarm Output current high alarm.
+
+in[1-2]_label "vin[1-2]"
+in[3-4]_label "vout[1-2]"
+in[1-4]_lcrit_alarm Input voltage critical low alarm.
+in[1-4]_crit_alarm Input voltage critical high alarm.
+in[1-2]_max_alarm Input voltage high alarm.
+in[1-2]_input Measured input voltage.
+in[3-4]_input Measured output voltage.
+
+power[1-2]_label "pin[1-2]"
+power[3-4]_label "pout[1-2]"
+power[3-4]_crit_alarm Output power critical high alarm.
+power[1-2]_max_alarm Output power high alarm.
+power[1-4]_max Power limit.
+power[1-4]_input Measured input power.
+power[3-4]_crit Critical maximum output power.
+
+temp[1-2]_crit_alarm Chip temperature critical high alarm.
+temp[1-2]_max_alarm Chip temperature high alarm.
+temp[1-2]_input Measured temperature.
+temp[1-2]_max Maximum temperature.
+temp[1-2]_crit Critical high temperature.
+
+====================== ====================================
+
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index 4aa910569c31..451f5433fa60 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -79,6 +79,7 @@ Hardware Monitoring Kernel Drivers
f71805f
f71882fg
fam15h_power
+ fd5121
fsp-3y
ftsteutates
g760a
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 1/3] Documentation/hwmon: Add onsemi's FD5121 controllers' documentation
@ 2026-06-23 5:55 ` Selvamani Rajagopal
0 siblings, 0 replies; 11+ messages in thread
From: Selvamani Rajagopal @ 2026-06-23 5:55 UTC (permalink / raw)
To: Guenter Roeck, Jonathan Corbet, Shuah Khan, Selva Rajagopal,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-hwmon, linux-doc, linux-kernel, devicetree,
Selvamani Rajagopal
Document the hardware monitoring support for the FD5121, FD5123, and
FD5125 devices.
Documentation describes the supported telemetry data exposed via
the sysfs for the hwmon subsystem, including voltage, current,
power and temperature measurements.
Signed-off-by: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>
---
Documentation/hwmon/fd5121.rst | 93 ++++++++++++++++++++++++++++++++++++++++++
Documentation/hwmon/index.rst | 1 +
2 files changed, 94 insertions(+)
diff --git a/Documentation/hwmon/fd5121.rst b/Documentation/hwmon/fd5121.rst
new file mode 100644
index 000000000000..c279db4641e4
--- /dev/null
+++ b/Documentation/hwmon/fd5121.rst
@@ -0,0 +1,93 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+Kernel driver fd5121
+=====================
+
+Supported chips:
+
+ * onsemi FD5121
+
+ Prefix: 'fd5121'
+
+ Datasheet: Datasheet is not publicly available.
+
+ * onsemi FD5123
+
+ Prefix: 'fd5121'
+
+ Datasheet: Datasheet is not publicly available.
+
+ * onsemi FD5125
+
+ Prefix: 'fd5121'
+
+ Datasheet: Datasheet is not publicly available.
+
+Author: Selva Rajagopal <selvamani.rajagopal@onsemi.com>
+
+Description
+-----------
+
+FD5121, FD5123, FD5125 are dual-rail, multi-phase controllers
+and compliant to
+
+ - AVS and AMD proprietary SVI3 protocol.
+ - PMBus rev 1.4.1 interface.
+
+Used as multi-phase voltage regulators for CPUs, high performance
+ASICs, SoCs or graphic cores.
+
+Gives full telemetry options including input/output voltage
+and current, as well as fault handling and identifications.
+
+Usage Notes
+-----------
+
+This driver does not probe for PMBus devices. You will have
+to instantiate devices explicitly.
+
+Example: the following commands will load the driver for the
+controller at address 0x50 on I2C bus #1::
+
+ # modprobe fd5121
+ # echo fd5121 0x50 > /sys/bus/i2c/devices/i2c-1/new_device
+
+It can also be instantiated by declaring in device tree
+
+Sysfs attributes
+----------------
+
+The following attributes are supported:
+
+====================== ====================================
+curr[1-2]_label "iin[1-2]"
+curr[3-4]_label "iout[1-2]"
+curr[1-2]_input Measured input current.
+curr[3-4]_input Measured output current.
+curr[1-4]_crit_alarm Output current critical high alarm.
+curr[1-4]_max_alarm Output current high alarm.
+
+in[1-2]_label "vin[1-2]"
+in[3-4]_label "vout[1-2]"
+in[1-4]_lcrit_alarm Input voltage critical low alarm.
+in[1-4]_crit_alarm Input voltage critical high alarm.
+in[1-2]_max_alarm Input voltage high alarm.
+in[1-2]_input Measured input voltage.
+in[3-4]_input Measured output voltage.
+
+power[1-2]_label "pin[1-2]"
+power[3-4]_label "pout[1-2]"
+power[3-4]_crit_alarm Output power critical high alarm.
+power[1-2]_max_alarm Output power high alarm.
+power[1-4]_max Power limit.
+power[1-4]_input Measured input power.
+power[3-4]_crit Critical maximum output power.
+
+temp[1-2]_crit_alarm Chip temperature critical high alarm.
+temp[1-2]_max_alarm Chip temperature high alarm.
+temp[1-2]_input Measured temperature.
+temp[1-2]_max Maximum temperature.
+temp[1-2]_crit Critical high temperature.
+
+====================== ====================================
+
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index 4aa910569c31..451f5433fa60 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -79,6 +79,7 @@ Hardware Monitoring Kernel Drivers
f71805f
f71882fg
fam15h_power
+ fd5121
fsp-3y
ftsteutates
g760a
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 2/3] dt-bindings: hwmon: pmbus: Support for onsemi's FD5121
2026-06-23 5:55 ` Selvamani Rajagopal
@ 2026-06-23 5:55 ` Selvamani Rajagopal
-1 siblings, 0 replies; 11+ messages in thread
From: Selvamani Rajagopal via B4 Relay @ 2026-06-23 5:55 UTC (permalink / raw)
To: Guenter Roeck, Jonathan Corbet, Shuah Khan, Selva Rajagopal,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-hwmon, linux-doc, linux-kernel, devicetree,
Selvamani Rajagopal
From: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>
Add devicetree schema for onsemi FD5121, FD5123, and
FD5125 dual rail, multi-phase digital controllers.
Signed-off-by: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>
---
.../bindings/hwmon/pmbus/onnn,fd5121.yaml | 41 ++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/onnn,fd5121.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/onnn,fd5121.yaml
new file mode 100644
index 000000000000..b0453b0634f0
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/pmbus/onnn,fd5121.yaml
@@ -0,0 +1,41 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/pmbus/onnn,fd5121.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: onsemi's multi-phase digital controllers
+
+maintainers:
+ - Selvamani Rajagopal <selvamani.rajagopal@onsemi.com>
+
+description:
+ onsemi multi-phase digital controllers with PMBus.
+
+properties:
+ compatible:
+ enum:
+ - onnn,fd5121
+ - onnn,fd5123
+ - onnn,fd5125
+
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ fd5121@50 {
+ compatible = "onnn,fd5121";
+ reg = <0x50>;
+ };
+ };
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 2/3] dt-bindings: hwmon: pmbus: Support for onsemi's FD5121
@ 2026-06-23 5:55 ` Selvamani Rajagopal
0 siblings, 0 replies; 11+ messages in thread
From: Selvamani Rajagopal @ 2026-06-23 5:55 UTC (permalink / raw)
To: Guenter Roeck, Jonathan Corbet, Shuah Khan, Selva Rajagopal,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-hwmon, linux-doc, linux-kernel, devicetree,
Selvamani Rajagopal
Add devicetree schema for onsemi FD5121, FD5123, and
FD5125 dual rail, multi-phase digital controllers.
Signed-off-by: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>
---
.../bindings/hwmon/pmbus/onnn,fd5121.yaml | 41 ++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/onnn,fd5121.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/onnn,fd5121.yaml
new file mode 100644
index 000000000000..b0453b0634f0
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/pmbus/onnn,fd5121.yaml
@@ -0,0 +1,41 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/pmbus/onnn,fd5121.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: onsemi's multi-phase digital controllers
+
+maintainers:
+ - Selvamani Rajagopal <selvamani.rajagopal@onsemi.com>
+
+description:
+ onsemi multi-phase digital controllers with PMBus.
+
+properties:
+ compatible:
+ enum:
+ - onnn,fd5121
+ - onnn,fd5123
+ - onnn,fd5125
+
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ fd5121@50 {
+ compatible = "onnn,fd5121";
+ reg = <0x50>;
+ };
+ };
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 3/3] hwmon: (pmbus/fd5121): Add support FD5121, FD5123 and FD5125
2026-06-23 5:55 ` Selvamani Rajagopal
@ 2026-06-23 5:55 ` Selvamani Rajagopal
-1 siblings, 0 replies; 11+ messages in thread
From: Selvamani Rajagopal via B4 Relay @ 2026-06-23 5:55 UTC (permalink / raw)
To: Guenter Roeck, Jonathan Corbet, Shuah Khan, Selva Rajagopal,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-hwmon, linux-doc, linux-kernel, devicetree,
Selvamani Rajagopal
From: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>
FD5121 is a dual-rail, multi-phase, digital controller that offers
full telemtry options including input/output voltage, current as
well as fault handling and identifications.
These controllers are compliant with PMBus specification.
Signed-off-by: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>
---
MAINTAINERS | 8 +
drivers/hwmon/pmbus/Kconfig | 9 +
drivers/hwmon/pmbus/Makefile | 1 +
drivers/hwmon/pmbus/fd5121.c | 1004 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 1022 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index d95d3ef77773..c0664c33324a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -20135,6 +20135,14 @@ L: linux-mips@vger.kernel.org
S: Maintained
F: arch/mips/boot/dts/ralink/omega2p.dts
+ONSEMI HARDWARE MONITOR DRIVER
+M: Selva Rajagopal <selvamani.rajagopal@onsemi.com>
+L: linux-hwmon@vger.kernel.org
+S: Supported
+W: https://www.onsemi.com
+F: Documentation/devicetree/bindings/hwmon/pmbus/onnn,fd5121.yaml
+F: drivers/hwmon/pmbus/fd5121.c
+
ONSEMI ETHERNET PHY DRIVERS
M: Piergiorgio Beruto <piergiorgio.beruto@gmail.com>
L: netdev@vger.kernel.org
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index c8cda160b5f8..3a06ed83539e 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -179,6 +179,15 @@ config SENSORS_E50SN12051
This driver can also be built as a module. If so, the module will
be called e50sn12051.
+config SENSORS_FD5121
+ tristate "FD5121/FD5123/FD5125 controllers from onsemi"
+ help
+ If you say yes here, you get support for onsemi
+ controllers FD5121, FD5123, FD5125.
+
+ This driver can also be built as a module. If so, the module will
+ be called fd5121.
+
config SENSORS_INA233
tristate "Texas Instruments INA233 and compatibles"
help
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index ffc05f493213..70f4afb41fe0 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_FD5121) += fd5121.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/fd5121.c b/drivers/hwmon/pmbus/fd5121.c
new file mode 100644
index 000000000000..e68c6d6cabbd
--- /dev/null
+++ b/drivers/hwmon/pmbus/fd5121.c
@@ -0,0 +1,1004 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2026 Semiconductor Components Industries, LLC ("onsemi").
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/unaligned.h>
+
+#include "pmbus.h"
+
+enum chips { chip_fd5121, chip_fd5123, chip_fd5125 };
+
+#define CTLR_ID_UNKNOWN 0
+#define CTLR_ID_FD5121 0xFD5121
+#define CTLR_ID_FD5123 0xFD5123
+#define CTLR_ID_FD5125 0xFD5125
+
+#define FD5121_NUM_PAGES 2
+
+/* Custom PMBUS commands */
+#define PMBUS_REG_VOUT_MIN 0x2B
+#define PMBUS_REG_POWER_MODE 0x34
+#define PMBUS_REG_VIN_ON 0x35
+#define PMBUS_REG_VIN_OFF 0x36
+#define PMBUS_REG_VIN_UV_FAULT_RESPONSE 0x5A
+#define PMBUS_REG_IIN_OC_FAULT_RESPONSE 0x5C
+#define PMBUS_REG_TON_DELAY 0x60
+#define PMBUS_REG_POUT_OP_FAULT_RESPONSE 0x69
+#define PMBUS_REG_READ_VAUX 0x85
+
+#define PMBUS_REG_IKNEE_SET 0x2D
+#define PMBUS_REG_PIN_COUNTER 0x2E
+#define PMBUS_REG_VMIN_AWARE 0x2F
+#define PMBUS_REG_VAUX_UV_FAULT_LIMIT 0x6C
+#define PMBUS_REG_VAUX_OV_FAULT_LIMIT 0x6D
+#define PMBUS_REG_VAUX_UV_FAULT_RESPONSE 0x6E
+#define PMBUS_REG_VAUX_OV_FAULT_RESPONSE 0x6F
+#define PMBUS_REG_VAUX_UV_WARNING 0x75
+#define PMBUS_REG_VAUX_OV_WARNING 0x76
+#define PMBUS_REG_MFR_FREE_USER_CONFIG_TABLES 0xCF
+#define PMBUS_REG_MFR_ADDRESS_TABLE 0xD0
+#define PMBUS_REG_MFR_STATUS_ONSEMI 0xD1
+#define PMBUS_REG_MFR_UNLOCK 0xD2
+#define PMBUS_REG_MFR_FAULTY_SPS 0xD3
+#define PMBUS_REG_TLVR_FAULTS 0xD4
+#define PMBUS_REG_MFR_USER_STORE_CONFIG_TAB 0xD5
+#define PMBUS_REG_MFR_USER_CONFIG_INDEX 0xD6
+#define PMBUS_REG_MFR_PWM_DISCONNECTION 0xD7
+#define PMBUS_REG_MFR_VR_DISCONNECTION 0xD8
+#define PMBUS_REG_MFR_TON_SLEW 0xD9
+#define PMBUS_REG_MFR_TOFF_SLEW 0xDA
+#define PMBUS_REG_MFR_RAIL_NAME 0xDB
+#define PMBUS_REG_MFR_VOUT_DROOP 0xDC
+#define PMBUS_REG_MFR_USER_RESTORE_CONFIG_TAB 0xDD
+#define PMBUS_REG_MFR_SVR_GO 0xDE
+#define PMBUS_REG_MFR_SET_PWD 0xDF
+#define PMBUS_REG_MFR_CONFIG_ACTIVATE 0xE0
+#define PMBUS_REG_MFR_CONFIG_RECOVER 0xE1
+#define PMBUS_REG_MFR_OTP_DUMP 0xE2
+#define PMBUS_REG_MFR_BBR_EN 0xE3
+#define PMBUS_REG_MFR_DPM_MIN 0xE4
+#define PMBUS_REG_MFR_VBOOT 0xE5
+#define PMBUS_REG_MFR_PRECLAMP_OFFSET 0xE6
+#define PMBUS_REG_MFR_TLVR_DIAGN 0xE7
+#define PMBUS_REG_MFR_READ_VSYS 0xE8
+#define PMBUS_REG_MFR_SPECIFIC_E9 0xE9
+#define PMBUS_REG_MFR_SPECIFIC_EA 0xEA
+#define PMBUS_REG_MFR_SS_CBC 0xEB
+#define PMBUS_REG_MFR_AMD_STATUS 0xEC
+#define PMBUS_REG_MFR_CHECKSUM 0xEE
+#define PMBUS_REG_CSE_INDEX 0xF0
+#define PMBUS_REG_COUT_MEASURE 0xF1
+#define PMBUS_REG_VR_COUT 0xF2
+#define PMBUS_REG_BBR_RAM 0xF3
+#define PMBUS_REG_BBR_OTP 0xF4
+#define PMBUS_REG_READ_PSYS 0xF5
+#define PMBUS_REG_POSTCLAMP_OFFSET 0xF6
+#define PMBUS_REG_PGOOD_DELAY 0xF7
+#define PMBUS_REG_MFR_SPECIFIC_F8 0xF8
+#define PMBUS_REG_MFR_SPECIFIC_F9 0xF9
+#define PMBUS_REG_MFR_PWD_PROGRAM_RAM 0xFA
+#define PMBUS_REG_MFR_PWD_PROGRAM_I2C 0xFB
+#define PMBUS_REG_MFR_PWD_ENABLE_OTP_STORE 0xFC
+
+/* List of recognized commands */
+static const u8 cc_list[] = {
+ PMBUS_PAGE,
+ PMBUS_OPERATION,
+ PMBUS_ON_OFF_CONFIG,
+ PMBUS_CLEAR_FAULTS,
+ PMBUS_WRITE_PROTECT,
+ PMBUS_CAPABILITY,
+ PMBUS_VOUT_MODE,
+ PMBUS_VOUT_COMMAND,
+ PMBUS_VOUT_MAX,
+ PMBUS_VOUT_MARGIN_HIGH,
+ PMBUS_VOUT_MARGIN_LOW,
+ PMBUS_VOUT_TRANSITION_RATE,
+ PMBUS_REG_VOUT_MIN,
+ PMBUS_REG_IKNEE_SET,
+ PMBUS_REG_PIN_COUNTER,
+ PMBUS_REG_VMIN_AWARE,
+ PMBUS_REG_POWER_MODE,
+ PMBUS_REG_VIN_ON,
+ PMBUS_REG_VIN_OFF,
+ PMBUS_VOUT_OV_FAULT_LIMIT,
+ PMBUS_VOUT_OV_FAULT_RESPONSE,
+ PMBUS_VOUT_UV_FAULT_LIMIT,
+ PMBUS_VOUT_UV_FAULT_RESPONSE,
+ PMBUS_IOUT_OC_FAULT_LIMIT,
+ PMBUS_IOUT_OC_FAULT_RESPONSE,
+ PMBUS_IOUT_OC_WARN_LIMIT,
+ PMBUS_OT_FAULT_LIMIT,
+ PMBUS_OT_FAULT_RESPONSE,
+ PMBUS_OT_WARN_LIMIT,
+ PMBUS_VIN_OV_FAULT_LIMIT,
+ PMBUS_VIN_OV_FAULT_RESPONSE,
+ PMBUS_VIN_OV_WARN_LIMIT,
+ PMBUS_VIN_UV_WARN_LIMIT,
+ PMBUS_VIN_UV_FAULT_LIMIT,
+ PMBUS_REG_VIN_UV_FAULT_RESPONSE,
+ PMBUS_IIN_OC_FAULT_LIMIT,
+ PMBUS_REG_IIN_OC_FAULT_RESPONSE,
+ PMBUS_IIN_OC_WARN_LIMIT,
+ PMBUS_REG_TON_DELAY,
+ PMBUS_POUT_OP_FAULT_LIMIT,
+ PMBUS_REG_POUT_OP_FAULT_RESPONSE,
+ PMBUS_POUT_OP_WARN_LIMIT,
+ PMBUS_PIN_OP_WARN_LIMIT,
+ PMBUS_REG_VAUX_UV_FAULT_LIMIT,
+ PMBUS_REG_VAUX_OV_FAULT_LIMIT,
+ PMBUS_REG_VAUX_UV_FAULT_RESPONSE,
+ PMBUS_REG_VAUX_OV_FAULT_RESPONSE,
+ PMBUS_REG_VAUX_UV_WARNING,
+ PMBUS_REG_VAUX_OV_WARNING,
+ PMBUS_STATUS_BYTE,
+ PMBUS_STATUS_WORD,
+ PMBUS_STATUS_VOUT,
+ PMBUS_STATUS_IOUT,
+ PMBUS_STATUS_INPUT,
+ PMBUS_STATUS_TEMPERATURE,
+ PMBUS_STATUS_CML,
+ PMBUS_STATUS_OTHER,
+ PMBUS_STATUS_MFR_SPECIFIC,
+ PMBUS_REG_READ_VAUX,
+ PMBUS_READ_VIN,
+ PMBUS_READ_IIN,
+ PMBUS_READ_VOUT,
+ PMBUS_READ_IOUT,
+ PMBUS_READ_TEMPERATURE_1,
+ PMBUS_READ_POUT,
+ PMBUS_READ_PIN,
+ PMBUS_REVISION,
+ PMBUS_MFR_ID,
+ PMBUS_MFR_MODEL,
+ PMBUS_MFR_REVISION,
+ PMBUS_IC_DEVICE_ID,
+ PMBUS_REG_MFR_FREE_USER_CONFIG_TABLES,
+ PMBUS_REG_MFR_ADDRESS_TABLE,
+ PMBUS_REG_MFR_STATUS_ONSEMI,
+ PMBUS_REG_MFR_UNLOCK,
+ PMBUS_REG_MFR_FAULTY_SPS,
+ PMBUS_REG_TLVR_FAULTS,
+ PMBUS_REG_MFR_USER_STORE_CONFIG_TAB,
+ PMBUS_REG_MFR_USER_CONFIG_INDEX,
+ PMBUS_REG_MFR_PWM_DISCONNECTION,
+ PMBUS_REG_MFR_VR_DISCONNECTION,
+ PMBUS_REG_MFR_TON_SLEW,
+ PMBUS_REG_MFR_TOFF_SLEW,
+ PMBUS_REG_MFR_RAIL_NAME,
+ PMBUS_REG_MFR_VOUT_DROOP,
+ PMBUS_REG_MFR_USER_RESTORE_CONFIG_TAB,
+ PMBUS_REG_MFR_SVR_GO,
+ PMBUS_REG_MFR_SET_PWD,
+ PMBUS_REG_MFR_CONFIG_ACTIVATE,
+ PMBUS_REG_MFR_CONFIG_RECOVER,
+ PMBUS_REG_MFR_OTP_DUMP,
+ PMBUS_REG_MFR_BBR_EN,
+ PMBUS_REG_MFR_DPM_MIN,
+ PMBUS_REG_MFR_VBOOT,
+ PMBUS_REG_MFR_PRECLAMP_OFFSET,
+ PMBUS_REG_MFR_TLVR_DIAGN,
+ PMBUS_REG_MFR_READ_VSYS,
+ PMBUS_REG_MFR_SPECIFIC_E9,
+ PMBUS_REG_MFR_SPECIFIC_EA,
+ PMBUS_REG_MFR_SS_CBC,
+ PMBUS_REG_MFR_AMD_STATUS,
+ PMBUS_REG_MFR_CHECKSUM,
+ PMBUS_REG_CSE_INDEX,
+ PMBUS_REG_COUT_MEASURE,
+ PMBUS_REG_VR_COUT,
+ PMBUS_REG_BBR_RAM,
+ PMBUS_REG_BBR_OTP,
+ PMBUS_REG_READ_PSYS,
+ PMBUS_REG_POSTCLAMP_OFFSET,
+ PMBUS_REG_PGOOD_DELAY,
+ PMBUS_REG_MFR_SPECIFIC_F8,
+ PMBUS_REG_MFR_SPECIFIC_F9,
+ PMBUS_REG_MFR_PWD_PROGRAM_RAM,
+ PMBUS_REG_MFR_PWD_PROGRAM_I2C,
+ PMBUS_REG_MFR_PWD_ENABLE_OTP_STORE,
+};
+
+/* Following registers expect block read */
+static const u8 blk_rd_cc[] = {
+ PMBUS_SMBALERT_MASK,
+ PMBUS_MFR_DATE,
+ PMBUS_IC_DEVICE_REV,
+};
+
+struct fd5121_data {
+ struct attribute_group *groups[3];
+ struct pmbus_driver_info info;
+ struct device *dev;
+ u32 id;
+};
+
+static s32 fd5121_read_block_data(const struct i2c_client *client,
+ u8 cmd_code, u8 len, u8 *pbuf)
+{
+ s32 ret = 0;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_BLOCK_DATA)) {
+
+ /* Payload length is in the first byte. */
+ ret = i2c_smbus_read_i2c_block_data(client, cmd_code,
+ len, pbuf);
+ if (ret < 0)
+ return ret;
+ ret = pbuf[0];
+ if (ret > len)
+ ret = len;
+ for (int idx = 0; idx < ret; idx++)
+ pbuf[idx] = pbuf[idx + 1];
+ return ret;
+ }
+ ret = i2c_smbus_read_block_data(client, cmd_code, pbuf);
+ if (ret < 0)
+ return ret;
+ return min_t(s32, ret, len);
+}
+
+/* Command code that expects block read, not word read */
+static bool fd5121_blk_rd_reg(u8 cmd_code)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(blk_rd_cc); i++) {
+ if (cmd_code == blk_rd_cc[i])
+ return true;
+ }
+ return false;
+}
+
+static ssize_t fd5121_send_byte_store(struct device *dev,
+ struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ u8 val = 0;
+ int ret;
+
+ ret = kstrtou8(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+ ret = i2c_smbus_write_byte(client, val);
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
+static int fd5121_config_activate(struct i2c_client *client)
+{
+ return i2c_smbus_write_byte_data(client,
+ PMBUS_REG_MFR_CONFIG_ACTIVATE, 0xAA);
+}
+
+static ssize_t fd5121_byte_store(struct device *dev,
+ struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ u8 reg = attr->index;
+ int ret = 0;
+ u8 val = 0;
+
+ switch (reg) {
+ case PMBUS_REG_MFR_CONFIG_ACTIVATE:
+ ret = fd5121_config_activate(client);
+ if (ret < 0)
+ return ret;
+ return count;
+ default:
+ ret = kstrtou8(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+ break;
+ }
+ if (reg == PMBUS_PAGE && ((val != 0 && val != 1 &&
+ val != GENMASK(7, 0))))
+ return -EINVAL;
+ ret = i2c_smbus_write_byte_data(client, reg, val);
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
+static ssize_t fd5121_byte_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ u8 reg = attr->index;
+ s32 ret;
+
+ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret < 0)
+ return ret;
+ return scnprintf(buf, PAGE_SIZE, "%d\n", ret & 0xFF);
+}
+
+static ssize_t fd5121_word_store(struct device *dev,
+ struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ u8 reg = attr->index;
+ s16 val = 0;
+ int ret = 0;
+
+ switch (reg) {
+ case PMBUS_REG_MFR_PWD_PROGRAM_RAM:
+ val = 0xC93F;
+ break;
+ default:
+ ret = kstrtos16(buf, 10, &val);
+ if (ret)
+ return ret;
+ break;
+ }
+ ret = i2c_smbus_write_word_data(client, reg, val);
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
+static ssize_t fd5121_word_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ u8 data[I2C_SMBUS_BLOCK_MAX] = { 0 };
+ u8 reg = attr->index;
+ s32 ret = 0;
+
+ if (fd5121_blk_rd_reg(reg)) {
+ ret = fd5121_read_block_data(client, reg, 2, data);
+ if (ret >= 0)
+ ret = get_unaligned_le16(data);
+ } else
+ ret = i2c_smbus_read_word_data(client, reg);
+ if (ret < 0)
+ return ret;
+ return scnprintf(buf, PAGE_SIZE, "%d\n", ret & 0xFFFF);
+}
+
+static s32 fd5121_write_block_data(const struct i2c_client *client,
+ u8 cmd_code, u8 len, u8 *pbuf)
+{
+ s32 ret = 0;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WRITE_BLOCK_DATA))
+ ret = i2c_smbus_write_i2c_block_data(client, cmd_code,
+ len, pbuf);
+ else
+ ret = i2c_smbus_write_block_data(client, cmd_code,
+ len, pbuf);
+ return ret;
+}
+
+static s32 fd5121_read_long(struct i2c_client *client, u8 cmd_code, u32 *pval)
+{
+ u8 buffer[I2C_SMBUS_BLOCK_MAX] = { 0 };
+ s32 ret;
+
+ ret = fd5121_read_block_data(client, cmd_code, 4, buffer);
+ if (ret < 0)
+ return ret;
+ if (ret < 4)
+ return -EIO;
+
+ *pval = get_unaligned_le32(buffer);
+ return 0;
+}
+
+static ssize_t fd5121_long_store(struct device *dev,
+ struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ u8 reg = attr->index;
+ u8 buffer[4];
+ u32 val = 0;
+ int ret = 0;
+ u8 len;
+
+ ret = kstrtou32(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ len = (u8) sizeof(buffer);
+ for (u8 i = 0; i < len; i++) {
+ buffer[i] = val & 0xFF;
+ val >>= 8;
+ }
+ ret = fd5121_write_block_data(client, reg, len, buffer);
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
+static ssize_t fd5121_long_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ u8 reg = attr->index;
+ u32 val = 0;
+ s32 ret = 0;
+
+ ret = fd5121_read_long(client, reg, &val);
+ if (ret < 0)
+ return ret;
+ return scnprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+
+static ssize_t fd5121_block_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ u8 buffer[I2C_SMBUS_BLOCK_MAX] = { 0 };
+ u8 reg = attr->index;
+ int printed = 0;
+ s32 ret = 0;
+ u8 len = 0;
+ int i = 0;
+
+ if (reg == PMBUS_REG_MFR_FAULTY_SPS) {
+ int to_print = 0;
+
+ len = 7;
+ ret = fd5121_read_block_data(client, reg, len, buffer);
+ if (ret < 0)
+ return ret;
+ printed = 0;
+ to_print = (ret < len) ? ret : len;
+ for (i = 0; i < to_print; i++)
+ printed += scnprintf(buf + printed,
+ PAGE_SIZE - printed,
+ "%02x", buffer[i]);
+ printed += scnprintf(buf + printed,
+ PAGE_SIZE - printed, "\n");
+ return printed;
+ } else if (reg == PMBUS_REG_BBR_RAM ||
+ reg == PMBUS_REG_BBR_OTP) {
+ u32 len = (reg == PMBUS_REG_BBR_OTP) ? 165 : 164;
+
+ /* Extra byte may be needed in case we need to store
+ * the length of the data
+ */
+ u8 *tmp_in = kcalloc(len+1, sizeof(u8), GFP_KERNEL);
+
+ if (tmp_in == NULL)
+ return -ENOMEM;
+ ret = fd5121_read_block_data(client, reg, len, tmp_in);
+ if (ret < 0) {
+ kfree(tmp_in);
+ return ret;
+ }
+
+ printed = 0;
+ for (i = 0; i < ret; i++)
+ printed += scnprintf(buf + printed,
+ PAGE_SIZE - printed, "%02x",
+ tmp_in[i]);
+ printed += scnprintf(buf + printed,
+ PAGE_SIZE - printed, "\n");
+
+ kfree(tmp_in);
+ return printed;
+ } else
+ return -ENODATA;
+}
+
+static SENSOR_DEVICE_ATTR_RW(page, fd5121_byte,
+ PMBUS_PAGE);
+static SENSOR_DEVICE_ATTR_RO(vout_raw, fd5121_word,
+ PMBUS_READ_VOUT);
+static SENSOR_DEVICE_ATTR_RW(operation, fd5121_byte,
+ PMBUS_OPERATION);
+static SENSOR_DEVICE_ATTR_RW(on_off_config, fd5121_byte,
+ PMBUS_ON_OFF_CONFIG);
+static SENSOR_DEVICE_ATTR_WO(clear_faults, fd5121_byte,
+ PMBUS_CLEAR_FAULTS);
+static SENSOR_DEVICE_ATTR_RW(write_protect, fd5121_byte,
+ PMBUS_WRITE_PROTECT);
+static SENSOR_DEVICE_ATTR_RO(capability, fd5121_byte,
+ PMBUS_CAPABILITY);
+static SENSOR_DEVICE_ATTR_RW(smbalert_mask, fd5121_word,
+ PMBUS_SMBALERT_MASK);
+static SENSOR_DEVICE_ATTR_RO(vout_mode, fd5121_byte,
+ PMBUS_VOUT_MODE);
+static SENSOR_DEVICE_ATTR_RW(vout_command, fd5121_word,
+ PMBUS_VOUT_COMMAND);
+static SENSOR_DEVICE_ATTR_RW(vout_max, fd5121_word,
+ PMBUS_VOUT_MAX);
+static SENSOR_DEVICE_ATTR_RW(vout_margin_high, fd5121_word,
+ PMBUS_VOUT_MARGIN_HIGH);
+static SENSOR_DEVICE_ATTR_RW(vout_margin_low, fd5121_word,
+ PMBUS_VOUT_MARGIN_LOW);
+static SENSOR_DEVICE_ATTR_RW(vout_transition_rate, fd5121_word,
+ PMBUS_VOUT_TRANSITION_RATE);
+static SENSOR_DEVICE_ATTR_RW(vout_min, fd5121_word,
+ PMBUS_REG_VOUT_MIN);
+static SENSOR_DEVICE_ATTR_RW(power_mode, fd5121_byte,
+ PMBUS_REG_POWER_MODE);
+static SENSOR_DEVICE_ATTR_RW(vin_on, fd5121_word,
+ PMBUS_REG_VIN_ON);
+static SENSOR_DEVICE_ATTR_RW(vin_off, fd5121_word,
+ PMBUS_REG_VIN_OFF);
+static SENSOR_DEVICE_ATTR_RW(vin_uv_fault_response, fd5121_byte,
+ PMBUS_REG_VIN_UV_FAULT_RESPONSE);
+static SENSOR_DEVICE_ATTR_RW(iin_oc_fault_response, fd5121_byte,
+ PMBUS_REG_IIN_OC_FAULT_RESPONSE);
+static SENSOR_DEVICE_ATTR_RW(ton_delay, fd5121_word,
+ PMBUS_REG_TON_DELAY);
+static SENSOR_DEVICE_ATTR_RW(pout_op_fault_response, fd5121_byte,
+ PMBUS_REG_POUT_OP_FAULT_RESPONSE);
+static SENSOR_DEVICE_ATTR_RO(read_vaux, fd5121_word,
+ PMBUS_REG_READ_VAUX);
+static SENSOR_DEVICE_ATTR_RW(iknee_set, fd5121_word,
+ PMBUS_REG_IKNEE_SET);
+static SENSOR_DEVICE_ATTR_RW(pin_counter, fd5121_byte,
+ PMBUS_REG_PIN_COUNTER);
+static SENSOR_DEVICE_ATTR_RW(vmin_aware, fd5121_word,
+ PMBUS_REG_VMIN_AWARE);
+static SENSOR_DEVICE_ATTR_RW(vout_ov_fault_response, fd5121_byte,
+ PMBUS_VOUT_OV_FAULT_RESPONSE);
+static SENSOR_DEVICE_ATTR_RW(vout_uv_fault_response, fd5121_byte,
+ PMBUS_VOUT_UV_FAULT_RESPONSE);
+static SENSOR_DEVICE_ATTR_RW(iout_oc_fault_response, fd5121_byte,
+ PMBUS_IOUT_OC_FAULT_RESPONSE);
+static SENSOR_DEVICE_ATTR_RW(ot_fault_response, fd5121_byte,
+ PMBUS_OT_FAULT_RESPONSE);
+static SENSOR_DEVICE_ATTR_RW(vin_ov_fault_response, fd5121_byte,
+ PMBUS_VIN_OV_FAULT_RESPONSE);
+static SENSOR_DEVICE_ATTR_RW(vaux_uv_fault_limit, fd5121_word,
+ PMBUS_REG_VAUX_UV_FAULT_LIMIT);
+static SENSOR_DEVICE_ATTR_RW(vaux_ov_fault_limit, fd5121_word,
+ PMBUS_REG_VAUX_OV_FAULT_LIMIT);
+static SENSOR_DEVICE_ATTR_RW(vaux_uv_fault_response, fd5121_byte,
+ PMBUS_REG_VAUX_UV_FAULT_RESPONSE);
+static SENSOR_DEVICE_ATTR_RW(vaux_ov_fault_response, fd5121_byte,
+ PMBUS_REG_VAUX_OV_FAULT_RESPONSE);
+static SENSOR_DEVICE_ATTR_RW(vaux_uv_warning, fd5121_word,
+ PMBUS_REG_VAUX_UV_WARNING);
+static SENSOR_DEVICE_ATTR_RW(vaux_ov_warning, fd5121_word,
+ PMBUS_REG_VAUX_OV_WARNING);
+static SENSOR_DEVICE_ATTR_RO(free_user_config_tables, fd5121_byte,
+ PMBUS_REG_MFR_FREE_USER_CONFIG_TABLES);
+static SENSOR_DEVICE_ATTR_RW(address_table, fd5121_byte,
+ PMBUS_REG_MFR_ADDRESS_TABLE);
+static SENSOR_DEVICE_ATTR_RW(status_onsemi, fd5121_word,
+ PMBUS_REG_MFR_STATUS_ONSEMI);
+static SENSOR_DEVICE_ATTR_RO(status_byte, fd5121_byte,
+ PMBUS_STATUS_BYTE);
+static SENSOR_DEVICE_ATTR_RO(status_cml, fd5121_byte,
+ PMBUS_STATUS_CML);
+static SENSOR_DEVICE_ATTR_RO(status_other, fd5121_byte,
+ PMBUS_STATUS_OTHER);
+static SENSOR_DEVICE_ATTR_RO(status_mfr_specific, fd5121_byte,
+ PMBUS_STATUS_MFR_SPECIFIC);
+static SENSOR_DEVICE_ATTR_RO(revision, fd5121_byte,
+ PMBUS_REVISION);
+static SENSOR_DEVICE_ATTR_RO(id, fd5121_long,
+ PMBUS_MFR_ID);
+static SENSOR_DEVICE_ATTR_RO(model, fd5121_long,
+ PMBUS_MFR_MODEL);
+static SENSOR_DEVICE_ATTR_RO(mfr_revision, fd5121_long,
+ PMBUS_MFR_REVISION);
+static SENSOR_DEVICE_ATTR_RW(date, fd5121_word,
+ PMBUS_MFR_DATE);
+static SENSOR_DEVICE_ATTR_RO(ic_device_id, fd5121_long,
+ PMBUS_IC_DEVICE_ID);
+static SENSOR_DEVICE_ATTR_RO(ic_device_rev, fd5121_word,
+ PMBUS_IC_DEVICE_REV);
+static SENSOR_DEVICE_ATTR_WO(unlock, fd5121_byte,
+ PMBUS_REG_MFR_UNLOCK);
+static SENSOR_DEVICE_ATTR_RO(faulty_sps, fd5121_block,
+ PMBUS_REG_MFR_FAULTY_SPS);
+static SENSOR_DEVICE_ATTR_RO(tlvr_faults, fd5121_word,
+ PMBUS_REG_TLVR_FAULTS);
+static SENSOR_DEVICE_ATTR_RW(user_store_config_tab, fd5121_byte,
+ PMBUS_REG_MFR_USER_STORE_CONFIG_TAB);
+static SENSOR_DEVICE_ATTR_RO(user_config_index, fd5121_byte,
+ PMBUS_REG_MFR_USER_CONFIG_INDEX);
+static SENSOR_DEVICE_ATTR_RO(pwm_disconnection, fd5121_word,
+ PMBUS_REG_MFR_PWM_DISCONNECTION);
+static SENSOR_DEVICE_ATTR_RO(vr_disconnection, fd5121_byte,
+ PMBUS_REG_MFR_VR_DISCONNECTION);
+static SENSOR_DEVICE_ATTR_RW(ton_slew, fd5121_byte,
+ PMBUS_REG_MFR_TON_SLEW);
+static SENSOR_DEVICE_ATTR_RW(toff_slew, fd5121_byte,
+ PMBUS_REG_MFR_TOFF_SLEW);
+static SENSOR_DEVICE_ATTR_RW(rail_name, fd5121_word,
+ PMBUS_REG_MFR_RAIL_NAME);
+static SENSOR_DEVICE_ATTR_RW(vout_droop, fd5121_byte,
+ PMBUS_REG_MFR_VOUT_DROOP);
+static SENSOR_DEVICE_ATTR_WO(svr_go, fd5121_send_byte,
+ PMBUS_REG_MFR_SVR_GO);
+static SENSOR_DEVICE_ATTR_RW(user_restore_config_tab, fd5121_byte,
+ PMBUS_REG_MFR_USER_RESTORE_CONFIG_TAB);
+static SENSOR_DEVICE_ATTR_WO(set_pwd, fd5121_byte,
+ PMBUS_REG_MFR_SET_PWD);
+static SENSOR_DEVICE_ATTR_RW(config_activate, fd5121_byte,
+ PMBUS_REG_MFR_CONFIG_ACTIVATE);
+static SENSOR_DEVICE_ATTR_RW(config_recover, fd5121_byte,
+ PMBUS_REG_MFR_CONFIG_RECOVER);
+static SENSOR_DEVICE_ATTR_RW(otp_dump, fd5121_byte,
+ PMBUS_REG_MFR_OTP_DUMP);
+static SENSOR_DEVICE_ATTR_RW(bbr_en, fd5121_byte,
+ PMBUS_REG_MFR_BBR_EN);
+static SENSOR_DEVICE_ATTR_RW(dpm_min, fd5121_byte,
+ PMBUS_REG_MFR_DPM_MIN);
+static SENSOR_DEVICE_ATTR_RW(vboot, fd5121_word,
+ PMBUS_REG_MFR_VBOOT);
+static SENSOR_DEVICE_ATTR_RW(preclamp_offset, fd5121_word,
+ PMBUS_REG_MFR_PRECLAMP_OFFSET);
+static SENSOR_DEVICE_ATTR_RW(tlvr_diagn, fd5121_word,
+ PMBUS_REG_MFR_TLVR_DIAGN);
+static SENSOR_DEVICE_ATTR_RO(vsys, fd5121_word,
+ PMBUS_REG_MFR_READ_VSYS);
+static SENSOR_DEVICE_ATTR_RW(specific_e9, fd5121_word,
+ PMBUS_REG_MFR_SPECIFIC_E9);
+static SENSOR_DEVICE_ATTR_RW(specific_ea, fd5121_long,
+ PMBUS_REG_MFR_SPECIFIC_EA);
+static SENSOR_DEVICE_ATTR_RO(ss_cbc, fd5121_word,
+ PMBUS_REG_MFR_SS_CBC);
+static SENSOR_DEVICE_ATTR_RO(amd_status, fd5121_byte,
+ PMBUS_REG_MFR_AMD_STATUS);
+static SENSOR_DEVICE_ATTR_RO(checksum, fd5121_word,
+ PMBUS_REG_MFR_CHECKSUM);
+static SENSOR_DEVICE_ATTR_RO(cse_index, fd5121_word,
+ PMBUS_REG_CSE_INDEX);
+static SENSOR_DEVICE_ATTR_RW(cout_measure, fd5121_word,
+ PMBUS_REG_COUT_MEASURE);
+static SENSOR_DEVICE_ATTR_RO(vr_cout, fd5121_word,
+ PMBUS_REG_VR_COUT);
+static SENSOR_DEVICE_ATTR_RO(bbr_ram, fd5121_block,
+ PMBUS_REG_BBR_RAM);
+static SENSOR_DEVICE_ATTR_RO(bbr_otp, fd5121_block,
+ PMBUS_REG_BBR_OTP);
+static SENSOR_DEVICE_ATTR_RO(psys, fd5121_word,
+ PMBUS_REG_READ_PSYS);
+static SENSOR_DEVICE_ATTR_RW(postclamp_offset, fd5121_word,
+ PMBUS_REG_POSTCLAMP_OFFSET);
+static SENSOR_DEVICE_ATTR_RW(pgood_delay, fd5121_byte,
+ PMBUS_REG_PGOOD_DELAY);
+static SENSOR_DEVICE_ATTR_RW(specific_f8, fd5121_word,
+ PMBUS_REG_MFR_SPECIFIC_F8);
+static SENSOR_DEVICE_ATTR_RW(specific_f9, fd5121_long,
+ PMBUS_REG_MFR_SPECIFIC_F9);
+static SENSOR_DEVICE_ATTR_RW(pwd_program_ram, fd5121_word,
+ PMBUS_REG_MFR_PWD_PROGRAM_RAM);
+static SENSOR_DEVICE_ATTR_RW(pwd_program_i2c, fd5121_word,
+ PMBUS_REG_MFR_PWD_PROGRAM_I2C);
+static SENSOR_DEVICE_ATTR_RW(pwd_enable_otp_store, fd5121_word,
+ PMBUS_REG_MFR_PWD_ENABLE_OTP_STORE);
+
+static struct attribute *fd5121_non_paged_attrs[] = {
+ &sensor_dev_attr_page.dev_attr.attr,
+ &sensor_dev_attr_capability.dev_attr.attr,
+ &sensor_dev_attr_pin_counter.dev_attr.attr,
+ &sensor_dev_attr_vaux_uv_fault_limit.dev_attr.attr,
+ &sensor_dev_attr_vaux_ov_fault_limit.dev_attr.attr,
+ &sensor_dev_attr_vaux_uv_warning.dev_attr.attr,
+ &sensor_dev_attr_vaux_ov_warning.dev_attr.attr,
+ &sensor_dev_attr_free_user_config_tables.dev_attr.attr,
+ &sensor_dev_attr_address_table.dev_attr.attr,
+ &sensor_dev_attr_unlock.dev_attr.attr,
+ &sensor_dev_attr_faulty_sps.dev_attr.attr,
+ &sensor_dev_attr_tlvr_faults.dev_attr.attr,
+ &sensor_dev_attr_user_store_config_tab.dev_attr.attr,
+ &sensor_dev_attr_user_config_index.dev_attr.attr,
+ &sensor_dev_attr_pwm_disconnection.dev_attr.attr,
+ &sensor_dev_attr_vr_disconnection.dev_attr.attr,
+ &sensor_dev_attr_user_restore_config_tab.dev_attr.attr,
+ &sensor_dev_attr_svr_go.dev_attr.attr,
+ &sensor_dev_attr_set_pwd.dev_attr.attr,
+ &sensor_dev_attr_config_activate.dev_attr.attr,
+ &sensor_dev_attr_config_recover.dev_attr.attr,
+ &sensor_dev_attr_otp_dump.dev_attr.attr,
+ &sensor_dev_attr_bbr_en.dev_attr.attr,
+ &sensor_dev_attr_vboot.dev_attr.attr,
+ &sensor_dev_attr_vsys.dev_attr.attr,
+ &sensor_dev_attr_specific_e9.dev_attr.attr,
+ &sensor_dev_attr_specific_ea.dev_attr.attr,
+ &sensor_dev_attr_ss_cbc.dev_attr.attr,
+ &sensor_dev_attr_checksum.dev_attr.attr,
+ &sensor_dev_attr_cse_index.dev_attr.attr,
+ &sensor_dev_attr_cout_measure.dev_attr.attr,
+ &sensor_dev_attr_vr_cout.dev_attr.attr,
+ &sensor_dev_attr_bbr_ram.dev_attr.attr,
+ &sensor_dev_attr_bbr_otp.dev_attr.attr,
+ &sensor_dev_attr_psys.dev_attr.attr,
+ &sensor_dev_attr_specific_f8.dev_attr.attr,
+ &sensor_dev_attr_specific_f9.dev_attr.attr,
+ &sensor_dev_attr_pwd_program_ram.dev_attr.attr,
+ &sensor_dev_attr_pwd_program_i2c.dev_attr.attr,
+ &sensor_dev_attr_pwd_enable_otp_store.dev_attr.attr,
+ &sensor_dev_attr_revision.dev_attr.attr,
+ &sensor_dev_attr_id.dev_attr.attr,
+ &sensor_dev_attr_model.dev_attr.attr,
+ &sensor_dev_attr_mfr_revision.dev_attr.attr,
+ &sensor_dev_attr_date.dev_attr.attr,
+ &sensor_dev_attr_ic_device_id.dev_attr.attr,
+ &sensor_dev_attr_ic_device_rev.dev_attr.attr,
+ NULL
+};
+
+static struct attribute *fd5121_paged_attrs[] = {
+ &sensor_dev_attr_operation.dev_attr.attr,
+ &sensor_dev_attr_vout_raw.dev_attr.attr,
+ &sensor_dev_attr_on_off_config.dev_attr.attr,
+ &sensor_dev_attr_clear_faults.dev_attr.attr,
+ &sensor_dev_attr_write_protect.dev_attr.attr,
+ &sensor_dev_attr_smbalert_mask.dev_attr.attr,
+ &sensor_dev_attr_vout_mode.dev_attr.attr,
+ &sensor_dev_attr_vout_command.dev_attr.attr,
+ &sensor_dev_attr_vout_margin_high.dev_attr.attr,
+ &sensor_dev_attr_vout_margin_low.dev_attr.attr,
+ &sensor_dev_attr_vout_min.dev_attr.attr,
+ &sensor_dev_attr_vin_on.dev_attr.attr,
+ &sensor_dev_attr_vin_off.dev_attr.attr,
+ &sensor_dev_attr_vout_ov_fault_response.dev_attr.attr,
+ &sensor_dev_attr_vout_uv_fault_response.dev_attr.attr,
+ &sensor_dev_attr_iout_oc_fault_response.dev_attr.attr,
+ &sensor_dev_attr_ot_fault_response.dev_attr.attr,
+ &sensor_dev_attr_vin_ov_fault_response.dev_attr.attr,
+ &sensor_dev_attr_status_byte.dev_attr.attr,
+ &sensor_dev_attr_iknee_set.dev_attr.attr,
+ &sensor_dev_attr_vmin_aware.dev_attr.attr,
+ &sensor_dev_attr_power_mode.dev_attr.attr,
+ &sensor_dev_attr_vin_uv_fault_response.dev_attr.attr,
+ &sensor_dev_attr_iin_oc_fault_response.dev_attr.attr,
+ &sensor_dev_attr_ton_delay.dev_attr.attr,
+ &sensor_dev_attr_pout_op_fault_response.dev_attr.attr,
+ &sensor_dev_attr_vaux_uv_fault_response.dev_attr.attr,
+ &sensor_dev_attr_vaux_ov_fault_response.dev_attr.attr,
+ &sensor_dev_attr_status_onsemi.dev_attr.attr,
+ &sensor_dev_attr_status_cml.dev_attr.attr,
+ &sensor_dev_attr_status_other.dev_attr.attr,
+ &sensor_dev_attr_status_mfr_specific.dev_attr.attr,
+ &sensor_dev_attr_read_vaux.dev_attr.attr,
+ &sensor_dev_attr_ton_slew.dev_attr.attr,
+ &sensor_dev_attr_toff_slew.dev_attr.attr,
+ &sensor_dev_attr_rail_name.dev_attr.attr,
+ &sensor_dev_attr_vout_droop.dev_attr.attr,
+ &sensor_dev_attr_dpm_min.dev_attr.attr,
+ &sensor_dev_attr_preclamp_offset.dev_attr.attr,
+ &sensor_dev_attr_tlvr_diagn.dev_attr.attr,
+ &sensor_dev_attr_amd_status.dev_attr.attr,
+ &sensor_dev_attr_postclamp_offset.dev_attr.attr,
+ &sensor_dev_attr_pgood_delay.dev_attr.attr,
+ &sensor_dev_attr_vout_max.dev_attr.attr,
+ &sensor_dev_attr_vout_transition_rate.dev_attr.attr,
+ NULL
+};
+
+static struct attribute_group fd5121_groups[2] = {
+ { .name = "global", .attrs = fd5121_non_paged_attrs },
+ { .name = "paged", .attrs = fd5121_paged_attrs }
+};
+
+/* Regulator descriptors for VOUT rails (VID encoded) */
+static struct regulator_desc fd5121_reg_desc[] = {
+ PMBUS_REGULATOR_STEP_ONE("vout1", 3001, 1000, 200000),
+ PMBUS_REGULATOR_STEP_ONE("vout2", 3001, 1000, 200000),
+};
+
+static int fd5121_valid_reg(struct i2c_client *client, int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cc_list); i++) {
+ if (reg == cc_list[i])
+ return 0;
+ }
+
+ if (fd5121_blk_rd_reg(reg))
+ return 0;
+ return -ENXIO;
+}
+
+static int fd5121_read_word_data(struct i2c_client *client, int page,
+ int phase, int reg)
+{
+ int ret;
+
+ ret = fd5121_valid_reg(client, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = pmbus_read_word_data(client, page, phase, reg);
+ if (ret < 0)
+ return ret;
+
+ /* Chip reports VOUT_MODE as vid. But gives raw value 1mV per bit.
+ * So, encode the READ_VOUT value so that it gets decoded and
+ * reported correctly.
+ */
+ if (reg == PMBUS_READ_VOUT)
+ ret = DIV_ROUND_CLOSEST(155000 - ret * 100, 625);
+ return ret;
+}
+
+static int fd5121_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ int ret;
+
+ ret = fd5121_valid_reg(client, reg);
+ if (ret < 0)
+ return ret;
+
+ return pmbus_read_byte_data(client, page, reg);
+}
+
+static int fd5121_write_byte_data(struct i2c_client *client, int page,
+ int reg, u8 value)
+{
+ int ret;
+
+ ret = fd5121_valid_reg(client, reg);
+ if (ret < 0)
+ return ret;
+ return pmbus_write_byte_data(client, page, reg, value);
+}
+
+static int fd5121_write_byte(struct i2c_client *client, int page, u8 byte)
+{
+ return pmbus_write_byte(client, page, byte);
+}
+
+static int fd5121_write_word_data(struct i2c_client *client, int page,
+ int reg, u16 word)
+{
+ int ret;
+
+ ret = fd5121_valid_reg(client, reg);
+ if (ret < 0)
+ return ret;
+ ret = pmbus_write_word_data(client, page, reg, word);
+ return ret;
+}
+
+static u32 fd5121_get_dev_id(struct i2c_client *client)
+{
+ u32 dev_id = 0;
+ s32 ret = 0;
+
+ ret = fd5121_read_long(client, PMBUS_IC_DEVICE_ID, &dev_id);
+ if (ret < 0)
+ return CTLR_ID_UNKNOWN;
+
+ switch (dev_id) {
+ case CTLR_ID_FD5121:
+ case CTLR_ID_FD5123:
+ case CTLR_ID_FD5125:
+ break;
+ default:
+ if (dev_id != 0)
+ dev_err(&client->dev, "Unknown device 0x%x",
+ dev_id);
+ return CTLR_ID_UNKNOWN;
+ }
+ return dev_id;
+}
+
+static int fd5121_probe(struct i2c_client *client)
+{
+ struct pmbus_driver_info *info;
+ struct fd5121_data *pdata;
+ u32 id;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -EOPNOTSUPP;
+
+ pdata = devm_kzalloc(&client->dev, sizeof(struct fd5121_data),
+ GFP_KERNEL);
+ if (pdata == NULL)
+ return -ENOMEM;
+
+ pdata->dev = &client->dev;
+ pdata->groups[0] = &fd5121_groups[0];
+ pdata->groups[1] = &fd5121_groups[1];
+
+ id = fd5121_get_dev_id(client);
+ if (id == CTLR_ID_UNKNOWN)
+ return -ENODEV;
+
+ pdata->id = id;
+
+ switch (id) {
+ case CTLR_ID_FD5121:
+ case CTLR_ID_FD5123:
+ case CTLR_ID_FD5125:
+ break;
+ default:
+ dev_err(&client->dev, "Failed to read device ID");
+ return -ENODEV;
+ }
+
+ info = &pdata->info;
+ info->groups = (const struct attribute_group **)&pdata->groups[0];
+ info->write_word_data = fd5121_write_word_data;
+ info->write_byte = fd5121_write_byte;
+ info->write_byte_data = fd5121_write_byte_data;
+ info->read_word_data = fd5121_read_word_data;
+ info->read_byte_data = fd5121_read_byte_data;
+
+ info->pages = FD5121_NUM_PAGES;
+ info->format[PSC_VOLTAGE_IN] = linear;
+ info->format[PSC_VOLTAGE_OUT] = vid;
+
+ fd5121_reg_desc[0].id = 0;
+ fd5121_reg_desc[1].id = 1;
+
+ /* Device implements VID coding with 1 mV steps from 0.200 V
+ * up to 3.200 V
+ */
+ info->num_regulators = FD5121_NUM_PAGES;
+ info->reg_desc = fd5121_reg_desc;
+ info->format[PSC_CURRENT_IN] = linear;
+ info->format[PSC_CURRENT_OUT] = linear;
+ info->format[PSC_POWER] = linear;
+ info->format[PSC_TEMPERATURE] = linear;
+ for (u8 idx = 0; idx < info->pages; idx++) {
+ info->func[idx] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT;
+ info->func[idx] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
+ info->func[idx] |= PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
+ info->func[idx] |= PMBUS_HAVE_PIN | PMBUS_HAVE_POUT;
+ info->func[idx] |= PMBUS_HAVE_VIN | PMBUS_HAVE_IIN;
+ info->func[idx] |= PMBUS_HAVE_STATUS_INPUT;
+ info->vrm_version[idx] = amd625mv;
+ }
+ return pmbus_do_probe(client, info);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id fd5121_of_match[] = {
+ { .compatible = "onnn,fd5121" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, fd5121_of_match);
+#endif
+
+static const struct i2c_device_id fd5121_id[] = {
+ { "fd5121", chip_fd5121 },
+ { "fd5123", chip_fd5123 },
+ { "fd5125", chip_fd5125 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, fd5121_id);
+
+static struct i2c_driver fd5121_driver = {
+ .driver = {
+ .name = "fd5121",
+ .of_match_table = of_match_ptr(fd5121_of_match),
+ },
+ .probe = fd5121_probe,
+ .id_table = fd5121_id,
+};
+
+module_i2c_driver(fd5121_driver);
+
+MODULE_AUTHOR("Selva Rajagopal <selvamani.rajagopal@onsemi.com>");
+MODULE_DESCRIPTION("PMBus driver for FD5121");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("PMBUS");
+
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 3/3] hwmon: (pmbus/fd5121): Add support FD5121, FD5123 and FD5125
@ 2026-06-23 5:55 ` Selvamani Rajagopal
0 siblings, 0 replies; 11+ messages in thread
From: Selvamani Rajagopal @ 2026-06-23 5:55 UTC (permalink / raw)
To: Guenter Roeck, Jonathan Corbet, Shuah Khan, Selva Rajagopal,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-hwmon, linux-doc, linux-kernel, devicetree,
Selvamani Rajagopal
FD5121 is a dual-rail, multi-phase, digital controller that offers
full telemtry options including input/output voltage, current as
well as fault handling and identifications.
These controllers are compliant with PMBus specification.
Signed-off-by: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>
---
MAINTAINERS | 8 +
drivers/hwmon/pmbus/Kconfig | 9 +
drivers/hwmon/pmbus/Makefile | 1 +
drivers/hwmon/pmbus/fd5121.c | 1004 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 1022 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index d95d3ef77773..c0664c33324a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -20135,6 +20135,14 @@ L: linux-mips@vger.kernel.org
S: Maintained
F: arch/mips/boot/dts/ralink/omega2p.dts
+ONSEMI HARDWARE MONITOR DRIVER
+M: Selva Rajagopal <selvamani.rajagopal@onsemi.com>
+L: linux-hwmon@vger.kernel.org
+S: Supported
+W: https://www.onsemi.com
+F: Documentation/devicetree/bindings/hwmon/pmbus/onnn,fd5121.yaml
+F: drivers/hwmon/pmbus/fd5121.c
+
ONSEMI ETHERNET PHY DRIVERS
M: Piergiorgio Beruto <piergiorgio.beruto@gmail.com>
L: netdev@vger.kernel.org
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index c8cda160b5f8..3a06ed83539e 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -179,6 +179,15 @@ config SENSORS_E50SN12051
This driver can also be built as a module. If so, the module will
be called e50sn12051.
+config SENSORS_FD5121
+ tristate "FD5121/FD5123/FD5125 controllers from onsemi"
+ help
+ If you say yes here, you get support for onsemi
+ controllers FD5121, FD5123, FD5125.
+
+ This driver can also be built as a module. If so, the module will
+ be called fd5121.
+
config SENSORS_INA233
tristate "Texas Instruments INA233 and compatibles"
help
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index ffc05f493213..70f4afb41fe0 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_FD5121) += fd5121.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/fd5121.c b/drivers/hwmon/pmbus/fd5121.c
new file mode 100644
index 000000000000..e68c6d6cabbd
--- /dev/null
+++ b/drivers/hwmon/pmbus/fd5121.c
@@ -0,0 +1,1004 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2026 Semiconductor Components Industries, LLC ("onsemi").
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/unaligned.h>
+
+#include "pmbus.h"
+
+enum chips { chip_fd5121, chip_fd5123, chip_fd5125 };
+
+#define CTLR_ID_UNKNOWN 0
+#define CTLR_ID_FD5121 0xFD5121
+#define CTLR_ID_FD5123 0xFD5123
+#define CTLR_ID_FD5125 0xFD5125
+
+#define FD5121_NUM_PAGES 2
+
+/* Custom PMBUS commands */
+#define PMBUS_REG_VOUT_MIN 0x2B
+#define PMBUS_REG_POWER_MODE 0x34
+#define PMBUS_REG_VIN_ON 0x35
+#define PMBUS_REG_VIN_OFF 0x36
+#define PMBUS_REG_VIN_UV_FAULT_RESPONSE 0x5A
+#define PMBUS_REG_IIN_OC_FAULT_RESPONSE 0x5C
+#define PMBUS_REG_TON_DELAY 0x60
+#define PMBUS_REG_POUT_OP_FAULT_RESPONSE 0x69
+#define PMBUS_REG_READ_VAUX 0x85
+
+#define PMBUS_REG_IKNEE_SET 0x2D
+#define PMBUS_REG_PIN_COUNTER 0x2E
+#define PMBUS_REG_VMIN_AWARE 0x2F
+#define PMBUS_REG_VAUX_UV_FAULT_LIMIT 0x6C
+#define PMBUS_REG_VAUX_OV_FAULT_LIMIT 0x6D
+#define PMBUS_REG_VAUX_UV_FAULT_RESPONSE 0x6E
+#define PMBUS_REG_VAUX_OV_FAULT_RESPONSE 0x6F
+#define PMBUS_REG_VAUX_UV_WARNING 0x75
+#define PMBUS_REG_VAUX_OV_WARNING 0x76
+#define PMBUS_REG_MFR_FREE_USER_CONFIG_TABLES 0xCF
+#define PMBUS_REG_MFR_ADDRESS_TABLE 0xD0
+#define PMBUS_REG_MFR_STATUS_ONSEMI 0xD1
+#define PMBUS_REG_MFR_UNLOCK 0xD2
+#define PMBUS_REG_MFR_FAULTY_SPS 0xD3
+#define PMBUS_REG_TLVR_FAULTS 0xD4
+#define PMBUS_REG_MFR_USER_STORE_CONFIG_TAB 0xD5
+#define PMBUS_REG_MFR_USER_CONFIG_INDEX 0xD6
+#define PMBUS_REG_MFR_PWM_DISCONNECTION 0xD7
+#define PMBUS_REG_MFR_VR_DISCONNECTION 0xD8
+#define PMBUS_REG_MFR_TON_SLEW 0xD9
+#define PMBUS_REG_MFR_TOFF_SLEW 0xDA
+#define PMBUS_REG_MFR_RAIL_NAME 0xDB
+#define PMBUS_REG_MFR_VOUT_DROOP 0xDC
+#define PMBUS_REG_MFR_USER_RESTORE_CONFIG_TAB 0xDD
+#define PMBUS_REG_MFR_SVR_GO 0xDE
+#define PMBUS_REG_MFR_SET_PWD 0xDF
+#define PMBUS_REG_MFR_CONFIG_ACTIVATE 0xE0
+#define PMBUS_REG_MFR_CONFIG_RECOVER 0xE1
+#define PMBUS_REG_MFR_OTP_DUMP 0xE2
+#define PMBUS_REG_MFR_BBR_EN 0xE3
+#define PMBUS_REG_MFR_DPM_MIN 0xE4
+#define PMBUS_REG_MFR_VBOOT 0xE5
+#define PMBUS_REG_MFR_PRECLAMP_OFFSET 0xE6
+#define PMBUS_REG_MFR_TLVR_DIAGN 0xE7
+#define PMBUS_REG_MFR_READ_VSYS 0xE8
+#define PMBUS_REG_MFR_SPECIFIC_E9 0xE9
+#define PMBUS_REG_MFR_SPECIFIC_EA 0xEA
+#define PMBUS_REG_MFR_SS_CBC 0xEB
+#define PMBUS_REG_MFR_AMD_STATUS 0xEC
+#define PMBUS_REG_MFR_CHECKSUM 0xEE
+#define PMBUS_REG_CSE_INDEX 0xF0
+#define PMBUS_REG_COUT_MEASURE 0xF1
+#define PMBUS_REG_VR_COUT 0xF2
+#define PMBUS_REG_BBR_RAM 0xF3
+#define PMBUS_REG_BBR_OTP 0xF4
+#define PMBUS_REG_READ_PSYS 0xF5
+#define PMBUS_REG_POSTCLAMP_OFFSET 0xF6
+#define PMBUS_REG_PGOOD_DELAY 0xF7
+#define PMBUS_REG_MFR_SPECIFIC_F8 0xF8
+#define PMBUS_REG_MFR_SPECIFIC_F9 0xF9
+#define PMBUS_REG_MFR_PWD_PROGRAM_RAM 0xFA
+#define PMBUS_REG_MFR_PWD_PROGRAM_I2C 0xFB
+#define PMBUS_REG_MFR_PWD_ENABLE_OTP_STORE 0xFC
+
+/* List of recognized commands */
+static const u8 cc_list[] = {
+ PMBUS_PAGE,
+ PMBUS_OPERATION,
+ PMBUS_ON_OFF_CONFIG,
+ PMBUS_CLEAR_FAULTS,
+ PMBUS_WRITE_PROTECT,
+ PMBUS_CAPABILITY,
+ PMBUS_VOUT_MODE,
+ PMBUS_VOUT_COMMAND,
+ PMBUS_VOUT_MAX,
+ PMBUS_VOUT_MARGIN_HIGH,
+ PMBUS_VOUT_MARGIN_LOW,
+ PMBUS_VOUT_TRANSITION_RATE,
+ PMBUS_REG_VOUT_MIN,
+ PMBUS_REG_IKNEE_SET,
+ PMBUS_REG_PIN_COUNTER,
+ PMBUS_REG_VMIN_AWARE,
+ PMBUS_REG_POWER_MODE,
+ PMBUS_REG_VIN_ON,
+ PMBUS_REG_VIN_OFF,
+ PMBUS_VOUT_OV_FAULT_LIMIT,
+ PMBUS_VOUT_OV_FAULT_RESPONSE,
+ PMBUS_VOUT_UV_FAULT_LIMIT,
+ PMBUS_VOUT_UV_FAULT_RESPONSE,
+ PMBUS_IOUT_OC_FAULT_LIMIT,
+ PMBUS_IOUT_OC_FAULT_RESPONSE,
+ PMBUS_IOUT_OC_WARN_LIMIT,
+ PMBUS_OT_FAULT_LIMIT,
+ PMBUS_OT_FAULT_RESPONSE,
+ PMBUS_OT_WARN_LIMIT,
+ PMBUS_VIN_OV_FAULT_LIMIT,
+ PMBUS_VIN_OV_FAULT_RESPONSE,
+ PMBUS_VIN_OV_WARN_LIMIT,
+ PMBUS_VIN_UV_WARN_LIMIT,
+ PMBUS_VIN_UV_FAULT_LIMIT,
+ PMBUS_REG_VIN_UV_FAULT_RESPONSE,
+ PMBUS_IIN_OC_FAULT_LIMIT,
+ PMBUS_REG_IIN_OC_FAULT_RESPONSE,
+ PMBUS_IIN_OC_WARN_LIMIT,
+ PMBUS_REG_TON_DELAY,
+ PMBUS_POUT_OP_FAULT_LIMIT,
+ PMBUS_REG_POUT_OP_FAULT_RESPONSE,
+ PMBUS_POUT_OP_WARN_LIMIT,
+ PMBUS_PIN_OP_WARN_LIMIT,
+ PMBUS_REG_VAUX_UV_FAULT_LIMIT,
+ PMBUS_REG_VAUX_OV_FAULT_LIMIT,
+ PMBUS_REG_VAUX_UV_FAULT_RESPONSE,
+ PMBUS_REG_VAUX_OV_FAULT_RESPONSE,
+ PMBUS_REG_VAUX_UV_WARNING,
+ PMBUS_REG_VAUX_OV_WARNING,
+ PMBUS_STATUS_BYTE,
+ PMBUS_STATUS_WORD,
+ PMBUS_STATUS_VOUT,
+ PMBUS_STATUS_IOUT,
+ PMBUS_STATUS_INPUT,
+ PMBUS_STATUS_TEMPERATURE,
+ PMBUS_STATUS_CML,
+ PMBUS_STATUS_OTHER,
+ PMBUS_STATUS_MFR_SPECIFIC,
+ PMBUS_REG_READ_VAUX,
+ PMBUS_READ_VIN,
+ PMBUS_READ_IIN,
+ PMBUS_READ_VOUT,
+ PMBUS_READ_IOUT,
+ PMBUS_READ_TEMPERATURE_1,
+ PMBUS_READ_POUT,
+ PMBUS_READ_PIN,
+ PMBUS_REVISION,
+ PMBUS_MFR_ID,
+ PMBUS_MFR_MODEL,
+ PMBUS_MFR_REVISION,
+ PMBUS_IC_DEVICE_ID,
+ PMBUS_REG_MFR_FREE_USER_CONFIG_TABLES,
+ PMBUS_REG_MFR_ADDRESS_TABLE,
+ PMBUS_REG_MFR_STATUS_ONSEMI,
+ PMBUS_REG_MFR_UNLOCK,
+ PMBUS_REG_MFR_FAULTY_SPS,
+ PMBUS_REG_TLVR_FAULTS,
+ PMBUS_REG_MFR_USER_STORE_CONFIG_TAB,
+ PMBUS_REG_MFR_USER_CONFIG_INDEX,
+ PMBUS_REG_MFR_PWM_DISCONNECTION,
+ PMBUS_REG_MFR_VR_DISCONNECTION,
+ PMBUS_REG_MFR_TON_SLEW,
+ PMBUS_REG_MFR_TOFF_SLEW,
+ PMBUS_REG_MFR_RAIL_NAME,
+ PMBUS_REG_MFR_VOUT_DROOP,
+ PMBUS_REG_MFR_USER_RESTORE_CONFIG_TAB,
+ PMBUS_REG_MFR_SVR_GO,
+ PMBUS_REG_MFR_SET_PWD,
+ PMBUS_REG_MFR_CONFIG_ACTIVATE,
+ PMBUS_REG_MFR_CONFIG_RECOVER,
+ PMBUS_REG_MFR_OTP_DUMP,
+ PMBUS_REG_MFR_BBR_EN,
+ PMBUS_REG_MFR_DPM_MIN,
+ PMBUS_REG_MFR_VBOOT,
+ PMBUS_REG_MFR_PRECLAMP_OFFSET,
+ PMBUS_REG_MFR_TLVR_DIAGN,
+ PMBUS_REG_MFR_READ_VSYS,
+ PMBUS_REG_MFR_SPECIFIC_E9,
+ PMBUS_REG_MFR_SPECIFIC_EA,
+ PMBUS_REG_MFR_SS_CBC,
+ PMBUS_REG_MFR_AMD_STATUS,
+ PMBUS_REG_MFR_CHECKSUM,
+ PMBUS_REG_CSE_INDEX,
+ PMBUS_REG_COUT_MEASURE,
+ PMBUS_REG_VR_COUT,
+ PMBUS_REG_BBR_RAM,
+ PMBUS_REG_BBR_OTP,
+ PMBUS_REG_READ_PSYS,
+ PMBUS_REG_POSTCLAMP_OFFSET,
+ PMBUS_REG_PGOOD_DELAY,
+ PMBUS_REG_MFR_SPECIFIC_F8,
+ PMBUS_REG_MFR_SPECIFIC_F9,
+ PMBUS_REG_MFR_PWD_PROGRAM_RAM,
+ PMBUS_REG_MFR_PWD_PROGRAM_I2C,
+ PMBUS_REG_MFR_PWD_ENABLE_OTP_STORE,
+};
+
+/* Following registers expect block read */
+static const u8 blk_rd_cc[] = {
+ PMBUS_SMBALERT_MASK,
+ PMBUS_MFR_DATE,
+ PMBUS_IC_DEVICE_REV,
+};
+
+struct fd5121_data {
+ struct attribute_group *groups[3];
+ struct pmbus_driver_info info;
+ struct device *dev;
+ u32 id;
+};
+
+static s32 fd5121_read_block_data(const struct i2c_client *client,
+ u8 cmd_code, u8 len, u8 *pbuf)
+{
+ s32 ret = 0;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_BLOCK_DATA)) {
+
+ /* Payload length is in the first byte. */
+ ret = i2c_smbus_read_i2c_block_data(client, cmd_code,
+ len, pbuf);
+ if (ret < 0)
+ return ret;
+ ret = pbuf[0];
+ if (ret > len)
+ ret = len;
+ for (int idx = 0; idx < ret; idx++)
+ pbuf[idx] = pbuf[idx + 1];
+ return ret;
+ }
+ ret = i2c_smbus_read_block_data(client, cmd_code, pbuf);
+ if (ret < 0)
+ return ret;
+ return min_t(s32, ret, len);
+}
+
+/* Command code that expects block read, not word read */
+static bool fd5121_blk_rd_reg(u8 cmd_code)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(blk_rd_cc); i++) {
+ if (cmd_code == blk_rd_cc[i])
+ return true;
+ }
+ return false;
+}
+
+static ssize_t fd5121_send_byte_store(struct device *dev,
+ struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ u8 val = 0;
+ int ret;
+
+ ret = kstrtou8(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+ ret = i2c_smbus_write_byte(client, val);
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
+static int fd5121_config_activate(struct i2c_client *client)
+{
+ return i2c_smbus_write_byte_data(client,
+ PMBUS_REG_MFR_CONFIG_ACTIVATE, 0xAA);
+}
+
+static ssize_t fd5121_byte_store(struct device *dev,
+ struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ u8 reg = attr->index;
+ int ret = 0;
+ u8 val = 0;
+
+ switch (reg) {
+ case PMBUS_REG_MFR_CONFIG_ACTIVATE:
+ ret = fd5121_config_activate(client);
+ if (ret < 0)
+ return ret;
+ return count;
+ default:
+ ret = kstrtou8(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+ break;
+ }
+ if (reg == PMBUS_PAGE && ((val != 0 && val != 1 &&
+ val != GENMASK(7, 0))))
+ return -EINVAL;
+ ret = i2c_smbus_write_byte_data(client, reg, val);
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
+static ssize_t fd5121_byte_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ u8 reg = attr->index;
+ s32 ret;
+
+ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret < 0)
+ return ret;
+ return scnprintf(buf, PAGE_SIZE, "%d\n", ret & 0xFF);
+}
+
+static ssize_t fd5121_word_store(struct device *dev,
+ struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ u8 reg = attr->index;
+ s16 val = 0;
+ int ret = 0;
+
+ switch (reg) {
+ case PMBUS_REG_MFR_PWD_PROGRAM_RAM:
+ val = 0xC93F;
+ break;
+ default:
+ ret = kstrtos16(buf, 10, &val);
+ if (ret)
+ return ret;
+ break;
+ }
+ ret = i2c_smbus_write_word_data(client, reg, val);
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
+static ssize_t fd5121_word_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ u8 data[I2C_SMBUS_BLOCK_MAX] = { 0 };
+ u8 reg = attr->index;
+ s32 ret = 0;
+
+ if (fd5121_blk_rd_reg(reg)) {
+ ret = fd5121_read_block_data(client, reg, 2, data);
+ if (ret >= 0)
+ ret = get_unaligned_le16(data);
+ } else
+ ret = i2c_smbus_read_word_data(client, reg);
+ if (ret < 0)
+ return ret;
+ return scnprintf(buf, PAGE_SIZE, "%d\n", ret & 0xFFFF);
+}
+
+static s32 fd5121_write_block_data(const struct i2c_client *client,
+ u8 cmd_code, u8 len, u8 *pbuf)
+{
+ s32 ret = 0;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WRITE_BLOCK_DATA))
+ ret = i2c_smbus_write_i2c_block_data(client, cmd_code,
+ len, pbuf);
+ else
+ ret = i2c_smbus_write_block_data(client, cmd_code,
+ len, pbuf);
+ return ret;
+}
+
+static s32 fd5121_read_long(struct i2c_client *client, u8 cmd_code, u32 *pval)
+{
+ u8 buffer[I2C_SMBUS_BLOCK_MAX] = { 0 };
+ s32 ret;
+
+ ret = fd5121_read_block_data(client, cmd_code, 4, buffer);
+ if (ret < 0)
+ return ret;
+ if (ret < 4)
+ return -EIO;
+
+ *pval = get_unaligned_le32(buffer);
+ return 0;
+}
+
+static ssize_t fd5121_long_store(struct device *dev,
+ struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ u8 reg = attr->index;
+ u8 buffer[4];
+ u32 val = 0;
+ int ret = 0;
+ u8 len;
+
+ ret = kstrtou32(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ len = (u8) sizeof(buffer);
+ for (u8 i = 0; i < len; i++) {
+ buffer[i] = val & 0xFF;
+ val >>= 8;
+ }
+ ret = fd5121_write_block_data(client, reg, len, buffer);
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
+static ssize_t fd5121_long_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ u8 reg = attr->index;
+ u32 val = 0;
+ s32 ret = 0;
+
+ ret = fd5121_read_long(client, reg, &val);
+ if (ret < 0)
+ return ret;
+ return scnprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+
+static ssize_t fd5121_block_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ u8 buffer[I2C_SMBUS_BLOCK_MAX] = { 0 };
+ u8 reg = attr->index;
+ int printed = 0;
+ s32 ret = 0;
+ u8 len = 0;
+ int i = 0;
+
+ if (reg == PMBUS_REG_MFR_FAULTY_SPS) {
+ int to_print = 0;
+
+ len = 7;
+ ret = fd5121_read_block_data(client, reg, len, buffer);
+ if (ret < 0)
+ return ret;
+ printed = 0;
+ to_print = (ret < len) ? ret : len;
+ for (i = 0; i < to_print; i++)
+ printed += scnprintf(buf + printed,
+ PAGE_SIZE - printed,
+ "%02x", buffer[i]);
+ printed += scnprintf(buf + printed,
+ PAGE_SIZE - printed, "\n");
+ return printed;
+ } else if (reg == PMBUS_REG_BBR_RAM ||
+ reg == PMBUS_REG_BBR_OTP) {
+ u32 len = (reg == PMBUS_REG_BBR_OTP) ? 165 : 164;
+
+ /* Extra byte may be needed in case we need to store
+ * the length of the data
+ */
+ u8 *tmp_in = kcalloc(len+1, sizeof(u8), GFP_KERNEL);
+
+ if (tmp_in == NULL)
+ return -ENOMEM;
+ ret = fd5121_read_block_data(client, reg, len, tmp_in);
+ if (ret < 0) {
+ kfree(tmp_in);
+ return ret;
+ }
+
+ printed = 0;
+ for (i = 0; i < ret; i++)
+ printed += scnprintf(buf + printed,
+ PAGE_SIZE - printed, "%02x",
+ tmp_in[i]);
+ printed += scnprintf(buf + printed,
+ PAGE_SIZE - printed, "\n");
+
+ kfree(tmp_in);
+ return printed;
+ } else
+ return -ENODATA;
+}
+
+static SENSOR_DEVICE_ATTR_RW(page, fd5121_byte,
+ PMBUS_PAGE);
+static SENSOR_DEVICE_ATTR_RO(vout_raw, fd5121_word,
+ PMBUS_READ_VOUT);
+static SENSOR_DEVICE_ATTR_RW(operation, fd5121_byte,
+ PMBUS_OPERATION);
+static SENSOR_DEVICE_ATTR_RW(on_off_config, fd5121_byte,
+ PMBUS_ON_OFF_CONFIG);
+static SENSOR_DEVICE_ATTR_WO(clear_faults, fd5121_byte,
+ PMBUS_CLEAR_FAULTS);
+static SENSOR_DEVICE_ATTR_RW(write_protect, fd5121_byte,
+ PMBUS_WRITE_PROTECT);
+static SENSOR_DEVICE_ATTR_RO(capability, fd5121_byte,
+ PMBUS_CAPABILITY);
+static SENSOR_DEVICE_ATTR_RW(smbalert_mask, fd5121_word,
+ PMBUS_SMBALERT_MASK);
+static SENSOR_DEVICE_ATTR_RO(vout_mode, fd5121_byte,
+ PMBUS_VOUT_MODE);
+static SENSOR_DEVICE_ATTR_RW(vout_command, fd5121_word,
+ PMBUS_VOUT_COMMAND);
+static SENSOR_DEVICE_ATTR_RW(vout_max, fd5121_word,
+ PMBUS_VOUT_MAX);
+static SENSOR_DEVICE_ATTR_RW(vout_margin_high, fd5121_word,
+ PMBUS_VOUT_MARGIN_HIGH);
+static SENSOR_DEVICE_ATTR_RW(vout_margin_low, fd5121_word,
+ PMBUS_VOUT_MARGIN_LOW);
+static SENSOR_DEVICE_ATTR_RW(vout_transition_rate, fd5121_word,
+ PMBUS_VOUT_TRANSITION_RATE);
+static SENSOR_DEVICE_ATTR_RW(vout_min, fd5121_word,
+ PMBUS_REG_VOUT_MIN);
+static SENSOR_DEVICE_ATTR_RW(power_mode, fd5121_byte,
+ PMBUS_REG_POWER_MODE);
+static SENSOR_DEVICE_ATTR_RW(vin_on, fd5121_word,
+ PMBUS_REG_VIN_ON);
+static SENSOR_DEVICE_ATTR_RW(vin_off, fd5121_word,
+ PMBUS_REG_VIN_OFF);
+static SENSOR_DEVICE_ATTR_RW(vin_uv_fault_response, fd5121_byte,
+ PMBUS_REG_VIN_UV_FAULT_RESPONSE);
+static SENSOR_DEVICE_ATTR_RW(iin_oc_fault_response, fd5121_byte,
+ PMBUS_REG_IIN_OC_FAULT_RESPONSE);
+static SENSOR_DEVICE_ATTR_RW(ton_delay, fd5121_word,
+ PMBUS_REG_TON_DELAY);
+static SENSOR_DEVICE_ATTR_RW(pout_op_fault_response, fd5121_byte,
+ PMBUS_REG_POUT_OP_FAULT_RESPONSE);
+static SENSOR_DEVICE_ATTR_RO(read_vaux, fd5121_word,
+ PMBUS_REG_READ_VAUX);
+static SENSOR_DEVICE_ATTR_RW(iknee_set, fd5121_word,
+ PMBUS_REG_IKNEE_SET);
+static SENSOR_DEVICE_ATTR_RW(pin_counter, fd5121_byte,
+ PMBUS_REG_PIN_COUNTER);
+static SENSOR_DEVICE_ATTR_RW(vmin_aware, fd5121_word,
+ PMBUS_REG_VMIN_AWARE);
+static SENSOR_DEVICE_ATTR_RW(vout_ov_fault_response, fd5121_byte,
+ PMBUS_VOUT_OV_FAULT_RESPONSE);
+static SENSOR_DEVICE_ATTR_RW(vout_uv_fault_response, fd5121_byte,
+ PMBUS_VOUT_UV_FAULT_RESPONSE);
+static SENSOR_DEVICE_ATTR_RW(iout_oc_fault_response, fd5121_byte,
+ PMBUS_IOUT_OC_FAULT_RESPONSE);
+static SENSOR_DEVICE_ATTR_RW(ot_fault_response, fd5121_byte,
+ PMBUS_OT_FAULT_RESPONSE);
+static SENSOR_DEVICE_ATTR_RW(vin_ov_fault_response, fd5121_byte,
+ PMBUS_VIN_OV_FAULT_RESPONSE);
+static SENSOR_DEVICE_ATTR_RW(vaux_uv_fault_limit, fd5121_word,
+ PMBUS_REG_VAUX_UV_FAULT_LIMIT);
+static SENSOR_DEVICE_ATTR_RW(vaux_ov_fault_limit, fd5121_word,
+ PMBUS_REG_VAUX_OV_FAULT_LIMIT);
+static SENSOR_DEVICE_ATTR_RW(vaux_uv_fault_response, fd5121_byte,
+ PMBUS_REG_VAUX_UV_FAULT_RESPONSE);
+static SENSOR_DEVICE_ATTR_RW(vaux_ov_fault_response, fd5121_byte,
+ PMBUS_REG_VAUX_OV_FAULT_RESPONSE);
+static SENSOR_DEVICE_ATTR_RW(vaux_uv_warning, fd5121_word,
+ PMBUS_REG_VAUX_UV_WARNING);
+static SENSOR_DEVICE_ATTR_RW(vaux_ov_warning, fd5121_word,
+ PMBUS_REG_VAUX_OV_WARNING);
+static SENSOR_DEVICE_ATTR_RO(free_user_config_tables, fd5121_byte,
+ PMBUS_REG_MFR_FREE_USER_CONFIG_TABLES);
+static SENSOR_DEVICE_ATTR_RW(address_table, fd5121_byte,
+ PMBUS_REG_MFR_ADDRESS_TABLE);
+static SENSOR_DEVICE_ATTR_RW(status_onsemi, fd5121_word,
+ PMBUS_REG_MFR_STATUS_ONSEMI);
+static SENSOR_DEVICE_ATTR_RO(status_byte, fd5121_byte,
+ PMBUS_STATUS_BYTE);
+static SENSOR_DEVICE_ATTR_RO(status_cml, fd5121_byte,
+ PMBUS_STATUS_CML);
+static SENSOR_DEVICE_ATTR_RO(status_other, fd5121_byte,
+ PMBUS_STATUS_OTHER);
+static SENSOR_DEVICE_ATTR_RO(status_mfr_specific, fd5121_byte,
+ PMBUS_STATUS_MFR_SPECIFIC);
+static SENSOR_DEVICE_ATTR_RO(revision, fd5121_byte,
+ PMBUS_REVISION);
+static SENSOR_DEVICE_ATTR_RO(id, fd5121_long,
+ PMBUS_MFR_ID);
+static SENSOR_DEVICE_ATTR_RO(model, fd5121_long,
+ PMBUS_MFR_MODEL);
+static SENSOR_DEVICE_ATTR_RO(mfr_revision, fd5121_long,
+ PMBUS_MFR_REVISION);
+static SENSOR_DEVICE_ATTR_RW(date, fd5121_word,
+ PMBUS_MFR_DATE);
+static SENSOR_DEVICE_ATTR_RO(ic_device_id, fd5121_long,
+ PMBUS_IC_DEVICE_ID);
+static SENSOR_DEVICE_ATTR_RO(ic_device_rev, fd5121_word,
+ PMBUS_IC_DEVICE_REV);
+static SENSOR_DEVICE_ATTR_WO(unlock, fd5121_byte,
+ PMBUS_REG_MFR_UNLOCK);
+static SENSOR_DEVICE_ATTR_RO(faulty_sps, fd5121_block,
+ PMBUS_REG_MFR_FAULTY_SPS);
+static SENSOR_DEVICE_ATTR_RO(tlvr_faults, fd5121_word,
+ PMBUS_REG_TLVR_FAULTS);
+static SENSOR_DEVICE_ATTR_RW(user_store_config_tab, fd5121_byte,
+ PMBUS_REG_MFR_USER_STORE_CONFIG_TAB);
+static SENSOR_DEVICE_ATTR_RO(user_config_index, fd5121_byte,
+ PMBUS_REG_MFR_USER_CONFIG_INDEX);
+static SENSOR_DEVICE_ATTR_RO(pwm_disconnection, fd5121_word,
+ PMBUS_REG_MFR_PWM_DISCONNECTION);
+static SENSOR_DEVICE_ATTR_RO(vr_disconnection, fd5121_byte,
+ PMBUS_REG_MFR_VR_DISCONNECTION);
+static SENSOR_DEVICE_ATTR_RW(ton_slew, fd5121_byte,
+ PMBUS_REG_MFR_TON_SLEW);
+static SENSOR_DEVICE_ATTR_RW(toff_slew, fd5121_byte,
+ PMBUS_REG_MFR_TOFF_SLEW);
+static SENSOR_DEVICE_ATTR_RW(rail_name, fd5121_word,
+ PMBUS_REG_MFR_RAIL_NAME);
+static SENSOR_DEVICE_ATTR_RW(vout_droop, fd5121_byte,
+ PMBUS_REG_MFR_VOUT_DROOP);
+static SENSOR_DEVICE_ATTR_WO(svr_go, fd5121_send_byte,
+ PMBUS_REG_MFR_SVR_GO);
+static SENSOR_DEVICE_ATTR_RW(user_restore_config_tab, fd5121_byte,
+ PMBUS_REG_MFR_USER_RESTORE_CONFIG_TAB);
+static SENSOR_DEVICE_ATTR_WO(set_pwd, fd5121_byte,
+ PMBUS_REG_MFR_SET_PWD);
+static SENSOR_DEVICE_ATTR_RW(config_activate, fd5121_byte,
+ PMBUS_REG_MFR_CONFIG_ACTIVATE);
+static SENSOR_DEVICE_ATTR_RW(config_recover, fd5121_byte,
+ PMBUS_REG_MFR_CONFIG_RECOVER);
+static SENSOR_DEVICE_ATTR_RW(otp_dump, fd5121_byte,
+ PMBUS_REG_MFR_OTP_DUMP);
+static SENSOR_DEVICE_ATTR_RW(bbr_en, fd5121_byte,
+ PMBUS_REG_MFR_BBR_EN);
+static SENSOR_DEVICE_ATTR_RW(dpm_min, fd5121_byte,
+ PMBUS_REG_MFR_DPM_MIN);
+static SENSOR_DEVICE_ATTR_RW(vboot, fd5121_word,
+ PMBUS_REG_MFR_VBOOT);
+static SENSOR_DEVICE_ATTR_RW(preclamp_offset, fd5121_word,
+ PMBUS_REG_MFR_PRECLAMP_OFFSET);
+static SENSOR_DEVICE_ATTR_RW(tlvr_diagn, fd5121_word,
+ PMBUS_REG_MFR_TLVR_DIAGN);
+static SENSOR_DEVICE_ATTR_RO(vsys, fd5121_word,
+ PMBUS_REG_MFR_READ_VSYS);
+static SENSOR_DEVICE_ATTR_RW(specific_e9, fd5121_word,
+ PMBUS_REG_MFR_SPECIFIC_E9);
+static SENSOR_DEVICE_ATTR_RW(specific_ea, fd5121_long,
+ PMBUS_REG_MFR_SPECIFIC_EA);
+static SENSOR_DEVICE_ATTR_RO(ss_cbc, fd5121_word,
+ PMBUS_REG_MFR_SS_CBC);
+static SENSOR_DEVICE_ATTR_RO(amd_status, fd5121_byte,
+ PMBUS_REG_MFR_AMD_STATUS);
+static SENSOR_DEVICE_ATTR_RO(checksum, fd5121_word,
+ PMBUS_REG_MFR_CHECKSUM);
+static SENSOR_DEVICE_ATTR_RO(cse_index, fd5121_word,
+ PMBUS_REG_CSE_INDEX);
+static SENSOR_DEVICE_ATTR_RW(cout_measure, fd5121_word,
+ PMBUS_REG_COUT_MEASURE);
+static SENSOR_DEVICE_ATTR_RO(vr_cout, fd5121_word,
+ PMBUS_REG_VR_COUT);
+static SENSOR_DEVICE_ATTR_RO(bbr_ram, fd5121_block,
+ PMBUS_REG_BBR_RAM);
+static SENSOR_DEVICE_ATTR_RO(bbr_otp, fd5121_block,
+ PMBUS_REG_BBR_OTP);
+static SENSOR_DEVICE_ATTR_RO(psys, fd5121_word,
+ PMBUS_REG_READ_PSYS);
+static SENSOR_DEVICE_ATTR_RW(postclamp_offset, fd5121_word,
+ PMBUS_REG_POSTCLAMP_OFFSET);
+static SENSOR_DEVICE_ATTR_RW(pgood_delay, fd5121_byte,
+ PMBUS_REG_PGOOD_DELAY);
+static SENSOR_DEVICE_ATTR_RW(specific_f8, fd5121_word,
+ PMBUS_REG_MFR_SPECIFIC_F8);
+static SENSOR_DEVICE_ATTR_RW(specific_f9, fd5121_long,
+ PMBUS_REG_MFR_SPECIFIC_F9);
+static SENSOR_DEVICE_ATTR_RW(pwd_program_ram, fd5121_word,
+ PMBUS_REG_MFR_PWD_PROGRAM_RAM);
+static SENSOR_DEVICE_ATTR_RW(pwd_program_i2c, fd5121_word,
+ PMBUS_REG_MFR_PWD_PROGRAM_I2C);
+static SENSOR_DEVICE_ATTR_RW(pwd_enable_otp_store, fd5121_word,
+ PMBUS_REG_MFR_PWD_ENABLE_OTP_STORE);
+
+static struct attribute *fd5121_non_paged_attrs[] = {
+ &sensor_dev_attr_page.dev_attr.attr,
+ &sensor_dev_attr_capability.dev_attr.attr,
+ &sensor_dev_attr_pin_counter.dev_attr.attr,
+ &sensor_dev_attr_vaux_uv_fault_limit.dev_attr.attr,
+ &sensor_dev_attr_vaux_ov_fault_limit.dev_attr.attr,
+ &sensor_dev_attr_vaux_uv_warning.dev_attr.attr,
+ &sensor_dev_attr_vaux_ov_warning.dev_attr.attr,
+ &sensor_dev_attr_free_user_config_tables.dev_attr.attr,
+ &sensor_dev_attr_address_table.dev_attr.attr,
+ &sensor_dev_attr_unlock.dev_attr.attr,
+ &sensor_dev_attr_faulty_sps.dev_attr.attr,
+ &sensor_dev_attr_tlvr_faults.dev_attr.attr,
+ &sensor_dev_attr_user_store_config_tab.dev_attr.attr,
+ &sensor_dev_attr_user_config_index.dev_attr.attr,
+ &sensor_dev_attr_pwm_disconnection.dev_attr.attr,
+ &sensor_dev_attr_vr_disconnection.dev_attr.attr,
+ &sensor_dev_attr_user_restore_config_tab.dev_attr.attr,
+ &sensor_dev_attr_svr_go.dev_attr.attr,
+ &sensor_dev_attr_set_pwd.dev_attr.attr,
+ &sensor_dev_attr_config_activate.dev_attr.attr,
+ &sensor_dev_attr_config_recover.dev_attr.attr,
+ &sensor_dev_attr_otp_dump.dev_attr.attr,
+ &sensor_dev_attr_bbr_en.dev_attr.attr,
+ &sensor_dev_attr_vboot.dev_attr.attr,
+ &sensor_dev_attr_vsys.dev_attr.attr,
+ &sensor_dev_attr_specific_e9.dev_attr.attr,
+ &sensor_dev_attr_specific_ea.dev_attr.attr,
+ &sensor_dev_attr_ss_cbc.dev_attr.attr,
+ &sensor_dev_attr_checksum.dev_attr.attr,
+ &sensor_dev_attr_cse_index.dev_attr.attr,
+ &sensor_dev_attr_cout_measure.dev_attr.attr,
+ &sensor_dev_attr_vr_cout.dev_attr.attr,
+ &sensor_dev_attr_bbr_ram.dev_attr.attr,
+ &sensor_dev_attr_bbr_otp.dev_attr.attr,
+ &sensor_dev_attr_psys.dev_attr.attr,
+ &sensor_dev_attr_specific_f8.dev_attr.attr,
+ &sensor_dev_attr_specific_f9.dev_attr.attr,
+ &sensor_dev_attr_pwd_program_ram.dev_attr.attr,
+ &sensor_dev_attr_pwd_program_i2c.dev_attr.attr,
+ &sensor_dev_attr_pwd_enable_otp_store.dev_attr.attr,
+ &sensor_dev_attr_revision.dev_attr.attr,
+ &sensor_dev_attr_id.dev_attr.attr,
+ &sensor_dev_attr_model.dev_attr.attr,
+ &sensor_dev_attr_mfr_revision.dev_attr.attr,
+ &sensor_dev_attr_date.dev_attr.attr,
+ &sensor_dev_attr_ic_device_id.dev_attr.attr,
+ &sensor_dev_attr_ic_device_rev.dev_attr.attr,
+ NULL
+};
+
+static struct attribute *fd5121_paged_attrs[] = {
+ &sensor_dev_attr_operation.dev_attr.attr,
+ &sensor_dev_attr_vout_raw.dev_attr.attr,
+ &sensor_dev_attr_on_off_config.dev_attr.attr,
+ &sensor_dev_attr_clear_faults.dev_attr.attr,
+ &sensor_dev_attr_write_protect.dev_attr.attr,
+ &sensor_dev_attr_smbalert_mask.dev_attr.attr,
+ &sensor_dev_attr_vout_mode.dev_attr.attr,
+ &sensor_dev_attr_vout_command.dev_attr.attr,
+ &sensor_dev_attr_vout_margin_high.dev_attr.attr,
+ &sensor_dev_attr_vout_margin_low.dev_attr.attr,
+ &sensor_dev_attr_vout_min.dev_attr.attr,
+ &sensor_dev_attr_vin_on.dev_attr.attr,
+ &sensor_dev_attr_vin_off.dev_attr.attr,
+ &sensor_dev_attr_vout_ov_fault_response.dev_attr.attr,
+ &sensor_dev_attr_vout_uv_fault_response.dev_attr.attr,
+ &sensor_dev_attr_iout_oc_fault_response.dev_attr.attr,
+ &sensor_dev_attr_ot_fault_response.dev_attr.attr,
+ &sensor_dev_attr_vin_ov_fault_response.dev_attr.attr,
+ &sensor_dev_attr_status_byte.dev_attr.attr,
+ &sensor_dev_attr_iknee_set.dev_attr.attr,
+ &sensor_dev_attr_vmin_aware.dev_attr.attr,
+ &sensor_dev_attr_power_mode.dev_attr.attr,
+ &sensor_dev_attr_vin_uv_fault_response.dev_attr.attr,
+ &sensor_dev_attr_iin_oc_fault_response.dev_attr.attr,
+ &sensor_dev_attr_ton_delay.dev_attr.attr,
+ &sensor_dev_attr_pout_op_fault_response.dev_attr.attr,
+ &sensor_dev_attr_vaux_uv_fault_response.dev_attr.attr,
+ &sensor_dev_attr_vaux_ov_fault_response.dev_attr.attr,
+ &sensor_dev_attr_status_onsemi.dev_attr.attr,
+ &sensor_dev_attr_status_cml.dev_attr.attr,
+ &sensor_dev_attr_status_other.dev_attr.attr,
+ &sensor_dev_attr_status_mfr_specific.dev_attr.attr,
+ &sensor_dev_attr_read_vaux.dev_attr.attr,
+ &sensor_dev_attr_ton_slew.dev_attr.attr,
+ &sensor_dev_attr_toff_slew.dev_attr.attr,
+ &sensor_dev_attr_rail_name.dev_attr.attr,
+ &sensor_dev_attr_vout_droop.dev_attr.attr,
+ &sensor_dev_attr_dpm_min.dev_attr.attr,
+ &sensor_dev_attr_preclamp_offset.dev_attr.attr,
+ &sensor_dev_attr_tlvr_diagn.dev_attr.attr,
+ &sensor_dev_attr_amd_status.dev_attr.attr,
+ &sensor_dev_attr_postclamp_offset.dev_attr.attr,
+ &sensor_dev_attr_pgood_delay.dev_attr.attr,
+ &sensor_dev_attr_vout_max.dev_attr.attr,
+ &sensor_dev_attr_vout_transition_rate.dev_attr.attr,
+ NULL
+};
+
+static struct attribute_group fd5121_groups[2] = {
+ { .name = "global", .attrs = fd5121_non_paged_attrs },
+ { .name = "paged", .attrs = fd5121_paged_attrs }
+};
+
+/* Regulator descriptors for VOUT rails (VID encoded) */
+static struct regulator_desc fd5121_reg_desc[] = {
+ PMBUS_REGULATOR_STEP_ONE("vout1", 3001, 1000, 200000),
+ PMBUS_REGULATOR_STEP_ONE("vout2", 3001, 1000, 200000),
+};
+
+static int fd5121_valid_reg(struct i2c_client *client, int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cc_list); i++) {
+ if (reg == cc_list[i])
+ return 0;
+ }
+
+ if (fd5121_blk_rd_reg(reg))
+ return 0;
+ return -ENXIO;
+}
+
+static int fd5121_read_word_data(struct i2c_client *client, int page,
+ int phase, int reg)
+{
+ int ret;
+
+ ret = fd5121_valid_reg(client, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = pmbus_read_word_data(client, page, phase, reg);
+ if (ret < 0)
+ return ret;
+
+ /* Chip reports VOUT_MODE as vid. But gives raw value 1mV per bit.
+ * So, encode the READ_VOUT value so that it gets decoded and
+ * reported correctly.
+ */
+ if (reg == PMBUS_READ_VOUT)
+ ret = DIV_ROUND_CLOSEST(155000 - ret * 100, 625);
+ return ret;
+}
+
+static int fd5121_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ int ret;
+
+ ret = fd5121_valid_reg(client, reg);
+ if (ret < 0)
+ return ret;
+
+ return pmbus_read_byte_data(client, page, reg);
+}
+
+static int fd5121_write_byte_data(struct i2c_client *client, int page,
+ int reg, u8 value)
+{
+ int ret;
+
+ ret = fd5121_valid_reg(client, reg);
+ if (ret < 0)
+ return ret;
+ return pmbus_write_byte_data(client, page, reg, value);
+}
+
+static int fd5121_write_byte(struct i2c_client *client, int page, u8 byte)
+{
+ return pmbus_write_byte(client, page, byte);
+}
+
+static int fd5121_write_word_data(struct i2c_client *client, int page,
+ int reg, u16 word)
+{
+ int ret;
+
+ ret = fd5121_valid_reg(client, reg);
+ if (ret < 0)
+ return ret;
+ ret = pmbus_write_word_data(client, page, reg, word);
+ return ret;
+}
+
+static u32 fd5121_get_dev_id(struct i2c_client *client)
+{
+ u32 dev_id = 0;
+ s32 ret = 0;
+
+ ret = fd5121_read_long(client, PMBUS_IC_DEVICE_ID, &dev_id);
+ if (ret < 0)
+ return CTLR_ID_UNKNOWN;
+
+ switch (dev_id) {
+ case CTLR_ID_FD5121:
+ case CTLR_ID_FD5123:
+ case CTLR_ID_FD5125:
+ break;
+ default:
+ if (dev_id != 0)
+ dev_err(&client->dev, "Unknown device 0x%x",
+ dev_id);
+ return CTLR_ID_UNKNOWN;
+ }
+ return dev_id;
+}
+
+static int fd5121_probe(struct i2c_client *client)
+{
+ struct pmbus_driver_info *info;
+ struct fd5121_data *pdata;
+ u32 id;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -EOPNOTSUPP;
+
+ pdata = devm_kzalloc(&client->dev, sizeof(struct fd5121_data),
+ GFP_KERNEL);
+ if (pdata == NULL)
+ return -ENOMEM;
+
+ pdata->dev = &client->dev;
+ pdata->groups[0] = &fd5121_groups[0];
+ pdata->groups[1] = &fd5121_groups[1];
+
+ id = fd5121_get_dev_id(client);
+ if (id == CTLR_ID_UNKNOWN)
+ return -ENODEV;
+
+ pdata->id = id;
+
+ switch (id) {
+ case CTLR_ID_FD5121:
+ case CTLR_ID_FD5123:
+ case CTLR_ID_FD5125:
+ break;
+ default:
+ dev_err(&client->dev, "Failed to read device ID");
+ return -ENODEV;
+ }
+
+ info = &pdata->info;
+ info->groups = (const struct attribute_group **)&pdata->groups[0];
+ info->write_word_data = fd5121_write_word_data;
+ info->write_byte = fd5121_write_byte;
+ info->write_byte_data = fd5121_write_byte_data;
+ info->read_word_data = fd5121_read_word_data;
+ info->read_byte_data = fd5121_read_byte_data;
+
+ info->pages = FD5121_NUM_PAGES;
+ info->format[PSC_VOLTAGE_IN] = linear;
+ info->format[PSC_VOLTAGE_OUT] = vid;
+
+ fd5121_reg_desc[0].id = 0;
+ fd5121_reg_desc[1].id = 1;
+
+ /* Device implements VID coding with 1 mV steps from 0.200 V
+ * up to 3.200 V
+ */
+ info->num_regulators = FD5121_NUM_PAGES;
+ info->reg_desc = fd5121_reg_desc;
+ info->format[PSC_CURRENT_IN] = linear;
+ info->format[PSC_CURRENT_OUT] = linear;
+ info->format[PSC_POWER] = linear;
+ info->format[PSC_TEMPERATURE] = linear;
+ for (u8 idx = 0; idx < info->pages; idx++) {
+ info->func[idx] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT;
+ info->func[idx] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
+ info->func[idx] |= PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
+ info->func[idx] |= PMBUS_HAVE_PIN | PMBUS_HAVE_POUT;
+ info->func[idx] |= PMBUS_HAVE_VIN | PMBUS_HAVE_IIN;
+ info->func[idx] |= PMBUS_HAVE_STATUS_INPUT;
+ info->vrm_version[idx] = amd625mv;
+ }
+ return pmbus_do_probe(client, info);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id fd5121_of_match[] = {
+ { .compatible = "onnn,fd5121" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, fd5121_of_match);
+#endif
+
+static const struct i2c_device_id fd5121_id[] = {
+ { "fd5121", chip_fd5121 },
+ { "fd5123", chip_fd5123 },
+ { "fd5125", chip_fd5125 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, fd5121_id);
+
+static struct i2c_driver fd5121_driver = {
+ .driver = {
+ .name = "fd5121",
+ .of_match_table = of_match_ptr(fd5121_of_match),
+ },
+ .probe = fd5121_probe,
+ .id_table = fd5121_id,
+};
+
+module_i2c_driver(fd5121_driver);
+
+MODULE_AUTHOR("Selva Rajagopal <selvamani.rajagopal@onsemi.com>");
+MODULE_DESCRIPTION("PMBus driver for FD5121");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("PMBUS");
+
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 1/3] Documentation/hwmon: Add onsemi's FD5121 controllers' documentation
2026-06-23 5:55 ` Selvamani Rajagopal
(?)
@ 2026-06-23 5:59 ` sashiko-bot
-1 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-06-23 5:59 UTC (permalink / raw)
To: Selvamani Rajagopal via B4 Relay; +Cc: robh, devicetree, conor+dt, linux-hwmon
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Low] Sysfs attribute descriptions contain mismatched input/output semantics due to copy-paste errors.
- [Low] Incomplete sentence missing a trailing period.
--
commit 10fc2806e78d641ce1fc7f05d01f0cec8dcccbe0
Author: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>
Documentation/hwmon: Add onsemi's FD5121 controllers' documentation
This commit documents the hardware monitoring support for FD5121, FD5123,
and FD5125 devices. It describes the supported telemetry data exposed via
sysfs, including voltage, current, power, and temperature measurements.
> diff --git a/Documentation/hwmon/fd5121.rst b/Documentation/hwmon/fd5121.rst
> new file mode 100644
> index 0000000000000..c279db4641e4a
> --- /dev/null
> +++ b/Documentation/hwmon/fd5121.rst
[ ... ]
> +Usage Notes
> +-----------
> +
> +This driver does not probe for PMBus devices. You will have
> +to instantiate devices explicitly.
> +
> +Example: the following commands will load the driver for the
> +controller at address 0x50 on I2C bus #1::
> +
> + # modprobe fd5121
> + # echo fd5121 0x50 > /sys/bus/i2c/devices/i2c-1/new_device
> +
> +It can also be instantiated by declaring in device tree
[Severity: Low]
This isn't a bug, but this sentence ends abruptly without a trailing period.
> +Sysfs attributes
> +----------------
> +
> +The following attributes are supported:
> +
> +====================== ====================================
> +curr[1-2]_label "iin[1-2]"
> +curr[3-4]_label "iout[1-2]"
> +curr[1-2]_input Measured input current.
> +curr[3-4]_input Measured output current.
> +curr[1-4]_crit_alarm Output current critical high alarm.
> +curr[1-4]_max_alarm Output current high alarm.
[Severity: Low]
Are the alarm descriptions for curr[1-2] accurate? Since curr[1-2] are labeled
as input current, should their alarms be described as input current alarms
instead of output current alarms?
> +
> +in[1-2]_label "vin[1-2]"
> +in[3-4]_label "vout[1-2]"
> +in[1-4]_lcrit_alarm Input voltage critical low alarm.
> +in[1-4]_crit_alarm Input voltage critical high alarm.
[Severity: Low]
Similarly, are the alarm descriptions for in[3-4] correct? They are labeled as
output voltage, but their alarms are described as input voltage alarms.
> +in[1-2]_max_alarm Input voltage high alarm.
> +in[1-2]_input Measured input voltage.
> +in[3-4]_input Measured output voltage.
> +
> +power[1-2]_label "pin[1-2]"
> +power[3-4]_label "pout[1-2]"
> +power[3-4]_crit_alarm Output power critical high alarm.
> +power[1-2]_max_alarm Output power high alarm.
[Severity: Low]
Should power[1-2]_max_alarm be described as an input power high alarm, given
that power[1-2] represents input power?
> +power[1-4]_max Power limit.
> +power[1-4]_input Measured input power.
[Severity: Low]
Does power[3-4]_input actually measure input power? Given that power[3-4] is
labeled as output power, shouldn't this be described as measured output power?
> +power[3-4]_crit Critical maximum output power.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260622-support-fd5121-from-onsemi-v1-0-b31767689c65@onsemi.com?part=1
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/3] dt-bindings: hwmon: pmbus: Support for onsemi's FD5121
2026-06-23 5:55 ` Selvamani Rajagopal
(?)
@ 2026-06-23 6:05 ` sashiko-bot
-1 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-06-23 6:05 UTC (permalink / raw)
To: Selvamani Rajagopal via B4 Relay; +Cc: conor+dt, robh, devicetree, linux-hwmon
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] The binding lacks a `regulators` node definition, preventing it from functioning as a voltage regulator provider in devicetree.
- [Low] The devicetree example uses a specific model name for the node instead of a generic name.
--
commit 52162fef263cc41ea7598b089db89b70bb4f6ebb
Author: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>
dt-bindings: hwmon: pmbus: Support for onsemi's FD5121
This commit adds the devicetree schema for onsemi FD5121, FD5123, and FD5125
dual rail, multi-phase digital controllers.
> diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/onnn,fd5121.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/onnn,fd5121.yaml
> new file mode 100644
> index 0000000000000..b0453b0634f0d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/hwmon/pmbus/onnn,fd5121.yaml
> @@ -0,0 +1,41 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
[ ... ]
> +required:
> + - compatible
> + - reg
> +
> +additionalProperties: false
[Severity: Medium]
Since this device is a multi-phase digital controller, should it have a
regulators subnode pattern?
The additionalProperties: false constraint strictly prohibits developers from
defining regulator constraints in their device trees. As the PMBus core
framework looks for the regulators child node, would it be better to add an
explicit pattern here to allow consumers to reference the device's voltage
rails?
> +
> +examples:
> + - |
> + i2c {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + fd5121@50 {
[Severity: Low]
Is it possible to use a generic node name here like regulator@50, pmic@50, or
sensor@50 instead of the specific model name fd5121@50?
The Devicetree specification recommends generic node names to ensure standard
patterns are used in examples.
> + compatible = "onnn,fd5121";
> + reg = <0x50>;
> + };
> + };
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260622-support-fd5121-from-onsemi-v1-0-b31767689c65@onsemi.com?part=2
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 3/3] hwmon: (pmbus/fd5121): Add support FD5121, FD5123 and FD5125
2026-06-23 5:55 ` Selvamani Rajagopal
(?)
@ 2026-06-23 6:07 ` sashiko-bot
-1 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-06-23 6:07 UTC (permalink / raw)
To: Selvamani Rajagopal via B4 Relay; +Cc: conor+dt, linux-hwmon, devicetree, robh
Thank you for your contribution! Sashiko AI review found 10 potential issue(s) to consider:
- [High] Directly exposing PMBUS_PAGE and paged PMBus registers via custom lockless sysfs attributes bypasses PMBus core locking and page caching, causing race conditions and hardware state corruption.
- [High] Incorrect mathematical conversion of READ_VOUT to AMD 6.25mV VIDs causes voltage read failures > 1.55V and applies dangerously incorrect voltages on writes.
- [High] The `svr_go` sysfs attribute ignores its mapped register index, allowing userspace to execute arbitrary PMBus/I2C Send Byte commands.
- [High] The I2C block read fallback in `fd5121_read_block_data` corrupts the payload by reading one byte too few and shifting uninitialized memory into the result.
- [High] The I2C block write fallback in `fd5121_write_block_data` omits the required PMBus length byte, corrupting write transactions.
- [Medium] Attempting to read 164/165 bytes for OTP/RAM using standard SMBus/I2C block read functions results in silent truncation to 32 bytes.
- [Medium] `fd5121_word_store` parses user input as a signed 16-bit integer, preventing valid unsigned PMBus values from being written.
- [Medium] The custom `clear_faults` sysfs attribute sends an invalid data payload for a standard PMBus Send Byte command.
- [Medium] The driver violates the hwmon subsystem ABI by exposing over 80 standard PMBus registers as custom raw sysfs attributes.
- [Medium] `fd5121_probe` enforces an overly restrictive `I2C_FUNC_I2C` check, breaking compatibility with standard SMBus controllers.
--
commit 1e218fd27c0a9b6bb8fade016af8d29cbdb6daa5
Author: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>
hwmon: (pmbus/fd5121): Add support FD5121, FD5123 and FD5125
FD5121 is a dual-rail, multi-phase, digital controller that offers
full telemtry options including input/output voltage, current as
well as fault handling and identifications.
These controllers are compliant with PMBus specification.
> diff --git a/drivers/hwmon/pmbus/fd5121.c b/drivers/hwmon/pmbus/fd5121.c
> new file mode 100644
> index 0000000000000..e68c6d6cabbd9
> --- /dev/null
> +++ b/drivers/hwmon/pmbus/fd5121.c
[ ... ]
> +static s32 fd5121_read_block_data(const struct i2c_client *client,
> + u8 cmd_code, u8 len, u8 *pbuf)
> +{
> + s32 ret = 0;
> +
> + if (!i2c_check_functionality(client->adapter,
> + I2C_FUNC_SMBUS_READ_BLOCK_DATA)) {
> +
> + /* Payload length is in the first byte. */
> + ret = i2c_smbus_read_i2c_block_data(client, cmd_code,
> + len, pbuf);
> + if (ret < 0)
> + return ret;
> + ret = pbuf[0];
> + if (ret > len)
> + ret = len;
> + for (int idx = 0; idx < ret; idx++)
> + pbuf[idx] = pbuf[idx + 1];
> + return ret;
[Severity: High]
Does this fallback logic corrupt the payload by reading one byte too few?
Since the device replies with an SMBus block read format (a length byte
followed by the payload), requesting exactly len bytes retrieves the length
byte and only len - 1 data bytes.
This means the last byte of the payload is dropped, and the subsequent
left-shift copies a trailing uninitialized byte into the final payload
position. Should it request len + 1 bytes instead?
[ ... ]
> +static ssize_t fd5121_send_byte_store(struct device *dev,
> + struct device_attribute *da,
> + const char *buf, size_t count)
> +{
> + struct i2c_client *client = to_i2c_client(dev->parent);
> + u8 val = 0;
> + int ret;
> +
> + ret = kstrtou8(buf, 10, &val);
> + if (ret < 0)
> + return ret;
> + ret = i2c_smbus_write_byte(client, val);
[Severity: High]
Does this allow userspace command injection?
This attribute is mapped to PMBUS_REG_MFR_SVR_GO, but it parses the
user-provided string into val and directly sends that as the SMBus command
code, completely ignoring attr->index. This allows any arbitrary command
code to be broadcast instead of SVR_GO.
[ ... ]
> +static ssize_t fd5121_byte_store(struct device *dev,
> + struct device_attribute *da,
> + const char *buf, size_t count)
> +{
> + struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> + struct i2c_client *client = to_i2c_client(dev->parent);
> + u8 reg = attr->index;
> + int ret = 0;
> + u8 val = 0;
> +
> + switch (reg) {
> + case PMBUS_REG_MFR_CONFIG_ACTIVATE:
> + ret = fd5121_config_activate(client);
> + if (ret < 0)
> + return ret;
> + return count;
> + default:
> + ret = kstrtou8(buf, 10, &val);
> + if (ret < 0)
> + return ret;
> + break;
> + }
> + if (reg == PMBUS_PAGE && ((val != 0 && val != 1 &&
> + val != GENMASK(7, 0))))
> + return -EINVAL;
> + ret = i2c_smbus_write_byte_data(client, reg, val);
[Severity: High]
Can writing to the PMBUS_PAGE attribute directly corrupt the PMBus core's
page tracking?
By modifying the physical hardware's page using raw i2c_smbus_write_byte_data
instead of the PMBus core accessors, the core's cached data->currpage
becomes out of sync. Subsequent accesses by the PMBus core could execute
on the wrong hardware rail.
[Severity: Medium]
Additionally, if this is used to send PMBUS_CLEAR_FAULTS, does it violate
the PMBus specification?
CLEAR_FAULTS is strictly a Send Byte command with no data payload, but
i2c_smbus_write_byte_data() transmits an extra data byte.
[ ... ]
> +static ssize_t fd5121_word_store(struct device *dev,
> + struct device_attribute *da,
> + const char *buf, size_t count)
> +{
> + struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> + struct i2c_client *client = to_i2c_client(dev->parent);
> + u8 reg = attr->index;
> + s16 val = 0;
> + int ret = 0;
> +
> + switch (reg) {
> + case PMBUS_REG_MFR_PWD_PROGRAM_RAM:
> + val = 0xC93F;
> + break;
> + default:
> + ret = kstrtos16(buf, 10, &val);
[Severity: Medium]
Does using kstrtos16() prevent configuring valid unsigned PMBus word values?
PMBus word registers represent 16-bit unsigned integers, but parsing
them as a signed s16 causes any valid input between 32768 and 65535 to
be rejected with an -ERANGE error.
[ ... ]
> +static ssize_t fd5121_word_show(struct device *dev,
> + struct device_attribute *da, char *buf)
> +{
> + struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> + struct i2c_client *client = to_i2c_client(dev->parent);
> + u8 data[I2C_SMBUS_BLOCK_MAX] = { 0 };
> + u8 reg = attr->index;
> + s32 ret = 0;
> +
> + if (fd5121_blk_rd_reg(reg)) {
> + ret = fd5121_read_block_data(client, reg, 2, data);
> + if (ret >= 0)
> + ret = get_unaligned_le16(data);
> + } else
> + ret = i2c_smbus_read_word_data(client, reg);
[Severity: High]
Does this direct sysfs read lead to a race condition with the PMBus core?
Since these paged registers are read directly via i2c_smbus_read_word_data
without acquiring data->update_lock or ensuring the correct page is active,
concurrent PMBus operations could cause this to read from the wrong rail.
[ ... ]
> +static s32 fd5121_write_block_data(const struct i2c_client *client,
> + u8 cmd_code, u8 len, u8 *pbuf)
> +{
> + s32 ret = 0;
> +
> + if (!i2c_check_functionality(client->adapter,
> + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA))
> + ret = i2c_smbus_write_i2c_block_data(client, cmd_code,
> + len, pbuf);
[Severity: High]
Does this I2C block write fallback omit the mandatory PMBus length byte?
Unlike the SMBus equivalent, the I2C block write API does not prepend
a length byte on the wire. Without this length byte, the hardware will
misinterpret the first data byte (pbuf[0]) as the length, corrupting
the data payload.
[ ... ]
> +static ssize_t fd5121_block_show(struct device *dev,
> + struct device_attribute *da, char *buf)
> +{
> + struct i2c_client *client = to_i2c_client(dev->parent);
> + struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> + u8 buffer[I2C_SMBUS_BLOCK_MAX] = { 0 };
> + u8 reg = attr->index;
> + int printed = 0;
> + s32 ret = 0;
> + u8 len = 0;
> + int i = 0;
> +
> + if (reg == PMBUS_REG_MFR_FAULTY_SPS) {
> + int to_print = 0;
> +
> + len = 7;
> + ret = fd5121_read_block_data(client, reg, len, buffer);
> + if (ret < 0)
> + return ret;
> + printed = 0;
> + to_print = (ret < len) ? ret : len;
> + for (i = 0; i < to_print; i++)
> + printed += scnprintf(buf + printed,
> + PAGE_SIZE - printed,
> + "%02x", buffer[i]);
> + printed += scnprintf(buf + printed,
> + PAGE_SIZE - printed, "\n");
> + return printed;
> + } else if (reg == PMBUS_REG_BBR_RAM ||
> + reg == PMBUS_REG_BBR_OTP) {
> + u32 len = (reg == PMBUS_REG_BBR_OTP) ? 165 : 164;
> +
> + /* Extra byte may be needed in case we need to store
> + * the length of the data
> + */
> + u8 *tmp_in = kcalloc(len+1, sizeof(u8), GFP_KERNEL);
> +
> + if (tmp_in == NULL)
> + return -ENOMEM;
> + ret = fd5121_read_block_data(client, reg, len, tmp_in);
[Severity: Medium]
Will this operation silently truncate the read to 32 bytes?
The requested lengths are 164 and 165, but the underlying I2C/SMBus
block read functions (i2c_smbus_read_block_data and its I2C fallback)
are strictly capped at I2C_SMBUS_BLOCK_MAX (32 bytes).
[ ... ]
> +static struct attribute *fd5121_paged_attrs[] = {
> + &sensor_dev_attr_operation.dev_attr.attr,
> + &sensor_dev_attr_vout_raw.dev_attr.attr,
> + &sensor_dev_attr_on_off_config.dev_attr.attr,
> + &sensor_dev_attr_clear_faults.dev_attr.attr,
> + &sensor_dev_attr_write_protect.dev_attr.attr,
> + &sensor_dev_attr_smbalert_mask.dev_attr.attr,
> + &sensor_dev_attr_vout_mode.dev_attr.attr,
> + &sensor_dev_attr_vout_command.dev_attr.attr,
[Severity: Medium]
Is it intentional to expose standard PMBus registers as raw custom sysfs
files here?
Defining over 80 standard PMBus commands (like vout_command, operation,
clear_faults) as custom attributes bypasses the standardized hwmon ABI
(e.g., in1_input). The PMBus core framework automatically translates
these standard features into the required ABI format.
[ ... ]
> +static int fd5121_read_word_data(struct i2c_client *client, int page,
> + int phase, int reg)
> +{
> + int ret;
> +
> + ret = fd5121_valid_reg(client, reg);
> + if (ret < 0)
> + return ret;
> +
> + ret = pmbus_read_word_data(client, page, phase, reg);
> + if (ret < 0)
> + return ret;
> +
> + /* Chip reports VOUT_MODE as vid. But gives raw value 1mV per bit.
> + * So, encode the READ_VOUT value so that it gets decoded and
> + * reported correctly.
> + */
> + if (reg == PMBUS_READ_VOUT)
> + ret = DIV_ROUND_CLOSEST(155000 - ret * 100, 625);
> + return ret;
> +}
[Severity: High]
Does this calculation fail for voltages above 1.55V?
Since the raw VOUT is 1mV per bit, if ret is greater than 1550, the
numerator (155000 - ret * 100) evaluates to a negative number. The PMBus
core will interpret this negative value as an error code (e.g., -72),
causing sysfs reads to fail.
> +static int fd5121_write_word_data(struct i2c_client *client, int page,
> + int reg, u16 word)
> +{
> + int ret;
> +
> + ret = fd5121_valid_reg(client, reg);
> + if (ret < 0)
> + return ret;
> + ret = pmbus_write_word_data(client, page, reg, word);
> + return ret;
> +}
[Severity: High]
Is there a missing reverse translation for PMBUS_VOUT_COMMAND?
Because info->vrm_version[idx] = amd625mv is declared, the PMBus core
translates requested voltages into AMD VIDs and sends them here. Without
an inverse translation to the hardware's native 1mV/bit format, a VID
like 88 would be written directly, severely undervolting the hardware
to 88mV.
[ ... ]
> +static int fd5121_probe(struct i2c_client *client)
> +{
> + struct pmbus_driver_info *info;
> + struct fd5121_data *pdata;
> + u32 id;
> +
> + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
> + return -EOPNOTSUPP;
[Severity: Medium]
Does this unconditionally break compatibility with pure SMBus controllers?
Many standard PC and server host controllers only support SMBus operations
and do not set the I2C_FUNC_I2C flag. Since the driver heavily utilizes
SMBus commands, checking for full I2C functionality might prevent the driver
from probing on common hardware.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260622-support-fd5121-from-onsemi-v1-0-b31767689c65@onsemi.com?part=3
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-06-23 6:07 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-23 5:55 [PATCH 0/3] Support onsemi's FD5121 multiphase digital controller Selvamani Rajagopal via B4 Relay
2026-06-23 5:55 ` Selvamani Rajagopal
2026-06-23 5:55 ` [PATCH 1/3] Documentation/hwmon: Add onsemi's FD5121 controllers' documentation Selvamani Rajagopal via B4 Relay
2026-06-23 5:55 ` Selvamani Rajagopal
2026-06-23 5:59 ` sashiko-bot
2026-06-23 5:55 ` [PATCH 2/3] dt-bindings: hwmon: pmbus: Support for onsemi's FD5121 Selvamani Rajagopal via B4 Relay
2026-06-23 5:55 ` Selvamani Rajagopal
2026-06-23 6:05 ` sashiko-bot
2026-06-23 5:55 ` [PATCH 3/3] hwmon: (pmbus/fd5121): Add support FD5121, FD5123 and FD5125 Selvamani Rajagopal via B4 Relay
2026-06-23 5:55 ` Selvamani Rajagopal
2026-06-23 6:07 ` sashiko-bot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.