public inbox for devicetree@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 1/5] i3c: master: Expose the APIs to support I3C hub
@ 2026-02-06 12:01 Aman Kumar Pandey
  2026-02-06 12:01 ` [PATCH v5 2/5] dt-bindings: i3c: Add NXP P3H2x4x i3c-hub support Aman Kumar Pandey
                   ` (4 more replies)
  0 siblings, 5 replies; 12+ messages in thread
From: Aman Kumar Pandey @ 2026-02-06 12:01 UTC (permalink / raw)
  To: linux-kernel, linux-i3c, alexandre.belloni, krzk+dt, robh,
	conor+dt, devicetree, broonie, lee, Frank.Li, lgirdwood
  Cc: vikash.bansal, priyanka.jain, shashank.rebbapragada,
	Aman Kumar Pandey

Expose i3c_master_send_ccc_cmd(), i3c_master_supports_ccc_cmd(),
i3c_master_reattach_i3c_dev(), i3c_master_direct_attach_i3c_dev(),
and i3c_master_direct_detach_i3c_dev() to support I3C hub.

Signed-off-by: Aman Kumar Pandey <aman.kumarpandey@nxp.com>

---
Changes in v5: 
 - No change

Changes in v4:
 - Updated I3C master to handle hub support
---
---
 drivers/i3c/master.c       | 123 ++++++++++++++++++++++++++++++++++++-
 include/linux/i3c/master.h |  11 ++++
 2 files changed, 132 insertions(+), 2 deletions(-)

diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 7f606c871648..c92152ff795f 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -1544,8 +1544,76 @@ static int i3c_master_attach_i3c_dev(struct i3c_master_controller *master,
 	return 0;
 }
 
-static int i3c_master_reattach_i3c_dev(struct i3c_dev_desc *dev,
-				       u8 old_dyn_addr)
+/**
+ * i3c_master_direct_attach_i3c_dev() - attach an I3C device to a master
+ * @master: I3C master controller to attach the device to
+ * @dev: I3C device descriptor representing the device
+ *
+ * This function attaches an I3C device to its master controller once the
+ * device has a valid address on the bus. Devices without
+ * an assigned address are ignored. The master device itself is never
+ * attached through this bus.
+ *
+ * Return: 0 on success, or a negative error code if the attach operation
+ *         fails in the master controller driver.
+ */
+int i3c_master_direct_attach_i3c_dev(struct i3c_master_controller *master,
+				     struct i3c_dev_desc *dev)
+{
+	int ret = 0;
+
+	/*
+	 * We don't attach devices to the controller until they are
+	 * addressable on the bus.
+	 */
+
+	if (!dev->info.static_addr && !dev->info.dyn_addr)
+		return 0;
+
+	/* Do not attach the master device itself. */
+	if (master->this != dev && master->ops->attach_i3c_dev)
+		ret = master->ops->attach_i3c_dev(dev);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(i3c_master_direct_attach_i3c_dev);
+
+/**
+ * i3c_master_direct_detach_i3c_dev - Detach an I3C device from its master
+ * @dev: I3C device descriptor to be detached
+ *
+ * This function detaches an I3C device from its master controller.
+ * It ensures that the master itself is not detached. If the device is not
+ * the master and the master controller provides a detach operation,
+ * the detach callback is invoked to perform the actual removal.
+ */
+void i3c_master_direct_detach_i3c_dev(struct i3c_dev_desc *dev)
+{
+	struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
+	/* Do not detach the master device itself. */
+	if (master->this != dev && master->ops->detach_i3c_dev)
+		master->ops->detach_i3c_dev(dev);
+}
+EXPORT_SYMBOL_GPL(i3c_master_direct_detach_i3c_dev);
+
+/**
+ * i3c_master_reattach_i3c_dev() - reattach an I3C device with a new address
+ * @dev: I3C device descriptor to reattach
+ * @old_dyn_addr: previous dynamic address of the device
+ *
+ * This function reattaches an existing I3C device to the bus when its dynamic
+ * address has changed. It updates the bus address slot status accordingly:
+ * - Marks the new dynamic address as occupied by an I3C device.
+ * - Frees the old dynamic address slot if applicable.
+ *
+ * This function must be called with the bus lock held in write mode.
+ *
+ * Return: 0 on success, or a negative error code if reattachment fails
+ *         (e.g. -EBUSY if the new address slot is not free).
+ */
+int i3c_master_reattach_i3c_dev(struct i3c_dev_desc *dev,
+				u8 old_dyn_addr)
 {
 	struct i3c_master_controller *master = i3c_dev_get_master(dev);
 	int ret;
@@ -1569,6 +1637,7 @@ static int i3c_master_reattach_i3c_dev(struct i3c_dev_desc *dev,
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(i3c_master_reattach_i3c_dev);
 
 static void i3c_master_detach_i3c_dev(struct i3c_dev_desc *dev)
 {
@@ -1692,6 +1761,56 @@ i3c_master_register_new_i3c_devs(struct i3c_master_controller *master)
 	}
 }
 
+/**
+ * i3c_master_supports_ccc_cmd() - check CCC command support
+ * @master: I3C master controller
+ * @cmd: CCC command to verify
+ *
+ * This function verifies whether the given I3C master controller supports
+ * the specified Common Command Code (CCC).
+ *
+ * Return: 0 if the CCC command is supported and executed successfully,
+ *         -EINVAL if arguments are invalid,
+ *         -EOPNOTSUPP if the master does not support CCC commands,
+ *         or another negative error code from the master's operation.
+ */
+int i3c_master_supports_ccc_cmd(struct i3c_master_controller *master,
+				const struct i3c_ccc_cmd *cmd)
+{
+	if (!cmd || !master)
+		return -EINVAL;
+
+	if (!master->ops->supports_ccc_cmd)
+		return -EOPNOTSUPP;
+
+	return master->ops->supports_ccc_cmd(master, cmd);
+}
+EXPORT_SYMBOL_GPL(i3c_master_supports_ccc_cmd);
+
+/**
+ * i3c_master_send_ccc_cmd() - send a CCC command
+ * @master: I3C master controller issuing the command
+ * @cmd: CCC command to be sent
+ *
+ * This function sends a Common Command Code (CCC) command to devices on the
+ * I3C bus. It acquires the bus maintenance lock, executes the command, and
+ * then releases the lock to ensure safe access to the bus.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+int i3c_master_send_ccc_cmd(struct i3c_master_controller *master,
+			    struct i3c_ccc_cmd *cmd)
+{
+	int ret;
+
+	i3c_bus_maintenance_lock(&master->bus);
+	ret = i3c_master_send_ccc_cmd_locked(master, cmd);
+	i3c_bus_maintenance_unlock(&master->bus);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(i3c_master_send_ccc_cmd);
+
 /**
  * i3c_master_do_daa() - do a DAA (Dynamic Address Assignment)
  * @master: master doing the DAA
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index 58d01ed4cce7..b059da7777d7 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -602,6 +602,17 @@ void i3c_master_dma_unmap_single(struct i3c_dma *dma_xfer);
 DEFINE_FREE(i3c_master_dma_unmap_single, void *,
 	    if (_T) i3c_master_dma_unmap_single(_T))
 
+int i3c_master_send_ccc_cmd(struct i3c_master_controller *master,
+			    struct i3c_ccc_cmd *cmd);
+
+int i3c_master_supports_ccc_cmd(struct i3c_master_controller *master,
+				const struct i3c_ccc_cmd *cmd);
+
+int i3c_master_reattach_i3c_dev(struct i3c_dev_desc *dev,
+				u8 old_dyn_addr);
+int i3c_master_direct_attach_i3c_dev(struct i3c_master_controller *master,
+				     struct i3c_dev_desc *dev);
+void i3c_master_direct_detach_i3c_dev(struct i3c_dev_desc *dev);
 int i3c_master_set_info(struct i3c_master_controller *master,
 			const struct i3c_device_info *info);
 
-- 
2.25.1


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

* [PATCH v5 2/5] dt-bindings: i3c: Add NXP P3H2x4x i3c-hub support
  2026-02-06 12:01 [PATCH v5 1/5] i3c: master: Expose the APIs to support I3C hub Aman Kumar Pandey
@ 2026-02-06 12:01 ` Aman Kumar Pandey
  2026-02-10  2:33   ` Rob Herring
  2026-02-06 12:01 ` [PATCH v5 3/5] mfd: p3h2x4x: Add driver for NXP P3H2x4x i3c hub and on-die regulator Aman Kumar Pandey
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 12+ messages in thread
From: Aman Kumar Pandey @ 2026-02-06 12:01 UTC (permalink / raw)
  To: linux-kernel, linux-i3c, alexandre.belloni, krzk+dt, robh,
	conor+dt, devicetree, broonie, lee, Frank.Li, lgirdwood
  Cc: vikash.bansal, priyanka.jain, shashank.rebbapragada,
	Aman Kumar Pandey

Add bindings for the NXP P3H2x4x (P3H2440/P3H2441/P3H2840/P3H2841)
multiport I3C hub family. These devices connect to a host via
I3C/I2C/SMBus and allow communication with multiple downstream
peripherals.

Signed-off-by: Aman Kumar Pandey <aman.kumarpandey@nxp.com>
Signed-off-by: Vikash Bansal <vikash.bansal@nxp.com>

---
Changes in v5:
 - Removed SW properties: cp0-ldo-microvolt,cp1-ldo-microvolt,
   tp0145-ldo-microvolt, tp2367-ldo-microvolt 
 - Changed supply entries and its descriptions

Changes in v4:
 - Fixed DT binding check warning
 - Removed SW properties: ibi-enable, local-dev, and always-enable

Changes in v3:
 - Added MFD (Multi-Function Device) support for I3C hub and on-die regulator
 - Added Regulator supply node

Changes in v2:
 - Fixed DT binding check warning
 - Revised logic for parsing DTS nodes
---
---
 .../devicetree/bindings/i3c/nxp,p3h2840.yaml  | 209 ++++++++++++++++++
 MAINTAINERS                                   |   8 +
 2 files changed, 217 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i3c/nxp,p3h2840.yaml

diff --git a/Documentation/devicetree/bindings/i3c/nxp,p3h2840.yaml b/Documentation/devicetree/bindings/i3c/nxp,p3h2840.yaml
new file mode 100644
index 000000000000..edc1f988c0d5
--- /dev/null
+++ b/Documentation/devicetree/bindings/i3c/nxp,p3h2840.yaml
@@ -0,0 +1,209 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright 2025 NXP
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/i3c/nxp,p3h2840.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP P3H2X4X I3C HUB
+
+maintainers:
+  - Aman Kumar Pandey <aman.kumarpandey@nxp.com>
+  - Vikash Bansal <vikash.bansal@nxp.com>
+
+description: |
+  P3H2x4x (P3H2440/P3H2441/P3H2840/P3H2841) is a family of multiport I3C
+  hub devices that connect to:-
+  1. A host CPU via I3C/I2C/SMBus bus on upstream side and connect to multiple
+     peripheral devices on the downstream  side.
+  2. Have two Controller Ports which can support either
+     I2C/SMBus or I3C buses and connect to a CPU, BMC or SOC.
+  3. P3H2840/ P3H2841 are 8 port I3C hub with eight I3C/I2C Target Port.
+  4. P3H2440/ P3H2441 are 4 port I3C hub with four I3C/I2C Target Port.
+     Target ports can be configured as I2C/SMBus, I3C or GPIO and connect to
+     peripherals.
+
+allOf:
+  - $ref: /schemas/i3c/i3c.yaml#
+
+properties:
+  compatible:
+    const: nxp,p3h2840
+
+  tp0145-pullup-ohms:
+    description:
+      Selects the pull up resistance for target Port 0/1/4/5, in ohms.
+    enum: [250, 500, 1000, 2000]
+    default: 500
+
+  tp2367-pullup-ohms:
+    description:
+      Selects the pull up resistance for target Port 2/3/6/7, in ohms.
+    enum: [250, 500, 1000, 2000]
+    default: 500
+
+  cp0-io-strength-ohms:
+    description:
+      Selects the IO drive strength for controller Port 0, in ohms.
+    enum: [20, 30, 40, 50]
+    default: 20
+
+  cp1-io-strength-ohms:
+    description:
+      Selects the IO drive strength for controller Port 1, in ohms.
+    enum: [20, 30, 40, 50]
+    default: 20
+
+  tp0145-io-strength-ohms:
+    description:
+      Selects the IO drive strength for target port 0/1/4/5, in ohms.
+    enum: [20, 30, 40, 50]
+    default: 20
+
+  tp2367-io-strength-ohms:
+    description:
+      Selects the IO drive strength for target port 2/3/6/7, in ohms.
+    enum: [20, 30, 40, 50]
+    default: 20
+
+  vcc1-supply:
+    description: Controller port 0 power supply.
+
+  vcc2-supply:
+    description: Controller port 1 power supply.
+
+  vcc3-supply:
+    description: Target port 0/1/4/5 power supply.
+
+  vcc4-supply:
+    description: Target port 2/3/6/7 power supply.
+
+  regulators:
+    type: object
+    additionalProperties: false
+
+    properties:
+      LDO_CP0:
+        type: object
+        $ref: /schemas/regulator/regulator.yaml#
+        unevaluatedProperties: false
+
+      LDO_CP1:
+        type: object
+        $ref: /schemas/regulator/regulator.yaml#
+        unevaluatedProperties: false
+
+      LDO_TPG0:
+        type: object
+        $ref: /schemas/regulator/regulator.yaml#
+        unevaluatedProperties: false
+
+      LDO_TPG1:
+        type: object
+        $ref: /schemas/regulator/regulator.yaml#
+        unevaluatedProperties: false
+
+patternProperties:
+  "^i3c@[0-7]$":
+    type: object
+    $ref: /schemas/i3c/i3c.yaml#
+    unevaluatedProperties: false
+
+    properties:
+      reg:
+        description:
+          The I3C HUB Target Port number.
+        maxItems: 1
+
+      pullup-enable:
+        type: boolean
+        description:
+          Enables the on-die pull-up for Target Port.
+
+  "^(i2c|smbus)@[0-7]$":
+    type: object
+    $ref: /schemas/i2c/i2c-controller.yaml#
+    unevaluatedProperties: false
+
+    properties:
+      reg:
+        description:
+          The I3C HUB Target Port number.
+        maxItems: 1
+
+      pullup-enable:
+        type: boolean
+        description:
+          Enables the on-die pull-up for Target Port.
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    i3c {
+        #address-cells = <3>;
+        #size-cells = <0>;
+
+        hub@70,236153000c2 {
+            reg = <0x70 0x236 0x3000c2>;
+            #address-cells = <1>;
+            #size-cells = <0>;
+            assigned-address = <0x50>;
+
+            tp0145-pullup-ohm = <1000>;
+            tp2367-pullup-ohm = <1000>;
+            cp0-io-strength-ohm = <50>;
+            cp1-io-strength-ohm = <50>;
+            tp0145-io-strength-ohm = <50>;
+            tp2367-io-strength-ohm = <50>;
+            vcc3-supply = <&reg_tpg0>;
+            vcc4-supply = <&reg_tpg1>;
+
+            regulators {
+                reg_cp0: LDO_CP0 {
+                    regulator-name = "ldo_cp0";
+                    regulator-min-microvolt = <1800000>;
+                    regulator-max-microvolt = <1800000>;
+                };
+
+                reg_cp1: LDO_CP1 {
+                    regulator-name = "ldo_cp1";
+                    regulator-min-microvolt = <1800000>;
+                    regulator-max-microvolt = <1800000>;
+                };
+
+                reg_tpg0: LDO_TPG0 {
+                    regulator-name = "ldo_tpg0";
+                    regulator-min-microvolt = <1800000>;
+                    regulator-max-microvolt = <1800000>;
+                };
+
+                reg_tpg1: LDO_TPG1 {
+                    regulator-name = "ldo_tpg1";
+                    regulator-min-microvolt = <1800000>;
+                    regulator-max-microvolt = <1800000>;
+                };
+            };
+
+            smbus@0 {
+                reg = <0x0>;
+                #address-cells = <1>;
+                #size-cells = <0>;
+                pullup-enable;
+            };
+
+            i2c@1 {
+                reg = <0x1>;
+                #address-cells = <1>;
+                #size-cells = <0>;
+                pullup-enable;
+            };
+
+            i3c@2 {
+                reg = <0x2>;
+                #address-cells = <3>;
+                #size-cells = <0>;
+                pullup-enable;
+            };
+        };
+    };
diff --git a/MAINTAINERS b/MAINTAINERS
index e08767323763..7e1a6cb22ca3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18906,6 +18906,14 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/ptp/nxp,ptp-netc.yaml
 F:	drivers/ptp/ptp_netc.c
 
+NXP P3H2X4X I3C-HUB DRIVER
+M:	Vikash Bansal <vikash.bansal@nxp.com>
+M:	Aman Kumar Pandey <aman.kumarpandey@nxp.com>
+L:	linux-kernel@vger.kernel.org
+L:	linux-i3c-owner@lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/i3c/nxp,p3h2840.yaml
+
 NXP PF5300/PF5301/PF5302 PMIC REGULATOR DEVICE DRIVER
 M:	Woodrow Douglass <wdouglass@carnegierobotics.com>
 S:	Maintained
-- 
2.25.1


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

* [PATCH v5 3/5] mfd: p3h2x4x: Add driver for NXP P3H2x4x i3c hub and on-die regulator
  2026-02-06 12:01 [PATCH v5 1/5] i3c: master: Expose the APIs to support I3C hub Aman Kumar Pandey
  2026-02-06 12:01 ` [PATCH v5 2/5] dt-bindings: i3c: Add NXP P3H2x4x i3c-hub support Aman Kumar Pandey
@ 2026-02-06 12:01 ` Aman Kumar Pandey
  2026-02-06 12:52   ` Krzysztof Kozlowski
  2026-02-06 12:01 ` [PATCH v5 4/5] regulator: p3h2x4x: Add driver for on-die regulators in NXP P3H2x4x i3c hub Aman Kumar Pandey
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 12+ messages in thread
From: Aman Kumar Pandey @ 2026-02-06 12:01 UTC (permalink / raw)
  To: linux-kernel, linux-i3c, alexandre.belloni, krzk+dt, robh,
	conor+dt, devicetree, broonie, lee, Frank.Li, lgirdwood
  Cc: vikash.bansal, priyanka.jain, shashank.rebbapragada,
	Aman Kumar Pandey

Add core MFD support for the NXP P3H2x4x (P3H2440/P3H2441/P3H2840/P3H2841)
family of multiport I3C hub devices. These devices connect to a host via
I3C/I2C/SMBus and expose multiple downstream target ports.

This patch introduces the MFD driver that registers sub-devices for the
I3C hub and on-die regulators.

Signed-off-by: Aman Kumar Pandey <aman.kumarpandey@nxp.com>
Signed-off-by: Vikash Bansal <vikash.bansal@nxp.com>

---
Changes in v5:
 - Corrected the ordering in the Makefile and Kconfig for MFD_P3H2X4X
 - Updated dev_err_probe() for regmap_init failure.
 - Updated module description

Changes in v4:
 - Split the driver into three separate patches(mfd, regulator and I3C hub)
 - Added support for NXP P3H2x4x MFD functionality
---
---
 MAINTAINERS                 |   2 +
 drivers/mfd/Kconfig         |  12 ++++
 drivers/mfd/Makefile        |   1 +
 drivers/mfd/p3h2840.c       | 128 ++++++++++++++++++++++++++++++++++++
 include/linux/mfd/p3h2840.h |  28 ++++++++
 5 files changed, 171 insertions(+)
 create mode 100644 drivers/mfd/p3h2840.c
 create mode 100644 include/linux/mfd/p3h2840.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 7e1a6cb22ca3..6c247e4a0c12 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18913,6 +18913,8 @@ L:	linux-kernel@vger.kernel.org
 L:	linux-i3c-owner@lists.infradead.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/i3c/nxp,p3h2840.yaml
+F:	drivers/mfd/p3h2840.c
+F:	include/linux/mfd/p3h2840.h
 
 NXP PF5300/PF5301/PF5302 PMIC REGULATOR DEVICE DRIVER
 M:	Woodrow Douglass <wdouglass@carnegierobotics.com>
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index aace5766b38a..10d03cf308d7 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -606,6 +606,18 @@ config MFD_MX25_TSADC
 	  i.MX25 processors. They consist of a conversion queue for general
 	  purpose ADC and a queue for Touchscreens.
 
+config MFD_P3H2X4X
+       tristate "NXP P3H2X4X I3C Hub Device"
+       depends on I3C
+       select MFD_CORE
+       select REGMAP_I3C
+       help
+         Enable Support for NXP P3H244x/P3H284x I3C HUB device using I3C/I2c
+         communication interface.
+
+         This driver provides support for I3C Hub and regulator, additional
+         drivers must be enabled in order to use the functionality of the device.
+
 config MFD_PF1550
 	tristate "NXP PF1550 PMIC Support"
 	depends on I2C=y && OF
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index e75e8045c28a..aaadf50fedf4 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -122,6 +122,7 @@ obj-$(CONFIG_MFD_MC13XXX)	+= mc13xxx-core.o
 obj-$(CONFIG_MFD_MC13XXX_SPI)	+= mc13xxx-spi.o
 obj-$(CONFIG_MFD_MC13XXX_I2C)	+= mc13xxx-i2c.o
 
+obj-$(CONFIG_MFD_P3H2X4X) 	+= p3h2840.o
 obj-$(CONFIG_MFD_PF1550)	+= pf1550.o
 
 obj-$(CONFIG_MFD_NCT6694)	+= nct6694.o
diff --git a/drivers/mfd/p3h2840.c b/drivers/mfd/p3h2840.c
new file mode 100644
index 000000000000..acc10c396b47
--- /dev/null
+++ b/drivers/mfd/p3h2840.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 NXP
+ * P3H2x4x i3c hub and regulator device.
+ */
+
+#include <linux/i3c/master.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/p3h2840.h>
+#include <linux/regmap.h>
+
+static const struct mfd_cell p3h2x4x_devs[] = {
+	{
+		.name = "p3h2x4x-regulator",
+	},
+	{
+		.name = "p3h2x4x-i3c-hub",
+	},
+};
+
+static const struct regmap_config p3h2x4x_regmap_config = {
+	.reg_bits = P3H2x4x_REG_BITS,
+	.val_bits = P3H2x4x_VAL_BITS,
+	.max_register = 0xFF,
+};
+
+static int p3h2x4x_device_probe_i3c(struct i3c_device *i3cdev)
+{
+	struct p3h2x4x_dev *p3h2x4x;
+	int ret;
+
+	p3h2x4x = devm_kzalloc(&i3cdev->dev, sizeof(*p3h2x4x), GFP_KERNEL);
+	if (!p3h2x4x)
+		return -ENOMEM;
+
+	i3cdev_set_drvdata(i3cdev, p3h2x4x);
+
+	p3h2x4x->regmap = devm_regmap_init_i3c(i3cdev, &p3h2x4x_regmap_config);
+	if (IS_ERR(p3h2x4x->regmap)) {
+		return dev_err_probe(&i3cdev->dev, PTR_ERR(p3h2x4x->regmap),
+				     "Failed to register I3C HUB regmap\n");
+	}
+
+	p3h2x4x->is_p3h2x4x_in_i3c = true;
+	p3h2x4x->i3cdev = i3cdev;
+
+	ret = devm_mfd_add_devices(&i3cdev->dev, PLATFORM_DEVID_NONE,
+				   p3h2x4x_devs, ARRAY_SIZE(p3h2x4x_devs),
+				   NULL, 0, NULL);
+	if (ret)
+		return dev_err_probe(&i3cdev->dev, ret, "Failed to add sub devices\n");
+
+	return 0;
+}
+
+static int p3h2x4x_device_probe_i2c(struct i2c_client *client)
+{
+	struct p3h2x4x_dev *p3h2x4x;
+	int ret;
+
+	p3h2x4x = devm_kzalloc(&client->dev, sizeof(*p3h2x4x), GFP_KERNEL);
+	if (!p3h2x4x)
+		return -ENOMEM;
+
+	i2c_set_clientdata(client, p3h2x4x);
+
+	p3h2x4x->regmap = devm_regmap_init_i2c(client, &p3h2x4x_regmap_config);
+	if (IS_ERR(p3h2x4x->regmap)) {
+		return dev_err_probe(&client->dev, PTR_ERR(p3h2x4x->regmap),
+				     "Failed to register I3C HUB regmap\n");
+	}
+
+	p3h2x4x->is_p3h2x4x_in_i3c = false;
+	p3h2x4x->i2c_client = client;
+
+	ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE,
+				   p3h2x4x_devs, ARRAY_SIZE(p3h2x4x_devs),
+				   NULL, 0, NULL);
+	if (ret)
+		return dev_err_probe(&client->dev, ret, "Failed to add sub devices\n");
+
+	return 0;
+}
+
+/* p3h2x4x ids (i3c) */
+static const struct i3c_device_id p3h2x4x_i3c_ids[] = {
+	I3C_CLASS(I3C_DCR_HUB, NULL),
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(i3c, p3h2x4x_i3c_ids);
+
+/* p3h2x4x ids (i2c) */
+static const struct i2c_device_id p3h2x4x_i2c_id_table[] = {
+	{ "nxp-i3c-hub" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, p3h2x4x_i2c_id_table);
+
+static const struct of_device_id  p3h2x4x_i2c_of_match[] = {
+	{ .compatible = "nxp,p3h2840", },
+	{ /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, p3h2x4x_i2c_of_match);
+static struct i3c_driver p3h2x4x_i3c = {
+	.driver = {
+		.name = "p3h2x4x_i3c_drv",
+	},
+	.probe = p3h2x4x_device_probe_i3c,
+	.id_table = p3h2x4x_i3c_ids,
+};
+
+static struct i2c_driver p3h2x4x_i2c = {
+	.driver = {
+		.name = "p3h2x4x_i2c_drv",
+		.of_match_table = p3h2x4x_i2c_of_match,
+	},
+	.probe =  p3h2x4x_device_probe_i2c,
+	.id_table = p3h2x4x_i2c_id_table,
+};
+
+module_i3c_i2c_driver(p3h2x4x_i3c, &p3h2x4x_i2c);
+
+MODULE_AUTHOR("Aman Kumar Pandey <aman.kumarpandey@nxp.com>");
+MODULE_AUTHOR("vikash Bansal <vikash.bansal@nxp.com>");
+MODULE_DESCRIPTION("P3H2x4x I3C HUB multi function driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/p3h2840.h b/include/linux/mfd/p3h2840.h
new file mode 100644
index 000000000000..b7a9c7f3aa4d
--- /dev/null
+++ b/include/linux/mfd/p3h2840.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2025 NXP
+ * This header file contain private Reg address and its bit mapping etc.
+ */
+
+#ifndef P3H2840_H
+#define P3H2840_H
+
+#include <linux/types.h>
+
+/* Device Configuration Registers */
+#define P3H2x4x_DEV_REG_PROTECTION_CODE				0x10
+#define P3H2x4x_REGISTERS_LOCK_CODE				0x00
+#define P3H2x4x_REGISTERS_UNLOCK_CODE				0x69
+#define P3H2x4x_CP1_REGISTERS_UNLOCK_CODE			0x6a
+
+/* Reg config for Regmap */
+#define P3H2x4x_REG_BITS					8
+#define P3H2x4x_VAL_BITS					8
+
+struct p3h2x4x_dev {
+	struct i3c_device *i3cdev;
+	struct i2c_client *i2c_client;
+	struct regmap *regmap;
+	bool is_p3h2x4x_in_i3c;
+};
+#endif /* P3H2840_H */
-- 
2.25.1


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

* [PATCH v5 4/5] regulator: p3h2x4x: Add driver for on-die regulators in NXP P3H2x4x i3c hub
  2026-02-06 12:01 [PATCH v5 1/5] i3c: master: Expose the APIs to support I3C hub Aman Kumar Pandey
  2026-02-06 12:01 ` [PATCH v5 2/5] dt-bindings: i3c: Add NXP P3H2x4x i3c-hub support Aman Kumar Pandey
  2026-02-06 12:01 ` [PATCH v5 3/5] mfd: p3h2x4x: Add driver for NXP P3H2x4x i3c hub and on-die regulator Aman Kumar Pandey
@ 2026-02-06 12:01 ` Aman Kumar Pandey
  2026-02-06 17:19   ` Frank Li
  2026-02-06 22:19   ` kernel test robot
  2026-02-06 12:01 ` [PATCH v5 5/5] i3c: hub: p3h2x4x: Add support for NXP P3H2x4x I3C hub functionality Aman Kumar Pandey
  2026-02-06 17:06 ` [PATCH v5 1/5] i3c: master: Expose the APIs to support I3C hub Frank Li
  4 siblings, 2 replies; 12+ messages in thread
From: Aman Kumar Pandey @ 2026-02-06 12:01 UTC (permalink / raw)
  To: linux-kernel, linux-i3c, alexandre.belloni, krzk+dt, robh,
	conor+dt, devicetree, broonie, lee, Frank.Li, lgirdwood
  Cc: vikash.bansal, priyanka.jain, shashank.rebbapragada,
	Aman Kumar Pandey

The NXP P3H2x4x family integrates on-die regulators alongside I3C hub
functionality. This driver registers the regulators using the MFD
framework and exposes them via the regulator subsystem.

Signed-off-by: Aman Kumar Pandey <aman.kumarpandey@nxp.com>
Signed-off-by: Vikash Bansal <vikash.bansal@nxp.com>

---
Changes in v5:
 - Updated dev_err_probe() for regmap_init failure.
 - Updated module description

Changes in v4:
 - Split the driver into three separate patches (mfd, regulator and I3C hub) 
 - Introduced driver for on-die regulators in NXP P3H2x4x I3C hub
---
---
 MAINTAINERS                                   |   1 +
 drivers/regulator/Kconfig                     |  10 +
 drivers/regulator/Makefile                    |   1 +
 drivers/regulator/p3h2840_i3c_hub_regulator.c | 247 ++++++++++++++++++
 4 files changed, 259 insertions(+)
 create mode 100644 drivers/regulator/p3h2840_i3c_hub_regulator.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 6c247e4a0c12..6601830b6e86 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18914,6 +18914,7 @@ L:	linux-i3c-owner@lists.infradead.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/i3c/nxp,p3h2840.yaml
 F:	drivers/mfd/p3h2840.c
+F:	drivers/regulator/p3h2840_i3c_hub_regulator.c
 F:	include/linux/mfd/p3h2840.h
 
 NXP PF5300/PF5301/PF5302 PMIC REGULATOR DEVICE DRIVER
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index d2335276cce5..84684701dad7 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -1010,6 +1010,16 @@ config REGULATOR_MTK_DVFSRC
 	  of Mediatek. It allows for voting on regulator state
 	  between multiple users.
 
+config REGULATOR_P3H2X4X
+       tristate "P3H2X4X regulator support"
+       depends on MFD_P3H2X4X
+       help
+         This driver provides support for the voltage regulators of the
+         P3H244x/P3H284x multi-function I3C Hub device.
+
+         Say M here if you want to include support for this regulator as
+         a module. The module will be named "p3h2840_i3c_hub_regulator".
+
 config REGULATOR_PALMAS
 	tristate "TI Palmas PMIC Regulators"
 	depends on MFD_PALMAS
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 1beba1493241..145138f437d8 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -127,6 +127,7 @@ obj-$(CONFIG_REGULATOR_QCOM_RPMH) += qcom-rpmh-regulator.o
 obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o
 obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o
 obj-$(CONFIG_REGULATOR_QCOM_USB_VBUS) += qcom_usb_vbus-regulator.o
+obj-$(CONFIG_REGULATOR_P3H2X4X) += p3h2840_i3c_hub_regulator.o
 obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
 obj-$(CONFIG_REGULATOR_PCA9450) += pca9450-regulator.o
 obj-$(CONFIG_REGULATOR_PF0900) += pf0900-regulator.o
diff --git a/drivers/regulator/p3h2840_i3c_hub_regulator.c b/drivers/regulator/p3h2840_i3c_hub_regulator.c
new file mode 100644
index 000000000000..1e9abc398f5a
--- /dev/null
+++ b/drivers/regulator/p3h2840_i3c_hub_regulator.c
@@ -0,0 +1,247 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 NXP
+ * This P3H2x4x driver file contain functions for enable/disable regulator and voltage set/get.
+ */
+#include <linux/bitfield.h>
+#include <linux/mfd/p3h2840.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+
+#define P3H2x4x_LDO_AND_PULLUP_CONF				0x19
+#define P3H2x4x_LDO_ENABLE_DISABLE_MASK				GENMASK(3, 0)
+#define P3H2x4x_CP0_EN_LDO				        BIT(0)
+#define P3H2x4x_CP1_EN_LDO				        BIT(1)
+#define P3H2x4x_TP0145_EN_LDO					BIT(2)
+#define P3H2x4x_TP2367_EN_LDO					BIT(3)
+
+#define P3H2x4x_NET_OPER_MODE_CONF				0x15
+#define P3H2x4x_VCCIO_LDO_CONF					0x16
+#define P3H2x4x_CP0_VCCIO_LDO_VOLTAGE_MASK			GENMASK(1, 0)
+#define P3H2x4x_CP0_VCCIO_LDO_VOLTAGE(x)	\
+		FIELD_PREP(P3H2x4x_CP0_VCCIO_LDO_VOLTAGE_MASK, x)
+#define P3H2x4x_CP1_VCCIO_LDO_VOLTAGE_MASK			GENMASK(3, 2)
+#define P3H2x4x_CP1_VCCIO_LDO_VOLTAGE(x)	\
+		FIELD_PREP(P3H2x4x_CP1_VCCIO_LDO_VOLTAGE_MASK, x)
+#define P3H2x4x_TP0145_VCCIO_LDO_VOLTAGE_MASK			GENMASK(5, 4)
+#define P3H2x4x_TP0145_VCCIO_LDO_VOLTAGE(x)	\
+		FIELD_PREP(P3H2x4x_TP0145_VCCIO_LDO_VOLTAGE_MASK, x)
+#define P3H2x4x_TP2367_VCCIO_LDO_VOLTAGE_MASK			GENMASK(7, 6)
+#define P3H2x4x_TP2367_VCCIO_LDO_VOLTAGE(x)	\
+		FIELD_PREP(P3H2x4x_TP2367_VCCIO_LDO_VOLTAGE_MASK, x)
+#define P3H2x4x_LDO_COUNT					4
+
+struct p3h2x4x_regulator_dev {
+	struct regulator_dev *rp3h2x4x_dev[P3H2x4x_LDO_COUNT];
+	struct regmap *regmap;
+};
+
+static int p3h2x4x_regulator_enable(struct regulator_dev *rdev)
+{
+	int ret;
+	unsigned int orig;
+
+	ret = regmap_read(rdev->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE, &orig);
+	if (ret)
+		return ret;
+
+	if (orig != P3H2x4x_REGISTERS_UNLOCK_CODE) {
+		ret = regmap_write(rdev->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE,
+				   P3H2x4x_REGISTERS_UNLOCK_CODE);
+		if (ret)
+			return ret;
+	}
+
+	ret = regulator_enable_regmap(rdev);
+	if (ret)
+		return ret;
+
+	if (orig != P3H2x4x_REGISTERS_UNLOCK_CODE) {
+		ret = regmap_write(rdev->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE, orig);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+static int p3h2x4x_regulator_disable(struct regulator_dev *rdev)
+{
+	int ret;
+	unsigned int orig;
+
+	ret = regmap_read(rdev->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE, &orig);
+	if (ret)
+		return ret;
+
+	if (orig != P3H2x4x_REGISTERS_UNLOCK_CODE) {
+		ret = regmap_write(rdev->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE,
+				   P3H2x4x_REGISTERS_UNLOCK_CODE);
+		if (ret)
+			return ret;
+	}
+
+	ret = regulator_disable_regmap(rdev);
+	if (ret)
+		return ret;
+
+	if (orig != P3H2x4x_REGISTERS_UNLOCK_CODE) {
+		ret = regmap_write(rdev->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE, orig);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+static int p3h2x4x_regulator_set_voltage_sel(struct regulator_dev *rdev, unsigned int sel)
+{
+	int ret;
+	unsigned int orig;
+
+	ret = regmap_read(rdev->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE, &orig);
+	if (ret)
+		return ret;
+
+	if (orig != P3H2x4x_REGISTERS_UNLOCK_CODE) {
+		ret = regmap_write(rdev->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE,
+				   P3H2x4x_REGISTERS_UNLOCK_CODE);
+		if (ret)
+			return ret;
+	}
+
+	ret = regulator_set_voltage_sel_regmap(rdev, sel);
+	if (ret)
+		return ret;
+
+	if (orig != P3H2x4x_REGISTERS_UNLOCK_CODE) {
+		ret = regmap_write(rdev->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE, orig);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+static const struct regulator_ops p3h2x4x_ldo_ops = {
+	.list_voltage = regulator_list_voltage_table,
+	.map_voltage = regulator_map_voltage_iterate,
+	.set_voltage_sel = p3h2x4x_regulator_set_voltage_sel,
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+	.enable = p3h2x4x_regulator_enable,
+	.disable = p3h2x4x_regulator_disable,
+	.is_enabled = regulator_is_enabled_regmap,
+};
+
+static const unsigned int p3h2x4x_voltage_table[] = {
+	1000000,
+	1100000,
+	1200000,
+	1800000,
+};
+
+static struct regulator_desc p3h2x4x_regulators[] = {
+	{
+		.name = "LDO_CP0",
+		.of_match = of_match_ptr("LDO_CP0"),
+		.regulators_node = of_match_ptr("regulators"),
+		.volt_table = p3h2x4x_voltage_table,
+		.n_voltages = ARRAY_SIZE(p3h2x4x_voltage_table),
+		.ops = &p3h2x4x_ldo_ops,
+		.type = REGULATOR_VOLTAGE,
+		.owner = THIS_MODULE,
+		.enable_reg = P3H2x4x_LDO_AND_PULLUP_CONF,
+		.enable_mask = P3H2x4x_CP0_EN_LDO,
+		.vsel_reg = P3H2x4x_VCCIO_LDO_CONF,
+		.vsel_mask = P3H2x4x_CP0_VCCIO_LDO_VOLTAGE_MASK,
+	},
+	{
+		.name = "LDO_CP1",
+		.of_match = of_match_ptr("LDO_CP1"),
+		.regulators_node = of_match_ptr("regulators"),
+		.volt_table = p3h2x4x_voltage_table,
+		.n_voltages = ARRAY_SIZE(p3h2x4x_voltage_table),
+		.ops = &p3h2x4x_ldo_ops,
+		.type = REGULATOR_VOLTAGE,
+		.owner = THIS_MODULE,
+		.enable_reg = P3H2x4x_LDO_AND_PULLUP_CONF,
+		.enable_mask = P3H2x4x_CP1_EN_LDO,
+		.vsel_reg = P3H2x4x_VCCIO_LDO_CONF,
+		.vsel_mask = P3H2x4x_CP1_VCCIO_LDO_VOLTAGE_MASK,
+	},
+	{
+		.name = "LDO_TPG0",
+		.of_match = of_match_ptr("LDO_TPG0"),
+		.regulators_node = of_match_ptr("regulators"),
+		.volt_table = p3h2x4x_voltage_table,
+		.n_voltages = ARRAY_SIZE(p3h2x4x_voltage_table),
+		.ops = &p3h2x4x_ldo_ops,
+		.type = REGULATOR_VOLTAGE,
+		.owner = THIS_MODULE,
+		.enable_reg = P3H2x4x_LDO_AND_PULLUP_CONF,
+		.enable_mask = P3H2x4x_TP0145_EN_LDO,
+		.vsel_reg = P3H2x4x_VCCIO_LDO_CONF,
+		.vsel_mask = P3H2x4x_TP0145_VCCIO_LDO_VOLTAGE_MASK,
+	},
+	{
+		.name = "LDO_TPG1",
+		.of_match = of_match_ptr("LDO_TPG1"),
+		.regulators_node = of_match_ptr("regulators"),
+		.volt_table = p3h2x4x_voltage_table,
+		.n_voltages = ARRAY_SIZE(p3h2x4x_voltage_table),
+		.ops = &p3h2x4x_ldo_ops,
+		.type = REGULATOR_VOLTAGE,
+		.owner = THIS_MODULE,
+		.enable_reg = P3H2x4x_LDO_AND_PULLUP_CONF,
+		.enable_mask = P3H2x4x_TP2367_EN_LDO,
+		.vsel_reg = P3H2x4x_VCCIO_LDO_CONF,
+		.vsel_mask = P3H2x4x_TP2367_VCCIO_LDO_VOLTAGE_MASK,
+	},
+};
+
+static int p3h2x4x_regulator_probe(struct platform_device *pdev)
+{
+	struct p3h2x4x_dev *p3h2x4x = dev_get_drvdata(pdev->dev.parent);
+	struct p3h2x4x_regulator_dev *p3h2x4x_regulator;
+	struct regulator_config rcfg = { };
+	struct device *dev = &pdev->dev;
+	struct regulator_dev *rdev;
+	int ret, i;
+
+	p3h2x4x_regulator = devm_kzalloc(dev, sizeof(*p3h2x4x_regulator), GFP_KERNEL);
+	if (!p3h2x4x_regulator)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, p3h2x4x_regulator);
+
+	p3h2x4x_regulator->regmap = p3h2x4x->regmap;
+	device_set_of_node_from_dev(dev, dev->parent);
+
+	rcfg.dev = dev;
+	rcfg.dev->of_node = dev->of_node;
+	rcfg.regmap = p3h2x4x_regulator->regmap;
+	rcfg.driver_data = p3h2x4x_regulator;
+
+	for (i = 0; i < ARRAY_SIZE(p3h2x4x_regulators); i++) {
+		rdev = devm_regulator_register(&pdev->dev, &p3h2x4x_regulators[i], &rcfg);
+		if (IS_ERR(rdev)) {
+			return dev_err_probe(dev, PTR_ERR(rdev), "Failed to register %s\n",
+					     p3h2x4x_regulators[i].name);
+		}
+		p3h2x4x_regulator->rp3h2x4x_dev[i] = rdev;
+	}
+	return 0;
+}
+
+static struct platform_driver p3h2x4x_regulator_driver = {
+	.driver = {
+		.name = "p3h2x4x-regulator",
+		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
+	},
+	.probe = p3h2x4x_regulator_probe,
+};
+module_platform_driver(p3h2x4x_regulator_driver);
+
+MODULE_AUTHOR("Aman Kumar Pandey <aman.kumarpandey@nxp.com>");
+MODULE_AUTHOR("vikash Bansal <vikash.bansal@nxp.com>");
+MODULE_DESCRIPTION("P3H2x4x I3C HUB Regulator driver");
+MODULE_LICENSE("GPL");
-- 
2.25.1


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

* [PATCH v5 5/5] i3c: hub: p3h2x4x: Add support for NXP P3H2x4x I3C hub functionality
  2026-02-06 12:01 [PATCH v5 1/5] i3c: master: Expose the APIs to support I3C hub Aman Kumar Pandey
                   ` (2 preceding siblings ...)
  2026-02-06 12:01 ` [PATCH v5 4/5] regulator: p3h2x4x: Add driver for on-die regulators in NXP P3H2x4x i3c hub Aman Kumar Pandey
@ 2026-02-06 12:01 ` Aman Kumar Pandey
  2026-02-06 17:33   ` Frank Li
  2026-02-06 21:25   ` kernel test robot
  2026-02-06 17:06 ` [PATCH v5 1/5] i3c: master: Expose the APIs to support I3C hub Frank Li
  4 siblings, 2 replies; 12+ messages in thread
From: Aman Kumar Pandey @ 2026-02-06 12:01 UTC (permalink / raw)
  To: linux-kernel, linux-i3c, alexandre.belloni, krzk+dt, robh,
	conor+dt, devicetree, broonie, lee, Frank.Li, lgirdwood
  Cc: vikash.bansal, priyanka.jain, shashank.rebbapragada,
	Aman Kumar Pandey

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=yes, Size: 51874 bytes --]

Add I3C hub functionality for the NXP P3H2x4x family of multiport hubs.
These devices support downstream target ports that can be configured
as I3C, I2C, or SMBus.

This driver enables:
- I3C/I2C communication between host and hub
- Transparent communication with downstream devices
- Target port configuration (I3C/I2C/SMBus)
- MCTP device support
- In-band interrupt handling

P3H2440/P3H2441 support 4 target ports;
P3H2840/P3H2841 support 8 target ports.

Signed-off-by: Aman Kumar Pandey <aman.kumarpandey@nxp.com>
Signed-off-by: Vikash Bansal <vikash.bansal@nxp.com>

---
Changes in v5:
 - Updated supply names.

Changes in v4:
 - Split the driver into three separate patches (mfd, regulator and I3C hub) 
 - Added support for NXP P3H2x4x I3C hub functionality
 - Integrated hub driver with its on-die regulator
 
Changes in v3:
 - Added MFD (Multi-Function Device) support for I3C hub and on-die regulator

Changes in v2:
 - Refined coding style and incorporated review feedback
 - Updated directory structure
 - Revised logic for parsing DTS nodes
---
---
 MAINTAINERS                              |   1 +
 drivers/i3c/Kconfig                      |   1 +
 drivers/i3c/Makefile                     |   1 +
 drivers/i3c/hub/Kconfig                  |  10 +
 drivers/i3c/hub/Makefile                 |   4 +
 drivers/i3c/hub/p3h2840_i3c_hub.h        | 340 ++++++++++++++++++
 drivers/i3c/hub/p3h2840_i3c_hub_common.c | 405 ++++++++++++++++++++++
 drivers/i3c/hub/p3h2840_i3c_hub_i3c.c    | 354 +++++++++++++++++++
 drivers/i3c/hub/p3h2840_i3c_hub_smbus.c  | 418 +++++++++++++++++++++++
 include/linux/i3c/device.h               |   1 +
 10 files changed, 1535 insertions(+)
 create mode 100644 drivers/i3c/hub/Kconfig
 create mode 100644 drivers/i3c/hub/Makefile
 create mode 100644 drivers/i3c/hub/p3h2840_i3c_hub.h
 create mode 100644 drivers/i3c/hub/p3h2840_i3c_hub_common.c
 create mode 100644 drivers/i3c/hub/p3h2840_i3c_hub_i3c.c
 create mode 100644 drivers/i3c/hub/p3h2840_i3c_hub_smbus.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 6601830b6e86..950c4595616b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18913,6 +18913,7 @@ L:	linux-kernel@vger.kernel.org
 L:	linux-i3c-owner@lists.infradead.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/i3c/nxp,p3h2840.yaml
+F:	drivers/i3c/hub/*
 F:	drivers/mfd/p3h2840.c
 F:	drivers/regulator/p3h2840_i3c_hub_regulator.c
 F:	include/linux/mfd/p3h2840.h
diff --git a/drivers/i3c/Kconfig b/drivers/i3c/Kconfig
index 30a441506f61..254c50c3d33a 100644
--- a/drivers/i3c/Kconfig
+++ b/drivers/i3c/Kconfig
@@ -21,4 +21,5 @@ menuconfig I3C
 
 if I3C
 source "drivers/i3c/master/Kconfig"
+source "drivers/i3c/hub/Kconfig"
 endif # I3C
diff --git a/drivers/i3c/Makefile b/drivers/i3c/Makefile
index 11982efbc6d9..ea01bdda938d 100644
--- a/drivers/i3c/Makefile
+++ b/drivers/i3c/Makefile
@@ -2,3 +2,4 @@
 i3c-y				:= device.o master.o
 obj-$(CONFIG_I3C)		+= i3c.o
 obj-$(CONFIG_I3C)		+= master/
+obj-$(CONFIG_I3C)		+= hub/
diff --git a/drivers/i3c/hub/Kconfig b/drivers/i3c/hub/Kconfig
new file mode 100644
index 000000000000..9d3e1a29e744
--- /dev/null
+++ b/drivers/i3c/hub/Kconfig
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright 2025 NXP
+config P3H2X4X_I3C_HUB
+    tristate "P3H2X4X I3C HUB support"
+    depends on MFD_P3H2X4X
+    help
+      This enables support for NXP P3H244x/P3H284x I3C HUB. These hubs
+      connect to a host via I3C/I2C/SMBus and allow communication with
+      multiple downstream peripherals. The Say Y or M here to use I3C
+      HUB driver to configure I3C HUB device.
diff --git a/drivers/i3c/hub/Makefile b/drivers/i3c/hub/Makefile
new file mode 100644
index 000000000000..9dbd8a7b4184
--- /dev/null
+++ b/drivers/i3c/hub/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright 2025 NXP
+p3h2840_i3c_hub-y := p3h2840_i3c_hub_common.o p3h2840_i3c_hub_i3c.o p3h2840_i3c_hub_smbus.o
+obj-$(CONFIG_P3H2X4X_I3C_HUB)	+= p3h2840_i3c_hub.o
diff --git a/drivers/i3c/hub/p3h2840_i3c_hub.h b/drivers/i3c/hub/p3h2840_i3c_hub.h
new file mode 100644
index 000000000000..92a126196c1d
--- /dev/null
+++ b/drivers/i3c/hub/p3h2840_i3c_hub.h
@@ -0,0 +1,340 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2025 NXP
+ * This header file contain private device structure definition.
+ */
+
+#ifndef P3H2840_I3C_HUB_H
+#define P3H2840_I3C_HUB_H
+
+#include <linux/bitfield.h>
+#include <linux/i2c.h>
+#include <linux/i3c/device.h>
+#include <linux/i3c/master.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regmap.h>
+
+/* I3C HUB REGISTERS */
+
+/* Device Information Registers */
+#define P3H2x4x_DEV_INFO_0					0x00
+#define P3H2x4x_DEV_INFO_1					0x01
+#define P3H2x4x_PID_5						0x02
+#define P3H2x4x_PID_4						0x03
+#define P3H2x4x_PID_3						0x04
+#define P3H2x4x_PID_2						0x05
+#define P3H2x4x_PID_1						0x06
+#define P3H2x4x_PID_0						0x07
+#define P3H2x4x_BCR						0x08
+#define P3H2x4x_DCR						0x09
+#define P3H2x4x_DEV_CAPAB					0x0a
+#define P3H2x4x_DEV_REV						0x0b
+
+/* Device Configuration Registers */
+#define P3H2x4x_CP_CONF						0x11
+#define P3H2x4x_TP_ENABLE					0x12
+
+#define P3H2x4x_DEV_CONF					0x13
+#define P3H2x4x_IO_STRENGTH					0x14
+#define P3H2x4x_TP0145_IO_STRENGTH_MASK				GENMASK(1, 0)
+#define P3H2x4x_TP0145_IO_STRENGTH(x)	\
+		FIELD_PREP(P3H2x4x_TP0145_IO_STRENGTH_MASK, x)
+#define P3H2x4x_TP2367_IO_STRENGTH_MASK				GENMASK(3, 2)
+#define P3H2x4x_TP2367_IO_STRENGTH(x)	\
+		FIELD_PREP(P3H2x4x_TP2367_IO_STRENGTH_MASK, x)
+#define P3H2x4x_CP0_IO_STRENGTH_MASK				GENMASK(5, 4)
+#define P3H2x4x_CP0_IO_STRENGTH(x)	\
+		FIELD_PREP(P3H2x4x_CP0_IO_STRENGTH_MASK, x)
+#define P3H2x4x_CP1_IO_STRENGTH_MASK				GENMASK(7, 6)
+#define P3H2x4x_CP1_IO_STRENGTH(x)	\
+		FIELD_PREP(P3H2x4x_CP1_IO_STRENGTH_MASK, x)
+#define P3H2x4x_IO_STRENGTH_MASK					GENMASK(7, 0)
+
+#define P3H2x4x_TP_IO_MODE_CONF					0x17
+#define P3H2x4x_TP_SMBUS_AGNT_EN				0x18
+
+#define P3H2x4x_LDO_AND_PULLUP_CONF				0x19
+
+#define P3H2x4x_TP0145_PULLUP_CONF_MASK				GENMASK(7, 6)
+#define P3H2x4x_TP0145_PULLUP_CONF(x)	\
+		FIELD_PREP(P3H2x4x_TP0145_PULLUP_CONF_MASK, x)
+#define P3H2x4x_TP2367_PULLUP_CONF_MASK				GENMASK(5, 4)
+#define P3H2x4x_TP2367_PULLUP_CONF(x)	\
+		FIELD_PREP(P3H2x4x_TP2367_PULLUP_CONF_MASK, x)
+#define P3H2x4x_PULLUP_CONF_MASK					GENMASK(7, 4)
+
+#define P3H2x4x_CP_IBI_CONF					0x1a
+
+#define P3H2x4x_TP_SMBUS_AGNT_IBI_CONFIG			0x1b
+
+#define P3H2x4x_IBI_MDB_CUSTOM					0x1c
+#define P3H2x4x_JEDEC_CONTEXT_ID				0x1d
+#define P3H2x4x_TP_GPIO_MODE_EN					0x1e
+
+/* Device Status and IBI Registers */
+#define P3H2x4x_DEV_AND_IBI_STS					0x20
+#define P3H2x4x_TP_SMBUS_AGNT_IBI_STS				0x21
+#define P3H2x4x_SMBUS_AGENT_EVENT_FLAG_STATUS			BIT(4)
+
+/* Controller Port Control/Status Registers */
+#define P3H2x4x_CP_MUX_SET					0x38
+#define P3H2x4x_CONTROLLER_PORT_MUX_REQ				BIT(0)
+#define P3H2x4x_CP_MUX_STS					0x39
+#define P3H2x4x_CONTROLLER_PORT_MUX_CONNECTION_STATUS		BIT(0)
+
+/* Target Ports Control Registers */
+#define P3H2x4x_TP_SMBUS_AGNT_TRANS_START			0x50
+#define P3H2x4x_TP_NET_CON_CONF					0x51
+
+#define P3H2x4x_TP_PULLUP_EN					0x53
+
+#define P3H2x4x_TP_SCL_OUT_EN					0x54
+#define P3H2x4x_TP_SDA_OUT_EN					0x55
+#define P3H2x4x_TP_SCL_OUT_LEVEL				0x56
+#define P3H2x4x_TP_SDA_OUT_LEVEL				0x57
+#define P3H2x4x_TP_IN_DETECT_MODE_CONF				0x58
+#define P3H2x4x_TP_SCL_IN_DETECT_IBI_EN				0x59
+#define P3H2x4x_TP_SDA_IN_DETECT_IBI_EN				0x5a
+
+/* Target Ports Status Registers */
+#define P3H2x4x_TP_SCL_IN_LEVEL_STS				0x60
+#define P3H2x4x_TP_SDA_IN_LEVEL_STS				0x61
+#define P3H2x4x_TP_SCL_IN_DETECT_FLG				0x62
+#define P3H2x4x_TP_SDA_IN_DETECT_FLG				0x63
+
+/* SMBus Agent Configuration and Status Registers */
+#define P3H2x4x_TP0_SMBUS_AGNT_STS				0x64
+#define P3H2x4x_TP1_SMBUS_AGNT_STS				0x65
+#define P3H2x4x_TP2_SMBUS_AGNT_STS				0x66
+#define P3H2x4x_TP3_SMBUS_AGNT_STS				0x67
+#define P3H2x4x_TP4_SMBUS_AGNT_STS				0x68
+#define P3H2x4x_TP5_SMBUS_AGNT_STS				0x69
+#define P3H2x4x_TP6_SMBUS_AGNT_STS				0x6a
+#define P3H2x4x_TP7_SMBUS_AGNT_STS				0x6b
+#define P3H2x4x_ONCHIP_TD_AND_SMBUS_AGNT_CONF			0x6c
+
+/* buf receive flag set */
+#define P3H2x4x_TARGET_BUF_CA_TF				BIT(0)
+#define P3H2x4x_TARGET_BUF_0_RECEIVE				BIT(1)
+#define P3H2x4x_TARGET_BUF_1_RECEIVE				BIT(2)
+#define P3H2x4x_TARGET_BUF_0_1_RECEIVE				GENMASK(2, 1)
+#define P3H2x4x_TARGET_BUF_OVRFL				GENMASK(3, 1)
+#define BUF_RECEIVED_FLAG_MASK					GENMASK(3, 1)
+#define BUF_RECEIVED_FLAG_TF_MASK				GENMASK(3, 0)
+
+#define P3H2x4x_TARGET_AGENT_LOCAL_DEV				0x11
+#define P3H2x4x_TARGET_BUFF_0_PAGE				0x12
+#define P3H2x4x_TARGET_BUFF_1_PAGE				0x13
+
+/* Special Function Registers */
+#define P3H2x4x_LDO_AND_CPSEL_STS				0x79
+#define P3H2x4x_CP_SDA1_LEVEL					BIT(7)
+#define P3H2x4x_CP_SCL1_LEVEL					BIT(6)
+
+#define P3H2x4x_CP_SEL_PIN_INPUT_CODE_MASK			GENMASK(5, 4)
+#define P3H2x4x_CP_SEL_PIN_INPUT_CODE_GET(x)	\
+		(((x) & P3H2x4x_CP_SEL_PIN_INPUT_CODE_MASK) >> 4)
+#define P3H2x4x_CP_SDA1_SCL1_PINS_CODE_MASK			GENMASK(7, 6)
+#define P3H2x4x_CP_SDA1_SCL1_PINS_CODE_GET(x)	\
+		(((x) & P3H2x4x_CP_SDA1_SCL1_PINS_CODE_MASK) >> 6)
+#define P3H2x4x_VCCIO1_PWR_GOOD					BIT(3)
+#define P3H2x4x_VCCIO0_PWR_GOOD					BIT(2)
+#define P3H2x4x_CP1_VCCIO_PWR_GOOD				BIT(1)
+#define P3H2x4x_CP0_VCCIO_PWR_GOOD				BIT(0)
+
+#define P3H2x4x_BUS_RESET_SCL_TIMEOUT				0x7a
+#define P3H2x4x_ONCHIP_TD_PROTO_ERR_FLG				0x7b
+#define P3H2x4x_DEV_CMD						0x7c
+#define P3H2x4x_ONCHIP_TD_STS					0x7d
+#define P3H2x4x_ONCHIP_TD_ADDR_CONF				0x7e
+#define P3H2x4x_PAGE_PTR					0x7f
+
+/* Paged Transaction Registers */
+#define P3H2x4x_CONTROLLER_BUFFER_PAGE				0x10
+#define P3H2x4x_CONTROLLER_AGENT_BUFF				0x80
+#define P3H2x4x_CONTROLLER_AGENT_BUFF_DATA			0x84
+
+#define P3H2x4x_TARGET_BUFF_LENGTH				0x80
+#define P3H2x4x_TARGET_BUFF_ADDRESS				0x81
+#define P3H2x4x_TARGET_BUFF_DATA				0x82
+
+#define P3H2x4x_TP_MAX_COUNT					0x08
+#define P3H2x4x_CP_MAX_COUNT					0x02
+#define P3H2x4x_TP_LOCAL_DEV					0x08
+
+/* LDO Disable/Enable DT settings */
+#define P3H2x4x_LDO_VOLT_1_0V					0x00
+#define P3H2x4x_LDO_VOLT_1_1V					0x01
+#define P3H2x4x_LDO_VOLT_1_2V					0x02
+#define P3H2x4x_LDO_VOLT_1_8V					0x03
+
+#define P3H2x4x_LDO_DISABLED					0x00
+#define P3H2x4x_LDO_ENABLED					0x01
+
+#define P3H2x4x_IBI_DISABLED					0x00
+#define P3H2x4x_IBI_ENABLED					0x01
+
+/* Pullup selection DT settings */
+#define P3H2x4x_TP_PULLUP_250R					0x00
+#define P3H2x4x_TP_PULLUP_500R					0x01
+#define P3H2x4x_TP_PULLUP_1000R					0x02
+#define P3H2x4x_TP_PULLUP_2000R					0x03
+
+#define P3H2x4x_TP_PULLUP_DISABLED				0x00
+#define P3H2x4x_TP_PULLUP_ENABLED				0x01
+
+#define P3H2x4x_IO_STRENGTH_20_OHM				0x00
+#define P3H2x4x_IO_STRENGTH_30_OHM				0x01
+#define P3H2x4x_IO_STRENGTH_40_OHM				0x02
+#define P3H2x4x_IO_STRENGTH_50_OHM				0x03
+
+#define P3H2x4x_TP_MODE_I3C					0x00
+#define P3H2x4x_TP_MODE_SMBUS					0x01
+#define P3H2x4x_TP_MODE_GPIO					0x02
+#define P3H2x4x_TP_MODE_I2C					0x03
+
+#define ONE_BYTE_SIZE						0x01
+
+/* holding SDA low when both SMBus Target Agent received data buffers are full.
+ * This feature can be used as a flow-control mechanism for MCTP applications to
+ * avoid MCTP transmitters on Target Ports time out when the SMBus agent buffers
+ * are not serviced in time by upstream controller and only receives write message
+ * from its downstream ports.
+ * SMBUS_AGENT_TX_RX_LOOPBACK_EN/TARGET_AGENT_BUF_FULL_SDA_LOW_EN
+ */
+
+#define P3H2x4x_TARGET_AGENT_DFT_IBI_CONF			0x20
+#define P3H2x4x_TARGET_AGENT_DFT_IBI_CONF_MASK			0x21
+
+/* Transaction status checking mask */
+
+#define P3H2x4x_SMBUS_TRANSACTION_FINISH_FLAG		1
+#define P3H2x4x_SMBUS_CNTRL_STATUS_TXN_SHIFT		4
+
+#define P3H2x4x_SMBUS_CNTRL_STATUS_TXN_OK		0
+#define P3H2x4x_SMBUS_CNTRL_STATUS_TXN_ADDR_NAK		1
+#define P3H2x4x_SMBUS_CNTRL_STATUS_TXN_DATA_NAK		2
+#define P3H2x4x_SMBUS_CNTRL_STATUS_TXN_WTR_NAK		3
+#define P3H2x4x_SMBUS_CNTRL_STATUS_TXN_SYNC_RCV		4
+#define P3H2x4x_SMBUS_CNTRL_STATUS_TXN_SYNC_RCVCLR	5
+#define P3H2x4x_SMBUS_CNTRL_STATUS_TXN_FAULT		6
+#define P3H2x4x_SMBUS_CNTRL_STATUS_TXN_ARB_LOSS		7
+#define P3H2x4x_SMBUS_CNTRL_STATUS_TXN_SCL_TO		8
+
+#define P3H2x4x_TP_BUFFER_STATUS_MASK				0x0f
+#define P3H2x4x_TP_TRANSACTION_CODE_MASK			0xf0
+
+/* SMBus transaction types fields */
+#define P3H2x4x_SMBUS_400kHz					BIT(2)
+
+/* SMBus polling */
+#define P3H2x4x_POLLING_ROLL_PERIOD_MS				10
+
+/* Hub buffer size */
+#define P3H2x4x_CONTROLLER_BUFFER_SIZE				88
+#define P3H2x4x_TARGET_BUFFER_SIZE				80
+#define P3H2x4x_SMBUS_DESCRIPTOR_SIZE				4
+#define P3H2x4x_SMBUS_PAYLOAD_SIZE	\
+		(P3H2x4x_CONTROLLER_BUFFER_SIZE - P3H2x4x_SMBUS_DESCRIPTOR_SIZE)
+#define P3H2x4x_SMBUS_TARGET_PAYLOAD_SIZE	(P3H2x4x_TARGET_BUFFER_SIZE - 2)
+
+/* Hub SMBus transaction time */
+#define P3H2x4x_SMBUS_400kHz_TRANSFER_TIMEOUT(x)		((20 * (x)) + 80)
+
+#define P3H2x4x_NO_PAGE_PER_TP					4
+
+#define P3H2x4x_MAX_PAYLOAD_LEN					2
+#define P3H2x4x_NUM_SLOTS					6
+
+#define P3H2x4x_HUB_ID						0
+
+#define P3H2x4x_SET_BIT(n)				BIT(n)
+
+enum p3h2x4x_tp {
+	TP_0,
+	TP_1,
+	TP_2,
+	TP_3,
+	TP_4,
+	TP_5,
+	TP_6,
+	TP_7,
+};
+
+enum p3h2x4x_rcv_buf {
+	RCV_BUF_0,
+	RCV_BUF_1,
+	RCV_BUF_OF,
+};
+
+struct p3h2x4x_regulators {
+	struct regulator *rcp0;
+	struct regulator *rcp1;
+	struct regulator *rtp0145;
+	struct regulator *rtp2367;
+};
+
+struct tp_configuration {
+	bool pullup_en;
+	bool ibi_en;
+	bool always_enable;
+	int mode;
+};
+
+struct hub_configuration {
+	int tp0145_pullup;
+	int tp2367_pullup;
+	int cp0_io_strength;
+	int cp1_io_strength;
+	int tp0145_io_strength;
+	int tp2367_io_strength;
+	struct tp_configuration tp_config[P3H2x4x_TP_MAX_COUNT];
+};
+
+struct tp_bus {
+	bool is_registered;	    /* bus was registered in the framework. */
+	u8 tp_mask;
+	u8 tp_port;
+	struct mutex port_mutex;      /* per port mutex */
+	struct device_node *of_node;
+	struct i2c_client *tp_smbus_client;
+	struct i2c_adapter *tp_smbus_adapter;
+	struct i3c_master_controller tp_i3c_controller;
+	struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub;
+};
+
+struct p3h2x4x_i3c_hub_dev {
+	struct device *dev;
+	struct regmap *regmap;
+	struct mutex etx_mutex;      /* all port mutex */
+	struct i3c_device *i3cdev;
+	struct i2c_client *i2c_client;
+	struct p3h2x4x_regulators rp3h2x4x;
+	struct hub_configuration hub_config;
+	struct tp_bus tp_bus[P3H2x4x_TP_MAX_COUNT];
+};
+
+/**
+ * p3h2x4x_tp_smbus_algo - add i2c adapter for target port configured as SMBus.
+ * @priv: p3h2x4x device structure.
+ * @tp: target port.
+ * Return: 0 in case of success, a negative EINVAL code if the error.
+ */
+int p3h2x4x_tp_smbus_algo(struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub);
+
+/**
+ * p3h2x4x_tp_i3c_algo - register i3c controller for target port configured as I3C.
+ * @priv: p3h2x4x device structure.
+ * @tp: target port.
+ * Return: 0 in case of success, a negative EINVAL code if the error.
+ */
+int p3h2x4x_tp_i3c_algo(struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub);
+
+/**
+ * p3h2x4x_ibi_handler - IBI handler.
+ * @i3cdev: i3c device.
+ * @payload: two byte IBI payload data.
+ */
+void p3h2x4x_ibi_handler(struct i3c_device *i3cdev,
+			 const struct i3c_ibi_payload *payload);
+#endif /* P3H2840_I3C_HUB_H */
diff --git a/drivers/i3c/hub/p3h2840_i3c_hub_common.c b/drivers/i3c/hub/p3h2840_i3c_hub_common.c
new file mode 100644
index 000000000000..3de306153fa9
--- /dev/null
+++ b/drivers/i3c/hub/p3h2840_i3c_hub_common.c
@@ -0,0 +1,405 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 NXP
+ * This P3H2x4x driver file implements functions for Hub probe and DT parsing.
+ */
+
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/p3h2840.h>
+
+#include "p3h2840_i3c_hub.h"
+
+/* LDO voltage DT settings */
+#define P3H2x4x_DT_LDO_VOLT_1_0V		1000000
+#define P3H2x4x_DT_LDO_VOLT_1_1V		1100000
+#define P3H2x4x_DT_LDO_VOLT_1_2V		1200000
+#define P3H2x4x_DT_LDO_VOLT_1_8V		1800000
+
+/* target port pull-up settings */
+#define P3H2x4x_DT_TP_PULLUP_250R		250
+#define P3H2x4x_DT_TP_PULLUP_500R		500
+#define P3H2x4x_DT_TP_PULLUP_1000R		1000
+#define P3H2x4x_DT_TP_PULLUP_2000R		2000
+
+/*  IO strenght settings */
+#define P3H2x4x_DT_IO_STRENGTH_20_OHM		20
+#define P3H2x4x_DT_IO_STRENGTH_30_OHM		30
+#define P3H2x4x_DT_IO_STRENGTH_40_OHM		40
+#define P3H2x4x_DT_IO_STRENGTH_50_OHM		50
+
+static u8 p3h2x4x_pullup_dt_to_reg(int dt_value)
+{
+	switch (dt_value) {
+	case P3H2x4x_DT_TP_PULLUP_2000R:
+		return P3H2x4x_TP_PULLUP_2000R;
+	case P3H2x4x_DT_TP_PULLUP_1000R:
+		return P3H2x4x_TP_PULLUP_1000R;
+	case P3H2x4x_DT_TP_PULLUP_250R:
+		return P3H2x4x_TP_PULLUP_250R;
+	default:
+		return P3H2x4x_TP_PULLUP_500R;
+	}
+}
+
+static int p3h2x4x_configure_pullup(struct device *dev)
+{
+	struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub = dev_get_drvdata(dev);
+	u8 pullup;
+
+	pullup = P3H2x4x_TP0145_PULLUP_CONF(p3h2x4x_pullup_dt_to_reg
+						(p3h2x4x_i3c_hub->hub_config.tp0145_pullup));
+
+	pullup |= P3H2x4x_TP2367_PULLUP_CONF(p3h2x4x_pullup_dt_to_reg
+						(p3h2x4x_i3c_hub->hub_config.tp2367_pullup));
+
+	return regmap_update_bits(p3h2x4x_i3c_hub->regmap, P3H2x4x_LDO_AND_PULLUP_CONF,
+							  P3H2x4x_PULLUP_CONF_MASK, pullup);
+}
+
+static u8 p3h2x4x_io_strength_dt_to_reg(int dt_value)
+{
+	switch (dt_value) {
+	case P3H2x4x_DT_IO_STRENGTH_50_OHM:
+		return P3H2x4x_IO_STRENGTH_50_OHM;
+	case P3H2x4x_DT_IO_STRENGTH_40_OHM:
+		return P3H2x4x_IO_STRENGTH_40_OHM;
+	case P3H2x4x_DT_IO_STRENGTH_30_OHM:
+		return P3H2x4x_IO_STRENGTH_30_OHM;
+	default:
+		return P3H2x4x_IO_STRENGTH_20_OHM;
+	}
+}
+
+static int p3h2x4x_configure_io_strength(struct device *dev)
+{
+	struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub = dev_get_drvdata(dev);
+	u8 io_strength;
+
+	io_strength = P3H2x4x_CP0_IO_STRENGTH(p3h2x4x_io_strength_dt_to_reg
+						(p3h2x4x_i3c_hub->hub_config.cp0_io_strength));
+
+	io_strength |= P3H2x4x_CP1_IO_STRENGTH(p3h2x4x_io_strength_dt_to_reg
+						(p3h2x4x_i3c_hub->hub_config.cp1_io_strength));
+
+	io_strength |= P3H2x4x_TP0145_IO_STRENGTH(p3h2x4x_io_strength_dt_to_reg
+						(p3h2x4x_i3c_hub->hub_config.tp0145_io_strength));
+
+	io_strength |= P3H2x4x_TP2367_IO_STRENGTH(p3h2x4x_io_strength_dt_to_reg
+						(p3h2x4x_i3c_hub->hub_config.tp2367_io_strength));
+
+	return regmap_update_bits(p3h2x4x_i3c_hub->regmap, P3H2x4x_IO_STRENGTH,
+							  P3H2x4x_IO_STRENGTH_MASK, io_strength);
+}
+
+static int p3h2x4x_configure_ldo(struct device *dev)
+{
+	struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub = dev_get_drvdata(dev);
+	int ret;
+
+	p3h2x4x_i3c_hub->rp3h2x4x.rcp0 = devm_regulator_get_optional(dev, "vcc1");
+	if (IS_ERR(p3h2x4x_i3c_hub->rp3h2x4x.rcp0)) {
+		p3h2x4x_i3c_hub->rp3h2x4x.rcp0 = NULL;
+		dev_dbg(dev, "vdd1-supply not found\n");
+	}
+
+	p3h2x4x_i3c_hub->rp3h2x4x.rcp1 = devm_regulator_get_optional(dev, "vcc2");
+	if (IS_ERR(p3h2x4x_i3c_hub->rp3h2x4x.rcp1)) {
+		p3h2x4x_i3c_hub->rp3h2x4x.rcp1 = NULL;
+		dev_dbg(dev, "vdd2-supply not found\n");
+	}
+
+	p3h2x4x_i3c_hub->rp3h2x4x.rtp0145 = devm_regulator_get_optional(dev, "vcc3");
+	if (IS_ERR(p3h2x4x_i3c_hub->rp3h2x4x.rtp0145)) {
+		p3h2x4x_i3c_hub->rp3h2x4x.rtp0145 = NULL;
+		dev_dbg(dev, "vdd3-supply not found\n");
+	}
+
+	p3h2x4x_i3c_hub->rp3h2x4x.rtp2367 = devm_regulator_get_optional(dev, "vcc4");
+	if (IS_ERR(p3h2x4x_i3c_hub->rp3h2x4x.rtp2367)) {
+		p3h2x4x_i3c_hub->rp3h2x4x.rtp2367 = NULL;
+		dev_dbg(dev, "vdd4-supply not found\n");
+	}
+
+	/* Enable regulators */
+	if (p3h2x4x_i3c_hub->rp3h2x4x.rcp0) {
+		ret = regulator_enable(p3h2x4x_i3c_hub->rp3h2x4x.rcp0);
+		if (ret)
+			dev_warn(dev, "Failed to enable regulator CP0 (%d)\n", ret);
+	}
+
+	if (p3h2x4x_i3c_hub->rp3h2x4x.rcp1) {
+		ret = regulator_enable(p3h2x4x_i3c_hub->rp3h2x4x.rcp1);
+		if (ret)
+			dev_warn(dev, "Failed to enable regulator CP1 (%d)\n", ret);
+	}
+
+	if (p3h2x4x_i3c_hub->rp3h2x4x.rtp0145) {
+		ret = regulator_enable(p3h2x4x_i3c_hub->rp3h2x4x.rtp0145);
+		if (ret)
+			dev_warn(dev, "Failed to enable regulator TPG0 (%d)\n", ret);
+	}
+
+	if (p3h2x4x_i3c_hub->rp3h2x4x.rtp2367) {
+		ret = regulator_enable(p3h2x4x_i3c_hub->rp3h2x4x.rtp2367);
+		if (ret)
+			dev_warn(dev, "Failed to enable regulator TPG1 (%d)\n", ret);
+	}
+
+	/* This delay is required for the regulator to stabilize its output voltage */
+	mdelay(5);
+
+	return 0;
+}
+
+static int p3h2x4x_configure_tp(struct device *dev)
+{
+	struct p3h2x4x_i3c_hub_dev *hub = dev_get_drvdata(dev);
+	u8 mode = 0, smbus = 0, pullup = 0, target_port = 0;
+	int tp, ret;
+
+	for (tp = 0; tp < P3H2x4x_TP_MAX_COUNT; tp++) {
+		pullup |= hub->hub_config.tp_config[tp].pullup_en ? P3H2x4x_SET_BIT(tp) : 0;
+		mode |= (hub->hub_config.tp_config[tp].mode != P3H2x4x_TP_MODE_I3C) ?
+			P3H2x4x_SET_BIT(tp) : 0;
+		smbus |= (hub->hub_config.tp_config[tp].mode == P3H2x4x_TP_MODE_SMBUS) ?
+			 P3H2x4x_SET_BIT(tp) : 0;
+		target_port |= (hub->tp_bus[tp].tp_mask == P3H2x4x_SET_BIT(tp)) ?
+			       hub->tp_bus[tp].tp_mask : 0;
+	}
+
+	ret = regmap_update_bits(hub->regmap, P3H2x4x_TP_PULLUP_EN, pullup, pullup);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(hub->regmap, P3H2x4x_TP_IO_MODE_CONF, mode, mode);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(hub->regmap, P3H2x4x_TP_SMBUS_AGNT_EN, smbus, smbus);
+	if (ret)
+		return ret;
+
+	if (target_port & ~smbus) {
+		ret = regmap_write(hub->regmap, P3H2x4x_CP_MUX_SET,
+				   P3H2x4x_CONTROLLER_PORT_MUX_REQ);
+		if (ret)
+			return ret;
+	}
+
+	return regmap_update_bits(hub->regmap, P3H2x4x_TP_ENABLE, target_port, target_port);
+}
+
+static int p3h2x4x_configure_hw(struct device *dev)
+{
+	int ret;
+
+	ret = p3h2x4x_configure_ldo(dev);
+	if (ret)
+		return ret;
+
+	ret = p3h2x4x_configure_pullup(dev);
+	if (ret)
+		return ret;
+
+	ret = p3h2x4x_configure_io_strength(dev);
+	if (ret)
+		return ret;
+
+	return p3h2x4x_configure_tp(dev);
+}
+
+static void p3h2x4x_get_target_port_dt_conf(struct device *dev,
+					    const struct device_node *node)
+{
+	struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub = dev_get_drvdata(dev);
+	u64 tp_port;
+
+	for_each_available_child_of_node_scoped(node, dev_node) {
+		if (of_property_read_reg(dev_node, 0, &tp_port, NULL))
+			continue;
+
+		if (tp_port < P3H2x4x_TP_MAX_COUNT) {
+			p3h2x4x_i3c_hub->tp_bus[tp_port].of_node = dev_node;
+			p3h2x4x_i3c_hub->tp_bus[tp_port].tp_mask = P3H2x4x_SET_BIT(tp_port);
+			p3h2x4x_i3c_hub->tp_bus[tp_port].p3h2x4x_i3c_hub = p3h2x4x_i3c_hub;
+			p3h2x4x_i3c_hub->tp_bus[tp_port].tp_port = tp_port;
+		}
+	}
+}
+
+static void p3h2x4x_parse_tp_dt_settings(struct device *dev,
+					 const struct device_node *node,
+					 struct tp_configuration tp_config[])
+{
+	u64 id;
+
+	for_each_available_child_of_node_scoped(node, tp_node) {
+		if (of_property_read_reg(tp_node, 0, &id, NULL))
+			continue;
+
+		if (id >= P3H2x4x_TP_MAX_COUNT) {
+			dev_warn(dev, "Invalid target port index found in DT: %lli\n", id);
+			continue;
+		}
+
+		if (strcmp(tp_node->name, "i3c") == 0)
+			tp_config[id].mode = P3H2x4x_TP_MODE_I3C;
+
+		if (strcmp(tp_node->name, "i2c") == 0)
+			tp_config[id].mode = P3H2x4x_TP_MODE_I2C;
+
+		if (strcmp(tp_node->name, "smbus") == 0)
+			tp_config[id].mode = P3H2x4x_TP_MODE_SMBUS;
+
+		tp_config[id].pullup_en =
+			of_property_read_bool(tp_node, "pullup-enable");
+	}
+}
+
+static void p3h2x4x_get_hub_dt_conf(struct device *dev,
+				    const struct device_node *node)
+{
+	struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub = dev_get_drvdata(dev);
+
+	of_property_read_u32(node, "tp0145-pullup-ohms",
+			     &p3h2x4x_i3c_hub->hub_config.tp0145_pullup);
+	of_property_read_u32(node, "tp2367-pullup-ohms",
+			     &p3h2x4x_i3c_hub->hub_config.tp2367_pullup);
+	of_property_read_u32(node, "cp0-io-strength-ohms",
+			     &p3h2x4x_i3c_hub->hub_config.cp0_io_strength);
+	of_property_read_u32(node, "cp1-io-strength-ohms",
+			     &p3h2x4x_i3c_hub->hub_config.cp1_io_strength);
+	of_property_read_u32(node, "tp0145-io-strength-ohms",
+			     &p3h2x4x_i3c_hub->hub_config.tp0145_io_strength);
+	of_property_read_u32(node, "tp2367-io-strength-ohms",
+			     &p3h2x4x_i3c_hub->hub_config.tp2367_io_strength);
+
+	p3h2x4x_parse_tp_dt_settings(dev, node, p3h2x4x_i3c_hub->hub_config.tp_config);
+}
+
+static void p3h2x4x_default_configuration(struct device *dev)
+{
+	struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub = dev_get_drvdata(dev);
+	int tp_count;
+
+	p3h2x4x_i3c_hub->hub_config.tp0145_pullup = P3H2x4x_TP_PULLUP_500R;
+	p3h2x4x_i3c_hub->hub_config.tp2367_pullup = P3H2x4x_TP_PULLUP_500R;
+	p3h2x4x_i3c_hub->hub_config.cp0_io_strength = P3H2x4x_IO_STRENGTH_20_OHM;
+	p3h2x4x_i3c_hub->hub_config.cp1_io_strength = P3H2x4x_IO_STRENGTH_20_OHM;
+	p3h2x4x_i3c_hub->hub_config.tp0145_io_strength = P3H2x4x_IO_STRENGTH_20_OHM;
+	p3h2x4x_i3c_hub->hub_config.tp2367_io_strength = P3H2x4x_IO_STRENGTH_20_OHM;
+
+	for (tp_count = 0; tp_count < P3H2x4x_TP_MAX_COUNT; ++tp_count)
+		p3h2x4x_i3c_hub->hub_config.tp_config[tp_count].mode =  P3H2x4x_TP_MODE_I3C;
+}
+
+static int p3h2x4x_i3c_hub_probe(struct platform_device *pdev)
+{
+	struct p3h2x4x_dev *p3h2x4x = dev_get_drvdata(pdev->dev.parent);
+	struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub;
+	struct device *dev = &pdev->dev;
+	struct device_node *node;
+	int ret, i;
+
+	p3h2x4x_i3c_hub = devm_kzalloc(dev, sizeof(*p3h2x4x_i3c_hub), GFP_KERNEL);
+	if (!p3h2x4x_i3c_hub)
+		return -ENOMEM;
+
+	p3h2x4x_i3c_hub->regmap = p3h2x4x->regmap;
+	p3h2x4x_i3c_hub->dev = dev;
+
+	platform_set_drvdata(pdev, p3h2x4x_i3c_hub);
+	device_set_of_node_from_dev(dev, dev->parent);
+
+	p3h2x4x_default_configuration(dev);
+
+	ret = devm_mutex_init(dev, &p3h2x4x_i3c_hub->etx_mutex);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++) {
+		ret = devm_mutex_init(dev, &p3h2x4x_i3c_hub->tp_bus[i].port_mutex);
+		if (ret)
+			return ret;
+	}
+
+	/* get hub node from DT */
+	node =  dev->of_node;
+	if (!node)
+		return dev_err_probe(dev, -ENODEV, "No Device Tree entry found\n");
+
+	p3h2x4x_get_hub_dt_conf(dev, node);
+	p3h2x4x_get_target_port_dt_conf(dev, node);
+
+	/* Unlock access to protected registers */
+	ret = regmap_write(p3h2x4x_i3c_hub->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE,
+			   P3H2x4x_REGISTERS_UNLOCK_CODE);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to unlock HUB's protected registers\n");
+
+	ret = p3h2x4x_configure_hw(dev);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to configure the HUB\n");
+
+	/* Register logic for native vertual I3C ports */
+	if (p3h2x4x->is_p3h2x4x_in_i3c) {
+		p3h2x4x_i3c_hub->i3cdev = p3h2x4x->i3cdev;
+		ret = p3h2x4x_tp_i3c_algo(p3h2x4x_i3c_hub);
+		if (ret)
+			return dev_err_probe(dev, ret, "Failed to register i3c bus\n");
+	} else {
+		p3h2x4x_i3c_hub->i2c_client = p3h2x4x->i2c_client;
+	}
+
+	/* Register logic for native SMBus ports */
+	ret = p3h2x4x_tp_smbus_algo(p3h2x4x_i3c_hub);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to add i2c adapter\n");
+
+	/* Lock access to protected registers */
+	ret = regmap_write(p3h2x4x_i3c_hub->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE,
+			   P3H2x4x_REGISTERS_LOCK_CODE);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to lock HUB's protected registers\n");
+
+	return 0;
+}
+
+static void p3h2x4x_i3c_hub_remove(struct platform_device *pdev)
+{
+	struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub = platform_get_drvdata(pdev);
+	struct p3h2x4x_dev *p3h2x4x = dev_get_drvdata(pdev->dev.parent);
+	u8 i;
+
+	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++) {
+		if (!p3h2x4x_i3c_hub->tp_bus[i].is_registered)
+			continue;
+
+		if (p3h2x4x_i3c_hub->hub_config.tp_config[i].mode == P3H2x4x_TP_MODE_SMBUS)
+			i2c_del_adapter(p3h2x4x_i3c_hub->tp_bus[i].tp_smbus_adapter);
+		else if (p3h2x4x_i3c_hub->hub_config.tp_config[i].mode == P3H2x4x_TP_MODE_I3C)
+			i3c_master_unregister(&p3h2x4x_i3c_hub->tp_bus[i].tp_i3c_controller);
+	}
+
+	if (p3h2x4x->is_p3h2x4x_in_i3c) {
+		i3c_device_disable_ibi(p3h2x4x->i3cdev);
+		i3c_device_free_ibi(p3h2x4x->i3cdev);
+	}
+}
+
+static struct platform_driver p3h2x4x_i3c_hub_driver = {
+	.driver = {
+		.name = "p3h2x4x-i3c-hub",
+		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
+	},
+	.probe = p3h2x4x_i3c_hub_probe,
+	.remove = p3h2x4x_i3c_hub_remove,
+};
+module_platform_driver(p3h2x4x_i3c_hub_driver);
+
+MODULE_AUTHOR("Aman Kumar Pandey <aman.kumarpandey@nxp.com>");
+MODULE_AUTHOR("vikash Bansal <vikash.bansal@nxp.com>");
+MODULE_DESCRIPTION("P3H2x4x I3C HUB driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i3c/hub/p3h2840_i3c_hub_i3c.c b/drivers/i3c/hub/p3h2840_i3c_hub_i3c.c
new file mode 100644
index 000000000000..cbc6626721fe
--- /dev/null
+++ b/drivers/i3c/hub/p3h2840_i3c_hub_i3c.c
@@ -0,0 +1,354 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 NXP
+ * This P3H2x4x driver file contain functions for I3C virtual Bus creation, connect/disconnect
+ * hub network and read/write.
+ */
+#include <linux/mfd/p3h2840.h>
+#include <linux/regmap.h>
+
+#include "p3h2840_i3c_hub.h"
+
+static const struct i3c_ibi_setup p3h2x4x_ibireq = {
+	.handler = p3h2x4x_ibi_handler,
+	.max_payload_len = P3H2x4x_MAX_PAYLOAD_LEN,
+	.num_slots = P3H2x4x_NUM_SLOTS,
+};
+
+static void p3h2x4x_en_p3h2x4x_ntwk_tp(struct tp_bus *bus)
+{
+	struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub = bus->p3h2x4x_i3c_hub;
+
+	if (p3h2x4x_i3c_hub->hub_config.tp_config[bus->tp_port].always_enable)
+		return;
+
+	regmap_set_bits(p3h2x4x_i3c_hub->regmap, P3H2x4x_TP_NET_CON_CONF, bus->tp_mask);
+}
+
+static void p3h2x4x_dis_p3h2x4x_ntwk_tp(struct tp_bus *bus)
+{
+	struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub = bus->p3h2x4x_i3c_hub;
+
+	if (p3h2x4x_i3c_hub->hub_config.tp_config[bus->tp_port].always_enable)
+		return;
+
+	regmap_clear_bits(p3h2x4x_i3c_hub->regmap, P3H2x4x_TP_NET_CON_CONF, bus->tp_mask);
+}
+
+static struct tp_bus *p3h2x4x_bus_from_i3c_desc(struct i3c_dev_desc *desc)
+{
+	struct i3c_master_controller *controller = i3c_dev_get_master(desc);
+
+	return container_of(controller, struct tp_bus, tp_i3c_controller);
+}
+
+static struct i3c_master_controller
+*get_parent_controller(struct i3c_master_controller *controller)
+{
+	struct tp_bus *bus = container_of(controller, struct tp_bus, tp_i3c_controller);
+
+	return i3c_dev_get_master(bus->p3h2x4x_i3c_hub->i3cdev->desc);
+}
+
+static struct i3c_master_controller
+*get_parent_controller_from_i3c_desc(struct i3c_dev_desc *desc)
+{
+	struct i3c_master_controller *controller = i3c_dev_get_master(desc);
+	struct tp_bus *bus = container_of(controller, struct tp_bus, tp_i3c_controller);
+
+	return i3c_dev_get_master(bus->p3h2x4x_i3c_hub->i3cdev->desc);
+}
+
+static struct i3c_master_controller
+*update_i3c_i2c_desc_parent(struct i3c_i2c_dev_desc *desc,
+				struct i3c_master_controller *parent)
+{
+	struct i3c_master_controller *orig_parent = desc->master;
+
+	desc->master = parent;
+
+	return orig_parent;
+}
+
+static void restore_i3c_i2c_desc_parent(struct i3c_i2c_dev_desc *desc,
+					struct i3c_master_controller *parent)
+{
+	desc->master = parent;
+}
+
+static int p3h2x4x_i3c_bus_init(struct i3c_master_controller *controller)
+{
+	struct tp_bus *bus = container_of(controller, struct tp_bus, tp_i3c_controller);
+
+	controller->this = bus->p3h2x4x_i3c_hub->i3cdev->desc;
+	return 0;
+}
+
+static void p3h2x4x_i3c_bus_cleanup(struct i3c_master_controller *controller)
+{
+	controller->this = NULL;
+}
+
+static int p3h2x4x_attach_i3c_dev(struct i3c_dev_desc *dev)
+{
+	return 0;
+}
+
+static int p3h2x4x_reattach_i3c_dev(struct i3c_dev_desc *dev, u8 old_dyn_addr)
+{
+	return 0;
+}
+
+static void p3h2x4x_detach_i3c_dev(struct i3c_dev_desc *dev)
+{
+}
+
+static int p3h2x4x_do_daa(struct i3c_master_controller *controller)
+{
+	struct tp_bus *bus = container_of(controller, struct tp_bus, tp_i3c_controller);
+	struct i3c_master_controller *parent = get_parent_controller(controller);
+	int ret;
+
+	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
+	ret = i3c_master_do_daa(parent);
+	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
+
+	return ret;
+}
+
+static bool p3h2x4x_supports_ccc_cmd(struct i3c_master_controller *controller,
+				     const struct i3c_ccc_cmd *cmd)
+{
+	struct i3c_master_controller *parent = get_parent_controller(controller);
+
+	return i3c_master_supports_ccc_cmd(parent, cmd);
+}
+
+static int p3h2x4x_send_ccc_cmd(struct i3c_master_controller *controller,
+				struct i3c_ccc_cmd *cmd)
+{
+	struct tp_bus *bus = container_of(controller, struct tp_bus, tp_i3c_controller);
+	struct i3c_master_controller *parent = get_parent_controller(controller);
+	int ret;
+
+	if (cmd->id == I3C_CCC_RSTDAA(true))
+		return 0;
+
+	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
+	ret = i3c_master_send_ccc_cmd(parent, cmd);
+	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
+
+	return ret;
+}
+
+static int p3h2x4x_i3c_xfers(struct i3c_dev_desc *dev,
+			     struct i3c_priv_xfer *xfers, int nxfers,
+			     enum i3c_xfer_mode mode)
+{
+	struct tp_bus *bus = p3h2x4x_bus_from_i3c_desc(dev);
+	struct i3c_dev_desc *hub_dev = bus->p3h2x4x_i3c_hub->i3cdev->desc;
+	u8 hub_addr, target_addr;
+	int ret;
+
+	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
+
+	/* hub’s current address */
+	hub_addr = hub_dev->info.dyn_addr ? hub_dev->info.dyn_addr :
+		hub_dev->info.static_addr;
+
+	/* Target device address */
+	target_addr = dev->info.dyn_addr ? dev->info.dyn_addr :
+					     dev->info.static_addr;
+
+	/* Only reattach if the address is different */
+	if (hub_addr != target_addr) {
+		hub_dev->info.dyn_addr = target_addr;
+		ret = i3c_master_reattach_i3c_dev(hub_dev, target_addr);
+		if (ret)
+			goto disable_ntwk;
+	}
+
+	ret = i3c_device_do_priv_xfers(bus->p3h2x4x_i3c_hub->i3cdev, xfers, nxfers);
+
+	/* Restore hub’s original address if it was changed */
+	if (hub_addr != target_addr) {
+		hub_dev->info.dyn_addr = hub_addr;
+		ret |= i3c_master_reattach_i3c_dev(hub_dev, hub_addr);
+	}
+
+disable_ntwk:
+	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
+	return ret;
+}
+
+static int p3h2x4x_attach_i2c_dev(struct i2c_dev_desc *dev)
+{
+	return 0;
+}
+
+static void p3h2x4x_detach_i2c_dev(struct i2c_dev_desc *dev)
+{
+}
+
+static int p3h2x4x_i2c_xfers(struct i2c_dev_desc *dev,
+			     struct i2c_msg *xfers, int nxfers)
+{
+	return 0;
+}
+
+static int p3h2x4x_request_ibi(struct i3c_dev_desc *desc,
+			       const struct i3c_ibi_setup *req)
+{
+	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(desc);
+	struct i3c_master_controller *orig_parent;
+	int ret;
+
+	orig_parent = update_i3c_i2c_desc_parent(&desc->common, parent);
+	ret = i3c_master_direct_attach_i3c_dev(parent, desc);
+	if (ret) {
+		restore_i3c_i2c_desc_parent(&desc->common, orig_parent);
+		return ret;
+	}
+
+	mutex_unlock(&desc->ibi_lock);
+	kfree(desc->ibi);
+	desc->ibi = NULL;
+	ret = i3c_device_request_ibi(desc->dev, req);
+	mutex_lock(&desc->ibi_lock);
+	restore_i3c_i2c_desc_parent(&desc->common, orig_parent);
+
+	return ret;
+}
+
+static void p3h2x4x_free_ibi(struct i3c_dev_desc *desc)
+{
+	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(desc);
+	struct tp_bus *bus = p3h2x4x_bus_from_i3c_desc(desc);
+	struct i3c_master_controller *orig_parent;
+
+	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
+
+	orig_parent = update_i3c_i2c_desc_parent(&desc->common, parent);
+	i3c_master_direct_detach_i3c_dev(desc);
+	mutex_unlock(&desc->ibi_lock);
+	i3c_device_free_ibi(desc->dev);
+	mutex_lock(&desc->ibi_lock);
+	restore_i3c_i2c_desc_parent(&desc->common, orig_parent);
+
+	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
+}
+
+static int p3h2x4x_enable_ibi(struct i3c_dev_desc *desc)
+{
+	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(desc);
+	struct tp_bus *bus = p3h2x4x_bus_from_i3c_desc(desc);
+	struct i3c_master_controller *orig_parent;
+	int ret;
+
+	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
+	orig_parent = update_i3c_i2c_desc_parent(&desc->common, parent);
+
+	down_write(&bus->p3h2x4x_i3c_hub->i3cdev->bus->lock);
+	mutex_unlock(&desc->ibi_lock);
+	ret = i3c_device_enable_ibi(desc->dev);
+	mutex_lock(&desc->ibi_lock);
+	up_write(&bus->p3h2x4x_i3c_hub->i3cdev->bus->lock);
+
+	restore_i3c_i2c_desc_parent(&desc->common, orig_parent);
+	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
+
+	return ret;
+}
+
+static int p3h2x4x_disable_ibi(struct i3c_dev_desc *desc)
+{
+	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(desc);
+	struct tp_bus *bus = p3h2x4x_bus_from_i3c_desc(desc);
+	struct i3c_master_controller *orig_parent;
+	int ret;
+
+	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
+	orig_parent = update_i3c_i2c_desc_parent(&desc->common, parent);
+
+	down_write(&bus->p3h2x4x_i3c_hub->i3cdev->bus->lock);
+	mutex_unlock(&desc->ibi_lock);
+	ret = i3c_device_disable_ibi(desc->dev);
+	mutex_lock(&desc->ibi_lock);
+	up_write(&bus->p3h2x4x_i3c_hub->i3cdev->bus->lock);
+
+	restore_i3c_i2c_desc_parent(&desc->common, orig_parent);
+	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
+
+	return ret;
+}
+
+static void p3h2x4x_recycle_ibi_slot(struct i3c_dev_desc *desc,
+				     struct i3c_ibi_slot *slot)
+{
+}
+
+static const struct i3c_master_controller_ops p3h2x4x_i3c_ops = {
+	.bus_init = p3h2x4x_i3c_bus_init,
+	.bus_cleanup = p3h2x4x_i3c_bus_cleanup,
+	.attach_i3c_dev = p3h2x4x_attach_i3c_dev,
+	.reattach_i3c_dev = p3h2x4x_reattach_i3c_dev,
+	.detach_i3c_dev = p3h2x4x_detach_i3c_dev,
+	.do_daa = p3h2x4x_do_daa,
+	.supports_ccc_cmd = p3h2x4x_supports_ccc_cmd,
+	.send_ccc_cmd = p3h2x4x_send_ccc_cmd,
+	.i3c_xfers = p3h2x4x_i3c_xfers,
+	.attach_i2c_dev = p3h2x4x_attach_i2c_dev,
+	.detach_i2c_dev = p3h2x4x_detach_i2c_dev,
+	.i2c_xfers = p3h2x4x_i2c_xfers,
+	.request_ibi = p3h2x4x_request_ibi,
+	.free_ibi = p3h2x4x_free_ibi,
+	.enable_ibi = p3h2x4x_enable_ibi,
+	.disable_ibi = p3h2x4x_disable_ibi,
+	.recycle_ibi_slot = p3h2x4x_recycle_ibi_slot,
+};
+
+/**
+ * p3h2x4x_tp_i3c_algo - register i3c master for target port who
+ * configured as i3c.
+ * @hub: p3h2x4x device structure.
+ * Return: 0 in case of success, negative error code on failur.
+ */
+int p3h2x4x_tp_i3c_algo(struct p3h2x4x_i3c_hub_dev *hub)
+{
+	u8 tp, ntwk_mask = 0;
+	int ret;
+
+	for (tp = 0; tp < P3H2x4x_TP_MAX_COUNT; tp++) {
+		if (!hub->tp_bus[tp].of_node ||
+		    hub->hub_config.tp_config[tp].mode != P3H2x4x_TP_MODE_I3C)
+			continue;
+
+		/* Assign DT node for this TP */
+		hub->dev->of_node = hub->tp_bus[tp].of_node;
+
+		/* Register I3C master for this TP */
+		ret = i3c_master_register(&hub->tp_bus[tp].tp_i3c_controller,
+					  hub->dev, &p3h2x4x_i3c_ops, false);
+		if (ret)
+			return ret;
+
+		/* Perform DAA */
+		ret = i3c_master_do_daa(i3c_dev_get_master(hub->i3cdev->desc));
+		if (ret)
+			return ret;
+
+		ntwk_mask |= hub->tp_bus[tp].tp_mask;
+		hub->tp_bus[tp].is_registered = true;
+		hub->hub_config.tp_config[tp].always_enable = true;
+	}
+
+	ret = i3c_device_request_ibi(hub->i3cdev, &p3h2x4x_ibireq);
+	if (ret)
+		return ret;
+
+	ret = i3c_device_enable_ibi(hub->i3cdev);
+	if (ret)
+		return ret;
+
+	return regmap_write(hub->regmap, P3H2x4x_TP_NET_CON_CONF, ntwk_mask);
+}
diff --git a/drivers/i3c/hub/p3h2840_i3c_hub_smbus.c b/drivers/i3c/hub/p3h2840_i3c_hub_smbus.c
new file mode 100644
index 000000000000..7869e8f82c0d
--- /dev/null
+++ b/drivers/i3c/hub/p3h2840_i3c_hub_smbus.c
@@ -0,0 +1,418 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 NXP
+ * This P3H2x4x driver file contain functions for SMBus/I2C virtual Bus creation and read/write.
+ */
+#include <linux/mfd/p3h2840.h>
+#include <linux/regmap.h>
+
+#include "p3h2840_i3c_hub.h"
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static void p3h2x4x_read_smbus_agent_rx_buf(struct i3c_device *i3cdev, enum p3h2x4x_rcv_buf rfbuf,
+					    enum p3h2x4x_tp tp, bool is_of)
+{
+	struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub = dev_get_drvdata(&i3cdev->dev);
+	u8 slave_rx_buffer[P3H2x4x_SMBUS_TARGET_PAYLOAD_SIZE] = { 0 };
+	u8 target_buffer_page, flag_clear = 0x0f, temp, i;
+	u32 packet_len, slave_address, ret;
+
+	target_buffer_page = (((rfbuf) ? P3H2x4x_TARGET_BUFF_1_PAGE : P3H2x4x_TARGET_BUFF_0_PAGE)
+							+  (P3H2x4x_NO_PAGE_PER_TP * tp));
+	ret = regmap_write(p3h2x4x_i3c_hub->regmap, P3H2x4x_PAGE_PTR, target_buffer_page);
+	if (ret)
+		goto ibi_err;
+
+	/* read buffer length */
+	ret = regmap_read(p3h2x4x_i3c_hub->regmap, P3H2x4x_TARGET_BUFF_LENGTH, &packet_len);
+	if (ret)
+		goto ibi_err;
+
+	if (packet_len)
+		packet_len = packet_len - 1;
+
+	if (packet_len > P3H2x4x_SMBUS_TARGET_PAYLOAD_SIZE) {
+		dev_err(&i3cdev->dev, "Received message too big for p3h2x4x buffer\n");
+		return;
+	}
+
+	/* read slave  address */
+	ret = regmap_read(p3h2x4x_i3c_hub->regmap, P3H2x4x_TARGET_BUFF_ADDRESS, &slave_address);
+	if (ret)
+		goto ibi_err;
+
+	/* read data */
+	if (packet_len) {
+		ret = regmap_bulk_read(p3h2x4x_i3c_hub->regmap, P3H2x4x_TARGET_BUFF_DATA,
+				       slave_rx_buffer, packet_len);
+		if (ret)
+			goto ibi_err;
+	}
+
+	if (is_of)
+		flag_clear = BUF_RECEIVED_FLAG_TF_MASK;
+	else
+		flag_clear = (((rfbuf == RCV_BUF_0) ? P3H2x4x_TARGET_BUF_0_RECEIVE :
+					P3H2x4x_TARGET_BUF_1_RECEIVE));
+
+	/* notify slave driver about received data */
+	if ((p3h2x4x_i3c_hub->tp_bus[tp].tp_smbus_client->addr & 0x7f) == (slave_address >> 1)) {
+		i2c_slave_event(p3h2x4x_i3c_hub->tp_bus[tp].tp_smbus_client,
+				I2C_SLAVE_WRITE_REQUESTED, (u8 *)&slave_address);
+		for (i = 0; i < packet_len; i++) {
+			temp = slave_rx_buffer[i];
+			i2c_slave_event(p3h2x4x_i3c_hub->tp_bus[tp].tp_smbus_client,
+					I2C_SLAVE_WRITE_RECEIVED, &temp);
+		}
+		i2c_slave_event(p3h2x4x_i3c_hub->tp_bus[tp].tp_smbus_client, I2C_SLAVE_STOP, &temp);
+	}
+
+ibi_err:
+	regmap_write(p3h2x4x_i3c_hub->regmap, P3H2x4x_PAGE_PTR, 0x00);
+	regmap_write(p3h2x4x_i3c_hub->regmap, P3H2x4x_TP0_SMBUS_AGNT_STS + tp, flag_clear);
+}
+#endif
+
+/**
+ * p3h2x4x_ibi_handler - IBI handler.
+ * @i3cdev: i3c device.
+ * @payload: two byte IBI payload data.
+ *
+ */
+void p3h2x4x_ibi_handler(struct i3c_device *i3cdev,
+			 const struct i3c_ibi_payload *payload)
+{
+	struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub = dev_get_drvdata(&i3cdev->dev);
+	u32 target_port_status, payload_byte_one, payload_byte_two;
+	u32 ret, i;
+
+	payload_byte_one = (*(int *)payload->data);
+	payload_byte_two = (*(int *)(payload->data + 4));
+
+	if (!(payload_byte_one & P3H2x4x_SMBUS_AGENT_EVENT_FLAG_STATUS))
+		return;
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+	guard(mutex)(&p3h2x4x_i3c_hub->etx_mutex);
+
+	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; ++i) {
+		if (p3h2x4x_i3c_hub->tp_bus[i].is_registered && (payload_byte_two >> i) & 0x01) {
+			ret = regmap_read(p3h2x4x_i3c_hub->regmap, P3H2x4x_TP0_SMBUS_AGNT_STS + i,
+					  &target_port_status);
+			if (ret) {
+				dev_err(&i3cdev->dev, "target port read status failed %d\n", ret);
+				return;
+			}
+
+			/* process data receive buffer */
+			switch (target_port_status & BUF_RECEIVED_FLAG_MASK) {
+			case P3H2x4x_TARGET_BUF_CA_TF:
+				break;
+			case P3H2x4x_TARGET_BUF_0_RECEIVE:
+				p3h2x4x_read_smbus_agent_rx_buf(i3cdev, RCV_BUF_0, i, false);
+				break;
+			case P3H2x4x_TARGET_BUF_1_RECEIVE:
+				p3h2x4x_read_smbus_agent_rx_buf(i3cdev, RCV_BUF_1, i, false);
+				break;
+			case P3H2x4x_TARGET_BUF_0_1_RECEIVE:
+				p3h2x4x_read_smbus_agent_rx_buf(i3cdev, RCV_BUF_0, i, false);
+				p3h2x4x_read_smbus_agent_rx_buf(i3cdev, RCV_BUF_1, i, false);
+				break;
+			case P3H2x4x_TARGET_BUF_OVRFL:
+				p3h2x4x_read_smbus_agent_rx_buf(i3cdev, RCV_BUF_0, i, false);
+				p3h2x4x_read_smbus_agent_rx_buf(i3cdev, RCV_BUF_1, i, true);
+				dev_err(&i3cdev->dev, "Overflow, reading buffer zero and one\n");
+				break;
+			default:
+				regmap_write(p3h2x4x_i3c_hub->regmap,
+					     P3H2x4x_TP0_SMBUS_AGNT_STS + i,
+					     BUF_RECEIVED_FLAG_TF_MASK);
+				break;
+			}
+		}
+	}
+#endif
+}
+
+static int p3h2x4x_read_smbus_transaction_status(struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub,
+						 u8 target_port_status,
+						 u8 data_length)
+{
+	u32 status_read;
+	int ret;
+
+	mutex_unlock(&p3h2x4x_i3c_hub->etx_mutex);
+	fsleep(P3H2x4x_SMBUS_400kHz_TRANSFER_TIMEOUT(data_length));
+	mutex_lock(&p3h2x4x_i3c_hub->etx_mutex);
+
+	ret = regmap_read(p3h2x4x_i3c_hub->regmap, target_port_status, &status_read);
+	if (ret)
+		return ret;
+
+	if (status_read & P3H2x4x_SMBUS_TRANSACTION_FINISH_FLAG) {
+		switch (status_read >> P3H2x4x_SMBUS_CNTRL_STATUS_TXN_SHIFT) {
+		case P3H2x4x_SMBUS_CNTRL_STATUS_TXN_OK:
+			ret = 0;
+			break;
+		case P3H2x4x_SMBUS_CNTRL_STATUS_TXN_ADDR_NAK:
+			ret = -ENXIO;
+			break;
+		case P3H2x4x_SMBUS_CNTRL_STATUS_TXN_DATA_NAK:
+			ret = -EIO;
+			break;
+		case P3H2x4x_SMBUS_CNTRL_STATUS_TXN_ARB_LOSS:
+			ret = -EAGAIN;
+			break;
+		case P3H2x4x_SMBUS_CNTRL_STATUS_TXN_SCL_TO:
+			ret = -ETIMEDOUT;
+			break;
+		case P3H2x4x_SMBUS_CNTRL_STATUS_TXN_WTR_NAK:
+			ret = -ENXIO;
+			break;
+		default:
+			ret = -EIO;
+			break;
+		}
+	}
+	return ret;
+}
+
+/**
+ * p3h2x4x_tp_i2c_xfer_msg() - This starts a SMBus write transaction by writing a descriptor
+ * and a message to the p3h2x4x registers. Controller buffer page is determined by multiplying the
+ * target port index by four and adding the base page number to it.
+ */
+static int p3h2x4x_tp_i2c_xfer_msg(struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub,
+				   struct i2c_msg *xfers,
+				   u8 target_port,
+				   u8 nxfers_i, u8 rw)
+{
+	u8 controller_buffer_page = P3H2x4x_CONTROLLER_BUFFER_PAGE + 4 * target_port;
+	u8 target_port_status = P3H2x4x_TP0_SMBUS_AGNT_STS + target_port;
+	u8 desc[P3H2x4x_SMBUS_DESCRIPTOR_SIZE] = { 0 };
+	u8 transaction_type = P3H2x4x_SMBUS_400kHz;
+	int write_length, read_length;
+	u8 addr = xfers[nxfers_i].addr;
+	u8 rw_address = 2 * addr;
+	int ret;
+
+	if (rw == 2) { /* write and read */
+		write_length = xfers[nxfers_i].len;
+		read_length =  xfers[nxfers_i + 1].len;
+	} else if (rw == 1) {
+		rw_address |= P3H2x4x_SET_BIT(0);
+		write_length = 0;
+		read_length =  xfers[nxfers_i].len;
+	} else {
+		write_length = xfers[nxfers_i].len;
+		read_length = 0;
+	}
+
+	desc[0] = rw_address;
+	if (rw == 2)
+		desc[1] = transaction_type | P3H2x4x_SET_BIT(0);
+	else
+		desc[1] = transaction_type;
+	desc[2] = write_length;
+	desc[3] = read_length;
+
+	ret = regmap_write(p3h2x4x_i3c_hub->regmap, target_port_status,
+			   P3H2x4x_TP_BUFFER_STATUS_MASK);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(p3h2x4x_i3c_hub->regmap, P3H2x4x_PAGE_PTR, controller_buffer_page);
+	if (ret)
+		return ret;
+
+	ret = regmap_bulk_write(p3h2x4x_i3c_hub->regmap, P3H2x4x_CONTROLLER_AGENT_BUFF,
+				desc, P3H2x4x_SMBUS_DESCRIPTOR_SIZE);
+	if (ret)
+		return ret;
+
+	if (!(rw % 2)) {
+		ret = regmap_bulk_write(p3h2x4x_i3c_hub->regmap,
+					P3H2x4x_CONTROLLER_AGENT_BUFF_DATA,
+					xfers[nxfers_i].buf, xfers[nxfers_i].len);
+		if (ret)
+			return ret;
+	}
+
+	ret = regmap_write(p3h2x4x_i3c_hub->regmap, P3H2x4x_TP_SMBUS_AGNT_TRANS_START,
+			   p3h2x4x_i3c_hub->tp_bus[target_port].tp_mask);
+	if (ret)
+		return ret;
+
+	ret = p3h2x4x_read_smbus_transaction_status(p3h2x4x_i3c_hub, target_port_status,
+						    (write_length + read_length));
+	if (ret)
+		return ret;
+
+	if (rw) {
+		if (rw == 2)
+			nxfers_i += 1;
+
+		ret = regmap_bulk_read(p3h2x4x_i3c_hub->regmap,
+				       P3H2x4x_CONTROLLER_AGENT_BUFF_DATA + write_length,
+				       xfers[nxfers_i].buf, xfers[nxfers_i].len);
+		if (ret)
+			return ret;
+	}
+
+	ret = regmap_write(p3h2x4x_i3c_hub->regmap, P3H2x4x_PAGE_PTR, 0x00);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+/*
+ * This function will be called whenever you call I2C read, write APIs like
+ * i2c_master_send(), i2c_master_recv() etc.
+ */
+static s32 p3h2x4x_tp_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+	int ret_sum = 0, ret;
+	u8 msg_count, rw;
+
+	struct tp_bus *bus = i2c_get_adapdata(adap);
+	struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub = bus->p3h2x4x_i3c_hub;
+
+	guard(mutex)(&p3h2x4x_i3c_hub->etx_mutex);
+	guard(mutex)(&bus->port_mutex);
+
+	for (msg_count = 0; msg_count < num; msg_count++) {
+		if (msgs[msg_count].len > P3H2x4x_SMBUS_PAYLOAD_SIZE) {
+			dev_err(p3h2x4x_i3c_hub->dev,
+				"Message nr. %d not sent - length over %d bytes.\n",
+				msg_count, P3H2x4x_SMBUS_PAYLOAD_SIZE);
+			continue;
+		}
+
+		rw = msgs[msg_count].flags % 2;
+		if (!rw) {
+		/* If a read message is immediately followed by a write message to
+		 * the same address,  consider combining them into a single transaction.
+		 */
+			if (msg_count < num && msgs[msg_count].addr == msgs[msg_count + 1].addr &&
+			    (msgs[msg_count + 1].flags & I2C_M_RD)) {
+				rw = 2;
+				msg_count += 1;
+				ret_sum += 1;
+			}
+		}
+
+		ret = p3h2x4x_tp_i2c_xfer_msg(p3h2x4x_i3c_hub,
+					      msgs,
+					      bus->tp_port,
+					      (rw == 2) ? (msg_count - 1) : msg_count,
+						  rw);
+		if (ret)
+			return ret;
+
+		ret_sum++;
+	}
+	return ret_sum;
+}
+
+static u32 p3h2x4x_tp_smbus_funcs(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static int p3h2x4x_tp_i2c_reg_slave(struct i2c_client *slave)
+{
+	struct tp_bus *bus = i2c_get_adapdata(slave->adapter);
+
+	if (bus->tp_smbus_client)
+		return -EBUSY;
+
+	bus->tp_smbus_client = slave;
+
+	return 0;
+}
+
+static int p3h2x4x_tp_i2c_unreg_slave(struct i2c_client *slave)
+{
+	struct tp_bus *bus = i2c_get_adapdata(slave->adapter);
+
+	bus->tp_smbus_client = NULL;
+
+	return 0;
+}
+#endif
+
+/*
+ * I2C algorithm Structure
+ */
+static struct i2c_algorithm p3h2x4x_tp_i2c_algorithm = {
+	.master_xfer    = p3h2x4x_tp_i2c_xfer,
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+	.reg_slave = p3h2x4x_tp_i2c_reg_slave,
+	.unreg_slave = p3h2x4x_tp_i2c_unreg_slave,
+#endif
+	.functionality  = p3h2x4x_tp_smbus_funcs,
+};
+
+/**
+ * p3h2x4x_tp_smbus_algo - add i2c adapter for target port who
+ * configured as SMBus.
+ * @hub: p3h2x4x device structure.
+ * Return: 0 in case of success, negative error code on failur.
+ */
+int p3h2x4x_tp_smbus_algo(struct p3h2x4x_i3c_hub_dev *hub)
+{
+	u8 tp, ibi_mask = 0;
+	int ret;
+
+	for (tp = 0; tp < P3H2x4x_TP_MAX_COUNT; tp++) {
+		if (!hub->tp_bus[tp].of_node ||
+		    hub->hub_config.tp_config[tp].mode != P3H2x4x_TP_MODE_SMBUS)
+			continue;
+
+		/* Allocate adapter */
+		struct i2c_adapter *smbus_adapter =
+			devm_kzalloc(hub->dev, sizeof(*smbus_adapter), GFP_KERNEL);
+		if (!smbus_adapter)
+			return -ENOMEM;
+
+		/* Initialize adapter */
+		smbus_adapter->owner = THIS_MODULE;
+		smbus_adapter->class = I2C_CLASS_HWMON;
+		smbus_adapter->algo = &p3h2x4x_tp_i2c_algorithm;
+		smbus_adapter->dev.parent = hub->dev;
+		smbus_adapter->dev.of_node = hub->tp_bus[tp].of_node;
+		snprintf(smbus_adapter->name, sizeof(smbus_adapter->name),
+			 "p3h2x4x-i3c-hub.tp-port-%d", tp);
+
+		i2c_set_adapdata(smbus_adapter, &hub->tp_bus[tp]);
+
+		/* Register adapter */
+		ret = i2c_add_adapter(smbus_adapter);
+		if (ret) {
+			devm_kfree(hub->dev, smbus_adapter);
+			return ret;
+		}
+
+		ibi_mask |= hub->tp_bus[tp].tp_mask;
+		hub->tp_bus[tp].is_registered = true;
+		hub->hub_config.tp_config[tp].ibi_en = true;
+		hub->tp_bus[tp].tp_smbus_adapter = smbus_adapter;
+	}
+
+	/*
+	 * holding SDA low when both SMBus Target Agent received data buffers are full.
+	 * This feature can be used as a flow-control mechanism for MCTP applications to
+	 * avoid MCTP transmitters on Target Ports time out when the SMBus agent buffers
+	 * are not serviced in time by upstream controller and only receives write message
+	 * from its downstream ports.
+	 */
+	ret = regmap_update_bits(hub->regmap, P3H2x4x_ONCHIP_TD_AND_SMBUS_AGNT_CONF,
+				 P3H2x4x_TARGET_AGENT_DFT_IBI_CONF_MASK,
+				 P3H2x4x_TARGET_AGENT_DFT_IBI_CONF);
+	if (ret)
+		return ret;
+
+	return regmap_write(hub->regmap, P3H2x4x_TP_SMBUS_AGNT_IBI_CONFIG, ibi_mask);
+}
diff --git a/include/linux/i3c/device.h b/include/linux/i3c/device.h
index 9fcb6410a584..cb81a85611ab 100644
--- a/include/linux/i3c/device.h
+++ b/include/linux/i3c/device.h
@@ -88,6 +88,7 @@ struct i3c_xfer {
  */
 enum i3c_dcr {
 	I3C_DCR_GENERIC_DEVICE = 0,
+	I3C_DCR_HUB = 194,
 };
 
 #define I3C_PID_MANUF_ID(pid)		(((pid) & GENMASK_ULL(47, 33)) >> 33)
-- 
2.25.1


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

* Re: [PATCH v5 3/5] mfd: p3h2x4x: Add driver for NXP P3H2x4x i3c hub and on-die regulator
  2026-02-06 12:01 ` [PATCH v5 3/5] mfd: p3h2x4x: Add driver for NXP P3H2x4x i3c hub and on-die regulator Aman Kumar Pandey
@ 2026-02-06 12:52   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 12+ messages in thread
From: Krzysztof Kozlowski @ 2026-02-06 12:52 UTC (permalink / raw)
  To: Aman Kumar Pandey, linux-kernel, linux-i3c, alexandre.belloni,
	krzk+dt, robh, conor+dt, devicetree, broonie, lee, Frank.Li,
	lgirdwood
  Cc: vikash.bansal, priyanka.jain, shashank.rebbapragada

On 06/02/2026 13:01, Aman Kumar Pandey wrote:
> Add core MFD support for the NXP P3H2x4x (P3H2440/P3H2441/P3H2840/P3H2841)
> family of multiport I3C hub devices. These devices connect to a host via
> I3C/I2C/SMBus and expose multiple downstream target ports.
> 
> This patch introduces the MFD driver that registers sub-devices for the
> I3C hub and on-die regulators.

Drop sentence. You already said it in the first place, didn't you?

Please do not use "This commit/patch/change", but imperative mood. See
longer explanation here:
https://elixir.bootlin.com/linux/v6.16/source/Documentation/process/submitting-patches.rst#L94

...


> +
> +	i2c_set_clientdata(client, p3h2x4x);
> +
> +	p3h2x4x->regmap = devm_regmap_init_i2c(client, &p3h2x4x_regmap_config);
> +	if (IS_ERR(p3h2x4x->regmap)) {

Drop {} now. When you change piece of the code, fix rest as well.

See Linux coding style.

> +		return dev_err_probe(&client->dev, PTR_ERR(p3h2x4x->regmap),
> +				     "Failed to register I3C HUB regmap\n");
> +	}
> +
> +	p3h2x4x->is_p3h2x4x_in_i3c = false;
> +	p3h2x4x->i2c_client = client;
> +
> +	ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE,
> +				   p3h2x4x_devs, ARRAY_SIZE(p3h2x4x_devs),
> +				   NULL, 0, NULL);
> +	if (ret)
> +		return dev_err_probe(&client->dev, ret, "Failed to add sub devices\n");
> +
> +	return 0;
> +}
> +
> +/* p3h2x4x ids (i3c) */
> +static const struct i3c_device_id p3h2x4x_i3c_ids[] = {
> +	I3C_CLASS(I3C_DCR_HUB, NULL),
> +	{ /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(i3c, p3h2x4x_i3c_ids);
> +
> +/* p3h2x4x ids (i2c) */
> +static const struct i2c_device_id p3h2x4x_i2c_id_table[] = {
> +	{ "nxp-i3c-hub" },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(i2c, p3h2x4x_i2c_id_table);
> +
> +static const struct of_device_id  p3h2x4x_i2c_of_match[] = {
> +	{ .compatible = "nxp,p3h2840", },
> +	{ /* sentinel */ }
> +};
> +
> +MODULE_DEVICE_TABLE(of, p3h2x4x_i2c_of_match);
> +static struct i3c_driver p3h2x4x_i3c = {
> +	.driver = {
> +		.name = "p3h2x4x_i3c_drv",
> +	},
> +	.probe = p3h2x4x_device_probe_i3c,
> +	.id_table = p3h2x4x_i3c_ids,
> +};
> +
> +static struct i2c_driver p3h2x4x_i2c = {
> +	.driver = {
> +		.name = "p3h2x4x_i2c_drv",
> +		.of_match_table = p3h2x4x_i2c_of_match,
> +	},
> +	.probe =  p3h2x4x_device_probe_i2c,
> +	.id_table = p3h2x4x_i2c_id_table,
> +};
> +
> +module_i3c_i2c_driver(p3h2x4x_i3c, &p3h2x4x_i2c);
> +
> +MODULE_AUTHOR("Aman Kumar Pandey <aman.kumarpandey@nxp.com>");
> +MODULE_AUTHOR("vikash Bansal <vikash.bansal@nxp.com>");
> +MODULE_DESCRIPTION("P3H2x4x I3C HUB multi function driver");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/mfd/p3h2840.h b/include/linux/mfd/p3h2840.h
> new file mode 100644
> index 000000000000..b7a9c7f3aa4d
> --- /dev/null
> +++ b/include/linux/mfd/p3h2840.h
> @@ -0,0 +1,28 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright 2025 NXP
> + * This header file contain private Reg address and its bit mapping etc.
> + */
> +
> +#ifndef P3H2840_H
> +#define P3H2840_H

That's pretty poor (too simple) guard for a global header.

> +
> +#include <linux/types.h>
> +
> +/* Device Configuration Registers */
> +#define P3H2x4x_DEV_REG_PROTECTION_CODE				0x10
> +#define P3H2x4x_REGISTERS_LOCK_CODE				0x00
> +#define P3H2x4x_REGISTERS_UNLOCK_CODE				0x69
> +#define P3H2x4x_CP1_REGISTERS_UNLOCK_CODE			0x6a
> +


Best regards,
Krzysztof

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

* Re: [PATCH v5 1/5] i3c: master: Expose the APIs to support I3C hub
  2026-02-06 12:01 [PATCH v5 1/5] i3c: master: Expose the APIs to support I3C hub Aman Kumar Pandey
                   ` (3 preceding siblings ...)
  2026-02-06 12:01 ` [PATCH v5 5/5] i3c: hub: p3h2x4x: Add support for NXP P3H2x4x I3C hub functionality Aman Kumar Pandey
@ 2026-02-06 17:06 ` Frank Li
  4 siblings, 0 replies; 12+ messages in thread
From: Frank Li @ 2026-02-06 17:06 UTC (permalink / raw)
  To: Aman Kumar Pandey
  Cc: linux-kernel, linux-i3c, alexandre.belloni, krzk+dt, robh,
	conor+dt, devicetree, broonie, lee, lgirdwood, vikash.bansal,
	priyanka.jain, shashank.rebbapragada

On Fri, Feb 06, 2026 at 02:01:17PM +0200, Aman Kumar Pandey wrote:
> Expose i3c_master_send_ccc_cmd(), i3c_master_supports_ccc_cmd(),
> i3c_master_reattach_i3c_dev(), i3c_master_direct_attach_i3c_dev(),
> and i3c_master_direct_detach_i3c_dev() to support I3C hub.
>
> Signed-off-by: Aman Kumar Pandey <aman.kumarpandey@nxp.com>
>
> ---
> Changes in v5:
>  - No change
>
> Changes in v4:
>  - Updated I3C master to handle hub support
> ---
> ---
>  drivers/i3c/master.c       | 123 ++++++++++++++++++++++++++++++++++++-
>  include/linux/i3c/master.h |  11 ++++
>  2 files changed, 132 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
> index 7f606c871648..c92152ff795f 100644
> --- a/drivers/i3c/master.c
> +++ b/drivers/i3c/master.c
> @@ -1544,8 +1544,76 @@ static int i3c_master_attach_i3c_dev(struct i3c_master_controller *master,
>  	return 0;
>  }
>
> -static int i3c_master_reattach_i3c_dev(struct i3c_dev_desc *dev,
> -				       u8 old_dyn_addr)
> +/**
> + * i3c_master_direct_attach_i3c_dev() - attach an I3C device to a master
> + * @master: I3C master controller to attach the device to
> + * @dev: I3C device descriptor representing the device
> + *
> + * This function attaches an I3C device to its master controller once the
> + * device has a valid address on the bus. Devices without
> + * an assigned address are ignored. The master device itself is never
> + * attached through this bus.
> + *
> + * Return: 0 on success, or a negative error code if the attach operation
> + *         fails in the master controller driver.
> + */
> +int i3c_master_direct_attach_i3c_dev(struct i3c_master_controller *master,
> +				     struct i3c_dev_desc *dev)
> +{
> +	int ret = 0;
> +
> +	/*
> +	 * We don't attach devices to the controller until they are
> +	 * addressable on the bus.
> +	 */
> +
> +	if (!dev->info.static_addr && !dev->info.dyn_addr)
> +		return 0;

AI: - Silent success on invalid input is questionable - should this be -EINVAL?

> +
> +	/* Do not attach the master device itself. */
> +	if (master->this != dev && master->ops->attach_i3c_dev)
> +		ret = master->ops->attach_i3c_dev(dev);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(i3c_master_direct_attach_i3c_dev);
> +
> +/**
> + * i3c_master_direct_detach_i3c_dev - Detach an I3C device from its master
> + * @dev: I3C device descriptor to be detached
> + *
> + * This function detaches an I3C device from its master controller.
> + * It ensures that the master itself is not detached. If the device is not
> + * the master and the master controller provides a detach operation,
> + * the detach callback is invoked to perform the actual removal.
> + */
> +void i3c_master_direct_detach_i3c_dev(struct i3c_dev_desc *dev)
> +{
> +	struct i3c_master_controller *master = i3c_dev_get_master(dev);
> +
> +	/* Do not detach the master device itself. */
> +	if (master->this != dev && master->ops->detach_i3c_dev)
> +		master->ops->detach_i3c_dev(dev);
> +}
> +EXPORT_SYMBOL_GPL(i3c_master_direct_detach_i3c_dev);

Can you create two patch,
- change existed static function to API.
- add new APIs.

Frank
> +
> +/**
> + * i3c_master_reattach_i3c_dev() - reattach an I3C device with a new address
> + * @dev: I3C device descriptor to reattach
> + * @old_dyn_addr: previous dynamic address of the device
> + *
> + * This function reattaches an existing I3C device to the bus when its dynamic
> + * address has changed. It updates the bus address slot status accordingly:
> + * - Marks the new dynamic address as occupied by an I3C device.
> + * - Frees the old dynamic address slot if applicable.
> + *
> + * This function must be called with the bus lock held in write mode.
> + *
> + * Return: 0 on success, or a negative error code if reattachment fails
> + *         (e.g. -EBUSY if the new address slot is not free).
> + */
> +int i3c_master_reattach_i3c_dev(struct i3c_dev_desc *dev,
> +				u8 old_dyn_addr)
>  {
>  	struct i3c_master_controller *master = i3c_dev_get_master(dev);
>  	int ret;
> @@ -1569,6 +1637,7 @@ static int i3c_master_reattach_i3c_dev(struct i3c_dev_desc *dev,
>
>  	return 0;
>  }
> +EXPORT_SYMBOL_GPL(i3c_master_reattach_i3c_dev);
>
>  static void i3c_master_detach_i3c_dev(struct i3c_dev_desc *dev)
>  {
> @@ -1692,6 +1761,56 @@ i3c_master_register_new_i3c_devs(struct i3c_master_controller *master)
>  	}
>  }
>
> +/**
> + * i3c_master_supports_ccc_cmd() - check CCC command support
> + * @master: I3C master controller
> + * @cmd: CCC command to verify
> + *
> + * This function verifies whether the given I3C master controller supports
> + * the specified Common Command Code (CCC).
> + *
> + * Return: 0 if the CCC command is supported and executed successfully,
> + *         -EINVAL if arguments are invalid,
> + *         -EOPNOTSUPP if the master does not support CCC commands,
> + *         or another negative error code from the master's operation.
> + */
> +int i3c_master_supports_ccc_cmd(struct i3c_master_controller *master,
> +				const struct i3c_ccc_cmd *cmd)
> +{
> +	if (!cmd || !master)
> +		return -EINVAL;
> +
> +	if (!master->ops->supports_ccc_cmd)
> +		return -EOPNOTSUPP;
> +
> +	return master->ops->supports_ccc_cmd(master, cmd);
> +}
> +EXPORT_SYMBOL_GPL(i3c_master_supports_ccc_cmd);
> +
> +/**
> + * i3c_master_send_ccc_cmd() - send a CCC command
> + * @master: I3C master controller issuing the command
> + * @cmd: CCC command to be sent
> + *
> + * This function sends a Common Command Code (CCC) command to devices on the
> + * I3C bus. It acquires the bus maintenance lock, executes the command, and
> + * then releases the lock to ensure safe access to the bus.
> + *
> + * Return: 0 on success, or a negative error code on failure.
> + */
> +int i3c_master_send_ccc_cmd(struct i3c_master_controller *master,
> +			    struct i3c_ccc_cmd *cmd)
> +{
> +	int ret;
> +
> +	i3c_bus_maintenance_lock(&master->bus);
> +	ret = i3c_master_send_ccc_cmd_locked(master, cmd);
> +	i3c_bus_maintenance_unlock(&master->bus);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(i3c_master_send_ccc_cmd);
> +
>  /**
>   * i3c_master_do_daa() - do a DAA (Dynamic Address Assignment)
>   * @master: master doing the DAA
> diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
> index 58d01ed4cce7..b059da7777d7 100644
> --- a/include/linux/i3c/master.h
> +++ b/include/linux/i3c/master.h
> @@ -602,6 +602,17 @@ void i3c_master_dma_unmap_single(struct i3c_dma *dma_xfer);
>  DEFINE_FREE(i3c_master_dma_unmap_single, void *,
>  	    if (_T) i3c_master_dma_unmap_single(_T))
>
> +int i3c_master_send_ccc_cmd(struct i3c_master_controller *master,
> +			    struct i3c_ccc_cmd *cmd);
> +
> +int i3c_master_supports_ccc_cmd(struct i3c_master_controller *master,
> +				const struct i3c_ccc_cmd *cmd);
> +
> +int i3c_master_reattach_i3c_dev(struct i3c_dev_desc *dev,
> +				u8 old_dyn_addr);
> +int i3c_master_direct_attach_i3c_dev(struct i3c_master_controller *master,
> +				     struct i3c_dev_desc *dev);
> +void i3c_master_direct_detach_i3c_dev(struct i3c_dev_desc *dev);
>  int i3c_master_set_info(struct i3c_master_controller *master,
>  			const struct i3c_device_info *info);
>
> --
> 2.25.1
>

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

* Re: [PATCH v5 4/5] regulator: p3h2x4x: Add driver for on-die regulators in NXP P3H2x4x i3c hub
  2026-02-06 12:01 ` [PATCH v5 4/5] regulator: p3h2x4x: Add driver for on-die regulators in NXP P3H2x4x i3c hub Aman Kumar Pandey
@ 2026-02-06 17:19   ` Frank Li
  2026-02-06 22:19   ` kernel test robot
  1 sibling, 0 replies; 12+ messages in thread
From: Frank Li @ 2026-02-06 17:19 UTC (permalink / raw)
  To: Aman Kumar Pandey
  Cc: linux-kernel, linux-i3c, alexandre.belloni, krzk+dt, robh,
	conor+dt, devicetree, broonie, lee, lgirdwood, vikash.bansal,
	priyanka.jain, shashank.rebbapragada

On Fri, Feb 06, 2026 at 02:01:20PM +0200, Aman Kumar Pandey wrote:
> The NXP P3H2x4x family integrates on-die regulators alongside I3C hub
> functionality. This driver registers the regulators using the MFD

Register the regulaltor ...

> framework and exposes them via the regulator subsystem.
>
> Signed-off-by: Aman Kumar Pandey <aman.kumarpandey@nxp.com>
> Signed-off-by: Vikash Bansal <vikash.bansal@nxp.com>
>
> ---
> Changes in v5:
>  - Updated dev_err_probe() for regmap_init failure.
>  - Updated module description
>
> Changes in v4:
>  - Split the driver into three separate patches (mfd, regulator and I3C hub)
>  - Introduced driver for on-die regulators in NXP P3H2x4x I3C hub
> ---
> ---
>  MAINTAINERS                                   |   1 +
>  drivers/regulator/Kconfig                     |  10 +
>  drivers/regulator/Makefile                    |   1 +
>  drivers/regulator/p3h2840_i3c_hub_regulator.c | 247 ++++++++++++++++++
>  4 files changed, 259 insertions(+)
>  create mode 100644 drivers/regulator/p3h2840_i3c_hub_regulator.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 6c247e4a0c12..6601830b6e86 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -18914,6 +18914,7 @@ L:	linux-i3c-owner@lists.infradead.org
>  S:	Maintained
>  F:	Documentation/devicetree/bindings/i3c/nxp,p3h2840.yaml
>  F:	drivers/mfd/p3h2840.c
> +F:	drivers/regulator/p3h2840_i3c_hub_regulator.c
>  F:	include/linux/mfd/p3h2840.h
>
>  NXP PF5300/PF5301/PF5302 PMIC REGULATOR DEVICE DRIVER
> diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
> index d2335276cce5..84684701dad7 100644
> --- a/drivers/regulator/Kconfig
> +++ b/drivers/regulator/Kconfig
> @@ -1010,6 +1010,16 @@ config REGULATOR_MTK_DVFSRC
>  	  of Mediatek. It allows for voting on regulator state
>  	  between multiple users.
>
> +config REGULATOR_P3H2X4X
> +       tristate "P3H2X4X regulator support"
> +       depends on MFD_P3H2X4X
> +       help
> +         This driver provides support for the voltage regulators of the
> +         P3H244x/P3H284x multi-function I3C Hub device.
> +
> +         Say M here if you want to include support for this regulator as
> +         a module. The module will be named "p3h2840_i3c_hub_regulator".
> +
>  config REGULATOR_PALMAS
>  	tristate "TI Palmas PMIC Regulators"
>  	depends on MFD_PALMAS
> diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
> index 1beba1493241..145138f437d8 100644
> --- a/drivers/regulator/Makefile
> +++ b/drivers/regulator/Makefile
> @@ -127,6 +127,7 @@ obj-$(CONFIG_REGULATOR_QCOM_RPMH) += qcom-rpmh-regulator.o
>  obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o
>  obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o
>  obj-$(CONFIG_REGULATOR_QCOM_USB_VBUS) += qcom_usb_vbus-regulator.o
> +obj-$(CONFIG_REGULATOR_P3H2X4X) += p3h2840_i3c_hub_regulator.o
>  obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
>  obj-$(CONFIG_REGULATOR_PCA9450) += pca9450-regulator.o
>  obj-$(CONFIG_REGULATOR_PF0900) += pf0900-regulator.o
> diff --git a/drivers/regulator/p3h2840_i3c_hub_regulator.c b/drivers/regulator/p3h2840_i3c_hub_regulator.c
> new file mode 100644
> index 000000000000..1e9abc398f5a
> --- /dev/null
> +++ b/drivers/regulator/p3h2840_i3c_hub_regulator.c
> @@ -0,0 +1,247 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 NXP
> + * This P3H2x4x driver file contain functions for enable/disable regulator and voltage set/get.
> + */
> +#include <linux/bitfield.h>
> +#include <linux/mfd/p3h2840.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/regulator/driver.h>
> +
> +#define P3H2x4x_LDO_AND_PULLUP_CONF				0x19
> +#define P3H2x4x_LDO_ENABLE_DISABLE_MASK				GENMASK(3, 0)
> +#define P3H2x4x_CP0_EN_LDO				        BIT(0)
> +#define P3H2x4x_CP1_EN_LDO				        BIT(1)
> +#define P3H2x4x_TP0145_EN_LDO					BIT(2)
> +#define P3H2x4x_TP2367_EN_LDO					BIT(3)
> +
> +#define P3H2x4x_NET_OPER_MODE_CONF				0x15
> +#define P3H2x4x_VCCIO_LDO_CONF					0x16
> +#define P3H2x4x_CP0_VCCIO_LDO_VOLTAGE_MASK			GENMASK(1, 0)
> +#define P3H2x4x_CP0_VCCIO_LDO_VOLTAGE(x)	\
> +		FIELD_PREP(P3H2x4x_CP0_VCCIO_LDO_VOLTAGE_MASK, x)
> +#define P3H2x4x_CP1_VCCIO_LDO_VOLTAGE_MASK			GENMASK(3, 2)
> +#define P3H2x4x_CP1_VCCIO_LDO_VOLTAGE(x)	\
> +		FIELD_PREP(P3H2x4x_CP1_VCCIO_LDO_VOLTAGE_MASK, x)
> +#define P3H2x4x_TP0145_VCCIO_LDO_VOLTAGE_MASK			GENMASK(5, 4)
> +#define P3H2x4x_TP0145_VCCIO_LDO_VOLTAGE(x)	\
> +		FIELD_PREP(P3H2x4x_TP0145_VCCIO_LDO_VOLTAGE_MASK, x)
> +#define P3H2x4x_TP2367_VCCIO_LDO_VOLTAGE_MASK			GENMASK(7, 6)
> +#define P3H2x4x_TP2367_VCCIO_LDO_VOLTAGE(x)	\
> +		FIELD_PREP(P3H2x4x_TP2367_VCCIO_LDO_VOLTAGE_MASK, x)
> +#define P3H2x4x_LDO_COUNT					4
> +
> +struct p3h2x4x_regulator_dev {
> +	struct regulator_dev *rp3h2x4x_dev[P3H2x4x_LDO_COUNT];
> +	struct regmap *regmap;
> +};
> +
> +static int p3h2x4x_regulator_enable(struct regulator_dev *rdev)
> +{
> +	int ret;
> +	unsigned int orig;
> +
> +	ret = regmap_read(rdev->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE, &orig);
> +	if (ret)
> +		return ret;
> +
> +	if (orig != P3H2x4x_REGISTERS_UNLOCK_CODE) {
> +		ret = regmap_write(rdev->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE,
> +				   P3H2x4x_REGISTERS_UNLOCK_CODE);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = regulator_enable_regmap(rdev);
> +	if (ret)
> +		return ret;

AI:
- If enable fails, protection register is left unlocked
- Should restore original state in error path

> +
> +	if (orig != P3H2x4x_REGISTERS_UNLOCK_CODE) {
> +		ret = regmap_write(rdev->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE, orig);
> +		if (ret)
> +			return ret;
> +	}

AI:
- `p3h2x4x_regulator_enable()`, `_disable()`, and `_set_voltage_sel()` have identical unlock/lock logic
- Should extract to helper function

you can use cleanup to define macro, like mutex, use guard(...) to simple
code.

Frank
> +	return 0;
> +}
> +
> +static int p3h2x4x_regulator_disable(struct regulator_dev *rdev)
> +{
> +	int ret;
> +	unsigned int orig;
> +
> +	ret = regmap_read(rdev->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE, &orig);
> +	if (ret)
> +		return ret;
> +
> +	if (orig != P3H2x4x_REGISTERS_UNLOCK_CODE) {
> +		ret = regmap_write(rdev->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE,
> +				   P3H2x4x_REGISTERS_UNLOCK_CODE);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = regulator_disable_regmap(rdev);
> +	if (ret)
> +		return ret;
> +
> +	if (orig != P3H2x4x_REGISTERS_UNLOCK_CODE) {
> +		ret = regmap_write(rdev->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE, orig);
> +		if (ret)
> +			return ret;
> +	}
> +	return 0;
> +}
> +
> +static int p3h2x4x_regulator_set_voltage_sel(struct regulator_dev *rdev, unsigned int sel)
> +{
> +	int ret;
> +	unsigned int orig;
> +
> +	ret = regmap_read(rdev->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE, &orig);
> +	if (ret)
> +		return ret;
> +
> +	if (orig != P3H2x4x_REGISTERS_UNLOCK_CODE) {
> +		ret = regmap_write(rdev->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE,
> +				   P3H2x4x_REGISTERS_UNLOCK_CODE);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = regulator_set_voltage_sel_regmap(rdev, sel);
> +	if (ret)
> +		return ret;
> +
> +	if (orig != P3H2x4x_REGISTERS_UNLOCK_CODE) {
> +		ret = regmap_write(rdev->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE, orig);
> +		if (ret)
> +			return ret;
> +	}
> +	return 0;
> +}
> +
> +static const struct regulator_ops p3h2x4x_ldo_ops = {
> +	.list_voltage = regulator_list_voltage_table,
> +	.map_voltage = regulator_map_voltage_iterate,
> +	.set_voltage_sel = p3h2x4x_regulator_set_voltage_sel,
> +	.get_voltage_sel = regulator_get_voltage_sel_regmap,
> +	.enable = p3h2x4x_regulator_enable,
> +	.disable = p3h2x4x_regulator_disable,
> +	.is_enabled = regulator_is_enabled_regmap,
> +};
> +
> +static const unsigned int p3h2x4x_voltage_table[] = {
> +	1000000,
> +	1100000,
> +	1200000,
> +	1800000,
> +};
> +
> +static struct regulator_desc p3h2x4x_regulators[] = {
> +	{
> +		.name = "LDO_CP0",
> +		.of_match = of_match_ptr("LDO_CP0"),
> +		.regulators_node = of_match_ptr("regulators"),
> +		.volt_table = p3h2x4x_voltage_table,
> +		.n_voltages = ARRAY_SIZE(p3h2x4x_voltage_table),
> +		.ops = &p3h2x4x_ldo_ops,
> +		.type = REGULATOR_VOLTAGE,
> +		.owner = THIS_MODULE,
> +		.enable_reg = P3H2x4x_LDO_AND_PULLUP_CONF,
> +		.enable_mask = P3H2x4x_CP0_EN_LDO,
> +		.vsel_reg = P3H2x4x_VCCIO_LDO_CONF,
> +		.vsel_mask = P3H2x4x_CP0_VCCIO_LDO_VOLTAGE_MASK,
> +	},
> +	{
> +		.name = "LDO_CP1",
> +		.of_match = of_match_ptr("LDO_CP1"),
> +		.regulators_node = of_match_ptr("regulators"),
> +		.volt_table = p3h2x4x_voltage_table,
> +		.n_voltages = ARRAY_SIZE(p3h2x4x_voltage_table),
> +		.ops = &p3h2x4x_ldo_ops,
> +		.type = REGULATOR_VOLTAGE,
> +		.owner = THIS_MODULE,
> +		.enable_reg = P3H2x4x_LDO_AND_PULLUP_CONF,
> +		.enable_mask = P3H2x4x_CP1_EN_LDO,
> +		.vsel_reg = P3H2x4x_VCCIO_LDO_CONF,
> +		.vsel_mask = P3H2x4x_CP1_VCCIO_LDO_VOLTAGE_MASK,
> +	},
> +	{
> +		.name = "LDO_TPG0",
> +		.of_match = of_match_ptr("LDO_TPG0"),
> +		.regulators_node = of_match_ptr("regulators"),
> +		.volt_table = p3h2x4x_voltage_table,
> +		.n_voltages = ARRAY_SIZE(p3h2x4x_voltage_table),
> +		.ops = &p3h2x4x_ldo_ops,
> +		.type = REGULATOR_VOLTAGE,
> +		.owner = THIS_MODULE,
> +		.enable_reg = P3H2x4x_LDO_AND_PULLUP_CONF,
> +		.enable_mask = P3H2x4x_TP0145_EN_LDO,
> +		.vsel_reg = P3H2x4x_VCCIO_LDO_CONF,
> +		.vsel_mask = P3H2x4x_TP0145_VCCIO_LDO_VOLTAGE_MASK,
> +	},
> +	{
> +		.name = "LDO_TPG1",
> +		.of_match = of_match_ptr("LDO_TPG1"),
> +		.regulators_node = of_match_ptr("regulators"),
> +		.volt_table = p3h2x4x_voltage_table,
> +		.n_voltages = ARRAY_SIZE(p3h2x4x_voltage_table),
> +		.ops = &p3h2x4x_ldo_ops,
> +		.type = REGULATOR_VOLTAGE,
> +		.owner = THIS_MODULE,
> +		.enable_reg = P3H2x4x_LDO_AND_PULLUP_CONF,
> +		.enable_mask = P3H2x4x_TP2367_EN_LDO,
> +		.vsel_reg = P3H2x4x_VCCIO_LDO_CONF,
> +		.vsel_mask = P3H2x4x_TP2367_VCCIO_LDO_VOLTAGE_MASK,
> +	},
> +};
> +
> +static int p3h2x4x_regulator_probe(struct platform_device *pdev)
> +{
> +	struct p3h2x4x_dev *p3h2x4x = dev_get_drvdata(pdev->dev.parent);
> +	struct p3h2x4x_regulator_dev *p3h2x4x_regulator;
> +	struct regulator_config rcfg = { };
> +	struct device *dev = &pdev->dev;
> +	struct regulator_dev *rdev;
> +	int ret, i;
> +
> +	p3h2x4x_regulator = devm_kzalloc(dev, sizeof(*p3h2x4x_regulator), GFP_KERNEL);
> +	if (!p3h2x4x_regulator)
> +		return -ENOMEM;
> +
> +	platform_set_drvdata(pdev, p3h2x4x_regulator);
> +
> +	p3h2x4x_regulator->regmap = p3h2x4x->regmap;
> +	device_set_of_node_from_dev(dev, dev->parent);
> +
> +	rcfg.dev = dev;
> +	rcfg.dev->of_node = dev->of_node;
> +	rcfg.regmap = p3h2x4x_regulator->regmap;
> +	rcfg.driver_data = p3h2x4x_regulator;
> +
> +	for (i = 0; i < ARRAY_SIZE(p3h2x4x_regulators); i++) {
> +		rdev = devm_regulator_register(&pdev->dev, &p3h2x4x_regulators[i], &rcfg);
> +		if (IS_ERR(rdev)) {
> +			return dev_err_probe(dev, PTR_ERR(rdev), "Failed to register %s\n",
> +					     p3h2x4x_regulators[i].name);
> +		}
> +		p3h2x4x_regulator->rp3h2x4x_dev[i] = rdev;
> +	}
> +	return 0;
> +}
> +
> +static struct platform_driver p3h2x4x_regulator_driver = {
> +	.driver = {
> +		.name = "p3h2x4x-regulator",
> +		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
> +	},
> +	.probe = p3h2x4x_regulator_probe,
> +};
> +module_platform_driver(p3h2x4x_regulator_driver);
> +
> +MODULE_AUTHOR("Aman Kumar Pandey <aman.kumarpandey@nxp.com>");
> +MODULE_AUTHOR("vikash Bansal <vikash.bansal@nxp.com>");
> +MODULE_DESCRIPTION("P3H2x4x I3C HUB Regulator driver");
> +MODULE_LICENSE("GPL");
> --
> 2.25.1
>

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

* Re: [PATCH v5 5/5] i3c: hub: p3h2x4x: Add support for NXP P3H2x4x I3C hub functionality
  2026-02-06 12:01 ` [PATCH v5 5/5] i3c: hub: p3h2x4x: Add support for NXP P3H2x4x I3C hub functionality Aman Kumar Pandey
@ 2026-02-06 17:33   ` Frank Li
  2026-02-06 21:25   ` kernel test robot
  1 sibling, 0 replies; 12+ messages in thread
From: Frank Li @ 2026-02-06 17:33 UTC (permalink / raw)
  To: Aman Kumar Pandey
  Cc: linux-kernel, linux-i3c, alexandre.belloni, krzk+dt, robh,
	conor+dt, devicetree, broonie, lee, lgirdwood, vikash.bansal,
	priyanka.jain, shashank.rebbapragada

On Fri, Feb 06, 2026 at 02:01:21PM +0200, Aman Kumar Pandey wrote:
> Add I3C hub functionality for the NXP P3H2x4x family of multiport hubs.
> These devices support downstream target ports that can be configured
> as I3C, I2C, or SMBus.
>
> This driver enables:
> - I3C/I2C communication between host and hub
> - Transparent communication with downstream devices
> - Target port configuration (I3C/I2C/SMBus)
> - MCTP device support
> - In-band interrupt handling
>
> P3H2440/P3H2441 support 4 target ports;
> P3H2840/P3H2841 support 8 target ports.
>
> Signed-off-by: Aman Kumar Pandey <aman.kumarpandey@nxp.com>
> Signed-off-by: Vikash Bansal <vikash.bansal@nxp.com>
>
> ---
> Changes in v5:
>  - Updated supply names.
>
> Changes in v4:
>  - Split the driver into three separate patches (mfd, regulator and I3C hub)
>  - Added support for NXP P3H2x4x I3C hub functionality
>  - Integrated hub driver with its on-die regulator
>
> Changes in v3:
>  - Added MFD (Multi-Function Device) support for I3C hub and on-die regulator
>
> Changes in v2:
>  - Refined coding style and incorporated review feedback
>  - Updated directory structure
>  - Revised logic for parsing DTS nodes
> ---
> ---
>  MAINTAINERS                              |   1 +
>  drivers/i3c/Kconfig                      |   1 +
>  drivers/i3c/Makefile                     |   1 +
>  drivers/i3c/hub/Kconfig                  |  10 +
>  drivers/i3c/hub/Makefile                 |   4 +
>  drivers/i3c/hub/p3h2840_i3c_hub.h        | 340 ++++++++++++++++++
>  drivers/i3c/hub/p3h2840_i3c_hub_common.c | 405 ++++++++++++++++++++++
>  drivers/i3c/hub/p3h2840_i3c_hub_i3c.c    | 354 +++++++++++++++++++
>  drivers/i3c/hub/p3h2840_i3c_hub_smbus.c  | 418 +++++++++++++++++++++++
>  include/linux/i3c/device.h               |   1 +
>  10 files changed, 1535 insertions(+)
>  create mode 100644 drivers/i3c/hub/Kconfig
>  create mode 100644 drivers/i3c/hub/Makefile
>  create mode 100644 drivers/i3c/hub/p3h2840_i3c_hub.h
>  create mode 100644 drivers/i3c/hub/p3h2840_i3c_hub_common.c
>  create mode 100644 drivers/i3c/hub/p3h2840_i3c_hub_i3c.c
>  create mode 100644 drivers/i3c/hub/p3h2840_i3c_hub_smbus.c
>
...
> +
> +static struct platform_driver p3h2x4x_i3c_hub_driver = {
> +	.driver = {
> +		.name = "p3h2x4x-i3c-hub",
> +		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
> +	},
> +	.probe = p3h2x4x_i3c_hub_probe,
> +	.remove = p3h2x4x_i3c_hub_remove,
> +};
> +module_platform_driver(p3h2x4x_i3c_hub_driver);
> +
> +MODULE_AUTHOR("Aman Kumar Pandey <aman.kumarpandey@nxp.com>");
> +MODULE_AUTHOR("vikash Bansal <vikash.bansal@nxp.com>");

AI: Should be "Vikash Bansal" (capitalize first name)

> +MODULE_DESCRIPTION("P3H2x4x I3C HUB driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/i3c/hub/p3h2840_i3c_hub_i3c.c b/drivers/i3c/hub/p3h2840_i3c_hub_i3c.c
> new file mode 100644
> index 000000000000..cbc6626721fe
> --- /dev/null
> +++ b/drivers/i3c/hub/p3h2840_i3c_hub_i3c.c
> @@ -0,0 +1,354 @@
...
> +
> +static int p3h2x4x_request_ibi(struct i3c_dev_desc *desc,
> +			       const struct i3c_ibi_setup *req)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(desc);
> +	struct i3c_master_controller *orig_parent;
> +	int ret;
> +
> +	orig_parent = update_i3c_i2c_desc_parent(&desc->common, parent);
> +	ret = i3c_master_direct_attach_i3c_dev(parent, desc);
> +	if (ret) {
> +		restore_i3c_i2c_desc_parent(&desc->common, orig_parent);
> +		return ret;
> +	}
> +
> +	mutex_unlock(&desc->ibi_lock);

supposed caller hold ibi_lock.

provide i3c_device_request_ibi_lock() version, which have not hold ibi_lock.

unlock and lock is not good enough.

> +	kfree(desc->ibi);
> +	desc->ibi = NULL;
> +	ret = i3c_device_request_ibi(desc->dev, req);
> +	mutex_lock(&desc->ibi_lock);
> +	restore_i3c_i2c_desc_parent(&desc->common, orig_parent);
> +
> +	return ret;
> +}
> +
...
> +static s32 p3h2x4x_tp_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
> +{
> +	int ret_sum = 0, ret;
> +	u8 msg_count, rw;
> +
> +	struct tp_bus *bus = i2c_get_adapdata(adap);
> +	struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub = bus->p3h2x4x_i3c_hub;
> +
> +	guard(mutex)(&p3h2x4x_i3c_hub->etx_mutex);
> +	guard(mutex)(&bus->port_mutex);
> +
> +	for (msg_count = 0; msg_count < num; msg_count++) {
> +		if (msgs[msg_count].len > P3H2x4x_SMBUS_PAYLOAD_SIZE) {
> +			dev_err(p3h2x4x_i3c_hub->dev,
> +				"Message nr. %d not sent - length over %d bytes.\n",
> +				msg_count, P3H2x4x_SMBUS_PAYLOAD_SIZE);
> +			continue;
> +		}
> +
> +		rw = msgs[msg_count].flags % 2;
> +		if (!rw) {
> +		/* If a read message is immediately followed by a write message to
> +		 * the same address,  consider combining them into a single transaction.
> +		 */
> +			if (msg_count < num && msgs[msg_count].addr == msgs[msg_count + 1].addr &&
> +			    (msgs[msg_count + 1].flags & I2C_M_RD)) {

AI:  `msg_count < num` should be `msg_count + 1 < num` to prevent out-of-bounds access

Frank
> +				rw = 2;
> +				msg_count += 1;
> +				ret_sum += 1;
> +			}
> +		}
> +
> +		ret = p3h2x4x_tp_i2c_xfer_msg(p3h2x4x_i3c_hub,
> +					      msgs,
> +					      bus->tp_port,
> +					      (rw == 2) ? (msg_count - 1) : msg_count,
> +						  rw);
> +		if (ret)
> +			return ret;
> +
> +		ret_sum++;
> +	}
> +	return ret_sum;
> +}
> +
...
> --
> 2.25.1
>

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

* Re: [PATCH v5 5/5] i3c: hub: p3h2x4x: Add support for NXP P3H2x4x I3C hub functionality
  2026-02-06 12:01 ` [PATCH v5 5/5] i3c: hub: p3h2x4x: Add support for NXP P3H2x4x I3C hub functionality Aman Kumar Pandey
  2026-02-06 17:33   ` Frank Li
@ 2026-02-06 21:25   ` kernel test robot
  1 sibling, 0 replies; 12+ messages in thread
From: kernel test robot @ 2026-02-06 21:25 UTC (permalink / raw)
  To: Aman Kumar Pandey, linux-kernel, linux-i3c, alexandre.belloni,
	krzk+dt, robh, conor+dt, devicetree, broonie, lee, Frank.Li,
	lgirdwood
  Cc: oe-kbuild-all, vikash.bansal, priyanka.jain,
	shashank.rebbapragada, Aman Kumar Pandey

Hi Aman,

kernel test robot noticed the following build warnings:

[auto build test WARNING on lee-mfd/for-mfd-next]
[also build test WARNING on lee-mfd/for-mfd-fixes broonie-regulator/for-next linus/master v6.19-rc8]
[cannot apply to i3c/i3c/next next-20260205]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Aman-Kumar-Pandey/dt-bindings-i3c-Add-NXP-P3H2x4x-i3c-hub-support/20260206-200552
base:   https://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git for-mfd-next
patch link:    https://lore.kernel.org/r/20260206120121.856471-5-aman.kumarpandey%40nxp.com
patch subject: [PATCH v5 5/5] i3c: hub: p3h2x4x: Add support for NXP P3H2x4x I3C hub functionality
config: m68k-randconfig-r054-20260207 (https://download.01.org/0day-ci/archive/20260207/202602070522.fcoiqtPw-lkp@intel.com/config)
compiler: m68k-linux-gcc (GCC) 8.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260207/202602070522.fcoiqtPw-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602070522.fcoiqtPw-lkp@intel.com/

All warnings (new ones prefixed by >>):

   drivers/i3c/hub/p3h2840_i3c_hub_smbus.c: In function 'p3h2x4x_ibi_handler':
>> drivers/i3c/hub/p3h2840_i3c_hub_smbus.c:87:11: warning: unused variable 'i' [-Wunused-variable]
     u32 ret, i;
              ^
>> drivers/i3c/hub/p3h2840_i3c_hub_smbus.c:87:6: warning: unused variable 'ret' [-Wunused-variable]
     u32 ret, i;
         ^~~
>> drivers/i3c/hub/p3h2840_i3c_hub_smbus.c:86:44: warning: variable 'payload_byte_two' set but not used [-Wunused-but-set-variable]
     u32 target_port_status, payload_byte_one, payload_byte_two;
                                               ^~~~~~~~~~~~~~~~
>> drivers/i3c/hub/p3h2840_i3c_hub_smbus.c:86:6: warning: unused variable 'target_port_status' [-Wunused-variable]
     u32 target_port_status, payload_byte_one, payload_byte_two;
         ^~~~~~~~~~~~~~~~~~
>> drivers/i3c/hub/p3h2840_i3c_hub_smbus.c:85:30: warning: unused variable 'p3h2x4x_i3c_hub' [-Wunused-variable]
     struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub = dev_get_drvdata(&i3cdev->dev);
                                 ^~~~~~~~~~~~~~~


vim +/i +87 drivers/i3c/hub/p3h2840_i3c_hub_smbus.c

    75	
    76	/**
    77	 * p3h2x4x_ibi_handler - IBI handler.
    78	 * @i3cdev: i3c device.
    79	 * @payload: two byte IBI payload data.
    80	 *
    81	 */
    82	void p3h2x4x_ibi_handler(struct i3c_device *i3cdev,
    83				 const struct i3c_ibi_payload *payload)
    84	{
  > 85		struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub = dev_get_drvdata(&i3cdev->dev);
  > 86		u32 target_port_status, payload_byte_one, payload_byte_two;
  > 87		u32 ret, i;
    88	
    89		payload_byte_one = (*(int *)payload->data);
    90		payload_byte_two = (*(int *)(payload->data + 4));
    91	
    92		if (!(payload_byte_one & P3H2x4x_SMBUS_AGENT_EVENT_FLAG_STATUS))
    93			return;
    94	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

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

* Re: [PATCH v5 4/5] regulator: p3h2x4x: Add driver for on-die regulators in NXP P3H2x4x i3c hub
  2026-02-06 12:01 ` [PATCH v5 4/5] regulator: p3h2x4x: Add driver for on-die regulators in NXP P3H2x4x i3c hub Aman Kumar Pandey
  2026-02-06 17:19   ` Frank Li
@ 2026-02-06 22:19   ` kernel test robot
  1 sibling, 0 replies; 12+ messages in thread
From: kernel test robot @ 2026-02-06 22:19 UTC (permalink / raw)
  To: Aman Kumar Pandey, linux-kernel, linux-i3c, alexandre.belloni,
	krzk+dt, robh, conor+dt, devicetree, broonie, lee, Frank.Li,
	lgirdwood
  Cc: oe-kbuild-all, vikash.bansal, priyanka.jain,
	shashank.rebbapragada, Aman Kumar Pandey

Hi Aman,

kernel test robot noticed the following build warnings:

[auto build test WARNING on lee-mfd/for-mfd-next]
[also build test WARNING on lee-mfd/for-mfd-fixes broonie-regulator/for-next linus/master v6.19-rc8]
[cannot apply to i3c/i3c/next next-20260205]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Aman-Kumar-Pandey/dt-bindings-i3c-Add-NXP-P3H2x4x-i3c-hub-support/20260206-200552
base:   https://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git for-mfd-next
patch link:    https://lore.kernel.org/r/20260206120121.856471-4-aman.kumarpandey%40nxp.com
patch subject: [PATCH v5 4/5] regulator: p3h2x4x: Add driver for on-die regulators in NXP P3H2x4x i3c hub
config: nios2-allmodconfig (https://download.01.org/0day-ci/archive/20260207/202602070610.sRd2Eapf-lkp@intel.com/config)
compiler: nios2-linux-gcc (GCC) 11.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260207/202602070610.sRd2Eapf-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602070610.sRd2Eapf-lkp@intel.com/

All warnings (new ones prefixed by >>):

   drivers/regulator/p3h2840_i3c_hub_regulator.c: In function 'p3h2x4x_regulator_probe':
>> drivers/regulator/p3h2840_i3c_hub_regulator.c:208:13: warning: unused variable 'ret' [-Wunused-variable]
     208 |         int ret, i;
         |             ^~~


vim +/ret +208 drivers/regulator/p3h2840_i3c_hub_regulator.c

   200	
   201	static int p3h2x4x_regulator_probe(struct platform_device *pdev)
   202	{
   203		struct p3h2x4x_dev *p3h2x4x = dev_get_drvdata(pdev->dev.parent);
   204		struct p3h2x4x_regulator_dev *p3h2x4x_regulator;
   205		struct regulator_config rcfg = { };
   206		struct device *dev = &pdev->dev;
   207		struct regulator_dev *rdev;
 > 208		int ret, i;
   209	
   210		p3h2x4x_regulator = devm_kzalloc(dev, sizeof(*p3h2x4x_regulator), GFP_KERNEL);
   211		if (!p3h2x4x_regulator)
   212			return -ENOMEM;
   213	
   214		platform_set_drvdata(pdev, p3h2x4x_regulator);
   215	
   216		p3h2x4x_regulator->regmap = p3h2x4x->regmap;
   217		device_set_of_node_from_dev(dev, dev->parent);
   218	
   219		rcfg.dev = dev;
   220		rcfg.dev->of_node = dev->of_node;
   221		rcfg.regmap = p3h2x4x_regulator->regmap;
   222		rcfg.driver_data = p3h2x4x_regulator;
   223	
   224		for (i = 0; i < ARRAY_SIZE(p3h2x4x_regulators); i++) {
   225			rdev = devm_regulator_register(&pdev->dev, &p3h2x4x_regulators[i], &rcfg);
   226			if (IS_ERR(rdev)) {
   227				return dev_err_probe(dev, PTR_ERR(rdev), "Failed to register %s\n",
   228						     p3h2x4x_regulators[i].name);
   229			}
   230			p3h2x4x_regulator->rp3h2x4x_dev[i] = rdev;
   231		}
   232		return 0;
   233	}
   234	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

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

* Re: [PATCH v5 2/5] dt-bindings: i3c: Add NXP P3H2x4x i3c-hub support
  2026-02-06 12:01 ` [PATCH v5 2/5] dt-bindings: i3c: Add NXP P3H2x4x i3c-hub support Aman Kumar Pandey
@ 2026-02-10  2:33   ` Rob Herring
  0 siblings, 0 replies; 12+ messages in thread
From: Rob Herring @ 2026-02-10  2:33 UTC (permalink / raw)
  To: Aman Kumar Pandey
  Cc: linux-kernel, linux-i3c, alexandre.belloni, krzk+dt, conor+dt,
	devicetree, broonie, lee, Frank.Li, lgirdwood, vikash.bansal,
	priyanka.jain, shashank.rebbapragada

On Fri, Feb 06, 2026 at 02:01:18PM +0200, Aman Kumar Pandey wrote:
> Add bindings for the NXP P3H2x4x (P3H2440/P3H2441/P3H2840/P3H2841)
> multiport I3C hub family. These devices connect to a host via
> I3C/I2C/SMBus and allow communication with multiple downstream
> peripherals.
> 
> Signed-off-by: Aman Kumar Pandey <aman.kumarpandey@nxp.com>
> Signed-off-by: Vikash Bansal <vikash.bansal@nxp.com>
> 
> ---
> Changes in v5:
>  - Removed SW properties: cp0-ldo-microvolt,cp1-ldo-microvolt,
>    tp0145-ldo-microvolt, tp2367-ldo-microvolt 
>  - Changed supply entries and its descriptions
> 
> Changes in v4:
>  - Fixed DT binding check warning
>  - Removed SW properties: ibi-enable, local-dev, and always-enable
> 
> Changes in v3:
>  - Added MFD (Multi-Function Device) support for I3C hub and on-die regulator
>  - Added Regulator supply node
> 
> Changes in v2:
>  - Fixed DT binding check warning
>  - Revised logic for parsing DTS nodes
> ---
> ---
>  .../devicetree/bindings/i3c/nxp,p3h2840.yaml  | 209 ++++++++++++++++++
>  MAINTAINERS                                   |   8 +
>  2 files changed, 217 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/i3c/nxp,p3h2840.yaml
> 
> diff --git a/Documentation/devicetree/bindings/i3c/nxp,p3h2840.yaml b/Documentation/devicetree/bindings/i3c/nxp,p3h2840.yaml
> new file mode 100644
> index 000000000000..edc1f988c0d5
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/i3c/nxp,p3h2840.yaml
> @@ -0,0 +1,209 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +# Copyright 2025 NXP
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/i3c/nxp,p3h2840.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: NXP P3H2X4X I3C HUB
> +
> +maintainers:
> +  - Aman Kumar Pandey <aman.kumarpandey@nxp.com>
> +  - Vikash Bansal <vikash.bansal@nxp.com>
> +
> +description: |
> +  P3H2x4x (P3H2440/P3H2441/P3H2840/P3H2841) is a family of multiport I3C
> +  hub devices that connect to:-
> +  1. A host CPU via I3C/I2C/SMBus bus on upstream side and connect to multiple
> +     peripheral devices on the downstream  side.
> +  2. Have two Controller Ports which can support either
> +     I2C/SMBus or I3C buses and connect to a CPU, BMC or SOC.
> +  3. P3H2840/ P3H2841 are 8 port I3C hub with eight I3C/I2C Target Port.
> +  4. P3H2440/ P3H2441 are 4 port I3C hub with four I3C/I2C Target Port.
> +     Target ports can be configured as I2C/SMBus, I3C or GPIO and connect to
> +     peripherals.
> +
> +allOf:
> +  - $ref: /schemas/i3c/i3c.yaml#
> +
> +properties:
> +  compatible:
> +    const: nxp,p3h2840
> +
> +  tp0145-pullup-ohms:

Needs vendor prefix. And on the rest of the -ohms properties.


> +    description:
> +      Selects the pull up resistance for target Port 0/1/4/5, in ohms.
> +    enum: [250, 500, 1000, 2000]
> +    default: 500
> +
> +  tp2367-pullup-ohms:
> +    description:
> +      Selects the pull up resistance for target Port 2/3/6/7, in ohms.
> +    enum: [250, 500, 1000, 2000]
> +    default: 500
> +
> +  cp0-io-strength-ohms:
> +    description:
> +      Selects the IO drive strength for controller Port 0, in ohms.
> +    enum: [20, 30, 40, 50]
> +    default: 20
> +
> +  cp1-io-strength-ohms:
> +    description:
> +      Selects the IO drive strength for controller Port 1, in ohms.
> +    enum: [20, 30, 40, 50]
> +    default: 20
> +
> +  tp0145-io-strength-ohms:
> +    description:
> +      Selects the IO drive strength for target port 0/1/4/5, in ohms.
> +    enum: [20, 30, 40, 50]
> +    default: 20
> +
> +  tp2367-io-strength-ohms:
> +    description:
> +      Selects the IO drive strength for target port 2/3/6/7, in ohms.
> +    enum: [20, 30, 40, 50]
> +    default: 20
> +
> +  vcc1-supply:
> +    description: Controller port 0 power supply.
> +
> +  vcc2-supply:
> +    description: Controller port 1 power supply.
> +
> +  vcc3-supply:
> +    description: Target port 0/1/4/5 power supply.
> +
> +  vcc4-supply:
> +    description: Target port 2/3/6/7 power supply.
> +
> +  regulators:
> +    type: object
> +    additionalProperties: false
> +
> +    properties:
> +      LDO_CP0:

Lowercase and use '-' rather than '_'.

> +        type: object
> +        $ref: /schemas/regulator/regulator.yaml#
> +        unevaluatedProperties: false
> +
> +      LDO_CP1:
> +        type: object
> +        $ref: /schemas/regulator/regulator.yaml#
> +        unevaluatedProperties: false
> +
> +      LDO_TPG0:
> +        type: object
> +        $ref: /schemas/regulator/regulator.yaml#
> +        unevaluatedProperties: false
> +
> +      LDO_TPG1:
> +        type: object
> +        $ref: /schemas/regulator/regulator.yaml#
> +        unevaluatedProperties: false
> +
> +patternProperties:
> +  "^i3c@[0-7]$":
> +    type: object
> +    $ref: /schemas/i3c/i3c.yaml#
> +    unevaluatedProperties: false
> +
> +    properties:
> +      reg:
> +        description:
> +          The I3C HUB Target Port number.
> +        maxItems: 1

maximum: 7

Instead of maxItems.

> +
> +      pullup-enable:
> +        type: boolean
> +        description:
> +          Enables the on-die pull-up for Target Port.
> +
> +  "^(i2c|smbus)@[0-7]$":
> +    type: object
> +    $ref: /schemas/i2c/i2c-controller.yaml#
> +    unevaluatedProperties: false
> +
> +    properties:
> +      reg:
> +        description:
> +          The I3C HUB Target Port number.
> +        maxItems: 1

maximum: 7

> +
> +      pullup-enable:
> +        type: boolean
> +        description:
> +          Enables the on-die pull-up for Target Port.
> +
> +unevaluatedProperties: false
> +
> +examples:
> +  - |
> +    i3c {
> +        #address-cells = <3>;
> +        #size-cells = <0>;
> +
> +        hub@70,236153000c2 {
> +            reg = <0x70 0x236 0x3000c2>;
> +            #address-cells = <1>;
> +            #size-cells = <0>;
> +            assigned-address = <0x50>;
> +
> +            tp0145-pullup-ohm = <1000>;
> +            tp2367-pullup-ohm = <1000>;
> +            cp0-io-strength-ohm = <50>;
> +            cp1-io-strength-ohm = <50>;
> +            tp0145-io-strength-ohm = <50>;
> +            tp2367-io-strength-ohm = <50>;
> +            vcc3-supply = <&reg_tpg0>;
> +            vcc4-supply = <&reg_tpg1>;
> +
> +            regulators {
> +                reg_cp0: LDO_CP0 {
> +                    regulator-name = "ldo_cp0";
> +                    regulator-min-microvolt = <1800000>;
> +                    regulator-max-microvolt = <1800000>;
> +                };
> +
> +                reg_cp1: LDO_CP1 {
> +                    regulator-name = "ldo_cp1";
> +                    regulator-min-microvolt = <1800000>;
> +                    regulator-max-microvolt = <1800000>;
> +                };
> +
> +                reg_tpg0: LDO_TPG0 {
> +                    regulator-name = "ldo_tpg0";
> +                    regulator-min-microvolt = <1800000>;
> +                    regulator-max-microvolt = <1800000>;
> +                };
> +
> +                reg_tpg1: LDO_TPG1 {
> +                    regulator-name = "ldo_tpg1";
> +                    regulator-min-microvolt = <1800000>;
> +                    regulator-max-microvolt = <1800000>;
> +                };
> +            };
> +
> +            smbus@0 {
> +                reg = <0x0>;
> +                #address-cells = <1>;
> +                #size-cells = <0>;
> +                pullup-enable;
> +            };
> +
> +            i2c@1 {
> +                reg = <0x1>;
> +                #address-cells = <1>;
> +                #size-cells = <0>;
> +                pullup-enable;
> +            };
> +
> +            i3c@2 {
> +                reg = <0x2>;
> +                #address-cells = <3>;
> +                #size-cells = <0>;
> +                pullup-enable;
> +            };
> +        };
> +    };
> diff --git a/MAINTAINERS b/MAINTAINERS
> index e08767323763..7e1a6cb22ca3 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -18906,6 +18906,14 @@ S:	Maintained
>  F:	Documentation/devicetree/bindings/ptp/nxp,ptp-netc.yaml
>  F:	drivers/ptp/ptp_netc.c
>  
> +NXP P3H2X4X I3C-HUB DRIVER
> +M:	Vikash Bansal <vikash.bansal@nxp.com>
> +M:	Aman Kumar Pandey <aman.kumarpandey@nxp.com>
> +L:	linux-kernel@vger.kernel.org
> +L:	linux-i3c-owner@lists.infradead.org
> +S:	Maintained
> +F:	Documentation/devicetree/bindings/i3c/nxp,p3h2840.yaml
> +
>  NXP PF5300/PF5301/PF5302 PMIC REGULATOR DEVICE DRIVER
>  M:	Woodrow Douglass <wdouglass@carnegierobotics.com>
>  S:	Maintained
> -- 
> 2.25.1
> 

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

end of thread, other threads:[~2026-02-10  2:33 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-06 12:01 [PATCH v5 1/5] i3c: master: Expose the APIs to support I3C hub Aman Kumar Pandey
2026-02-06 12:01 ` [PATCH v5 2/5] dt-bindings: i3c: Add NXP P3H2x4x i3c-hub support Aman Kumar Pandey
2026-02-10  2:33   ` Rob Herring
2026-02-06 12:01 ` [PATCH v5 3/5] mfd: p3h2x4x: Add driver for NXP P3H2x4x i3c hub and on-die regulator Aman Kumar Pandey
2026-02-06 12:52   ` Krzysztof Kozlowski
2026-02-06 12:01 ` [PATCH v5 4/5] regulator: p3h2x4x: Add driver for on-die regulators in NXP P3H2x4x i3c hub Aman Kumar Pandey
2026-02-06 17:19   ` Frank Li
2026-02-06 22:19   ` kernel test robot
2026-02-06 12:01 ` [PATCH v5 5/5] i3c: hub: p3h2x4x: Add support for NXP P3H2x4x I3C hub functionality Aman Kumar Pandey
2026-02-06 17:33   ` Frank Li
2026-02-06 21:25   ` kernel test robot
2026-02-06 17:06 ` [PATCH v5 1/5] i3c: master: Expose the APIs to support I3C hub Frank Li

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