linux-i3c.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/2] dt-bindings: i3c: Add NXP P3H2x4x i3c-hub support
@ 2025-02-12 13:22 Aman Kumar Pandey
  2025-02-12 13:22 ` [PATCH 2/2] drivers: i3c: Add driver for NXP P3H2x4x i3c-hub device Aman Kumar Pandey
                   ` (3 more replies)
  0 siblings, 4 replies; 13+ messages in thread
From: Aman Kumar Pandey @ 2025-02-12 13:22 UTC (permalink / raw)
  To: linux-kernel, linux-i3c, alexandre.belloni, krzk+dt, robh,
	conor+dt, devicetree
  Cc: vikash.bansal, priyanka.jain, shashank.rebbapragada, Frank.Li,
	Aman Kumar Pandey

P3H2x4x (P3H2440/P3H2441/P3H2840/P3H2841) is multiport I3C hub
device which connects to a host CPU via I3C/I2C/SMBus bus on one
side and to multiple peripheral devices on the other side.

Signed-off-by: Aman Kumar Pandey <aman.kumarpandey@nxp.com>
Signed-off-by: Vikash Bansal <vikash.bansal@nxp.com>
---
 .../bindings/i3c/p3h2x4x_i3c_hub.yaml         | 404 ++++++++++++++++++
 MAINTAINERS                                   |   7 +
 2 files changed, 411 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i3c/p3h2x4x_i3c_hub.yaml

diff --git a/Documentation/devicetree/bindings/i3c/p3h2x4x_i3c_hub.yaml b/Documentation/devicetree/bindings/i3c/p3h2x4x_i3c_hub.yaml
new file mode 100644
index 000000000000..33ea524e5432
--- /dev/null
+++ b/Documentation/devicetree/bindings/i3c/p3h2x4x_i3c_hub.yaml
@@ -0,0 +1,404 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright 2024-2025 NXP
+
+%YAML 1.2
+
+$id: http://devicetree.org/schemas/i3c/p3h2x4x_i3c_hub.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: P3H2X4X I3C HUB
+
+maintainers:
+  - Vikash Bansal <vikash.bansal@nxp.com>
+  - Aman Kumar Pandey <aman.kumarpandey@nxp.com>
+
+description: |
+	P3H2x4x (P3H2440/P3H2441/P3H2840/P3H2841) are multiport I3C hub devices
+  that connect to:-
+  1. A host CPU via I3C/I2C/SMBus bus on one side and connect to multiple
+     peripheral devices on the other 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 devices with eight I3C/I2C Target Port.
+  4. P3H2440/ P3H2441 are 4 port I3C hub devices with four I3C/I2C Target Port.
+     Target ports can be configured as I2C/SMBus, I3C or GPIO and connect to
+     peripherals.
+
+  Device tree node can be used for following configurations:-
+  1. Controller Port can be configured via dt entry to support
+  2. I2C/SMBus mode or I3C buses.
+  3. Enabling voltage compatibility across I3C Controller and
+     Target devices.
+  4. Up to eight target devices and their modes(I3C/I2C/SMbus modes).
+  5. To support MCTP device
+  6. To support In-band interrupts
+
+  Controller Port can be configured via dt entry to support I2C/SMBus mode or
+  I3C buses.
+
+  For I2C devices, use below format:
+      DT node: hub@static_address
+      reg: encodes the static I2C address.
+
+  For I3C devices, use below format:
+      DT node: hub@static_address,PID
+      reg: encodes the static I2C address (0 if the device does not have one), and the
+      Provisioned ID (PID) used to uniquely identify a device on a bus.
+      This PID contains information about the vendor, the part, and an instance ID so
+      that several devices of the same type can be connected on the same bus.
+      assigned-address: represents the dynamic address to be assigned to this device.
+
+allOf:
+  - $ref: i3c.yaml#
+
+properties:
+
+  cp0-ldo-en:
+    enum:
+      - disabled
+      - enabled
+    description: |
+      I3C HUB Controller Port 0 LDO setting for turning on and off. If enabled, the dedicated
+      pin will provide the voltage generated by the on-die LDO. It is an optional property,
+      the configuration remains default if it is not supplied.
+
+  cp1-ldo-en:
+    enum:
+      - disabled
+      - enabled
+
+    description: |
+      I3C HUB Controller Port 0 LDO setting for turning on and off. If enabled, the dedicated
+      pin will provide the voltage generated by the on-die LDO. It is an optional property,
+      the configuration remains default if it is not supplied.
+
+  tp0145-ldo-en:
+    enum:
+      - disabled
+      - enabled
+
+    description: |
+      I3C HUB Target Ports 0/1/4/5 LDO setting for turning on and off. If enabled, the dedicated
+      pin will provide the voltage generated by the on-die LDO. It is an optional property,
+      the configuration remains default if it is not supplied.
+
+  tp2367-ldo-en:
+    enum:
+      - disabled
+      - enabled
+
+    description: |
+      I3C HUB Target Ports 2/3/6/7 LDO setting for turning on and off. If enabled, the dedicated
+      pin will provide the voltage generated by the on-die LDO. It is an optional property,
+      the configuration remains default if it is not supplied.
+
+  cp0-ldo-volt:
+    enum:
+      - 1.0V
+      - 1.1V
+      - 1.2V
+      - 1.8V
+
+    description: |
+      Controller Port 0 voltage level is controlled by the I3C HUB Controller Port 0
+      LDO setting.
+      Since this parameter is optional, the configuration remains default if it is not supplied.
+
+  cp1-ldo-volt:
+    enum:
+      - 1.0V
+      - 1.1V
+      - 1.2V
+      - 1.8V
+
+    description: |
+    Controller Port 1 voltage level is controlled by the I3C HUB Controller Port 1
+    LDO setting.
+    Since this parameter is optional, the configuration remains default if it is not supplied.
+
+  tp0145-ldo-volt:
+    enum:
+      - 1.0V
+      - 1.1V
+      - 1.2V
+      - 1.8V
+
+    description: |
+      Target Port 0/1/4/5 voltage level is controlled by the I3C HUB Target Port 0/1/4/5
+      LDO setting.
+      Since this parameter is optional, the configuration remains default if it is not supplied.
+
+  tp2367-ldo-volt:
+    enum:
+      - 1.0V
+      - 1.1V
+      - 1.2V
+      - 1.8V
+
+    description: |
+      Target Port 2/3/6/7 voltage level is controlled by the I3C HUB Target Port 2/3/6/7
+      LDO setting.
+      Since this parameter is optional, the configuration remains default if it is not supplied.
+
+  tp0145-pullup:
+    enum:
+      - 250R
+      - 500R
+      - 1000R
+      - 2000R
+
+    description: |
+      Target Port 0/1/4/5 pull up setting is controlled by the I3C HUB Target Port 0/1/4/5
+      pull up resistance level.
+      Since this parameter is optional, the configuration remains default if it is not supplied.
+
+  tp2367-pullup:
+    enum:
+      - 250R
+      - 500R
+      - 1000R
+      - 2000R
+
+    description: |
+      Target Port 2/3/6/7 pull up setting is controlled by the I3C HUB Target Port 2/3/6/7
+      pull up resistance level.
+      Since this parameter is optional, the configuration remains default if it is not supplied.
+
+  cp0-io-strength:
+    enum:
+      - 20Ohms
+      - 30Ohms
+      - 40Ohms
+      - 50Ohms
+
+    description: |
+      To regulate the output driver strength at Controller Port 0, use the I3C HUB Controller
+      Port 0 IO strength setting.
+      It is an optional property, the configuration remains default if it is not supplied.
+
+  cp1-io-strength:
+    enum:
+      - 20Ohms
+      - 30Ohms
+      - 40Ohms
+      - 50Ohms
+    description: |
+      To regulate the output driver strength at Controller Port 1, use the I3C HUB Controller
+      Port 1 IO strength setting.
+      It is an optional property, the configuration remains default if it is not supplied.
+
+  tp0145-io-strength:
+    enum:
+      - 20Ohms
+      - 30Ohms
+      - 40Ohms
+      - 50Ohms
+
+    description: |
+      To regulate the output driver strength at Target port 0/1/4/5, use the I3C HUB Target
+      port 0/1/4/5 IO strength setting.
+      It is an optional property, the configuration remains default if it is not supplied.
+
+  tp2367-io-strength:
+    enum:
+      - 20Ohms
+      - 30Ohms
+      - 40Ohms
+      - 50Ohms
+
+    description: |
+      To regulate the output driver strength at Target port 2/3/6/7, use the I3C HUB Target
+      port 2/3/6/7 IO strength setting.
+      It is an optional property, the configuration remains default if it is not supplied.
+
+patternProperties:
+  "@[0-7]$":
+    type: object
+    description: |
+      I3C HUB Target Port child, should be named: target-port@<target-port-id>
+
+    properties:
+      mode:
+        enum:
+          - i3c
+          - smbus
+          - i2c
+          - gpio
+
+        description: |
+          I3C HUB Target Port mode setting to control Target Port functionality.
+          As per now it is oly supporting SMBus, i2c and i3c( i2c mode will work
+          with hub network).
+
+      pullup:
+        enum:
+          - disabled
+          - enabled
+        description: |
+          I3C HUB Target Port pull-up setting to disable/enable Target Port pull-up.
+          It is an optional property, the configuration remains default if it is not supplied.
+
+      ibi_en:
+        enum:
+          - disabled
+          - enabled
+        description: |
+          I3C HUB Target Port IBI setting to disable/enable IBI for Target Port.
+          This property is optional. If not provided, Target Port IBI will disabled.
+
+      local_dev:
+        description: |
+          SMBus Target Agent can discard transactions of downstream device and not generate an IBI to upstream I3C Hub
+          Controller Port. Up to 8 device (addresses) can be configured as local Devices.
+
+          This property is optional. If not provided, local device list will empty.
+
+      always-enable:
+        description: |
+          Add this field to enable hub network(Controller port -> target port).
+
+          This property is optional. If not provided, by default hub network will disabled for respective port.
+
+    patternProperties:
+      "@slave-address,pid$":
+        type: object
+        description: |
+          Adding this node to install the downstream devices.
+
+        properties:
+          compatible:
+            description:
+              Compatible of the I2C/SMBus downstream device.
+
+          reg:
+            description:
+              Downstream device addresses which are connected to target port.
+
+          I3c HUB driver supports standerd i2c/i3c DT entry as it passes DT node to respective driver for
+          downstream device.
+
+additionalProperties: true
+
+examples:
+  - |
+      i3c_hub: hub@70,236153000c2{
+          reg = <0x70 0x236 0x3000c2>;
+          assigned-address = <0x50>;
+          dcr = <0xC2>;
+
+          cp0-ldo-en = "disabled";
+          cp1-ldo-en = "disabled";
+          cp0-ldo-volt = "1.8V";
+          cp1-ldo-volt = "1.8V";
+          tp0145-ldo-en = "disabled";
+          tp2367-ldo-en = "disabled";
+          tp0145-ldo-volt = "1.8V";
+          tp2367-ldo-volt = "1.8V";
+          tp0145-pullup= "1000R";
+          tp2367-pullup = "1000R";
+          tp0145-io-strength = "20Ohms";
+          tp2367-io-strength = "20Ohms";
+          cp0-io-strength = "20Ohms";
+          cp1-io-strength = "20Ohms";
+          target-port@0 {
+            #address-cells = <1>;
+                #size-cells = <0>;
+            mode = "smbus";
+            pullup_en = "enabled";
+            ibi_en = "enabled";
+            local_dev = <0x30>, <0x40>, <0x50>;
+            backend@4c{
+              compatible = "i3c-hub";
+              reg = <0x4c>;
+            };
+            rtc@68 {
+              compatible = "dallas,ds3232";
+              reg = <0x68>;
+              interrupt-parent = <&gpio2>;
+              interrupts = <20 0>;
+              trickle-resistor-ohms = <250>;
+            };
+
+            eeprom@57 {
+              compatible = "atmel,24c32";
+              reg = <0x57>;
+              pagesize = <32>;
+              wp-gpios = <&gpio2 2 0>;
+              num-addresses = <8>;
+            };
+          };
+          target-port@1 {
+            #address-cells = <1>;
+                #size-cells = <0>;
+            mode = "smbus";
+            pullup_en = "enabled";
+            ibi_en = "enabled";
+              local_dev = <0x35>, <0x45>, <0x55>;
+          };
+          target-port@2 {
+            #address-cells = <1>;
+                #size-cells = <0>;
+            mode = "i3c";
+            pullup_en = "enabled";
+            hub-test@4c,25400000000{
+              reg = <0x4c 0x254 0x0000>;
+              assigned-address = <0x50>;
+            };
+          };
+          target-port@3 {
+            #address-cells = <1>;
+                #size-cells = <0>;
+            mode = "i3c";
+            pullup_en = "enabled";
+            always-enable;
+          };
+      };
+
+      i3c_hub: hub@70 {
+          compatible = "nxp,p3h2x4x";
+          reg = <0x70>;
+
+          cp0-ldo-en = "disabled";
+          cp1-ldo-en = "disabled";
+          cp0-ldo-volt = "1.8V";
+          cp1-ldo-volt = "1.8V";
+          tp0145-ldo-en = "disabled";
+          tp2367-ldo-en = "disabled";
+          tp0145-ldo-volt = "1.8V";
+          tp2367-ldo-volt = "1.8V";
+          tp0145-pullup= "1000R";
+          tp2367-pullup = "1000R";
+          tp0145-io-strength = "20Ohms";
+          tp2367-io-strength = "20Ohms";
+          cp0-io-strength = "20Ohms";
+          cp1-io-strength = "20Ohms";
+          target-port@0 {
+            #address-cells = <1>;
+                #size-cells = <0>;
+            mode = "smbus";
+            pullup_en = "enabled";
+            ibi_en = "enabled";
+            local_dev = <0x30>, <0x40>, <0x50>;
+
+            backend@4c{
+              compatible = "test-dvr";
+              reg = <0x4c>;
+            };
+            rtc@68 {
+              compatible = "dallas,ds3232";
+              reg = <0x68>;
+              interrupt-parent = <&gpio2>;
+              interrupts = <20 0>;
+              trickle-resistor-ohms = <250>;
+            };
+
+            eeprom@57 {
+              compatible = "atmel,24c32";
+              reg = <0x57>;
+              pagesize = <32>;
+              wp-gpios = <&gpio2 2 0>;
+              num-addresses = <8>;
+            };
+          };
+	    };
+-  |
diff --git a/MAINTAINERS b/MAINTAINERS
index af686e0bb6d7..20aa3e987ac5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17167,6 +17167,13 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml
 F:	sound/soc/codecs/tfa989x.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
+S:	Maintained
+F:	Documentation/devicetree/bindings/i3c/p3h2x4x_i3c_hub.yaml
+
 NZXT-KRAKEN2 HARDWARE MONITORING DRIVER
 M:	Jonas Malaco <jonas@protocubo.io>
 L:	linux-hwmon@vger.kernel.org
-- 
2.25.1


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

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

* [PATCH 2/2] drivers: i3c: Add driver for NXP P3H2x4x i3c-hub device
  2025-02-12 13:22 [PATCH 1/2] dt-bindings: i3c: Add NXP P3H2x4x i3c-hub support Aman Kumar Pandey
@ 2025-02-12 13:22 ` Aman Kumar Pandey
  2025-02-12 16:42   ` Frank Li
                     ` (7 more replies)
  2025-02-12 15:25 ` [PATCH 1/2] dt-bindings: i3c: Add NXP P3H2x4x i3c-hub support Rob Herring (Arm)
                   ` (2 subsequent siblings)
  3 siblings, 8 replies; 13+ messages in thread
From: Aman Kumar Pandey @ 2025-02-12 13:22 UTC (permalink / raw)
  To: linux-kernel, linux-i3c, alexandre.belloni, krzk+dt, robh,
	conor+dt, devicetree
  Cc: vikash.bansal, priyanka.jain, shashank.rebbapragada, Frank.Li,
	Aman Kumar Pandey

P3H2x4x (P3H2440/P3H2441/P3H2840/P3H2841) is multiport I3C hub devices.
It connects to a host CPU via I3C/I2C/SMBus bus on one side and to multiple
peripheral devices on the other side.
P3H2840/ P3H2841 have eight I3C/I2C Target Port.
P3H2440/ P3H2441 have four I3C/I2C Target Port.

This driver can support
1. i3c/i2c communication between host and hub
2. i3c/i2c transparent mode between host and downstream devices.
3. Target ports can be configured as I2C/SMBus mode or I3C.
4. MCTP devices
5. In-band interrupts

Signed-off-by: Aman Kumar Pandey <aman.kumarpandey@nxp.com>
Signed-off-by: Vikash Bansal <vikash.bansal@nxp.com>
---
 MAINTAINERS                                  |   1 +
 drivers/i3c/Kconfig                          |   1 +
 drivers/i3c/Makefile                         |   1 +
 drivers/i3c/p3h2x4x/Kconfig                  |  11 +
 drivers/i3c/p3h2x4x/Makefile                 |   4 +
 drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub.h        | 454 +++++++++
 drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_common.c | 941 +++++++++++++++++++
 drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_i3c.c    | 353 +++++++
 drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c  | 719 ++++++++++++++
 include/linux/i3c/device.h                   |   1 +
 10 files changed, 2486 insertions(+)
 create mode 100644 drivers/i3c/p3h2x4x/Kconfig
 create mode 100644 drivers/i3c/p3h2x4x/Makefile
 create mode 100644 drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub.h
 create mode 100644 drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_common.c
 create mode 100644 drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_i3c.c
 create mode 100644 drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 20aa3e987ac5..8e4ec4e3656e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17173,6 +17173,7 @@ M:	Aman Kumar Pandey <aman.kumarpandey@nxp.com>
 L:	linux-kernel@vger.kernel.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/i3c/p3h2x4x_i3c_hub.yaml
+F:	i3c/p3h2x4x/*
 
 NZXT-KRAKEN2 HARDWARE MONITORING DRIVER
 M:	Jonas Malaco <jonas@protocubo.io>
diff --git a/drivers/i3c/Kconfig b/drivers/i3c/Kconfig
index 30a441506f61..3160437cc8e1 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/p3h2x4x/Kconfig"
 endif # I3C
diff --git a/drivers/i3c/Makefile b/drivers/i3c/Makefile
index 11982efbc6d9..696747cc7f18 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)		+= p3h2x4x/
diff --git a/drivers/i3c/p3h2x4x/Kconfig b/drivers/i3c/p3h2x4x/Kconfig
new file mode 100644
index 000000000000..b8b18342b065
--- /dev/null
+++ b/drivers/i3c/p3h2x4x/Kconfig
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright 2024-2025 NXP
+config P3H2X4X_I3C_HUB
+	tristate "P3H2X4X I3C HUB support"
+	depends on I3C
+	select REGMAP_I3C
+	help
+		This enables support for NXP P3H244x/P3H284x I3C HUB. Say Y or M here to use
+		I3C HUB driver to configure I3C HUB device.
+		I3C HUB driver's probe will trigger when I3C device with DCR
+		equals to 0xC2 (HUB device) is detected on the bus.
diff --git a/drivers/i3c/p3h2x4x/Makefile b/drivers/i3c/p3h2x4x/Makefile
new file mode 100644
index 000000000000..214a3eeb62f2
--- /dev/null
+++ b/drivers/i3c/p3h2x4x/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright 2024-2025 NXP
+p3h2x4x_i3c_hub-y := p3h2x4x_i3c_hub_common.o p3h2x4x_i3c_hub_i3c.o p3h2x4x_i3c_hub_smbus.o
+obj-$(CONFIG_P3H2X4X_I3C_HUB)	+= p3h2x4x_i3c_hub.o
diff --git a/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub.h b/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub.h
new file mode 100644
index 000000000000..e8c627019556
--- /dev/null
+++ b/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub.h
@@ -0,0 +1,454 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2024-2025 NXP
+ * This header file contain private device structure definition, Reg address and its bit
+ * mapping etc.
+ */
+
+#ifndef P3H2X4X_I3C_HUB_H
+#define P3H2X4X_I3C_HUB_H
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/i3c/device.h>
+#include <linux/i3c/master.h>
+#include <linux/mutex.h>
+#include <linux/rwsem.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/of.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_DEV_REG_PROTECTION_CODE				0x10
+#define P3H2x4x_REGISTERS_LOCK_CODE				0x00
+#define P3H2x4x_REGISTERS_UNLOCK_CODE				0x69
+#define P3H2x4x_CP1_REGISTERS_UNLOCK_CODE			0x6A
+
+#define P3H2x4x_CP_CONF						0x11
+#define P3H2x4x_TP_ENABLE					0x12
+#define P3H2x4x_TPn_ENABLE(n)					BIT(n)
+
+#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)	\
+		(((x) << 0) & P3H2x4x_TP0145_IO_STRENGTH_MASK)
+#define P3H2x4x_TP2367_IO_STRENGTH_MASK				GENMASK(3, 2)
+#define P3H2x4x_TP2367_IO_STRENGTH(x)	\
+		(((x) << 2) & P3H2x4x_TP2367_IO_STRENGTH_MASK)
+#define P3H2x4x_CP0_IO_STRENGTH_MASK				GENMASK(5, 4)
+#define P3H2x4x_CP0_IO_STRENGTH(x)	\
+		(((x) << 4) & P3H2x4x_CP0_IO_STRENGTH_MASK)
+#define P3H2x4x_CP1_IO_STRENGTH_MASK				GENMASK(7, 6)
+#define P3H2x4x_CP1_IO_STRENGTH(x)	\
+		(((x) << 6) & P3H2x4x_CP1_IO_STRENGTH_MASK)
+
+#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)	\
+		(((x) << 0) & P3H2x4x_CP0_VCCIO_LDO_VOLTAGE_MASK)
+#define P3H2x4x_CP1_VCCIO_LDO_VOLTAGE_MASK			GENMASK(3, 2)
+#define P3H2x4x_CP1_VCCIO_LDO_VOLTAGE(x)	\
+		(((x) << 2) & P3H2x4x_CP1_VCCIO_LDO_VOLTAGE_MASK)
+#define P3H2x4x_TP0145_VCCIO_LDO_VOLTAGE_MASK			GENMASK(5, 4)
+#define P3H2x4x_TP0145_VCCIO_LDO_VOLTAGE(x)	\
+		(((x) << 4) & P3H2x4x_TP0145_VCCIO_LDO_VOLTAGE_MASK)
+#define P3H2x4x_TP2367_VCCIO_LDO_VOLTAGE_MASK			GENMASK(7, 6)
+#define P3H2x4x_TP2367_VCCIO_LDO_VOLTAGE(x)	\
+		(((x) << 6) & P3H2x4x_TP2367_VCCIO_LDO_VOLTAGE_MASK)
+
+#define P3H2x4x_TP_IO_MODE_CONF					0x17
+#define P3H2x4x_TP_SMBUS_AGNT_EN				0x18
+#define P3H2x4x_TPn_SMBUS_MODE_EN(n)				BIT(n)
+#define P3H2x4x_TPn_I2C_MODE_EN(n)				BIT(n)
+
+#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_TP0145_PULLUP_CONF_MASK				GENMASK(7, 6)
+#define P3H2x4x_TP0145_PULLUP_CONF(x)	\
+		(((x) << 6) & P3H2x4x_TP0145_PULLUP_CONF_MASK)
+#define P3H2x4x_TP2367_PULLUP_CONF_MASK				GENMASK(5, 4)
+#define P3H2x4x_TP2367_PULLUP_CONF(x)	\
+		(((x) << 4) & P3H2x4x_TP2367_PULLUP_CONF_MASK)
+
+#define P3H2x4x_CP_IBI_CONF					0x1A
+
+#define P3H2x4x_TP_SMBUS_AGNT_IBI_CONFIG			0x1B
+#define P3H2x4x_TPn_IBI_EN(n)					BIT(n)
+#define P3H2x4x_ALL_TP_IBI_EN					0xFF
+#define P3H2x4x_ALL_TP_IBI_DIS					0x00
+
+#define P3H2x4x_IBI_MDB_CUSTOM					0x1C
+#define P3H2x4x_JEDEC_CONTEXT_ID				0x1D
+#define P3H2x4x_TP_GPIO_MODE_EN					0x1E
+#define P3H2x4x_TPn_GPIO_MODE_EN(n)				BIT(n)
+
+/* 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_TPn_NET_CON(n)					BIT(n)
+
+#define P3H2x4x_TP_PULLUP_EN					0x53
+#define P3H2x4x_TPn_PULLUP_EN(n)				BIT(n)
+
+#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_DT_LDO_VOLT_NOT_SET				0x04
+
+#define P3H2x4x_LDO_DISABLED					0x00
+#define P3H2x4x_LDO_ENABLED					0x01
+#define P3H2x4x_DT_LDO_NOT_DEFINED				0x02
+
+#define P3H2x4x_IBI_DISABLED					0x00
+#define P3H2x4x_IBI_ENABLED					0x01
+#define P3H2x4x_IBI_NOT_DEFINED					0x02
+
+/* 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_NOT_SET				0x04
+
+#define P3H2x4x_TP_PULLUP_DISABLED				0x00
+#define P3H2x4x_TP_PULLUP_ENABLED				0x01
+#define P3H2x4x_TP_PULLUP_NOT_DEFINED				0x02
+
+#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_IO_STRENGTH_NOT_SET				0x04
+
+#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 P3H2x4x_TP_MODE_NOT_SET					0x04
+
+#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_XFER_SUCCESS					0x01
+#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					10
+
+#define P3H2x4x_HUB_ID						0
+
+/* Reg config for Regmap */
+#define P3H2x4x_REG_BITS					8
+#define P3H2x4x_VAL_BITS					8
+
+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_setting {
+	const char *const name;
+	const u8 value;
+};
+
+/* IBI enable/disable settings */
+static const struct p3h2x4x_setting ibi_en_settings[] = {
+	{ "disabled",	P3H2x4x_IBI_DISABLED },
+	{ "enabled",	P3H2x4x_IBI_ENABLED },
+};
+
+/* LDO enable/disable settings */
+static const struct p3h2x4x_setting ldo_en_settings[] = {
+	{ "disabled",	P3H2x4x_LDO_DISABLED },
+	{ "enabled",	P3H2x4x_LDO_ENABLED },
+};
+
+/* LDO voltage settings */
+static const struct p3h2x4x_setting ldo_volt_settings[] = {
+	{ "1.0V",	P3H2x4x_LDO_VOLT_1_0V },
+	{ "1.1V",	P3H2x4x_LDO_VOLT_1_1V },
+	{ "1.2V",	P3H2x4x_LDO_VOLT_1_2V },
+	{ "1.8V",	P3H2x4x_LDO_VOLT_1_8V },
+};
+
+/* target port pull-up settings */
+static const struct p3h2x4x_setting pullup_settings[] = {
+	{ "250R",		P3H2x4x_TP_PULLUP_250R },
+	{ "500R",		P3H2x4x_TP_PULLUP_500R },
+	{ "1000R",		P3H2x4x_TP_PULLUP_1000R },
+	{ "2000R",		P3H2x4x_TP_PULLUP_2000R },
+};
+
+/* target port mode settings */
+static const struct p3h2x4x_setting tp_mode_settings[] = {
+	{ "i3c",		P3H2x4x_TP_MODE_I3C },
+	{ "smbus",		P3H2x4x_TP_MODE_SMBUS },
+	{ "gpio",		P3H2x4x_TP_MODE_GPIO },
+	{ "i2c",		P3H2x4x_TP_MODE_I2C },
+};
+
+/* pull-up enable/disable settings */
+static const struct p3h2x4x_setting tp_pullup_settings[] = {
+	{ "disabled",	P3H2x4x_TP_PULLUP_DISABLED },
+	{ "enabled",	P3H2x4x_TP_PULLUP_ENABLED },
+};
+
+/*  IO strenght settings */
+static const struct p3h2x4x_setting io_strength_settings[] = {
+	{ "20Ohms",		P3H2x4x_IO_STRENGTH_20_OHM },
+	{ "30Ohms",		P3H2x4x_IO_STRENGTH_30_OHM },
+	{ "40Ohms",		P3H2x4x_IO_STRENGTH_40_OHM },
+	{ "50Ohms",		P3H2x4x_IO_STRENGTH_50_OHM },
+};
+
+struct tp_setting {
+	u8 mode;
+	u8 pullup_en;
+	u8 ibi_en;
+	bool always_enable;
+};
+
+/*
+ * struct svc_i3c_i2c_dev_data - Device specific data
+ * @index: Index in the master tables corresponding to this device
+ * @ibi: IBI slot index in the master structure
+ * @ibi_pool: IBI pool associated to this device
+ */
+struct dt_settings {
+	u8 cp0_ldo_en;
+	u8 cp1_ldo_en;
+	u8 tp0145_ldo_en;
+	u8 tp2367_ldo_en;
+	u8 cp0_ldo_volt;
+	u8 cp1_ldo_volt;
+	u8 tp0145_ldo_volt;
+	u8 tp2367_ldo_volt;
+	u8 tp0145_pullup;
+	u8 tp2367_pullup;
+	u8 cp0_io_strength;
+	u8 cp1_io_strength;
+	u8 tp0145_io_strength;
+	u8 tp2367_io_strength;
+	struct tp_setting tp[P3H2x4x_TP_MAX_COUNT];
+};
+
+struct smbus_device {
+	struct i2c_client *client;
+	const char *compatible;
+	int addr;
+	struct list_head list;
+	struct device_node *tp_device_dt_node;
+};
+
+struct tp_bus {
+	bool dt_available;	    /* bus configuration is available in DT. */
+	bool is_registered;	    /* bus was registered in the framework. */
+	u8 tp_mask;
+	u8 tp_port;
+	u8 local_dev_list[P3H2x4x_TP_LOCAL_DEV];
+	u8 local_dev_count;
+	struct i3c_master_controller i3c_port_controller;
+	struct i2c_adapter smbus_port_adapter;
+	struct device_node *of_node;
+	struct p3h2x4x *priv;
+	struct list_head tp_device_entry;
+	struct mutex port_mutex;
+};
+
+struct p3h2x4x {
+	struct i3c_device *i3cdev;
+	struct i2c_client *i2cdev;
+	struct i3c_master_controller *driving_master;
+	struct regmap *regmap;
+	struct mutex etx_mutex;
+	struct dt_settings settings;
+	struct tp_bus tp_bus[P3H2x4x_TP_MAX_COUNT];
+	/* Offset for reading HUB's register. */
+	u8 tp_ibi_mask;
+	u8 tp_i3c_bus_mask;
+	u8 tp_always_enable_mask;
+	u8 is_slave_reg;
+	bool is_p3h2x4x_in_i3c;
+};
+
+/*
+ * p3h2x4x_tp_add_downstream_device - prove downstream devices for target port who
+ * configured as SMBus.
+ * @priv: p3h2x4x device structure.
+ * Return: 0 in case of success, a negative EINVAL code if the error.
+ */
+int p3h2x4x_tp_add_downstream_device(struct p3h2x4x *priv);
+
+/*
+ * p3h2x4x_tp_smbus_algo - add i2c adapter for target port who
+ * 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 *priv, int tp);
+
+int p3h2x4x_tp_i3c_algo(struct p3h2x4x *priv, int tp);
+
+/*
+ * 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 /* P3H2X4X_I3C_HUB_H */
diff --git a/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_common.c b/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_common.c
new file mode 100644
index 000000000000..74b86a668f12
--- /dev/null
+++ b/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_common.c
@@ -0,0 +1,941 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2024-2025 NXP
+ * This P3H2x4x driver file implements functions for Hub probe and DT parsing.
+ */
+
+#include "p3h2x4x_i3c_hub.h"
+
+struct i3c_ibi_setup p3h2x4x_ibireq = {
+		.handler = p3h2x4x_ibi_handler,
+		.max_payload_len = P3H2x4x_MAX_PAYLOAD_LEN,
+		.num_slots = P3H2x4x_NUM_SLOTS,
+};
+
+struct regmap_config p3h2x4x_regmap_config = {
+		.reg_bits = P3H2x4x_REG_BITS,
+		.val_bits = P3H2x4x_VAL_BITS,
+	};
+
+/* 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", P3H2x4x_HUB_ID },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(i2c, p3h2x4x_i2c_id_table);
+
+static const struct of_device_id  p3h2x4x_i2c_of_match[] = {
+	{
+		.compatible = "nxp,p3h2x4x",
+		.data =  P3H2x4x_HUB_ID,
+	},
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, p3h2x4x_i2c_of_match);
+
+static void p3h2x4x_of_get_dt_setting(struct device *dev,
+				const struct device_node *node,
+				const char *setting_name,
+				const struct p3h2x4x_setting settings[],
+				const u8 settings_count, u8 *setting_value)
+{
+	const char *sval;
+	int ret;
+	int i;
+
+	ret = of_property_read_string(node, setting_name, &sval);
+	if (ret) {
+		if (ret != -EINVAL)
+			dev_warn(dev, "No setting or invalid setting for %s, err=%i\n",
+				setting_name, ret);
+		return;
+	}
+
+	for (i = 0; i < settings_count; ++i) {
+		const struct p3h2x4x_setting *const setting = &settings[i];
+
+		if (!strcmp(setting->name, sval)) {
+			*setting_value = setting->value;
+			return;
+		}
+	}
+	dev_warn(dev, "Unknown setting for %s\n", setting_name);
+}
+
+static int p3h2x4x_configure_pullup(struct device *dev)
+{
+	struct p3h2x4x *priv = dev_get_drvdata(dev);
+	u8 mask = 0, value = 0;
+
+	if (priv->settings.tp0145_pullup != P3H2x4x_TP_PULLUP_NOT_SET) {
+		mask |= P3H2x4x_TP0145_PULLUP_CONF_MASK;
+		value |= P3H2x4x_TP0145_PULLUP_CONF(priv->settings.tp0145_pullup);
+	}
+
+	if (priv->settings.tp2367_pullup != P3H2x4x_TP_PULLUP_NOT_SET) {
+		mask |= P3H2x4x_TP2367_PULLUP_CONF_MASK;
+		value |= P3H2x4x_TP2367_PULLUP_CONF(priv->settings.tp2367_pullup);
+	}
+
+	return regmap_update_bits(priv->regmap, P3H2x4x_LDO_AND_PULLUP_CONF,
+				mask, value);
+}
+
+static int p3h2x4x_configure_ldo(struct device *dev)
+{
+	struct p3h2x4x *priv = dev_get_drvdata(dev);
+	u8 ldo_config_mask = 0, ldo_config_val = 0;
+	u8 ldo_disable_mask = 0, ldo_en_val = 0;
+	u32 reg_val;
+	int ret;
+	u8 val;
+
+	/* Enable or Disable LDO's. If there is no DT entry - disable LDO for safety reasons */
+	if (priv->settings.cp0_ldo_en == P3H2x4x_LDO_ENABLED)
+		ldo_en_val |= P3H2x4x_CP0_EN_LDO;
+	if (priv->settings.cp1_ldo_en == P3H2x4x_LDO_ENABLED)
+		ldo_en_val |= P3H2x4x_CP1_EN_LDO;
+	if (priv->settings.tp0145_ldo_en == P3H2x4x_LDO_ENABLED)
+		ldo_en_val |= P3H2x4x_TP0145_EN_LDO;
+	if (priv->settings.tp2367_ldo_en == P3H2x4x_LDO_ENABLED)
+		ldo_en_val |= P3H2x4x_TP2367_EN_LDO;
+
+	/* Get current LDOs configuration */
+	ret = regmap_read(priv->regmap, P3H2x4x_VCCIO_LDO_CONF, &reg_val);
+	if (ret)
+		return ret;
+
+	/* LDOs Voltage level (Skip if not defined in the DT)
+	 * Set the mask only if there is a change from current value
+	 */
+	if (priv->settings.cp0_ldo_volt != P3H2x4x_DT_LDO_VOLT_NOT_SET) {
+		val = P3H2x4x_CP0_VCCIO_LDO_VOLTAGE(priv->settings.cp0_ldo_volt);
+		if ((reg_val & P3H2x4x_CP0_VCCIO_LDO_VOLTAGE_MASK) != val) {
+			ldo_config_mask |= P3H2x4x_CP0_VCCIO_LDO_VOLTAGE_MASK;
+			ldo_config_val |= val;
+
+			ldo_disable_mask |= P3H2x4x_CP0_EN_LDO;
+		}
+	}
+	if (priv->settings.cp1_ldo_volt != P3H2x4x_DT_LDO_VOLT_NOT_SET) {
+		val = P3H2x4x_CP1_VCCIO_LDO_VOLTAGE(priv->settings.cp1_ldo_volt);
+		if ((reg_val & P3H2x4x_CP1_VCCIO_LDO_VOLTAGE_MASK) != val) {
+			ldo_config_mask |= P3H2x4x_CP1_VCCIO_LDO_VOLTAGE_MASK;
+			ldo_config_val |= val;
+
+			ldo_disable_mask |= P3H2x4x_CP1_EN_LDO;
+		}
+	}
+	if (priv->settings.tp0145_ldo_volt != P3H2x4x_DT_LDO_VOLT_NOT_SET) {
+		val = P3H2x4x_TP0145_VCCIO_LDO_VOLTAGE(priv->settings.tp0145_ldo_volt);
+		if ((reg_val & P3H2x4x_TP0145_VCCIO_LDO_VOLTAGE_MASK) != val) {
+			ldo_config_mask |= P3H2x4x_TP0145_VCCIO_LDO_VOLTAGE_MASK;
+			ldo_config_val |= val;
+
+			ldo_disable_mask |= P3H2x4x_TP0145_EN_LDO;
+		}
+	}
+	if (priv->settings.tp2367_ldo_volt != P3H2x4x_DT_LDO_VOLT_NOT_SET) {
+		val = P3H2x4x_TP2367_VCCIO_LDO_VOLTAGE(priv->settings.tp2367_ldo_volt);
+		if ((reg_val & P3H2x4x_TP2367_VCCIO_LDO_VOLTAGE_MASK) != val) {
+			ldo_config_mask |= P3H2x4x_TP2367_VCCIO_LDO_VOLTAGE_MASK;
+			ldo_config_val |= val;
+
+			ldo_disable_mask |= P3H2x4x_TP2367_EN_LDO;
+		}
+	}
+
+	/*
+	 *Update LDO voltage configuration only if value is changed from already existing register
+	 * value. It is a good practice to disable the LDO's before making any voltage changes.
+	 * Presence of config mask indicates voltage change to be applied.
+	 */
+	if (ldo_config_mask) {
+		/* Disable LDO's before making voltage changes */
+		ret = regmap_update_bits(priv->regmap,
+					P3H2x4x_LDO_AND_PULLUP_CONF,
+					ldo_disable_mask, 0);
+		if (ret)
+			return ret;
+
+		/* Update the LDOs configuration */
+		ret = regmap_update_bits(priv->regmap, P3H2x4x_VCCIO_LDO_CONF,
+					ldo_config_mask, ldo_config_val);
+		if (ret)
+			return ret;
+	}
+
+	/* Update the LDOs Enable/disable register. This will enable only LDOs enabled in DT */
+	return regmap_update_bits(priv->regmap, P3H2x4x_LDO_AND_PULLUP_CONF,
+				P3H2x4x_LDO_ENABLE_DISABLE_MASK, ldo_en_val);
+}
+
+static int p3h2x4x_configure_io_strength(struct device *dev)
+{
+	struct p3h2x4x *priv = dev_get_drvdata(dev);
+	u8 mask_all = 0, val_all = 0;
+	u32 reg_val;
+	u8 val;
+	struct dt_settings tmp;
+	int ret;
+
+	/* Get IO strength configuration to figure out what needs to be changed */
+	ret = regmap_read(priv->regmap, P3H2x4x_IO_STRENGTH, &reg_val);
+	if (ret)
+		return ret;
+
+	tmp = priv->settings;
+	if (tmp.cp0_io_strength != P3H2x4x_IO_STRENGTH_NOT_SET) {
+		val = P3H2x4x_CP0_IO_STRENGTH(tmp.cp0_io_strength);
+		mask_all |= P3H2x4x_CP0_IO_STRENGTH_MASK;
+		val_all |= val;
+	}
+	if (tmp.cp1_io_strength != P3H2x4x_IO_STRENGTH_NOT_SET) {
+		val = P3H2x4x_CP1_IO_STRENGTH(tmp.cp1_io_strength);
+		mask_all |= P3H2x4x_CP1_IO_STRENGTH_MASK;
+		val_all |= val;
+	}
+	if (tmp.tp0145_io_strength != P3H2x4x_IO_STRENGTH_NOT_SET) {
+		val = P3H2x4x_TP0145_IO_STRENGTH(tmp.tp0145_io_strength);
+		mask_all |= P3H2x4x_TP0145_IO_STRENGTH_MASK;
+		val_all |= val;
+	}
+	if (tmp.tp2367_io_strength != P3H2x4x_IO_STRENGTH_NOT_SET) {
+		val = P3H2x4x_TP2367_IO_STRENGTH(tmp.tp2367_io_strength);
+		mask_all |= P3H2x4x_TP2367_IO_STRENGTH_MASK;
+		val_all |= val;
+	}
+
+	/* Set IO strength if required */
+	return regmap_update_bits(priv->regmap, P3H2x4x_IO_STRENGTH, mask_all, val_all);
+}
+
+static int p3h2x4x_configure_tp(struct device *dev)
+{
+	struct p3h2x4x *priv = dev_get_drvdata(dev);
+	u8 pullup_mask = 0, pullup_val = 0;
+	u8 smbus_mask = 0, smbus_val = 0;
+	u8 gpio_mask = 0, gpio_val = 0;
+	u8 i3c_mask = 0, i3c_val = 0;
+	u8 ibi_mask = 0, ibi_val = 0;
+	u8 i2c_mask = 0, i2c_val = 0;
+	int ret;
+	int i;
+
+	/* TBD: Read type of HUB from register P3H2x4x_DEV_INFO_0 to learn target ports count. */
+	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; ++i) {
+		if (priv->settings.tp[i].mode != P3H2x4x_TP_MODE_NOT_SET) {
+			i3c_mask |= P3H2x4x_TPn_NET_CON(i);
+			smbus_mask |= P3H2x4x_TPn_SMBUS_MODE_EN(i);
+			gpio_mask |= P3H2x4x_TPn_GPIO_MODE_EN(i);
+			i2c_mask |= P3H2x4x_TPn_I2C_MODE_EN(i);
+
+			if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_I3C)
+				i3c_val |= P3H2x4x_TPn_NET_CON(i);
+			else if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_SMBUS)
+				smbus_val |= P3H2x4x_TPn_SMBUS_MODE_EN(i);
+			else if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_GPIO)
+				gpio_val |= P3H2x4x_TPn_GPIO_MODE_EN(i);
+			else if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_I2C)
+				i2c_val |= P3H2x4x_TPn_I2C_MODE_EN(i);
+		}
+		if (priv->settings.tp[i].pullup_en != P3H2x4x_TP_PULLUP_NOT_DEFINED) {
+			pullup_mask |= P3H2x4x_TPn_PULLUP_EN(i);
+
+			if (priv->settings.tp[i].pullup_en == P3H2x4x_TP_PULLUP_ENABLED)
+				pullup_val |= P3H2x4x_TPn_PULLUP_EN(i);
+		}
+
+		if (priv->settings.tp[i].ibi_en != P3H2x4x_IBI_NOT_DEFINED) {
+			ibi_mask |= P3H2x4x_TPn_IBI_EN(i);
+
+			if (priv->settings.tp[i].ibi_en == P3H2x4x_IBI_ENABLED)
+				ibi_val |= P3H2x4x_TPn_IBI_EN(i);
+		}
+	}
+
+	ret = regmap_update_bits(priv->regmap, P3H2x4x_TP_IO_MODE_CONF, (smbus_mask | i2c_mask),
+							(smbus_val | i2c_val));
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(priv->regmap, P3H2x4x_TP_PULLUP_EN, pullup_mask, pullup_val);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(priv->regmap, P3H2x4x_TP_SMBUS_AGNT_IBI_CONFIG, ibi_mask, ibi_val);
+	if (ret)
+		return ret;
+	priv->tp_ibi_mask = ibi_val;
+
+	ret = regmap_update_bits(priv->regmap, P3H2x4x_TP_SMBUS_AGNT_EN, smbus_mask, smbus_val);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(priv->regmap, P3H2x4x_TP_GPIO_MODE_EN, gpio_mask, gpio_val);
+	if (ret)
+		return ret;
+
+	/* Request for HUB Network connection in case any TP is configured in I3C mode */
+	if ((i3c_val) || (i2c_val)) {
+		ret = regmap_write(priv->regmap, P3H2x4x_CP_MUX_SET,
+							P3H2x4x_CONTROLLER_PORT_MUX_REQ);
+		if (ret)
+			return ret;
+	}
+
+	/* Enable TP here in case TP was configured */
+	ret = regmap_update_bits(priv->regmap, P3H2x4x_TP_ENABLE,
+				i3c_mask | smbus_mask | gpio_mask | i2c_mask,
+				i3c_val | smbus_val | gpio_val | i2c_val);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int p3h2x4x_configure_smbus_local_dev(struct device *dev)
+{
+	struct p3h2x4x *priv = dev_get_drvdata(dev);
+	u8 target_buffer_page, hub_tp;
+	int ret = 0;
+
+	for (hub_tp = 0; hub_tp < P3H2x4x_TP_MAX_COUNT; hub_tp++) {
+		if ((priv->tp_bus[hub_tp].local_dev_count) &&
+				(priv->settings.tp[hub_tp].mode == P3H2x4x_TP_MODE_SMBUS)) {
+			target_buffer_page = P3H2x4x_TARGET_AGENT_LOCAL_DEV + 4 * hub_tp;
+			ret = regmap_write(priv->regmap, P3H2x4x_PAGE_PTR, target_buffer_page);
+			if (ret) {
+				dev_err(dev, "Failed to configure local device settings\n");
+				break;
+			}
+
+			ret = regmap_bulk_write(priv->regmap,
+							P3H2x4x_CONTROLLER_AGENT_BUFF,
+							priv->tp_bus[hub_tp].local_dev_list,
+							priv->tp_bus[hub_tp].local_dev_count);
+			if (ret) {
+				dev_err(dev, "Failed to add local devices\n");
+				break;
+			}
+		}
+	}
+	regmap_write(priv->regmap, P3H2x4x_PAGE_PTR, 0x00);
+	return ret;
+}
+
+static int p3h2x4x_configure_hw(struct device *dev)
+{
+	int ret;
+
+	ret = p3h2x4x_configure_ldo(dev);
+	if (ret)
+		return ret;
+
+	ret = p3h2x4x_configure_io_strength(dev);
+	if (ret)
+		return ret;
+
+	ret = p3h2x4x_configure_pullup(dev);
+	if (ret)
+		return ret;
+
+	ret = p3h2x4x_configure_smbus_local_dev(dev);
+	if (ret)
+		return ret;
+
+	return p3h2x4x_configure_tp(dev);
+}
+
+static void p3h2x4x_of_get_tp_dt_conf(struct device *dev,
+					const struct device_node *node)
+{
+	struct p3h2x4x *priv = dev_get_drvdata(dev);
+	struct device_node *dev_node;
+	int tp_port;
+
+	for_each_available_child_of_node(node, dev_node) {
+		if (!dev_node->full_name ||
+			(sscanf(dev_node->full_name, "target-port@%d", &tp_port) != 1))
+			continue;
+
+		if (tp_port < P3H2x4x_TP_MAX_COUNT) {
+			priv->tp_bus[tp_port].dt_available = true;
+			priv->tp_bus[tp_port].of_node = dev_node;
+			priv->tp_bus[tp_port].tp_mask = BIT(tp_port);
+			priv->tp_bus[tp_port].priv = priv;
+			priv->tp_bus[tp_port].tp_port = tp_port;
+		}
+	}
+}
+
+/* return true when backend node exist */
+static bool p3h2x4x_is_backend_node_exist(int port, struct p3h2x4x *priv, u32 addr)
+{
+	struct smbus_device *backend = NULL;
+
+	list_for_each_entry(backend,
+			&priv->tp_bus[port].tp_device_entry, list) {
+		if (backend->addr == addr)
+			return true;
+	}
+	return false;
+}
+
+static int p3h2x4x_read_backend_from_p3h2x4x_dts(struct device_node *i3c_node_target,
+					struct p3h2x4x *priv)
+{
+	struct device_node *i3c_node_tp;
+	const char *compatible;
+	int tp_port, ret;
+	u32 addr_dts;
+	struct smbus_device *backend;
+
+	if (sscanf(i3c_node_target->full_name, "target-port@%d", &tp_port) == 0)
+		return -EINVAL;
+
+	if (tp_port > P3H2x4x_TP_MAX_COUNT)
+		return -ERANGE;
+
+	if (tp_port < 0)
+		return -EINVAL;
+
+	INIT_LIST_HEAD(&priv->tp_bus[tp_port].tp_device_entry);
+
+	if (priv->settings.tp[tp_port].mode == P3H2x4x_TP_MODE_I3C)
+		return 0;
+
+	for_each_available_child_of_node(i3c_node_target, i3c_node_tp) {
+
+		ret = of_property_read_u32(i3c_node_tp, "reg", &addr_dts);
+		if (ret)
+			return ret;
+
+		if (p3h2x4x_is_backend_node_exist(tp_port, priv, addr_dts))
+			continue;
+
+		ret = of_property_read_string(i3c_node_tp, "compatible", &compatible);
+		if (ret)
+			return ret;
+
+		backend = kzalloc(sizeof(*backend), GFP_KERNEL);
+		if (!backend)
+			return -ENOMEM;
+
+		backend->addr = addr_dts;
+		backend->compatible = compatible;
+		backend->tp_device_dt_node = i3c_node_tp;
+		backend->client = NULL;
+
+		list_add(&backend->list,
+			&priv->tp_bus[tp_port].tp_device_entry);
+	}
+
+	return 0;
+}
+
+static void p3h2x4x_parse_dt_tp(struct device *dev,
+				const struct device_node *i3c_node_hub,
+				struct p3h2x4x *priv)
+{
+	struct device_node *i3c_node_target;
+	int ret;
+
+	for_each_available_child_of_node(i3c_node_hub, i3c_node_target) {
+		if (!strcmp(i3c_node_target->name, "target-port")) {
+			ret = p3h2x4x_read_backend_from_p3h2x4x_dts(i3c_node_target, priv);
+			if (ret)
+				dev_err(dev, "DTS entry invalid - error %d", ret);
+		}
+	}
+}
+
+static int p3h2x4x_get_tp_local_device_dt_setting(struct device *dev,
+					const struct device_node *node, u32 id)
+{
+	u8 i;
+	u32 local_dev_count, local_dev;
+	struct p3h2x4x *priv = dev_get_drvdata(dev);
+
+	if (!of_get_property(node, "local_dev", &local_dev_count))
+		return -EINVAL;
+
+	local_dev_count = local_dev_count / (sizeof(u32));
+
+	if (local_dev_count > P3H2x4x_TP_LOCAL_DEV)
+		return -ERANGE;
+
+	for (i = 0; i < local_dev_count; i++) {
+		if (of_property_read_u32_index(node, "local_dev", i, &local_dev)) {
+			priv->tp_bus[id].local_dev_count = 0;
+			return -EINVAL;
+		}
+		priv->tp_bus[id].local_dev_list[i] = (u8)(local_dev*2);
+	}
+	priv->tp_bus[id].local_dev_count = local_dev_count;
+	return 0;
+}
+
+static void p3h2x4x_get_tp_of_get_setting(struct device *dev,
+					const struct device_node *node,
+					struct tp_setting tp_setting[])
+{
+	struct device_node *tp_node;
+	u32 id;
+
+	for_each_available_child_of_node(node, tp_node) {
+		if (!tp_node->name || of_node_cmp(tp_node->name, "target-port"))
+			continue;
+
+		if (!tp_node->full_name ||
+			(sscanf(tp_node->full_name, "target-port@%u", &id) != 1)) {
+			dev_warn(dev, "Invalid target port node found in DT: %s\n",
+				tp_node->full_name);
+			continue;
+		}
+
+		if (id >= P3H2x4x_TP_MAX_COUNT) {
+			dev_warn(dev, "Invalid target port index found in DT: %i\n", id);
+			continue;
+		}
+		p3h2x4x_of_get_dt_setting(dev, tp_node, "mode", tp_mode_settings,
+					ARRAY_SIZE(tp_mode_settings),
+					&tp_setting[id].mode);
+		p3h2x4x_of_get_dt_setting(dev, tp_node, "pullup_en", tp_pullup_settings,
+					ARRAY_SIZE(tp_pullup_settings),
+					&tp_setting[id].pullup_en);
+		p3h2x4x_of_get_dt_setting(dev, tp_node, "ibi_en", ibi_en_settings,
+					ARRAY_SIZE(ibi_en_settings),
+					&tp_setting[id].ibi_en);
+		tp_setting[id].always_enable =
+					of_property_read_bool(tp_node, "always-enable");
+
+		p3h2x4x_get_tp_local_device_dt_setting(dev, tp_node, id);
+	}
+}
+
+static void p3h2x4x_of_get_p3h2x4x_conf(struct device *dev,
+					const struct device_node *node)
+{
+	struct p3h2x4x *priv = dev_get_drvdata(dev);
+
+	p3h2x4x_of_get_dt_setting(dev, node, "cp0-ldo-en", ldo_en_settings,
+				ARRAY_SIZE(ldo_en_settings),
+				&priv->settings.cp0_ldo_en);
+	p3h2x4x_of_get_dt_setting(dev, node, "cp1-ldo-en", ldo_en_settings,
+				ARRAY_SIZE(ldo_en_settings),
+				&priv->settings.cp1_ldo_en);
+	p3h2x4x_of_get_dt_setting(dev, node, "tp0145-ldo-en", ldo_en_settings,
+				ARRAY_SIZE(ldo_en_settings),
+				&priv->settings.tp0145_ldo_en);
+	p3h2x4x_of_get_dt_setting(dev, node, "tp2367-ldo-en", ldo_en_settings,
+				ARRAY_SIZE(ldo_en_settings),
+				&priv->settings.tp2367_ldo_en);
+	p3h2x4x_of_get_dt_setting(dev, node, "cp0-ldo-volt", ldo_volt_settings,
+				ARRAY_SIZE(ldo_volt_settings),
+				&priv->settings.cp0_ldo_volt);
+	p3h2x4x_of_get_dt_setting(dev, node, "cp1-ldo-volt", ldo_volt_settings,
+				ARRAY_SIZE(ldo_volt_settings),
+				&priv->settings.cp1_ldo_volt);
+	p3h2x4x_of_get_dt_setting(dev, node, "tp0145-ldo-volt", ldo_volt_settings,
+				ARRAY_SIZE(ldo_volt_settings),
+				&priv->settings.tp0145_ldo_volt);
+	p3h2x4x_of_get_dt_setting(dev, node, "tp2367-ldo-volt", ldo_volt_settings,
+				ARRAY_SIZE(ldo_volt_settings),
+				&priv->settings.tp2367_ldo_volt);
+	p3h2x4x_of_get_dt_setting(dev, node, "tp0145-pullup", pullup_settings,
+				ARRAY_SIZE(pullup_settings),
+				&priv->settings.tp0145_pullup);
+	p3h2x4x_of_get_dt_setting(dev, node, "tp2367-pullup", pullup_settings,
+				ARRAY_SIZE(pullup_settings),
+				&priv->settings.tp2367_pullup);
+	p3h2x4x_of_get_dt_setting(dev, node, "cp0-io-strength", io_strength_settings,
+				ARRAY_SIZE(io_strength_settings),
+				&priv->settings.cp0_io_strength);
+	p3h2x4x_of_get_dt_setting(dev, node, "cp1-io-strength", io_strength_settings,
+				ARRAY_SIZE(io_strength_settings),
+				&priv->settings.cp1_io_strength);
+	p3h2x4x_of_get_dt_setting(dev, node, "tp0145-io-strength", io_strength_settings,
+				ARRAY_SIZE(io_strength_settings),
+				&priv->settings.tp0145_io_strength);
+	p3h2x4x_of_get_dt_setting(dev, node, "tp2367-io-strength", io_strength_settings,
+				ARRAY_SIZE(io_strength_settings),
+				&priv->settings.tp2367_io_strength);
+
+p3h2x4x_get_tp_of_get_setting(dev, node, priv->settings.tp);
+}
+
+static struct device_node *p3h2x4x_get_dt_p3h2x4x_node(struct device_node *node,
+		struct device *dev)
+{
+	struct device_node *hub_node_no_id = NULL;
+	struct device_node *hub_node;
+
+	for_each_available_child_of_node(node, hub_node) {
+		if (strstr(hub_node->name, "hub"))
+			return hub_node;
+	}
+	return hub_node_no_id;
+}
+
+static void p3h2x4x_of_default_configuration(struct device *dev)
+{
+	struct p3h2x4x *priv = dev_get_drvdata(dev);
+	int tp_count;
+
+	priv->settings.cp0_ldo_en = P3H2x4x_DT_LDO_NOT_DEFINED;
+	priv->settings.cp1_ldo_en = P3H2x4x_DT_LDO_NOT_DEFINED;
+	priv->settings.tp0145_ldo_en = P3H2x4x_DT_LDO_NOT_DEFINED;
+	priv->settings.tp2367_ldo_en = P3H2x4x_DT_LDO_NOT_DEFINED;
+	priv->settings.cp0_ldo_volt = P3H2x4x_DT_LDO_VOLT_NOT_SET;
+	priv->settings.cp1_ldo_volt = P3H2x4x_DT_LDO_VOLT_NOT_SET;
+	priv->settings.tp0145_ldo_volt = P3H2x4x_DT_LDO_VOLT_NOT_SET;
+	priv->settings.tp2367_ldo_volt = P3H2x4x_DT_LDO_VOLT_NOT_SET;
+	priv->settings.tp0145_pullup = P3H2x4x_TP_PULLUP_NOT_SET;
+	priv->settings.tp2367_pullup = P3H2x4x_TP_PULLUP_NOT_SET;
+	priv->settings.cp0_io_strength = P3H2x4x_IO_STRENGTH_NOT_SET;
+	priv->settings.cp1_io_strength = P3H2x4x_IO_STRENGTH_NOT_SET;
+	priv->settings.tp0145_io_strength = P3H2x4x_IO_STRENGTH_NOT_SET;
+	priv->settings.tp2367_io_strength = P3H2x4x_IO_STRENGTH_NOT_SET;
+
+	for (tp_count = 0; tp_count < P3H2x4x_TP_MAX_COUNT; ++tp_count) {
+		priv->settings.tp[tp_count].mode =  P3H2x4x_TP_MODE_NOT_SET;
+		priv->settings.tp[tp_count].pullup_en = P3H2x4x_TP_PULLUP_NOT_DEFINED;
+		priv->settings.tp[tp_count].ibi_en = P3H2x4x_IBI_DISABLED;
+	}
+}
+
+static int p3h2x4x_device_probe_i3c(struct i3c_device *i3cdev)
+{
+	struct device_node *node = NULL;
+	struct regmap *regmap;
+	struct device *dev = &i3cdev->dev;
+	struct p3h2x4x *priv;
+	char hub_id[64];
+	int ret, i;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->i3cdev = i3cdev;
+	priv->driving_master = i3c_dev_get_master(i3cdev->desc);
+	i3cdev_set_drvdata(i3cdev, priv);
+	sprintf(hub_id, "i3c-hub-device-%d-%llx", i3c_dev_get_master(i3cdev->desc)->bus.id,
+		i3cdev->desc->info.pid);
+	p3h2x4x_of_default_configuration(dev);
+	regmap = devm_regmap_init_i3c(i3cdev, &p3h2x4x_regmap_config);
+	if (IS_ERR(regmap)) {
+		ret = PTR_ERR(regmap);
+		dev_err(dev, "Failed to register I3C HUB regmap\n");
+		goto error;
+	}
+	priv->regmap = regmap;
+	priv->is_p3h2x4x_in_i3c = true;
+
+	mutex_init(&priv->etx_mutex);
+
+	/* Register logic for native SMBus ports */
+	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++)
+		mutex_init(&priv->tp_bus[i].port_mutex);
+
+	node = p3h2x4x_get_dt_p3h2x4x_node(dev->parent->of_node, dev); /* Find hub node in DT */
+	if (!node) {
+		dev_warn(dev, "No DT entry - running with hardware defaults.\n");
+	} else {
+		of_node_get(node);
+		p3h2x4x_of_get_p3h2x4x_conf(dev, node);
+		p3h2x4x_of_get_tp_dt_conf(dev, node);
+		of_node_put(node);
+		/* Parse DTS to find data on the SMBus target mode */
+		p3h2x4x_parse_dt_tp(dev, node, priv);
+	}
+
+	/* Unlock access to protected registers */
+	ret = regmap_write(priv->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE,
+						P3H2x4x_REGISTERS_UNLOCK_CODE);
+	if (ret) {
+		dev_err(dev, "Failed to unlock HUB's protected registers\n");
+		goto error;
+	}
+
+	ret = p3h2x4x_configure_hw(dev);
+	if (ret) {
+		dev_err(dev, "Failed to configure the HUB\n");
+		goto error;
+	}
+
+	/* Register logic for native SMBus ports */
+	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++) {
+		if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_SMBUS)
+			ret = p3h2x4x_tp_smbus_algo(priv, i);
+	}
+
+	ret = p3h2x4x_tp_add_downstream_device(priv);
+	if (ret) {
+		dev_err(dev, "Failed to add backend device\n");
+		goto error;
+	}
+
+	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; ++i) {
+		if (priv->tp_bus[i].dt_available) {
+			if (priv->settings.tp[i].always_enable)
+				priv->tp_always_enable_mask =
+						(priv->tp_always_enable_mask |  BIT(i));
+
+			if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_I3C)
+				priv->tp_i3c_bus_mask = (priv->tp_i3c_bus_mask |  BIT(i));
+		}
+	}
+
+	/* Register logic for native vertual I3C ports */
+	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++) {
+		if ((priv->settings.tp[i].mode == P3H2x4x_TP_MODE_I3C) &&
+			(!priv->settings.tp[i].always_enable))
+			ret = p3h2x4x_tp_i3c_algo(priv, i);
+	}
+
+	ret = regmap_write(priv->regmap, P3H2x4x_TP_NET_CON_CONF, priv->tp_always_enable_mask);
+	if (ret) {
+		dev_err(dev, "Failed to open Target Port(s)\n");
+		goto error;
+	}
+
+	ret = i3c_master_do_daa(priv->driving_master);
+	if (ret)
+		dev_warn(dev, "Failed to run DAA\n");
+
+
+	/* 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(priv->regmap, P3H2x4x_ONCHIP_TD_AND_SMBUS_AGNT_CONF,
+						P3H2x4x_TARGET_AGENT_DFT_IBI_CONF_MASK,
+						P3H2x4x_TARGET_AGENT_DFT_IBI_CONF);
+	if (ret) {
+		dev_err(dev, "Failed to P3H2x4x_ONCHIP_TD_AND_SMBUS_AGNT_CONF\n");
+		goto error;
+	}
+
+	ret = i3c_device_request_ibi(i3cdev, &p3h2x4x_ibireq);
+	if (ret) {
+		dev_err(dev, "Failed to request IBI\n");
+		goto error;
+	}
+
+	ret = i3c_device_enable_ibi(i3cdev);
+	if (ret) {
+		dev_err(dev, "Failed to Enable IBI\n");
+		goto error;
+	}
+
+	/* Lock access to protected registers */
+	ret = regmap_write(priv->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE,
+						P3H2x4x_REGISTERS_LOCK_CODE);
+	if (ret) {
+		dev_err(dev, "Failed to lock HUB's protected registers\n");
+		goto error;
+	}
+
+	return 0;
+
+error:
+	devm_kfree(dev, priv);
+	return ret;
+}
+
+static void p3h2x4x_device_remove_i3c(struct i3c_device *i3cdev)
+{
+	struct p3h2x4x *priv = i3cdev_get_drvdata(i3cdev);
+	struct i2c_adapter *tp_adap;
+	struct i3c_master_controller *tp_controller;
+	struct smbus_device *backend = NULL;
+	int i;
+
+	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++) {
+		tp_adap = &priv->tp_bus[i].smbus_port_adapter;
+		tp_controller = &priv->tp_bus[i].i3c_port_controller;
+
+		if (priv->tp_bus[i].is_registered) {
+			if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_SMBUS) {
+				list_for_each_entry(backend,
+						&priv->tp_bus[i].tp_device_entry,
+						list) {
+					i2c_unregister_device(backend->client);
+					kfree(backend);
+				}
+				i2c_del_adapter(tp_adap);
+			} else if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_I3C) {
+				i3c_master_unregister(tp_controller);
+			}
+		}
+	}
+
+	i3c_device_disable_ibi(i3cdev);
+	i3c_device_free_ibi(i3cdev);
+
+	mutex_destroy(&priv->etx_mutex);
+	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++)
+		mutex_destroy(&priv->tp_bus[i].port_mutex);
+
+	devm_kfree(&i3cdev->dev, priv);
+}
+
+static int p3h2x4x_device_probe_i2c(struct i2c_client *client)
+{
+	struct device_node *node = NULL;
+	struct regmap *regmap;
+	struct device *dev = &client->dev;
+	struct p3h2x4x *priv;
+	int ret, i;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->i2cdev = client;
+	i2c_set_clientdata(client, priv);
+
+	p3h2x4x_of_default_configuration(dev);
+
+	regmap = devm_regmap_init_i2c(client, &p3h2x4x_regmap_config);
+	if (IS_ERR(regmap)) {
+		ret = PTR_ERR(regmap);
+		dev_err(dev, "Failed to register I3C HUB regmap\n");
+		goto error;
+	}
+	priv->regmap = regmap;
+	priv->is_p3h2x4x_in_i3c = false;
+
+	mutex_init(&priv->etx_mutex);
+
+	/* Register logic for native SMBus ports */
+	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++)
+		mutex_init(&priv->tp_bus[i].port_mutex);
+
+	node = p3h2x4x_get_dt_p3h2x4x_node(dev->parent->of_node, dev); /* Find hub node in DT */
+	if (!node) {
+		dev_warn(dev, "No DT entry - running with hardware defaults.\n");
+	} else {
+		of_node_get(node);
+		p3h2x4x_of_get_p3h2x4x_conf(dev, node);
+		p3h2x4x_of_get_tp_dt_conf(dev, node);
+		of_node_put(node);
+		/* Parse DTS to find data on the SMBus target mode */
+		p3h2x4x_parse_dt_tp(dev, node, priv);
+	}
+
+	/* Unlock access to protected registers */
+	ret = regmap_write(priv->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE,
+						P3H2x4x_REGISTERS_UNLOCK_CODE);
+	if (ret) {
+		dev_err(dev, "Failed to unlock HUB's protected registers\n");
+		goto error;
+	}
+
+	ret = p3h2x4x_configure_hw(dev);
+	if (ret) {
+		dev_err(dev, "Failed to configure the HUB\n");
+		goto error;
+	}
+
+	/* Register logic for native SMBus ports */
+	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++) {
+		if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_SMBUS)
+			ret = p3h2x4x_tp_smbus_algo(priv, i);
+	}
+
+	ret = p3h2x4x_tp_add_downstream_device(priv);
+	if (ret) {
+		dev_err(dev, "Failed to add backend device\n");
+		goto error;
+	}
+
+	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; ++i) {
+		if (priv->tp_bus[i].dt_available) {
+			if (priv->settings.tp[i].always_enable)
+				priv->tp_always_enable_mask =
+							(priv->tp_always_enable_mask |  BIT(i));
+		}
+	}
+
+	ret = regmap_write(priv->regmap, P3H2x4x_TP_NET_CON_CONF, priv->tp_always_enable_mask);
+	if (ret) {
+		dev_err(dev, "Failed to open Target Port(s)\n");
+		goto error;
+	}
+
+	/* Lock access to protected registers */
+	ret = regmap_write(priv->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE,
+						P3H2x4x_REGISTERS_LOCK_CODE);
+	if (ret) {
+		dev_err(dev, "Failed to lock HUB's protected registers\n");
+		goto error;
+	}
+	return 0;
+
+error:
+	devm_kfree(dev, priv);
+	return ret;
+}
+
+static void p3h2x4x_device_remove_i2c(struct i2c_client *client)
+{
+	struct p3h2x4x *priv = i2c_get_clientdata(client);
+	struct i2c_adapter *tp_adap;
+	struct smbus_device *backend = NULL;
+	int i;
+
+	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++) {
+		tp_adap = &priv->tp_bus[i].smbus_port_adapter;
+		if (priv->tp_bus[i].is_registered) {
+			list_for_each_entry(backend, &priv->tp_bus[i].tp_device_entry, list) {
+				i2c_unregister_device(backend->client);
+				kfree(backend);
+			}
+			i2c_del_adapter(tp_adap);
+		}
+	}
+
+	mutex_destroy(&priv->etx_mutex);
+	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++)
+		mutex_destroy(&priv->tp_bus[i].port_mutex);
+
+	devm_kfree(&client->dev, priv);
+}
+
+static struct i3c_driver p3h2x4x_i3c = {
+	.driver = {
+		.name = "p3h2x4x_i3c_drv",
+	},
+	.id_table = p3h2x4x_i3c_ids,
+	.probe = p3h2x4x_device_probe_i3c,
+	.remove = p3h2x4x_device_remove_i3c,
+};
+
+static struct i2c_driver p3h2x4x_i2c = {
+	.driver = {
+		.name = "p3h2x4x_i2c_drv",
+		.of_match_table = p3h2x4x_i2c_of_match,
+	},
+	.probe =  p3h2x4x_device_probe_i2c,
+	.remove = p3h2x4x_device_remove_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 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_i3c.c b/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_i3c.c
new file mode 100644
index 000000000000..c7827c4b6f57
--- /dev/null
+++ b/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_i3c.c
@@ -0,0 +1,353 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2024-2025 NXP
+ * This P3H2x4x driver file contain functions for I3C virtual Bus creation, connect/disconnect
+ * hub network and read/write.
+ */
+#include "p3h2x4x_i3c_hub.h"
+
+static void p3h2x4x_en_p3h2x4x_ntwk_tp(struct tp_bus *bus)
+{
+	struct p3h2x4x *priv = bus->priv;
+	struct device *dev = i3cdev_to_dev(priv->i3cdev);
+	int ret;
+
+	if (priv->settings.tp[bus->tp_port].always_enable)
+		return;
+
+	ret = regmap_write(priv->regmap, P3H2x4x_TP_NET_CON_CONF,
+				(bus->tp_mask | priv->tp_always_enable_mask));
+	if (ret)
+		dev_warn(dev, "Failed to open Target Port(s)\n");
+}
+
+static void p3h2x4x_dis_p3h2x4x_ntwk_tp(struct tp_bus *bus)
+{
+	struct p3h2x4x *priv = bus->priv;
+	struct device *dev = i3cdev_to_dev(priv->i3cdev);
+	int ret;
+
+	if (priv->settings.tp[bus->tp_port].always_enable)
+		return;
+
+	ret = regmap_write(priv->regmap, P3H2x4x_TP_NET_CON_CONF, priv->tp_always_enable_mask);
+	if (ret)
+		dev_warn(dev, "Failed to close Target Port(s)\n");
+}
+
+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, i3c_port_controller);
+}
+
+static struct tp_bus *p3h2x4x_bus_from_i2c_desc(struct i2c_dev_desc *desc)
+{
+	struct i3c_master_controller *controller = i2c_dev_get_master(desc);
+
+	return container_of(controller, struct tp_bus, i3c_port_controller);
+}
+
+static struct i3c_master_controller
+*get_parent_controller(struct i3c_master_controller *controller)
+{
+	struct tp_bus *bus = container_of(controller, struct tp_bus, i3c_port_controller);
+
+	return bus->priv->driving_master;
+}
+
+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, i3c_port_controller);
+
+	return bus->priv->driving_master;
+}
+
+static struct i3c_master_controller
+*get_parent_controller_from_i2c_desc(struct i2c_dev_desc *desc)
+{
+	struct i3c_master_controller *controller = desc->common.master;
+	struct tp_bus *bus = container_of(controller, struct tp_bus, i3c_port_controller);
+
+	return bus->priv->driving_master;
+}
+
+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, i3c_port_controller);
+
+	controller->this = bus->priv->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)
+{
+	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
+	struct i3c_master_controller *orig_parent;
+	int ret;
+
+	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
+	ret = parent->ops->attach_i3c_dev(dev);
+	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
+	return ret;
+}
+
+static int p3h2x4x_reattach_i3c_dev(struct i3c_dev_desc *dev, u8 old_dyn_addr)
+{
+	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
+	struct i3c_master_controller *orig_parent;
+	int ret;
+
+	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
+	ret = parent->ops->reattach_i3c_dev(dev, old_dyn_addr);
+	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
+	return ret;
+}
+
+static void p3h2x4x_detach_i3c_dev(struct i3c_dev_desc *dev)
+{
+	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
+	struct i3c_master_controller *orig_parent;
+
+	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
+	parent->ops->detach_i3c_dev(dev);
+	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
+}
+
+static int p3h2x4x_do_daa(struct i3c_master_controller *controller)
+{
+	struct i3c_master_controller *parent = get_parent_controller(controller);
+
+	struct tp_bus *bus = container_of(controller, struct tp_bus, i3c_port_controller);
+	int ret;
+
+	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
+	down_write(&parent->bus.lock);
+	ret = parent->ops->do_daa(parent);
+	up_write(&parent->bus.lock);
+	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 parent->ops->supports_ccc_cmd(parent, cmd);
+}
+
+static int p3h2x4x_send_ccc_cmd(struct i3c_master_controller *controller,
+				struct i3c_ccc_cmd *cmd)
+{
+	struct i3c_master_controller *parent = get_parent_controller(controller);
+	struct tp_bus *bus = container_of(controller, struct tp_bus, i3c_port_controller);
+	int ret;
+
+	if (cmd->id == I3C_CCC_RSTDAA(true))
+		return 0;
+
+	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
+	ret = parent->ops->send_ccc_cmd(parent, cmd);
+	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
+
+	return ret;
+}
+
+static int p3h2x4x_priv_xfers(struct i3c_dev_desc *dev,
+				struct i3c_priv_xfer *xfers, int nxfers)
+{
+	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
+	struct i3c_master_controller *orig_parent;
+	struct tp_bus *bus = p3h2x4x_bus_from_i3c_desc(dev);
+	int res;
+
+	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
+	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
+	res = parent->ops->priv_xfers(dev, xfers, nxfers);
+	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
+	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
+
+	return res;
+}
+
+static int p3h2x4x_attach_i2c_dev(struct i2c_dev_desc *dev)
+{
+	struct i3c_master_controller *parent = get_parent_controller_from_i2c_desc(dev);
+	struct i3c_master_controller *orig_parent;
+	int ret;
+
+	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
+	ret = parent->ops->attach_i2c_dev(dev);
+	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
+	return ret;
+}
+
+static void p3h2x4x_detach_i2c_dev(struct i2c_dev_desc *dev)
+{
+	struct i3c_master_controller *parent = get_parent_controller_from_i2c_desc(dev);
+	struct i3c_master_controller *orig_parent;
+
+	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
+	parent->ops->detach_i2c_dev(dev);
+	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
+}
+
+static int p3h2x4x_i2c_xfers(struct i2c_dev_desc *dev,
+				const struct i2c_msg *xfers, int nxfers)
+{
+	struct i3c_master_controller *parent = get_parent_controller_from_i2c_desc(dev);
+	struct tp_bus *bus = p3h2x4x_bus_from_i2c_desc(dev);
+	struct i3c_master_controller *orig_parent;
+	int ret;
+
+	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
+	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
+	ret = parent->ops->i2c_xfers(dev, xfers, nxfers);
+	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
+	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
+	return ret;
+}
+
+static int p3h2x4x_request_ibi(struct i3c_dev_desc *dev,
+				const struct i3c_ibi_setup *req)
+{
+	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
+	struct tp_bus *bus = p3h2x4x_bus_from_i3c_desc(dev);
+	struct i3c_master_controller *orig_parent;
+	int ret;
+
+	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
+	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
+	ret = parent->ops->request_ibi(dev, req);
+	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
+	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
+	return ret;
+}
+
+static void p3h2x4x_free_ibi(struct i3c_dev_desc *dev)
+{
+	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
+	struct tp_bus *bus = p3h2x4x_bus_from_i3c_desc(dev);
+	struct i3c_master_controller *orig_parent;
+
+	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
+	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
+	parent->ops->free_ibi(dev);
+	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
+	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
+}
+
+static int p3h2x4x_enable_ibi(struct i3c_dev_desc *dev)
+{
+	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
+	struct tp_bus *bus = p3h2x4x_bus_from_i3c_desc(dev);
+	struct i3c_master_controller *orig_parent;
+	int ret;
+
+	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
+	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
+	ret = parent->ops->enable_ibi(dev);
+	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
+	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
+	return ret;
+}
+
+static int p3h2x4x_disable_ibi(struct i3c_dev_desc *dev)
+{
+	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
+	struct tp_bus *bus = p3h2x4x_bus_from_i3c_desc(dev);
+	struct i3c_master_controller *orig_parent;
+	int ret;
+
+	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
+	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
+	ret = parent->ops->disable_ibi(dev);
+	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
+	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
+	return ret;
+}
+
+static void p3h2x4x_recycle_ibi_slot(struct i3c_dev_desc *dev,
+					struct i3c_ibi_slot *slot)
+{
+	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
+	struct i3c_master_controller *orig_parent;
+
+	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
+	parent->ops->recycle_ibi_slot(dev, slot);
+	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
+}
+
+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,
+	.priv_xfers = p3h2x4x_priv_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.
+ * @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 *priv, int tp)
+{
+	struct device *dev = i3cdev_to_dev(priv->i3cdev);
+	int ret;
+
+	priv->tp_bus[tp].tp_mask =  BIT(tp);
+	dev->of_node = priv->tp_bus[tp].of_node;
+
+	ret = i3c_master_register(&priv->tp_bus[tp].i3c_port_controller,
+				dev, &p3h2x4x_i3c_ops, false);
+	if (ret) {
+		dev_warn(dev, "Failed to register i3c controller for tp %d\n", tp);
+		return -EINVAL;
+	}
+
+	priv->tp_bus[tp].is_registered = true;
+	return 0;
+}
diff --git a/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c b/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c
new file mode 100644
index 000000000000..8cdbaaf49378
--- /dev/null
+++ b/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c
@@ -0,0 +1,719 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2024-2025 NXP
+ * This P3H2x4x driver file contain functions for SMBus/I2C virtual Bus creation and read/write.
+ */
+#include "p3h2x4x_i3c_hub.h"
+
+static struct device *i2cdev_to_dev(struct i2c_client *i2cdev)
+{
+	return &i2cdev->dev;
+}
+
+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 device *dev = i3cdev_to_dev(i3cdev);
+	struct p3h2x4x *priv = dev_get_drvdata(dev);
+	struct smbus_device *backend = NULL;
+
+	u8 target_buffer_page, flag_clear, rx_data, temp, i;
+	u8 slave_rx_buffer[P3H2x4x_SMBUS_TARGET_PAYLOAD_SIZE] = { 0 };
+	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(priv->regmap, P3H2x4x_PAGE_PTR, target_buffer_page);
+	if (ret)
+		goto ibi_err;
+
+	/* read buffer length */
+	ret = regmap_read(priv->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(dev, "Received message too big for p3h2x4x buffer\n");
+		return;
+	}
+
+	/* read slave  address */
+	ret = regmap_read(priv->regmap, P3H2x4x_TARGET_BUFF_ADDRESS, &slave_address);
+	if (ret)
+		goto ibi_err;
+
+	/* read data */
+	if (packet_len) {
+		ret = regmap_bulk_read(priv->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 */
+	list_for_each_entry(backend, &priv->tp_bus[tp].tp_device_entry, list) {
+		if ((slave_address >> 1 == backend->addr) && (priv->is_slave_reg)) {
+			i2c_slave_event(backend->client, I2C_SLAVE_WRITE_REQUESTED,
+				(u8 *)&slave_address);
+
+			for (i = 0; i < packet_len; i++) {
+				rx_data = slave_rx_buffer[i];
+				i2c_slave_event(backend->client, I2C_SLAVE_WRITE_RECEIVED,
+						&rx_data);
+			}
+			i2c_slave_event(backend->client, I2C_SLAVE_STOP, &temp);
+			break;
+		}
+	}
+
+ibi_err:
+	regmap_write(priv->regmap, P3H2x4x_PAGE_PTR, 0x00);
+	regmap_write(priv->regmap, P3H2x4x_TP0_SMBUS_AGNT_STS + tp, flag_clear);
+}
+
+/**
+ * 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 device *dev = i3cdev_to_dev(i3cdev);
+	struct p3h2x4x *priv = dev_get_drvdata(dev);
+
+	u32 target_port_status, payload_byte_one, payload_byte_two;
+	u8 i;
+	int  ret;
+
+	payload_byte_one = (*(int *)payload->data);
+	payload_byte_two = (*(int *)(payload->data + 4));
+
+	if (!(payload_byte_one & P3H2x4x_SMBUS_AGENT_EVENT_FLAG_STATUS))
+		goto err;
+
+	mutex_lock(&priv->etx_mutex);
+	ret = regmap_write(priv->regmap, P3H2x4x_TP_SMBUS_AGNT_IBI_CONFIG, P3H2x4x_ALL_TP_IBI_DIS);
+	if (ret) {
+		dev_err(dev, "Failed to Disable IBI\n");
+		goto err;
+	}
+
+	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; ++i) {
+		if ((priv->tp_bus[i].is_registered) && ((payload_byte_two >> i) & 0x01)) {
+			ret = regmap_read(priv->regmap, P3H2x4x_TP0_SMBUS_AGNT_STS + i,
+					&target_port_status);
+			if (ret) {
+				dev_err(dev, "target port read status failed %d\n", ret);
+				goto err;
+			}
+
+			/* 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(dev, "Overflow, reading buffer zero and one\n");
+				break;
+			default:
+				regmap_write(priv->regmap, P3H2x4x_TP0_SMBUS_AGNT_STS + i,
+					BUF_RECEIVED_FLAG_TF_MASK);
+					break;
+			}
+		}
+	}
+err:
+	regmap_write(priv->regmap, P3H2x4x_TP_SMBUS_AGNT_IBI_CONFIG, priv->tp_ibi_mask);
+	mutex_unlock(&priv->etx_mutex);
+}
+
+static int p3h2x4x_read_p3h2x4x_id(struct device *dev)
+{
+	struct p3h2x4x *priv = dev_get_drvdata(dev);
+	u32 reg_val;
+	int ret;
+
+	ret = regmap_read(priv->regmap, P3H2x4x_LDO_AND_CPSEL_STS, &reg_val);
+	if (ret) {
+		dev_err(dev, "Failed to read status register\n");
+		return ret;
+	}
+	if (P3H2x4x_CP_SEL_PIN_INPUT_CODE_GET(reg_val) == 3)
+		return 1;
+	else
+		return 0;
+}
+
+
+static void unlock_port_ext_mutex(struct mutex *ext, struct mutex *port)
+{
+	mutex_unlock(ext);
+	mutex_unlock(port);
+}
+
+static void lock_port_ext_mutex(struct mutex *ext, struct mutex *port)
+{
+	mutex_lock(ext);
+	mutex_lock(port);
+}
+
+static int p3h2x4x_read_smbus_transaction_status(struct p3h2x4x *priv,
+					u8 target_port_status, u8 *status, u8 data_length)
+{
+	unsigned int status_read;
+	int ret;
+
+	mutex_unlock(&priv->etx_mutex);
+	fsleep(P3H2x4x_SMBUS_400kHz_TRANSFER_TIMEOUT(data_length));
+	mutex_lock(&priv->etx_mutex);
+
+	ret = regmap_read(priv->regmap, target_port_status, &status_read);
+	if (ret)
+		return ret;
+
+	*status = (u8)status_read;
+
+	if ((*status & P3H2x4x_TP_BUFFER_STATUS_MASK) == P3H2x4x_XFER_SUCCESS)
+		return 0;
+
+	dev_err(&priv->i3cdev->dev, "Status read timeout reached\n");
+	return 0;
+}
+
+/*
+ * 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 *priv,
+				struct i2c_msg *xfers,
+				u8 target_port,
+				u8 nxfers_i, u8 rw, u8 *return_status)
+{
+	u8 transaction_type = P3H2x4x_SMBUS_400kHz;
+	u8 controller_buffer_page = P3H2x4x_CONTROLLER_BUFFER_PAGE + 4 * target_port;
+	int write_length = xfers[nxfers_i].len;
+	int read_length = xfers[nxfers_i].len;
+	u8 target_port_status = P3H2x4x_TP0_SMBUS_AGNT_STS + target_port;
+	u8 addr = xfers[nxfers_i].addr;
+	u8 target_port_code = BIT(target_port);
+	u8 rw_address = 2 * addr;
+	u8 desc[P3H2x4x_SMBUS_DESCRIPTOR_SIZE] = { 0 };
+	u8 status;
+	int ret;
+
+	if (rw) {
+		rw_address |= BIT(0);
+		write_length = 0;
+	} else {
+		read_length = 0;
+	}
+
+	desc[0] = rw_address;
+	desc[1] = transaction_type;
+	desc[2] = write_length;
+	desc[3] = read_length;
+
+	ret = regmap_write(priv->regmap, target_port_status,
+			P3H2x4x_TP_BUFFER_STATUS_MASK);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(priv->regmap, P3H2x4x_PAGE_PTR, controller_buffer_page);
+	if (ret)
+		return ret;
+
+	ret = regmap_bulk_write(priv->regmap, P3H2x4x_CONTROLLER_AGENT_BUFF,
+				desc, P3H2x4x_SMBUS_DESCRIPTOR_SIZE);
+	if (ret)
+		return ret;
+
+	if (!rw) {
+		ret = regmap_bulk_write(priv->regmap,
+					P3H2x4x_CONTROLLER_AGENT_BUFF_DATA,
+					xfers[nxfers_i].buf, xfers[nxfers_i].len);
+		if (ret)
+			return ret;
+	}
+
+	ret = regmap_write(priv->regmap, P3H2x4x_TP_SMBUS_AGNT_TRANS_START,
+			target_port_code);
+	if (ret)
+		return ret;
+
+	ret = p3h2x4x_read_smbus_transaction_status(priv, target_port_status,
+				&status, (write_length + read_length));
+	if (ret)
+		return ret;
+
+	*return_status = status;
+
+	if (rw) {
+		ret = regmap_bulk_read(priv->regmap,
+				P3H2x4x_CONTROLLER_AGENT_BUFF_DATA,
+				xfers[nxfers_i].buf, xfers[nxfers_i].len);
+		if (ret)
+			return ret;
+	}
+
+	ret = regmap_write(priv->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;
+	int ret;
+	u8 return_status;
+	u8 msg_count;
+	u8 rw;
+
+	struct device *dev;
+	struct tp_bus *bus =
+		container_of(adap, struct tp_bus, smbus_port_adapter);
+	struct p3h2x4x *priv = bus->priv;
+
+	if (priv->is_p3h2x4x_in_i3c)
+		dev = i3cdev_to_dev(priv->i3cdev);
+	else
+		dev = i2cdev_to_dev(priv->i2cdev);
+
+	lock_port_ext_mutex(&priv->etx_mutex, &bus->port_mutex);
+
+	if (priv->is_p3h2x4x_in_i3c) {
+		ret = i3c_device_disable_ibi(priv->i3cdev);
+		if (ret) {
+			dev_err(dev, "Failed to Disable IBI\n");
+			unlock_port_ext_mutex(&priv->etx_mutex, &bus->port_mutex);
+			return ret;
+		}
+	}
+
+	for (msg_count = 0; msg_count < num; msg_count++) {
+		if (msgs[msg_count].len > P3H2x4x_SMBUS_PAYLOAD_SIZE) {
+			dev_err(&adap->dev,
+				"Message nr. %d not sent - length over %d bytes.\n",
+				msg_count, P3H2x4x_SMBUS_PAYLOAD_SIZE);
+			continue;
+		}
+		rw = msgs[msg_count].flags % 2;
+
+		ret = p3h2x4x_tp_i2c_xfer_msg(priv,
+					msgs,
+					bus->tp_port,
+					msg_count, rw, &return_status);
+
+		if (ret)
+			goto error;
+
+		if (return_status == P3H2x4x_XFER_SUCCESS)
+			ret_sum++;
+	}
+
+error:
+	if (priv->is_p3h2x4x_in_i3c) {
+		ret =  i3c_device_enable_ibi(priv->i3cdev);
+		if (ret) {
+			dev_err(dev, "Failed to Enable IBI\n");
+			unlock_port_ext_mutex(&priv->etx_mutex, &bus->port_mutex);
+			return ret;
+		}
+	}
+	unlock_port_ext_mutex(&priv->etx_mutex, &bus->port_mutex);
+	return ret_sum;
+}
+
+static int p3h2x4x_tp_smbus_xfer_msg(struct p3h2x4x *priv,
+					u8 target_port,
+					u8 addr,
+					u8 rw,
+					u8 cmd,
+					int sz,
+					union i2c_smbus_data *data,
+					u8 *return_status)
+{
+	u8 transaction_type = P3H2x4x_SMBUS_400kHz;
+	u8 controller_buffer_page = P3H2x4x_CONTROLLER_BUFFER_PAGE + 4 * target_port;
+	u8 target_port_status = P3H2x4x_TP0_SMBUS_AGNT_STS + target_port;
+	u8 target_port_code = BIT(target_port);
+	u8 rw_address = 2 * addr;
+	u8 desc[P3H2x4x_SMBUS_DESCRIPTOR_SIZE] = { 0 };
+	u8 status;
+	int ret, i;
+	u8 read_length, write_length;
+	u8 buf[I2C_SMBUS_BLOCK_MAX + 2] = {0};
+	struct device *dev;
+
+	if (priv->is_p3h2x4x_in_i3c)
+		dev = i3cdev_to_dev(priv->i3cdev);
+	else
+		dev = i2cdev_to_dev(priv->i2cdev);
+
+	/* Map the size to what the chip understands */
+	switch (sz) {
+	case I2C_SMBUS_QUICK:
+	case I2C_SMBUS_BYTE:
+		if (rw)	{
+			buf[0] = data->byte;
+			read_length = ONE_BYTE_SIZE;
+			write_length = 0;
+			rw_address |= BIT(0);
+		} else {
+			buf[0] = cmd;
+			write_length = ONE_BYTE_SIZE;
+			read_length = 0;
+		}
+		break;
+	case I2C_SMBUS_BYTE_DATA:
+		if (rw) {   /* read write */
+			buf[0] = cmd;
+			write_length = ONE_BYTE_SIZE;
+			read_length = ONE_BYTE_SIZE;
+			transaction_type |= BIT(0);
+		} else {  /* only write */
+			buf[0] = cmd;
+			buf[1] = data->byte;
+			write_length = ONE_BYTE_SIZE + 1;
+			read_length = 0;
+		}
+		break;
+	case I2C_SMBUS_WORD_DATA:
+		if (rw) {         /* read write */
+			buf[0] = cmd;
+			write_length = ONE_BYTE_SIZE;
+			read_length = 2;
+			transaction_type |= BIT(0);
+		} else {  /* only write */
+			buf[0] = cmd;
+			buf[1] = data->word & 0xff;
+			buf[2] = (data->word & 0xff00) >> 8;
+			write_length = ONE_BYTE_SIZE + 2;
+			read_length = 0;
+		}
+		break;
+	case I2C_SMBUS_BLOCK_DATA:
+		if (rw) {         /* read write */
+			buf[0] = cmd;
+			write_length = ONE_BYTE_SIZE;
+			read_length = data->block[0] + 1;
+			transaction_type |= BIT(0);
+		} else {  /* only write */
+			buf[0] = cmd;
+			for (i = 0 ; i <= data->block[0]; i++)
+				buf[i+1] = data->block[i];
+
+			write_length = data->block[0] + 2;
+			read_length = 0;
+		}
+		break;
+	case I2C_SMBUS_I2C_BLOCK_DATA:
+		if (rw) {         /* read write */
+			buf[0] = cmd;
+			write_length = ONE_BYTE_SIZE;
+			read_length = data->block[0];
+			transaction_type |= BIT(0);
+		} else {  /* only write */
+			buf[0] = cmd;
+			for (i = 0 ; i < data->block[0]; i++)
+				buf[i+1] = data->block[i+1];
+
+			write_length = data->block[0] + 1;
+			read_length = 0;
+		}
+		break;
+	default:
+		dev_warn(dev, "Unsupported transaction %d\n", sz);
+		break;
+	}
+
+	desc[0] = rw_address;
+	desc[1] = transaction_type;
+	desc[2] = write_length;
+	desc[3] = read_length;
+
+	ret = regmap_write(priv->regmap, target_port_status,
+			P3H2x4x_TP_BUFFER_STATUS_MASK);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(priv->regmap, P3H2x4x_PAGE_PTR, controller_buffer_page);
+	if (ret)
+		return ret;
+
+	ret = regmap_bulk_write(priv->regmap, P3H2x4x_CONTROLLER_AGENT_BUFF,
+				desc, P3H2x4x_SMBUS_DESCRIPTOR_SIZE);
+	if (ret)
+		return ret;
+
+	if (write_length) {
+		ret = regmap_bulk_write(priv->regmap,
+				P3H2x4x_CONTROLLER_AGENT_BUFF_DATA,
+				buf, write_length);
+		if (ret)
+			return ret;
+	}
+
+	ret = regmap_write(priv->regmap, P3H2x4x_TP_SMBUS_AGNT_TRANS_START,
+			target_port_code);
+	if (ret)
+		return ret;
+
+	ret = p3h2x4x_read_smbus_transaction_status(priv, target_port_status, &status,
+			(write_length + read_length));
+	if (ret)
+		return ret;
+
+	*return_status = status;
+
+	if (rw) {
+		switch (sz) {
+		case I2C_SMBUS_QUICK:
+		case I2C_SMBUS_BYTE:
+		case I2C_SMBUS_BYTE_DATA:
+			{
+				ret = regmap_bulk_read(priv->regmap,
+					P3H2x4x_CONTROLLER_AGENT_BUFF_DATA + write_length,
+					&data->byte, read_length);
+					break;
+			}
+		case I2C_SMBUS_WORD_DATA:
+			{
+				ret = regmap_bulk_read(priv->regmap,
+					P3H2x4x_CONTROLLER_AGENT_BUFF_DATA + write_length,
+					(u8 *)&data->word, read_length);
+				break;
+			}
+		case I2C_SMBUS_BLOCK_DATA:
+			{
+				ret = regmap_bulk_read(priv->regmap,
+					P3H2x4x_CONTROLLER_AGENT_BUFF_DATA + write_length,
+					data->block, read_length);
+				break;
+			}
+		case I2C_SMBUS_I2C_BLOCK_DATA:
+			{
+				ret = regmap_bulk_read(priv->regmap,
+					P3H2x4x_CONTROLLER_AGENT_BUFF_DATA + write_length,
+					data->block + 1, read_length);
+				break;
+			}
+		default:
+				dev_warn(dev, "Unsupported transaction %d\n", sz);
+				break;
+		}
+
+		if (ret)
+			return ret;
+	}
+
+	ret = regmap_write(priv->regmap, P3H2x4x_PAGE_PTR, 0x00);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static s32 p3h2x4x_tp_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags,
+		char read_write, u8 command, int size, union i2c_smbus_data *data)
+{
+	struct tp_bus *bus =
+		container_of(adap, struct tp_bus, smbus_port_adapter);
+
+	struct p3h2x4x *priv = bus->priv;
+	struct device *dev;
+
+	if (priv->is_p3h2x4x_in_i3c)
+		dev = i3cdev_to_dev(priv->i3cdev);
+	else
+		dev = i2cdev_to_dev(priv->i2cdev);
+
+	int ret, ret_status;
+	u8 return_status;
+
+	lock_port_ext_mutex(&priv->etx_mutex, &bus->port_mutex);
+
+	if (priv->is_p3h2x4x_in_i3c) {
+		ret = i3c_device_disable_ibi(priv->i3cdev);
+		if (ret) {
+			dev_err(dev, "Failed to Disable IBI\n");
+			unlock_port_ext_mutex(&priv->etx_mutex, &bus->port_mutex);
+			return ret;
+		}
+	}
+
+	ret_status = p3h2x4x_tp_smbus_xfer_msg(priv,
+				(u8)bus->tp_port,
+				(u8)addr,
+				(u8)read_write,
+				(u8)command,
+				size,
+				data,
+				&return_status);
+
+	if (priv->is_p3h2x4x_in_i3c) {
+		ret = i3c_device_enable_ibi(priv->i3cdev);
+		if (ret) {
+			dev_err(dev, "Failed to Enable IBI\n");
+			unlock_port_ext_mutex(&priv->etx_mutex, &bus->port_mutex);
+			return ret;
+		}
+	}
+	unlock_port_ext_mutex(&priv->etx_mutex, &bus->port_mutex);
+
+	if (ret_status)
+		return ret_status;
+
+	if (return_status == P3H2x4x_XFER_SUCCESS)
+		return 0;
+	else
+		return -EINVAL;
+}
+
+static u32 p3h2x4x_tp_smbus_funcs(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
+			I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_WORD_DATA |
+			I2C_FUNC_SMBUS_I2C_BLOCK  | I2C_FUNC_SMBUS_BLOCK_DATA |
+			I2C_FUNC_I2C;
+}
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static int p3h2x4x_tp_i2c_reg_slave(struct i2c_client *slave)
+{
+	struct tp_bus *bus =
+		container_of(slave->adapter, struct tp_bus, smbus_port_adapter);
+	struct p3h2x4x *priv = bus->priv;
+
+	priv->is_slave_reg = true;
+
+	return 0;
+}
+static int p3h2x4x_tp_i2c_unreg_slave(struct i2c_client *slave)
+{
+	struct tp_bus *bus =
+		container_of(slave->adapter, struct tp_bus, smbus_port_adapter);
+	struct p3h2x4x *priv = bus->priv;
+
+	priv->is_slave_reg = false;
+
+	return 0;
+}
+#endif
+
+/*
+ * I2C algorithm Structure
+ */
+static struct i2c_algorithm p3h2x4x_tp_i2c_algorithm = {
+	.master_xfer    = p3h2x4x_tp_i2c_xfer,
+	.smbus_xfer = p3h2x4x_tp_smbus_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_add_downstream_device - prove downstream devices for target port who
+ * configured as SMBus.
+ * @priv: p3h2x4x device structure.
+ * Return: 0 in case of success, a negative EINVAL code if the error.
+ */
+int p3h2x4x_tp_add_downstream_device(struct p3h2x4x *priv)
+{
+	struct i2c_board_info smbus_tp_device_info = {0};
+	struct smbus_device *backend = NULL;
+	struct tp_bus *bus;
+	int i;
+	struct device *dev;
+
+	if (priv->is_p3h2x4x_in_i3c)
+		dev = i3cdev_to_dev(priv->i3cdev);
+	else
+		dev = i2cdev_to_dev(priv->i2cdev);
+
+	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++) {
+		bus = &priv->tp_bus[i];
+		if (!bus->is_registered)
+			continue;
+
+		list_for_each_entry(backend,
+					&bus->tp_device_entry, list) {
+
+			smbus_tp_device_info.addr = backend->addr;
+			smbus_tp_device_info.flags = I2C_CLIENT_SLAVE;
+			smbus_tp_device_info.of_node = backend->tp_device_dt_node;
+			snprintf(smbus_tp_device_info.type, I2C_NAME_SIZE, backend->compatible);
+			backend->client = i2c_new_client_device(&bus->smbus_port_adapter,
+						&smbus_tp_device_info);
+			if (IS_ERR(backend->client)) {
+				dev_warn(dev, "Error while registering backend\n");
+				return -EINVAL;
+			}
+		}
+	}
+	return 0;
+}
+
+/*
+ * p3h2x4x_tp_smbus_algo - add i2c adapter for target port who
+ * 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 *priv, int tp)
+{
+	int ret;
+	struct device *dev;
+
+	if (priv->is_p3h2x4x_in_i3c)
+		dev = i3cdev_to_dev(priv->i3cdev);
+	else
+		dev = i2cdev_to_dev(priv->i2cdev);
+
+	priv->tp_bus[tp].smbus_port_adapter.owner = THIS_MODULE;
+	priv->tp_bus[tp].smbus_port_adapter.class = I2C_CLASS_HWMON;
+	priv->tp_bus[tp].smbus_port_adapter.algo = &p3h2x4x_tp_i2c_algorithm;
+
+	sprintf(priv->tp_bus[tp].smbus_port_adapter.name, "p3h2x4x-cp-%d.tp-port-%d",
+		p3h2x4x_read_p3h2x4x_id(dev), tp);
+
+	ret = i2c_add_adapter(&priv->tp_bus[tp].smbus_port_adapter);
+	if (ret) {
+		dev_warn(dev, "failled to add adapter for tp %d\n", tp);
+		return -EINVAL;
+	}
+	priv->tp_bus[tp].is_registered = true;
+
+	return 0;
+}
diff --git a/include/linux/i3c/device.h b/include/linux/i3c/device.h
index b674f64d0822..5ab199abb653 100644
--- a/include/linux/i3c/device.h
+++ b/include/linux/i3c/device.h
@@ -77,6 +77,7 @@ struct i3c_priv_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


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

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

* Re: [PATCH 1/2] dt-bindings: i3c: Add NXP P3H2x4x i3c-hub support
  2025-02-12 13:22 [PATCH 1/2] dt-bindings: i3c: Add NXP P3H2x4x i3c-hub support Aman Kumar Pandey
  2025-02-12 13:22 ` [PATCH 2/2] drivers: i3c: Add driver for NXP P3H2x4x i3c-hub device Aman Kumar Pandey
@ 2025-02-12 15:25 ` Rob Herring (Arm)
  2025-02-12 16:23 ` Frank Li
  2025-02-12 16:49 ` Krzysztof Kozlowski
  3 siblings, 0 replies; 13+ messages in thread
From: Rob Herring (Arm) @ 2025-02-12 15:25 UTC (permalink / raw)
  To: Aman Kumar Pandey
  Cc: shashank.rebbapragada, Frank.Li, krzk+dt, conor+dt, priyanka.jain,
	vikash.bansal, alexandre.belloni, linux-i3c, linux-kernel,
	devicetree


On Wed, 12 Feb 2025 15:22:26 +0200, Aman Kumar Pandey wrote:
> P3H2x4x (P3H2440/P3H2441/P3H2840/P3H2841) is multiport I3C hub
> device which connects to a host CPU via I3C/I2C/SMBus bus on one
> side and to multiple peripheral devices on the other side.
> 
> Signed-off-by: Aman Kumar Pandey <aman.kumarpandey@nxp.com>
> Signed-off-by: Vikash Bansal <vikash.bansal@nxp.com>
> ---
>  .../bindings/i3c/p3h2x4x_i3c_hub.yaml         | 404 ++++++++++++++++++
>  MAINTAINERS                                   |   7 +
>  2 files changed, 411 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/i3c/p3h2x4x_i3c_hub.yaml
> 

My bot found errors running 'make dt_binding_check' on your patch:

yamllint warnings/errors:
./Documentation/devicetree/bindings/i3c/p3h2x4x_i3c_hub.yaml:6:1: [error] syntax error: expected '<document start>', but found '<block mapping start>' (syntax)
./Documentation/devicetree/bindings/i3c/p3h2x4x_i3c_hub.yaml:251:111: [warning] line too long (118 > 110 characters) (line-length)
./Documentation/devicetree/bindings/i3c/p3h2x4x_i3c_hub.yaml:260:111: [warning] line too long (111 > 110 characters) (line-length)

dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/i3c/p3h2x4x_i3c_hub.yaml: ignoring, error parsing file
./Documentation/devicetree/bindings/i3c/p3h2x4x_i3c_hub.yaml:6:1: expected '<document start>', but found ('<block mapping start>',)
make[2]: *** Deleting file 'Documentation/devicetree/bindings/i3c/p3h2x4x_i3c_hub.example.dts'
Documentation/devicetree/bindings/i3c/p3h2x4x_i3c_hub.yaml:6:1: expected '<document start>', but found ('<block mapping start>',)
make[2]: *** [Documentation/devicetree/bindings/Makefile:26: Documentation/devicetree/bindings/i3c/p3h2x4x_i3c_hub.example.dts] Error 1
make[2]: *** Waiting for unfinished jobs....
make[1]: *** [/builds/robherring/dt-review-ci/linux/Makefile:1511: dt_binding_check] Error 2
make: *** [Makefile:251: __sub-make] Error 2

doc reference errors (make refcheckdocs):

See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/20250212132227.1348374-1-aman.kumarpandey@nxp.com

The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

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

* Re: [PATCH 1/2] dt-bindings: i3c: Add NXP P3H2x4x i3c-hub support
  2025-02-12 13:22 [PATCH 1/2] dt-bindings: i3c: Add NXP P3H2x4x i3c-hub support Aman Kumar Pandey
  2025-02-12 13:22 ` [PATCH 2/2] drivers: i3c: Add driver for NXP P3H2x4x i3c-hub device Aman Kumar Pandey
  2025-02-12 15:25 ` [PATCH 1/2] dt-bindings: i3c: Add NXP P3H2x4x i3c-hub support Rob Herring (Arm)
@ 2025-02-12 16:23 ` Frank Li
  2025-02-12 16:49 ` Krzysztof Kozlowski
  3 siblings, 0 replies; 13+ messages in thread
From: Frank Li @ 2025-02-12 16:23 UTC (permalink / raw)
  To: Aman Kumar Pandey
  Cc: linux-kernel, linux-i3c, alexandre.belloni, krzk+dt, robh,
	conor+dt, devicetree, vikash.bansal, priyanka.jain,
	shashank.rebbapragada

On Wed, Feb 12, 2025 at 03:22:26PM +0200, Aman Kumar Pandey wrote:
> P3H2x4x (P3H2440/P3H2441/P3H2840/P3H2841) is multiport I3C hub
> device which connects to a host CPU via I3C/I2C/SMBus bus on one
> side and to multiple peripheral devices on the other side.
>
> Signed-off-by: Aman Kumar Pandey <aman.kumarpandey@nxp.com>
> Signed-off-by: Vikash Bansal <vikash.bansal@nxp.com>
> ---

You need run before post

make dt_binding_check DT_SCHEMA_FILES=p3h2x4x_i3c_hub.yaml

I just go through it.

>  .../bindings/i3c/p3h2x4x_i3c_hub.yaml         | 404 ++++++++++++++++++
>  MAINTAINERS                                   |   7 +
>  2 files changed, 411 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/i3c/p3h2x4x_i3c_hub.yaml
>
> diff --git a/Documentation/devicetree/bindings/i3c/p3h2x4x_i3c_hub.yaml b/Documentation/devicetree/bindings/i3c/p3h2x4x_i3c_hub.yaml
> new file mode 100644
> index 000000000000..33ea524e5432
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/i3c/p3h2x4x_i3c_hub.yaml
> @@ -0,0 +1,404 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +# Copyright 2024-2025 NXP
> +
> +%YAML 1.2
> +
> +$id: http://devicetree.org/schemas/i3c/p3h2x4x_i3c_hub.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: P3H2X4X I3C HUB
> +
> +maintainers:
> +  - Vikash Bansal <vikash.bansal@nxp.com>
> +  - Aman Kumar Pandey <aman.kumarpandey@nxp.com>
> +
> +description: |
> +	P3H2x4x (P3H2440/P3H2441/P3H2840/P3H2841) are multiport I3C hub devices
> +  that connect to:-
> +  1. A host CPU via I3C/I2C/SMBus bus on one side and connect to multiple
> +     peripheral devices on the other 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 devices with eight I3C/I2C Target Port.
> +  4. P3H2440/ P3H2441 are 4 port I3C hub devices with four I3C/I2C Target Port.
> +     Target ports can be configured as I2C/SMBus, I3C or GPIO and connect to
> +     peripherals.
> +
> +  Device tree node can be used for following configurations:-
> +  1. Controller Port can be configured via dt entry to support
> +  2. I2C/SMBus mode or I3C buses.
> +  3. Enabling voltage compatibility across I3C Controller and
> +     Target devices.
> +  4. Up to eight target devices and their modes(I3C/I2C/SMbus modes).
> +  5. To support MCTP device
> +  6. To support In-band interrupts
> +
> +  Controller Port can be configured via dt entry to support I2C/SMBus mode or
> +  I3C buses.
> +
> +  For I2C devices, use below format:
> +      DT node: hub@static_address
> +      reg: encodes the static I2C address.
> +
> +  For I3C devices, use below format:
> +      DT node: hub@static_address,PID
> +      reg: encodes the static I2C address (0 if the device does not have one), and the
> +      Provisioned ID (PID) used to uniquely identify a device on a bus.
> +      This PID contains information about the vendor, the part, and an instance ID so
> +      that several devices of the same type can be connected on the same bus.
> +      assigned-address: represents the dynamic address to be assigned to this device.
> +
> +allOf:
> +  - $ref: i3c.yaml#
> +
> +properties:
> +

missed compatible string?

> +  cp0-ldo-en:
> +    enum:
> +      - disabled
> +      - enabled

vendor specic property should be start with vendor name.
nxp,cp0-ldo-en

bool property, simple use flag.
$ref: /schemas/types.yaml#/definitions/flag

The same as below other properties

> +    description: |

Needn't "|", if not format below context.

> +      I3C HUB Controller Port 0 LDO setting for turning on and off. If enabled, the dedicated
> +      pin will provide the voltage generated by the on-die LDO. It is an optional property,
> +      the configuration remains default if it is not supplied.
> +
> +  cp1-ldo-en:
> +    enum:
> +      - disabled
> +      - enabled
> +
> +    description: |
> +      I3C HUB Controller Port 0 LDO setting for turning on and off. If enabled, the dedicated
> +      pin will provide the voltage generated by the on-die LDO. It is an optional property,
> +      the configuration remains default if it is not supplied.
> +
> +  tp0145-ldo-en:
> +    enum:
> +      - disabled
> +      - enabled
> +
> +    description: |
> +      I3C HUB Target Ports 0/1/4/5 LDO setting for turning on and off. If enabled, the dedicated
> +      pin will provide the voltage generated by the on-die LDO. It is an optional property,
> +      the configuration remains default if it is not supplied.
> +
> +  tp2367-ldo-en:
> +    enum:
> +      - disabled
> +      - enabled
> +
> +    description: |
> +      I3C HUB Target Ports 2/3/6/7 LDO setting for turning on and off. If enabled, the dedicated
> +      pin will provide the voltage generated by the on-die LDO. It is an optional property,
> +      the configuration remains default if it is not supplied.
> +
> +  cp0-ldo-volt:
> +    enum:
> +      - 1.0V
> +      - 1.1V
> +      - 1.2V
> +      - 1.8V
> +
> +    description: |
> +      Controller Port 0 voltage level is controlled by the I3C HUB Controller Port 0
> +      LDO setting.
> +      Since this parameter is optional, the configuration remains default if it is not supplied.
> +
> +  cp1-ldo-volt:
> +    enum:
> +      - 1.0V
> +      - 1.1V
> +      - 1.2V
> +      - 1.8V
> +
> +    description: |
> +    Controller Port 1 voltage level is controlled by the I3C HUB Controller Port 1
> +    LDO setting.
> +    Since this parameter is optional, the configuration remains default if it is not supplied.
> +
> +  tp0145-ldo-volt:
> +    enum:
> +      - 1.0V
> +      - 1.1V
> +      - 1.2V
> +      - 1.8V
> +
> +    description: |
> +      Target Port 0/1/4/5 voltage level is controlled by the I3C HUB Target Port 0/1/4/5
> +      LDO setting.
> +      Since this parameter is optional, the configuration remains default if it is not supplied.
> +
> +  tp2367-ldo-volt:
> +    enum:
> +      - 1.0V
> +      - 1.1V
> +      - 1.2V
> +      - 1.8V
> +
> +    description: |
> +      Target Port 2/3/6/7 voltage level is controlled by the I3C HUB Target Port 2/3/6/7
> +      LDO setting.
> +      Since this parameter is optional, the configuration remains default if it is not supplied.
> +
> +  tp0145-pullup:
> +    enum:
> +      - 250R
> +      - 500R
> +      - 1000R
> +      - 2000R
> +
> +    description: |
> +      Target Port 0/1/4/5 pull up setting is controlled by the I3C HUB Target Port 0/1/4/5
> +      pull up resistance level.
> +      Since this parameter is optional, the configuration remains default if it is not supplied.
> +
> +  tp2367-pullup:
> +    enum:
> +      - 250R
> +      - 500R
> +      - 1000R
> +      - 2000R
> +
> +    description: |
> +      Target Port 2/3/6/7 pull up setting is controlled by the I3C HUB Target Port 2/3/6/7
> +      pull up resistance level.
> +      Since this parameter is optional, the configuration remains default if it is not supplied.
> +
> +  cp0-io-strength:
> +    enum:
> +      - 20Ohms
> +      - 30Ohms
> +      - 40Ohms
> +      - 50Ohms
> +
> +    description: |
> +      To regulate the output driver strength at Controller Port 0, use the I3C HUB Controller
> +      Port 0 IO strength setting.
> +      It is an optional property, the configuration remains default if it is not supplied.
> +
> +  cp1-io-strength:
> +    enum:
> +      - 20Ohms
> +      - 30Ohms
> +      - 40Ohms
> +      - 50Ohms
> +    description: |
> +      To regulate the output driver strength at Controller Port 1, use the I3C HUB Controller
> +      Port 1 IO strength setting.
> +      It is an optional property, the configuration remains default if it is not supplied.
> +
> +  tp0145-io-strength:
> +    enum:
> +      - 20Ohms
> +      - 30Ohms
> +      - 40Ohms
> +      - 50Ohms
> +
> +    description: |
> +      To regulate the output driver strength at Target port 0/1/4/5, use the I3C HUB Target
> +      port 0/1/4/5 IO strength setting.
> +      It is an optional property, the configuration remains default if it is not supplied.
> +
> +  tp2367-io-strength:
> +    enum:
> +      - 20Ohms
> +      - 30Ohms
> +      - 40Ohms
> +      - 50Ohms
> +
> +    description: |
> +      To regulate the output driver strength at Target port 2/3/6/7, use the I3C HUB Target
> +      port 2/3/6/7 IO strength setting.
> +      It is an optional property, the configuration remains default if it is not supplied.
> +
> +patternProperties:
> +  "@[0-7]$":
> +    type: object
> +    description: |
> +      I3C HUB Target Port child, should be named: target-port@<target-port-id>
> +
> +    properties:
> +      mode:
> +        enum:
> +          - i3c
> +          - smbus
> +          - i2c
> +          - gpio
> +
> +        description: |
> +          I3C HUB Target Port mode setting to control Target Port functionality.
> +          As per now it is oly supporting SMBus, i2c and i3c( i2c mode will work
> +          with hub network).
> +
> +      pullup:
> +        enum:
> +          - disabled
> +          - enabled
> +        description: |
> +          I3C HUB Target Port pull-up setting to disable/enable Target Port pull-up.
> +          It is an optional property, the configuration remains default if it is not supplied.
> +
> +      ibi_en:
> +        enum:
> +          - disabled
> +          - enabled
> +        description: |
> +          I3C HUB Target Port IBI setting to disable/enable IBI for Target Port.
> +          This property is optional. If not provided, Target Port IBI will disabled.
> +
> +      local_dev:
> +        description: |
> +          SMBus Target Agent can discard transactions of downstream device and not generate an IBI to upstream I3C Hub
> +          Controller Port. Up to 8 device (addresses) can be configured as local Devices.
> +
> +          This property is optional. If not provided, local device list will empty.
> +
> +      always-enable:
> +        description: |
> +          Add this field to enable hub network(Controller port -> target port).
> +
> +          This property is optional. If not provided, by default hub network will disabled for respective port.
> +
> +    patternProperties:
> +      "@slave-address,pid$":
> +        type: object
> +        description: |
> +          Adding this node to install the downstream devices.
> +
> +        properties:
> +          compatible:
> +            description:
> +              Compatible of the I2C/SMBus downstream device.
> +
> +          reg:
> +            description:
> +              Downstream device addresses which are connected to target port.
> +
> +          I3c HUB driver supports standerd i2c/i3c DT entry as it passes DT node to respective driver for
> +          downstream device.
> +
> +additionalProperties: true

should be false
unevaluatedProperties: false


> +
> +examples:
> +  - |
> +      i3c_hub: hub@70,236153000c2{
> +          reg = <0x70 0x236 0x3000c2>;
> +          assigned-address = <0x50>;
> +          dcr = <0xC2>;
> +
> +          cp0-ldo-en = "disabled";
> +          cp1-ldo-en = "disabled";
> +          cp0-ldo-volt = "1.8V";
> +          cp1-ldo-volt = "1.8V";
> +          tp0145-ldo-en = "disabled";
> +          tp2367-ldo-en = "disabled";
> +          tp0145-ldo-volt = "1.8V";
> +          tp2367-ldo-volt = "1.8V";
> +          tp0145-pullup= "1000R";
> +          tp2367-pullup = "1000R";
> +          tp0145-io-strength = "20Ohms";
> +          tp2367-io-strength = "20Ohms";
> +          cp0-io-strength = "20Ohms";
> +          cp1-io-strength = "20Ohms";
> +          target-port@0 {
> +            #address-cells = <1>;
> +                #size-cells = <0>;
> +            mode = "smbus";
> +            pullup_en = "enabled";
> +            ibi_en = "enabled";
> +            local_dev = <0x30>, <0x40>, <0x50>;
> +            backend@4c{
> +              compatible = "i3c-hub";
> +              reg = <0x4c>;
> +            };
> +            rtc@68 {
> +              compatible = "dallas,ds3232";
> +              reg = <0x68>;
> +              interrupt-parent = <&gpio2>;
> +              interrupts = <20 0>;
> +              trickle-resistor-ohms = <250>;
> +            };
> +
> +            eeprom@57 {
> +              compatible = "atmel,24c32";
> +              reg = <0x57>;
> +              pagesize = <32>;
> +              wp-gpios = <&gpio2 2 0>;
> +              num-addresses = <8>;
> +            };
> +          };
> +          target-port@1 {
> +            #address-cells = <1>;
> +                #size-cells = <0>;
> +            mode = "smbus";
> +            pullup_en = "enabled";
> +            ibi_en = "enabled";
> +              local_dev = <0x35>, <0x45>, <0x55>;
> +          };
> +          target-port@2 {
> +            #address-cells = <1>;
> +                #size-cells = <0>;
> +            mode = "i3c";
> +            pullup_en = "enabled";
> +            hub-test@4c,25400000000{
> +              reg = <0x4c 0x254 0x0000>;
> +              assigned-address = <0x50>;
> +            };
> +          };
> +          target-port@3 {
> +            #address-cells = <1>;
> +                #size-cells = <0>;
> +            mode = "i3c";
> +            pullup_en = "enabled";
> +            always-enable;
> +          };
> +      };
> +
> +      i3c_hub: hub@70 {
> +          compatible = "nxp,p3h2x4x";
> +          reg = <0x70>;
> +
> +          cp0-ldo-en = "disabled";
> +          cp1-ldo-en = "disabled";
> +          cp0-ldo-volt = "1.8V";
> +          cp1-ldo-volt = "1.8V";
> +          tp0145-ldo-en = "disabled";
> +          tp2367-ldo-en = "disabled";
> +          tp0145-ldo-volt = "1.8V";
> +          tp2367-ldo-volt = "1.8V";
> +          tp0145-pullup= "1000R";
> +          tp2367-pullup = "1000R";
> +          tp0145-io-strength = "20Ohms";
> +          tp2367-io-strength = "20Ohms";
> +          cp0-io-strength = "20Ohms";
> +          cp1-io-strength = "20Ohms";
> +          target-port@0 {
> +            #address-cells = <1>;
> +                #size-cells = <0>;

align to 4 spaces

> +            mode = "smbus";
> +            pullup_en = "enabled";
> +            ibi_en = "enabled";
> +            local_dev = <0x30>, <0x40>, <0x50>;
> +
> +            backend@4c{
> +              compatible = "test-dvr";
> +              reg = <0x4c>;
> +            };

Need space line here

> +            rtc@68 {
> +              compatible = "dallas,ds3232";
> +              reg = <0x68>;
> +              interrupt-parent = <&gpio2>;
> +              interrupts = <20 0>;
> +              trickle-resistor-ohms = <250>;
> +            };
> +
> +            eeprom@57 {
> +              compatible = "atmel,24c32";
> +              reg = <0x57>;
> +              pagesize = <32>;
> +              wp-gpios = <&gpio2 2 0>;
> +              num-addresses = <8>;
> +            };
> +          };
> +	    };
> +-  |
> diff --git a/MAINTAINERS b/MAINTAINERS
> index af686e0bb6d7..20aa3e987ac5 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -17167,6 +17167,13 @@ S:	Maintained
>  F:	Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml
>  F:	sound/soc/codecs/tfa989x.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

Need I3C and imx maillist.

> +S:	Maintained
> +F:	Documentation/devicetree/bindings/i3c/p3h2x4x_i3c_hub.yaml
> +
>  NZXT-KRAKEN2 HARDWARE MONITORING DRIVER
>  M:	Jonas Malaco <jonas@protocubo.io>
>  L:	linux-hwmon@vger.kernel.org
> --
> 2.25.1
>

-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

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

* Re: [PATCH 2/2] drivers: i3c: Add driver for NXP P3H2x4x i3c-hub device
  2025-02-12 13:22 ` [PATCH 2/2] drivers: i3c: Add driver for NXP P3H2x4x i3c-hub device Aman Kumar Pandey
@ 2025-02-12 16:42   ` Frank Li
  2025-02-12 16:53   ` Krzysztof Kozlowski
                     ` (6 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Frank Li @ 2025-02-12 16:42 UTC (permalink / raw)
  To: Aman Kumar Pandey
  Cc: linux-kernel, linux-i3c, alexandre.belloni, krzk+dt, robh,
	conor+dt, devicetree, vikash.bansal, priyanka.jain,
	shashank.rebbapragada

On Wed, Feb 12, 2025 at 03:22:27PM +0200, Aman Kumar Pandey wrote:
> P3H2x4x (P3H2440/P3H2441/P3H2840/P3H2841) is multiport I3C hub devices.
> It connects to a host CPU via I3C/I2C/SMBus bus on one side and to multiple
> peripheral devices on the other side.
> P3H2840/ P3H2841 have eight I3C/I2C Target Port.
> P3H2440/ P3H2441 have four I3C/I2C Target Port.
>
> This driver can support
> 1. i3c/i2c communication between host and hub
> 2. i3c/i2c transparent mode between host and downstream devices.
> 3. Target ports can be configured as I2C/SMBus mode or I3C.
> 4. MCTP devices
> 5. In-band interrupts
>
> Signed-off-by: Aman Kumar Pandey <aman.kumarpandey@nxp.com>
> Signed-off-by: Vikash Bansal <vikash.bansal@nxp.com>
> ---
>  MAINTAINERS                                  |   1 +
>  drivers/i3c/Kconfig                          |   1 +
>  drivers/i3c/Makefile                         |   1 +
>  drivers/i3c/p3h2x4x/Kconfig                  |  11 +
>  drivers/i3c/p3h2x4x/Makefile                 |   4 +
>  drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub.h        | 454 +++++++++
>  drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_common.c | 941 +++++++++++++++++++
>  drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_i3c.c    | 353 +++++++
>  drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c  | 719 ++++++++++++++

drivers/i3c/p3h2x4x/ is not good name,
Maybe drivers/i3c/hub

Can you split to some small patches?

first patches, add very basic funtion by use default hardware parameter,
just focus on the basic function.

2nd patches, add handle dt advance's settings

3nd patches, add smbus support

...


>  include/linux/i3c/device.h                   |   1 +
>  10 files changed, 2486 insertions(+)
>  create mode 100644 drivers/i3c/p3h2x4x/Kconfig
>  create mode 100644 drivers/i3c/p3h2x4x/Makefile
>  create mode 100644 drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub.h
>  create mode 100644 drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_common.c
>  create mode 100644 drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_i3c.c
>  create mode 100644 drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 20aa3e987ac5..8e4ec4e3656e 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -17173,6 +17173,7 @@ M:	Aman Kumar Pandey <aman.kumarpandey@nxp.com>
>  L:	linux-kernel@vger.kernel.org
>  S:	Maintained
>  F:	Documentation/devicetree/bindings/i3c/p3h2x4x_i3c_hub.yaml
> +F:	i3c/p3h2x4x/*
>
>  NZXT-KRAKEN2 HARDWARE MONITORING DRIVER
>  M:	Jonas Malaco <jonas@protocubo.io>
> diff --git a/drivers/i3c/Kconfig b/drivers/i3c/Kconfig
> index 30a441506f61..3160437cc8e1 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/p3h2x4x/Kconfig"
>  endif # I3C
> diff --git a/drivers/i3c/Makefile b/drivers/i3c/Makefile
> index 11982efbc6d9..696747cc7f18 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)		+= p3h2x4x/
> diff --git a/drivers/i3c/p3h2x4x/Kconfig b/drivers/i3c/p3h2x4x/Kconfig
> new file mode 100644
> index 000000000000..b8b18342b065
> --- /dev/null
> +++ b/drivers/i3c/p3h2x4x/Kconfig
> @@ -0,0 +1,11 @@
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright 2024-2025 NXP
> +config P3H2X4X_I3C_HUB
> +	tristate "P3H2X4X I3C HUB support"
> +	depends on I3C
> +	select REGMAP_I3C
> +	help
> +		This enables support for NXP P3H244x/P3H284x I3C HUB. Say Y or M here to use
> +		I3C HUB driver to configure I3C HUB device.
> +		I3C HUB driver's probe will trigger when I3C device with DCR
> +		equals to 0xC2 (HUB device) is detected on the bus.

wrong align, should be

help
  Thsi enables ...

> diff --git a/drivers/i3c/p3h2x4x/Makefile b/drivers/i3c/p3h2x4x/Makefile
> new file mode 100644
> index 000000000000..214a3eeb62f2
> --- /dev/null
> +++ b/drivers/i3c/p3h2x4x/Makefile
> @@ -0,0 +1,4 @@
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright 2024-2025 NXP
> +p3h2x4x_i3c_hub-y := p3h2x4x_i3c_hub_common.o p3h2x4x_i3c_hub_i3c.o p3h2x4x_i3c_hub_smbus.o
> +obj-$(CONFIG_P3H2X4X_I3C_HUB)	+= p3h2x4x_i3c_hub.o
> diff --git a/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub.h b/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub.h
> new file mode 100644
> index 000000000000..e8c627019556
> --- /dev/null
> +++ b/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub.h
> @@ -0,0 +1,454 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright 2024-2025 NXP

Just 2025

> + * This header file contain private device structure definition, Reg address and its bit
> + * mapping etc.
> + */
> +
> +#ifndef P3H2X4X_I3C_HUB_H
> +#define P3H2X4X_I3C_HUB_H
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +#include <linux/i3c/device.h>
> +#include <linux/i3c/master.h>
> +#include <linux/mutex.h>
> +#include <linux/rwsem.h>
> +#include <linux/i2c.h>
> +#include <linux/slab.h>
> +#include <linux/of.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_DEV_REG_PROTECTION_CODE				0x10
> +#define P3H2x4x_REGISTERS_LOCK_CODE				0x00
> +#define P3H2x4x_REGISTERS_UNLOCK_CODE				0x69
> +#define P3H2x4x_CP1_REGISTERS_UNLOCK_CODE			0x6A
> +
> +#define P3H2x4x_CP_CONF						0x11
> +#define P3H2x4x_TP_ENABLE					0x12
> +#define P3H2x4x_TPn_ENABLE(n)					BIT(n)
> +
> +#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)	\
> +		(((x) << 0) & P3H2x4x_TP0145_IO_STRENGTH_MASK)
> +#define P3H2x4x_TP2367_IO_STRENGTH_MASK				GENMASK(3, 2)
> +#define P3H2x4x_TP2367_IO_STRENGTH(x)	\
> +		(((x) << 2) & P3H2x4x_TP2367_IO_STRENGTH_MASK)
> +#define P3H2x4x_CP0_IO_STRENGTH_MASK				GENMASK(5, 4)
> +#define P3H2x4x_CP0_IO_STRENGTH(x)	\
> +		(((x) << 4) & P3H2x4x_CP0_IO_STRENGTH_MASK)
> +#define P3H2x4x_CP1_IO_STRENGTH_MASK				GENMASK(7, 6)
> +#define P3H2x4x_CP1_IO_STRENGTH(x)	\
> +		(((x) << 6) & P3H2x4x_CP1_IO_STRENGTH_MASK)
> +
> +#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)	\
> +		(((x) << 0) & P3H2x4x_CP0_VCCIO_LDO_VOLTAGE_MASK)
> +#define P3H2x4x_CP1_VCCIO_LDO_VOLTAGE_MASK			GENMASK(3, 2)
> +#define P3H2x4x_CP1_VCCIO_LDO_VOLTAGE(x)	\
> +		(((x) << 2) & P3H2x4x_CP1_VCCIO_LDO_VOLTAGE_MASK)
> +#define P3H2x4x_TP0145_VCCIO_LDO_VOLTAGE_MASK			GENMASK(5, 4)
> +#define P3H2x4x_TP0145_VCCIO_LDO_VOLTAGE(x)	\
> +		(((x) << 4) & P3H2x4x_TP0145_VCCIO_LDO_VOLTAGE_MASK)
> +#define P3H2x4x_TP2367_VCCIO_LDO_VOLTAGE_MASK			GENMASK(7, 6)
> +#define P3H2x4x_TP2367_VCCIO_LDO_VOLTAGE(x)	\
> +		(((x) << 6) & P3H2x4x_TP2367_VCCIO_LDO_VOLTAGE_MASK)
> +
> +#define P3H2x4x_TP_IO_MODE_CONF					0x17
> +#define P3H2x4x_TP_SMBUS_AGNT_EN				0x18
> +#define P3H2x4x_TPn_SMBUS_MODE_EN(n)				BIT(n)
> +#define P3H2x4x_TPn_I2C_MODE_EN(n)				BIT(n)
> +
> +#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_TP0145_PULLUP_CONF_MASK				GENMASK(7, 6)
> +#define P3H2x4x_TP0145_PULLUP_CONF(x)	\
> +		(((x) << 6) & P3H2x4x_TP0145_PULLUP_CONF_MASK)
> +#define P3H2x4x_TP2367_PULLUP_CONF_MASK				GENMASK(5, 4)
> +#define P3H2x4x_TP2367_PULLUP_CONF(x)	\
> +		(((x) << 4) & P3H2x4x_TP2367_PULLUP_CONF_MASK)
> +
> +#define P3H2x4x_CP_IBI_CONF					0x1A
> +
> +#define P3H2x4x_TP_SMBUS_AGNT_IBI_CONFIG			0x1B
> +#define P3H2x4x_TPn_IBI_EN(n)					BIT(n)
> +#define P3H2x4x_ALL_TP_IBI_EN					0xFF
> +#define P3H2x4x_ALL_TP_IBI_DIS					0x00
> +
> +#define P3H2x4x_IBI_MDB_CUSTOM					0x1C
> +#define P3H2x4x_JEDEC_CONTEXT_ID				0x1D
> +#define P3H2x4x_TP_GPIO_MODE_EN					0x1E
> +#define P3H2x4x_TPn_GPIO_MODE_EN(n)				BIT(n)
> +
> +/* 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_TPn_NET_CON(n)					BIT(n)
> +
> +#define P3H2x4x_TP_PULLUP_EN					0x53
> +#define P3H2x4x_TPn_PULLUP_EN(n)				BIT(n)
> +
> +#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_DT_LDO_VOLT_NOT_SET				0x04
> +
> +#define P3H2x4x_LDO_DISABLED					0x00
> +#define P3H2x4x_LDO_ENABLED					0x01
> +#define P3H2x4x_DT_LDO_NOT_DEFINED				0x02
> +
> +#define P3H2x4x_IBI_DISABLED					0x00
> +#define P3H2x4x_IBI_ENABLED					0x01
> +#define P3H2x4x_IBI_NOT_DEFINED					0x02
> +
> +/* 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_NOT_SET				0x04
> +
> +#define P3H2x4x_TP_PULLUP_DISABLED				0x00
> +#define P3H2x4x_TP_PULLUP_ENABLED				0x01
> +#define P3H2x4x_TP_PULLUP_NOT_DEFINED				0x02
> +
> +#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_IO_STRENGTH_NOT_SET				0x04
> +
> +#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 P3H2x4x_TP_MODE_NOT_SET					0x04
> +
> +#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_XFER_SUCCESS					0x01
> +#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					10
> +
> +#define P3H2x4x_HUB_ID						0
> +
> +/* Reg config for Regmap */
> +#define P3H2x4x_REG_BITS					8
> +#define P3H2x4x_VAL_BITS					8
> +
> +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_setting {
> +	const char *const name;
> +	const u8 value;
> +};
> +
> +/* IBI enable/disable settings */
> +static const struct p3h2x4x_setting ibi_en_settings[] = {
> +	{ "disabled",	P3H2x4x_IBI_DISABLED },
> +	{ "enabled",	P3H2x4x_IBI_ENABLED },
> +};
> +
> +/* LDO enable/disable settings */
> +static const struct p3h2x4x_setting ldo_en_settings[] = {
> +	{ "disabled",	P3H2x4x_LDO_DISABLED },
> +	{ "enabled",	P3H2x4x_LDO_ENABLED },
> +};
> +
> +/* LDO voltage settings */
> +static const struct p3h2x4x_setting ldo_volt_settings[] = {
> +	{ "1.0V",	P3H2x4x_LDO_VOLT_1_0V },
> +	{ "1.1V",	P3H2x4x_LDO_VOLT_1_1V },
> +	{ "1.2V",	P3H2x4x_LDO_VOLT_1_2V },
> +	{ "1.8V",	P3H2x4x_LDO_VOLT_1_8V },
> +};
> +
> +/* target port pull-up settings */
> +static const struct p3h2x4x_setting pullup_settings[] = {
> +	{ "250R",		P3H2x4x_TP_PULLUP_250R },
> +	{ "500R",		P3H2x4x_TP_PULLUP_500R },
> +	{ "1000R",		P3H2x4x_TP_PULLUP_1000R },
> +	{ "2000R",		P3H2x4x_TP_PULLUP_2000R },
> +};
> +
> +/* target port mode settings */
> +static const struct p3h2x4x_setting tp_mode_settings[] = {
> +	{ "i3c",		P3H2x4x_TP_MODE_I3C },
> +	{ "smbus",		P3H2x4x_TP_MODE_SMBUS },
> +	{ "gpio",		P3H2x4x_TP_MODE_GPIO },
> +	{ "i2c",		P3H2x4x_TP_MODE_I2C },
> +};
> +
> +/* pull-up enable/disable settings */
> +static const struct p3h2x4x_setting tp_pullup_settings[] = {
> +	{ "disabled",	P3H2x4x_TP_PULLUP_DISABLED },
> +	{ "enabled",	P3H2x4x_TP_PULLUP_ENABLED },
> +};
> +
> +/*  IO strenght settings */
> +static const struct p3h2x4x_setting io_strength_settings[] = {
> +	{ "20Ohms",		P3H2x4x_IO_STRENGTH_20_OHM },
> +	{ "30Ohms",		P3H2x4x_IO_STRENGTH_30_OHM },
> +	{ "40Ohms",		P3H2x4x_IO_STRENGTH_40_OHM },
> +	{ "50Ohms",		P3H2x4x_IO_STRENGTH_50_OHM },
> +};
> +
> +struct tp_setting {
> +	u8 mode;
> +	u8 pullup_en;
> +	u8 ibi_en;
> +	bool always_enable;
> +};
> +
> +/*
> + * struct svc_i3c_i2c_dev_data - Device specific data
> + * @index: Index in the master tables corresponding to this device
> + * @ibi: IBI slot index in the master structure
> + * @ibi_pool: IBI pool associated to this device
> + */
> +struct dt_settings {
> +	u8 cp0_ldo_en;
> +	u8 cp1_ldo_en;
> +	u8 tp0145_ldo_en;
> +	u8 tp2367_ldo_en;
> +	u8 cp0_ldo_volt;
> +	u8 cp1_ldo_volt;
> +	u8 tp0145_ldo_volt;
> +	u8 tp2367_ldo_volt;
> +	u8 tp0145_pullup;
> +	u8 tp2367_pullup;
> +	u8 cp0_io_strength;
> +	u8 cp1_io_strength;
> +	u8 tp0145_io_strength;
> +	u8 tp2367_io_strength;
> +	struct tp_setting tp[P3H2x4x_TP_MAX_COUNT];
> +};
> +
> +struct smbus_device {
> +	struct i2c_client *client;
> +	const char *compatible;
> +	int addr;
> +	struct list_head list;
> +	struct device_node *tp_device_dt_node;
> +};
> +
> +struct tp_bus {
> +	bool dt_available;	    /* bus configuration is available in DT. */
> +	bool is_registered;	    /* bus was registered in the framework. */
> +	u8 tp_mask;
> +	u8 tp_port;
> +	u8 local_dev_list[P3H2x4x_TP_LOCAL_DEV];
> +	u8 local_dev_count;
> +	struct i3c_master_controller i3c_port_controller;
> +	struct i2c_adapter smbus_port_adapter;
> +	struct device_node *of_node;
> +	struct p3h2x4x *priv;
> +	struct list_head tp_device_entry;
> +	struct mutex port_mutex;
> +};
> +
> +struct p3h2x4x {
> +	struct i3c_device *i3cdev;
> +	struct i2c_client *i2cdev;
> +	struct i3c_master_controller *driving_master;
> +	struct regmap *regmap;
> +	struct mutex etx_mutex;
> +	struct dt_settings settings;
> +	struct tp_bus tp_bus[P3H2x4x_TP_MAX_COUNT];
> +	/* Offset for reading HUB's register. */
> +	u8 tp_ibi_mask;
> +	u8 tp_i3c_bus_mask;
> +	u8 tp_always_enable_mask;
> +	u8 is_slave_reg;
> +	bool is_p3h2x4x_in_i3c;
> +};
> +
> +/*
> + * p3h2x4x_tp_add_downstream_device - prove downstream devices for target port who
> + * configured as SMBus.
> + * @priv: p3h2x4x device structure.
> + * Return: 0 in case of success, a negative EINVAL code if the error.
> + */
> +int p3h2x4x_tp_add_downstream_device(struct p3h2x4x *priv);
> +
> +/*
> + * p3h2x4x_tp_smbus_algo - add i2c adapter for target port who
> + * 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 *priv, int tp);
> +
> +int p3h2x4x_tp_i3c_algo(struct p3h2x4x *priv, int tp);
> +
> +/*
> + * 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 /* P3H2X4X_I3C_HUB_H */
> diff --git a/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_common.c b/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_common.c
> new file mode 100644
> index 000000000000..74b86a668f12
> --- /dev/null
> +++ b/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_common.c
> @@ -0,0 +1,941 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2024-2025 NXP
> + * This P3H2x4x driver file implements functions for Hub probe and DT parsing.
> + */
> +
> +#include "p3h2x4x_i3c_hub.h"
> +
> +struct i3c_ibi_setup p3h2x4x_ibireq = {
> +		.handler = p3h2x4x_ibi_handler,
> +		.max_payload_len = P3H2x4x_MAX_PAYLOAD_LEN,
> +		.num_slots = P3H2x4x_NUM_SLOTS,
> +};
> +
> +struct regmap_config p3h2x4x_regmap_config = {
> +		.reg_bits = P3H2x4x_REG_BITS,
> +		.val_bits = P3H2x4x_VAL_BITS,

One tab is enough

> +	};

Need tab here, Do you run check patch?

> +
> +/* p3h2x4x ids (i3c) */
> +static const struct i3c_device_id p3h2x4x_i3c_ids[] = {
> +	I3C_CLASS(I3C_DCR_HUB, NULL),
> +	{ /* sentinel */ },

needn't "," at sentinel.

> +};
> +MODULE_DEVICE_TABLE(i3c, p3h2x4x_i3c_ids);
> +
> +/* p3h2x4x ids (i2c) */
> +static const struct i2c_device_id p3h2x4x_i2c_id_table[] = {
> +	{ "nxp-i3c-hub", P3H2x4x_HUB_ID },
> +	{ /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(i2c, p3h2x4x_i2c_id_table);
> +
> +static const struct of_device_id  p3h2x4x_i2c_of_match[] = {
> +	{
> +		.compatible = "nxp,p3h2x4x",
> +		.data =  P3H2x4x_HUB_ID,
> +	},
> +	{ /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, p3h2x4x_i2c_of_match);

Generally this is end part of file.

> +
> +static void p3h2x4x_of_get_dt_setting(struct device *dev,
> +				const struct device_node *node,
> +				const char *setting_name,
> +				const struct p3h2x4x_setting settings[],
> +				const u8 settings_count, u8 *setting_value)
> +{
> +	const char *sval;
> +	int ret;
> +	int i;
> +
> +	ret = of_property_read_string(node, setting_name, &sval);
> +	if (ret) {
> +		if (ret != -EINVAL)
> +			dev_warn(dev, "No setting or invalid setting for %s, err=%i\n",
> +				setting_name, ret);
> +		return;
> +	}
> +
> +	for (i = 0; i < settings_count; ++i) {
> +		const struct p3h2x4x_setting *const setting = &settings[i];
> +
> +		if (!strcmp(setting->name, sval)) {
> +			*setting_value = setting->value;
> +			return;
> +		}
> +	}
> +	dev_warn(dev, "Unknown setting for %s\n", setting_name);
> +}
> +
> +static int p3h2x4x_configure_pullup(struct device *dev)
> +{
> +	struct p3h2x4x *priv = dev_get_drvdata(dev);
> +	u8 mask = 0, value = 0;
> +
> +	if (priv->settings.tp0145_pullup != P3H2x4x_TP_PULLUP_NOT_SET) {
> +		mask |= P3H2x4x_TP0145_PULLUP_CONF_MASK;
> +		value |= P3H2x4x_TP0145_PULLUP_CONF(priv->settings.tp0145_pullup);
> +	}
> +
> +	if (priv->settings.tp2367_pullup != P3H2x4x_TP_PULLUP_NOT_SET) {
> +		mask |= P3H2x4x_TP2367_PULLUP_CONF_MASK;
> +		value |= P3H2x4x_TP2367_PULLUP_CONF(priv->settings.tp2367_pullup);
> +	}
> +
> +	return regmap_update_bits(priv->regmap, P3H2x4x_LDO_AND_PULLUP_CONF,
> +				mask, value);
> +}
> +
> +static int p3h2x4x_configure_ldo(struct device *dev)
> +{
> +	struct p3h2x4x *priv = dev_get_drvdata(dev);
> +	u8 ldo_config_mask = 0, ldo_config_val = 0;
> +	u8 ldo_disable_mask = 0, ldo_en_val = 0;
> +	u32 reg_val;
> +	int ret;
> +	u8 val;
> +
> +	/* Enable or Disable LDO's. If there is no DT entry - disable LDO for safety reasons */
> +	if (priv->settings.cp0_ldo_en == P3H2x4x_LDO_ENABLED)
> +		ldo_en_val |= P3H2x4x_CP0_EN_LDO;
> +	if (priv->settings.cp1_ldo_en == P3H2x4x_LDO_ENABLED)
> +		ldo_en_val |= P3H2x4x_CP1_EN_LDO;
> +	if (priv->settings.tp0145_ldo_en == P3H2x4x_LDO_ENABLED)
> +		ldo_en_val |= P3H2x4x_TP0145_EN_LDO;
> +	if (priv->settings.tp2367_ldo_en == P3H2x4x_LDO_ENABLED)
> +		ldo_en_val |= P3H2x4x_TP2367_EN_LDO;
> +
> +	/* Get current LDOs configuration */
> +	ret = regmap_read(priv->regmap, P3H2x4x_VCCIO_LDO_CONF, &reg_val);
> +	if (ret)
> +		return ret;
> +
> +	/* LDOs Voltage level (Skip if not defined in the DT)
> +	 * Set the mask only if there is a change from current value
> +	 */
> +	if (priv->settings.cp0_ldo_volt != P3H2x4x_DT_LDO_VOLT_NOT_SET) {
> +		val = P3H2x4x_CP0_VCCIO_LDO_VOLTAGE(priv->settings.cp0_ldo_volt);
> +		if ((reg_val & P3H2x4x_CP0_VCCIO_LDO_VOLTAGE_MASK) != val) {
> +			ldo_config_mask |= P3H2x4x_CP0_VCCIO_LDO_VOLTAGE_MASK;
> +			ldo_config_val |= val;
> +
> +			ldo_disable_mask |= P3H2x4x_CP0_EN_LDO;
> +		}
> +	}
> +	if (priv->settings.cp1_ldo_volt != P3H2x4x_DT_LDO_VOLT_NOT_SET) {
> +		val = P3H2x4x_CP1_VCCIO_LDO_VOLTAGE(priv->settings.cp1_ldo_volt);
> +		if ((reg_val & P3H2x4x_CP1_VCCIO_LDO_VOLTAGE_MASK) != val) {
> +			ldo_config_mask |= P3H2x4x_CP1_VCCIO_LDO_VOLTAGE_MASK;
> +			ldo_config_val |= val;
> +
> +			ldo_disable_mask |= P3H2x4x_CP1_EN_LDO;
> +		}
> +	}
> +	if (priv->settings.tp0145_ldo_volt != P3H2x4x_DT_LDO_VOLT_NOT_SET) {
> +		val = P3H2x4x_TP0145_VCCIO_LDO_VOLTAGE(priv->settings.tp0145_ldo_volt);
> +		if ((reg_val & P3H2x4x_TP0145_VCCIO_LDO_VOLTAGE_MASK) != val) {
> +			ldo_config_mask |= P3H2x4x_TP0145_VCCIO_LDO_VOLTAGE_MASK;
> +			ldo_config_val |= val;
> +
> +			ldo_disable_mask |= P3H2x4x_TP0145_EN_LDO;
> +		}
> +	}
> +	if (priv->settings.tp2367_ldo_volt != P3H2x4x_DT_LDO_VOLT_NOT_SET) {
> +		val = P3H2x4x_TP2367_VCCIO_LDO_VOLTAGE(priv->settings.tp2367_ldo_volt);
> +		if ((reg_val & P3H2x4x_TP2367_VCCIO_LDO_VOLTAGE_MASK) != val) {
> +			ldo_config_mask |= P3H2x4x_TP2367_VCCIO_LDO_VOLTAGE_MASK;
> +			ldo_config_val |= val;
> +
> +			ldo_disable_mask |= P3H2x4x_TP2367_EN_LDO;
> +		}
> +	}
> +
> +	/*
> +	 *Update LDO voltage configuration only if value is changed from already existing register
> +	 * value. It is a good practice to disable the LDO's before making any voltage changes.
> +	 * Presence of config mask indicates voltage change to be applied.
> +	 */
> +	if (ldo_config_mask) {
> +		/* Disable LDO's before making voltage changes */
> +		ret = regmap_update_bits(priv->regmap,
> +					P3H2x4x_LDO_AND_PULLUP_CONF,
> +					ldo_disable_mask, 0);
> +		if (ret)
> +			return ret;
> +
> +		/* Update the LDOs configuration */
> +		ret = regmap_update_bits(priv->regmap, P3H2x4x_VCCIO_LDO_CONF,
> +					ldo_config_mask, ldo_config_val);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	/* Update the LDOs Enable/disable register. This will enable only LDOs enabled in DT */
> +	return regmap_update_bits(priv->regmap, P3H2x4x_LDO_AND_PULLUP_CONF,
> +				P3H2x4x_LDO_ENABLE_DISABLE_MASK, ldo_en_val);
> +}
> +
> +static int p3h2x4x_configure_io_strength(struct device *dev)
> +{
> +	struct p3h2x4x *priv = dev_get_drvdata(dev);
> +	u8 mask_all = 0, val_all = 0;
> +	u32 reg_val;
> +	u8 val;
> +	struct dt_settings tmp;
> +	int ret;
> +
> +	/* Get IO strength configuration to figure out what needs to be changed */
> +	ret = regmap_read(priv->regmap, P3H2x4x_IO_STRENGTH, &reg_val);
> +	if (ret)
> +		return ret;
> +
> +	tmp = priv->settings;
> +	if (tmp.cp0_io_strength != P3H2x4x_IO_STRENGTH_NOT_SET) {
> +		val = P3H2x4x_CP0_IO_STRENGTH(tmp.cp0_io_strength);
> +		mask_all |= P3H2x4x_CP0_IO_STRENGTH_MASK;
> +		val_all |= val;
> +	}
> +	if (tmp.cp1_io_strength != P3H2x4x_IO_STRENGTH_NOT_SET) {
> +		val = P3H2x4x_CP1_IO_STRENGTH(tmp.cp1_io_strength);
> +		mask_all |= P3H2x4x_CP1_IO_STRENGTH_MASK;
> +		val_all |= val;
> +	}
> +	if (tmp.tp0145_io_strength != P3H2x4x_IO_STRENGTH_NOT_SET) {
> +		val = P3H2x4x_TP0145_IO_STRENGTH(tmp.tp0145_io_strength);
> +		mask_all |= P3H2x4x_TP0145_IO_STRENGTH_MASK;
> +		val_all |= val;
> +	}
> +	if (tmp.tp2367_io_strength != P3H2x4x_IO_STRENGTH_NOT_SET) {
> +		val = P3H2x4x_TP2367_IO_STRENGTH(tmp.tp2367_io_strength);
> +		mask_all |= P3H2x4x_TP2367_IO_STRENGTH_MASK;
> +		val_all |= val;
> +	}
> +
> +	/* Set IO strength if required */
> +	return regmap_update_bits(priv->regmap, P3H2x4x_IO_STRENGTH, mask_all, val_all);
> +}
> +
> +static int p3h2x4x_configure_tp(struct device *dev)
> +{
> +	struct p3h2x4x *priv = dev_get_drvdata(dev);
> +	u8 pullup_mask = 0, pullup_val = 0;
> +	u8 smbus_mask = 0, smbus_val = 0;
> +	u8 gpio_mask = 0, gpio_val = 0;
> +	u8 i3c_mask = 0, i3c_val = 0;
> +	u8 ibi_mask = 0, ibi_val = 0;
> +	u8 i2c_mask = 0, i2c_val = 0;
> +	int ret;
> +	int i;
> +
> +	/* TBD: Read type of HUB from register P3H2x4x_DEV_INFO_0 to learn target ports count. */
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; ++i) {
> +		if (priv->settings.tp[i].mode != P3H2x4x_TP_MODE_NOT_SET) {
> +			i3c_mask |= P3H2x4x_TPn_NET_CON(i);
> +			smbus_mask |= P3H2x4x_TPn_SMBUS_MODE_EN(i);
> +			gpio_mask |= P3H2x4x_TPn_GPIO_MODE_EN(i);
> +			i2c_mask |= P3H2x4x_TPn_I2C_MODE_EN(i);
> +
> +			if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_I3C)
> +				i3c_val |= P3H2x4x_TPn_NET_CON(i);
> +			else if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_SMBUS)
> +				smbus_val |= P3H2x4x_TPn_SMBUS_MODE_EN(i);
> +			else if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_GPIO)
> +				gpio_val |= P3H2x4x_TPn_GPIO_MODE_EN(i);
> +			else if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_I2C)
> +				i2c_val |= P3H2x4x_TPn_I2C_MODE_EN(i);
> +		}
> +		if (priv->settings.tp[i].pullup_en != P3H2x4x_TP_PULLUP_NOT_DEFINED) {
> +			pullup_mask |= P3H2x4x_TPn_PULLUP_EN(i);
> +
> +			if (priv->settings.tp[i].pullup_en == P3H2x4x_TP_PULLUP_ENABLED)
> +				pullup_val |= P3H2x4x_TPn_PULLUP_EN(i);
> +		}
> +
> +		if (priv->settings.tp[i].ibi_en != P3H2x4x_IBI_NOT_DEFINED) {
> +			ibi_mask |= P3H2x4x_TPn_IBI_EN(i);
> +
> +			if (priv->settings.tp[i].ibi_en == P3H2x4x_IBI_ENABLED)
> +				ibi_val |= P3H2x4x_TPn_IBI_EN(i);
> +		}
> +	}
> +
> +	ret = regmap_update_bits(priv->regmap, P3H2x4x_TP_IO_MODE_CONF, (smbus_mask | i2c_mask),
> +							(smbus_val | i2c_val));
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_update_bits(priv->regmap, P3H2x4x_TP_PULLUP_EN, pullup_mask, pullup_val);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_update_bits(priv->regmap, P3H2x4x_TP_SMBUS_AGNT_IBI_CONFIG, ibi_mask, ibi_val);
> +	if (ret)
> +		return ret;
> +	priv->tp_ibi_mask = ibi_val;
> +
> +	ret = regmap_update_bits(priv->regmap, P3H2x4x_TP_SMBUS_AGNT_EN, smbus_mask, smbus_val);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_update_bits(priv->regmap, P3H2x4x_TP_GPIO_MODE_EN, gpio_mask, gpio_val);
> +	if (ret)
> +		return ret;
> +
> +	/* Request for HUB Network connection in case any TP is configured in I3C mode */
> +	if ((i3c_val) || (i2c_val)) {
> +		ret = regmap_write(priv->regmap, P3H2x4x_CP_MUX_SET,
> +							P3H2x4x_CONTROLLER_PORT_MUX_REQ);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	/* Enable TP here in case TP was configured */
> +	ret = regmap_update_bits(priv->regmap, P3H2x4x_TP_ENABLE,
> +				i3c_mask | smbus_mask | gpio_mask | i2c_mask,
> +				i3c_val | smbus_val | gpio_val | i2c_val);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int p3h2x4x_configure_smbus_local_dev(struct device *dev)
> +{
> +	struct p3h2x4x *priv = dev_get_drvdata(dev);
> +	u8 target_buffer_page, hub_tp;
> +	int ret = 0;
> +
> +	for (hub_tp = 0; hub_tp < P3H2x4x_TP_MAX_COUNT; hub_tp++) {
> +		if ((priv->tp_bus[hub_tp].local_dev_count) &&
> +				(priv->settings.tp[hub_tp].mode == P3H2x4x_TP_MODE_SMBUS)) {
> +			target_buffer_page = P3H2x4x_TARGET_AGENT_LOCAL_DEV + 4 * hub_tp;
> +			ret = regmap_write(priv->regmap, P3H2x4x_PAGE_PTR, target_buffer_page);
> +			if (ret) {
> +				dev_err(dev, "Failed to configure local device settings\n");
> +				break;
> +			}
> +
> +			ret = regmap_bulk_write(priv->regmap,
> +							P3H2x4x_CONTROLLER_AGENT_BUFF,
> +							priv->tp_bus[hub_tp].local_dev_list,
> +							priv->tp_bus[hub_tp].local_dev_count);
> +			if (ret) {
> +				dev_err(dev, "Failed to add local devices\n");
> +				break;
> +			}
> +		}
> +	}
> +	regmap_write(priv->regmap, P3H2x4x_PAGE_PTR, 0x00);
> +	return ret;
> +}
> +
> +static int p3h2x4x_configure_hw(struct device *dev)
> +{
> +	int ret;
> +
> +	ret = p3h2x4x_configure_ldo(dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = p3h2x4x_configure_io_strength(dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = p3h2x4x_configure_pullup(dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = p3h2x4x_configure_smbus_local_dev(dev);
> +	if (ret)
> +		return ret;
> +
> +	return p3h2x4x_configure_tp(dev);
> +}
> +
> +static void p3h2x4x_of_get_tp_dt_conf(struct device *dev,
> +					const struct device_node *node)
> +{
> +	struct p3h2x4x *priv = dev_get_drvdata(dev);
> +	struct device_node *dev_node;
> +	int tp_port;
> +
> +	for_each_available_child_of_node(node, dev_node) {
> +		if (!dev_node->full_name ||
> +			(sscanf(dev_node->full_name, "target-port@%d", &tp_port) != 1))
> +			continue;
> +
> +		if (tp_port < P3H2x4x_TP_MAX_COUNT) {
> +			priv->tp_bus[tp_port].dt_available = true;
> +			priv->tp_bus[tp_port].of_node = dev_node;
> +			priv->tp_bus[tp_port].tp_mask = BIT(tp_port);
> +			priv->tp_bus[tp_port].priv = priv;
> +			priv->tp_bus[tp_port].tp_port = tp_port;
> +		}
> +	}
> +}
> +
> +/* return true when backend node exist */
> +static bool p3h2x4x_is_backend_node_exist(int port, struct p3h2x4x *priv, u32 addr)
> +{
> +	struct smbus_device *backend = NULL;
> +
> +	list_for_each_entry(backend,
> +			&priv->tp_bus[port].tp_device_entry, list) {
> +		if (backend->addr == addr)
> +			return true;
> +	}
> +	return false;
> +}
> +
> +static int p3h2x4x_read_backend_from_p3h2x4x_dts(struct device_node *i3c_node_target,
> +					struct p3h2x4x *priv)
> +{
> +	struct device_node *i3c_node_tp;
> +	const char *compatible;
> +	int tp_port, ret;
> +	u32 addr_dts;
> +	struct smbus_device *backend;
> +
> +	if (sscanf(i3c_node_target->full_name, "target-port@%d", &tp_port) == 0)
> +		return -EINVAL;
> +
> +	if (tp_port > P3H2x4x_TP_MAX_COUNT)
> +		return -ERANGE;
> +
> +	if (tp_port < 0)
> +		return -EINVAL;
> +
> +	INIT_LIST_HEAD(&priv->tp_bus[tp_port].tp_device_entry);
> +
> +	if (priv->settings.tp[tp_port].mode == P3H2x4x_TP_MODE_I3C)
> +		return 0;
> +
> +	for_each_available_child_of_node(i3c_node_target, i3c_node_tp) {
> +
> +		ret = of_property_read_u32(i3c_node_tp, "reg", &addr_dts);
> +		if (ret)
> +			return ret;
> +
> +		if (p3h2x4x_is_backend_node_exist(tp_port, priv, addr_dts))
> +			continue;
> +
> +		ret = of_property_read_string(i3c_node_tp, "compatible", &compatible);
> +		if (ret)
> +			return ret;
> +
> +		backend = kzalloc(sizeof(*backend), GFP_KERNEL);
> +		if (!backend)
> +			return -ENOMEM;
> +
> +		backend->addr = addr_dts;
> +		backend->compatible = compatible;
> +		backend->tp_device_dt_node = i3c_node_tp;
> +		backend->client = NULL;
> +
> +		list_add(&backend->list,
> +			&priv->tp_bus[tp_port].tp_device_entry);
> +	}
> +
> +	return 0;
> +}
> +
> +static void p3h2x4x_parse_dt_tp(struct device *dev,
> +				const struct device_node *i3c_node_hub,
> +				struct p3h2x4x *priv)
> +{
> +	struct device_node *i3c_node_target;
> +	int ret;
> +
> +	for_each_available_child_of_node(i3c_node_hub, i3c_node_target) {
> +		if (!strcmp(i3c_node_target->name, "target-port")) {
> +			ret = p3h2x4x_read_backend_from_p3h2x4x_dts(i3c_node_target, priv);
> +			if (ret)
> +				dev_err(dev, "DTS entry invalid - error %d", ret);
> +		}
> +	}
> +}
> +
> +static int p3h2x4x_get_tp_local_device_dt_setting(struct device *dev,
> +					const struct device_node *node, u32 id)
> +{
> +	u8 i;
> +	u32 local_dev_count, local_dev;
> +	struct p3h2x4x *priv = dev_get_drvdata(dev);

try keep reverse christmas order.

> +
> +	if (!of_get_property(node, "local_dev", &local_dev_count))
> +		return -EINVAL;
> +
> +	local_dev_count = local_dev_count / (sizeof(u32));
> +
> +	if (local_dev_count > P3H2x4x_TP_LOCAL_DEV)
> +		return -ERANGE;
> +
> +	for (i = 0; i < local_dev_count; i++) {
> +		if (of_property_read_u32_index(node, "local_dev", i, &local_dev)) {
> +			priv->tp_bus[id].local_dev_count = 0;
> +			return -EINVAL;
> +		}
> +		priv->tp_bus[id].local_dev_list[i] = (u8)(local_dev*2);
> +	}
> +	priv->tp_bus[id].local_dev_count = local_dev_count;
> +	return 0;
> +}
> +
> +static void p3h2x4x_get_tp_of_get_setting(struct device *dev,
> +					const struct device_node *node,
> +					struct tp_setting tp_setting[])
> +{
> +	struct device_node *tp_node;
> +	u32 id;
> +
> +	for_each_available_child_of_node(node, tp_node) {
> +		if (!tp_node->name || of_node_cmp(tp_node->name, "target-port"))
> +			continue;
> +
> +		if (!tp_node->full_name ||
> +			(sscanf(tp_node->full_name, "target-port@%u", &id) != 1)) {
> +			dev_warn(dev, "Invalid target port node found in DT: %s\n",
> +				tp_node->full_name);
> +			continue;
> +		}
> +
> +		if (id >= P3H2x4x_TP_MAX_COUNT) {
> +			dev_warn(dev, "Invalid target port index found in DT: %i\n", id);
> +			continue;
> +		}
> +		p3h2x4x_of_get_dt_setting(dev, tp_node, "mode", tp_mode_settings,
> +					ARRAY_SIZE(tp_mode_settings),
> +					&tp_setting[id].mode);
> +		p3h2x4x_of_get_dt_setting(dev, tp_node, "pullup_en", tp_pullup_settings,
> +					ARRAY_SIZE(tp_pullup_settings),
> +					&tp_setting[id].pullup_en);
> +		p3h2x4x_of_get_dt_setting(dev, tp_node, "ibi_en", ibi_en_settings,
> +					ARRAY_SIZE(ibi_en_settings),
> +					&tp_setting[id].ibi_en);
> +		tp_setting[id].always_enable =
> +					of_property_read_bool(tp_node, "always-enable");
> +
> +		p3h2x4x_get_tp_local_device_dt_setting(dev, tp_node, id);
> +	}
> +}
> +
> +static void p3h2x4x_of_get_p3h2x4x_conf(struct device *dev,
> +					const struct device_node *node)
> +{
> +	struct p3h2x4x *priv = dev_get_drvdata(dev);
> +
> +	p3h2x4x_of_get_dt_setting(dev, node, "cp0-ldo-en", ldo_en_settings,
> +				ARRAY_SIZE(ldo_en_settings),
> +				&priv->settings.cp0_ldo_en);
> +	p3h2x4x_of_get_dt_setting(dev, node, "cp1-ldo-en", ldo_en_settings,
> +				ARRAY_SIZE(ldo_en_settings),
> +				&priv->settings.cp1_ldo_en);
> +	p3h2x4x_of_get_dt_setting(dev, node, "tp0145-ldo-en", ldo_en_settings,
> +				ARRAY_SIZE(ldo_en_settings),
> +				&priv->settings.tp0145_ldo_en);
> +	p3h2x4x_of_get_dt_setting(dev, node, "tp2367-ldo-en", ldo_en_settings,
> +				ARRAY_SIZE(ldo_en_settings),
> +				&priv->settings.tp2367_ldo_en);
> +	p3h2x4x_of_get_dt_setting(dev, node, "cp0-ldo-volt", ldo_volt_settings,
> +				ARRAY_SIZE(ldo_volt_settings),
> +				&priv->settings.cp0_ldo_volt);
> +	p3h2x4x_of_get_dt_setting(dev, node, "cp1-ldo-volt", ldo_volt_settings,
> +				ARRAY_SIZE(ldo_volt_settings),
> +				&priv->settings.cp1_ldo_volt);
> +	p3h2x4x_of_get_dt_setting(dev, node, "tp0145-ldo-volt", ldo_volt_settings,
> +				ARRAY_SIZE(ldo_volt_settings),
> +				&priv->settings.tp0145_ldo_volt);
> +	p3h2x4x_of_get_dt_setting(dev, node, "tp2367-ldo-volt", ldo_volt_settings,
> +				ARRAY_SIZE(ldo_volt_settings),
> +				&priv->settings.tp2367_ldo_volt);
> +	p3h2x4x_of_get_dt_setting(dev, node, "tp0145-pullup", pullup_settings,
> +				ARRAY_SIZE(pullup_settings),
> +				&priv->settings.tp0145_pullup);
> +	p3h2x4x_of_get_dt_setting(dev, node, "tp2367-pullup", pullup_settings,
> +				ARRAY_SIZE(pullup_settings),
> +				&priv->settings.tp2367_pullup);
> +	p3h2x4x_of_get_dt_setting(dev, node, "cp0-io-strength", io_strength_settings,
> +				ARRAY_SIZE(io_strength_settings),
> +				&priv->settings.cp0_io_strength);
> +	p3h2x4x_of_get_dt_setting(dev, node, "cp1-io-strength", io_strength_settings,
> +				ARRAY_SIZE(io_strength_settings),
> +				&priv->settings.cp1_io_strength);
> +	p3h2x4x_of_get_dt_setting(dev, node, "tp0145-io-strength", io_strength_settings,
> +				ARRAY_SIZE(io_strength_settings),
> +				&priv->settings.tp0145_io_strength);
> +	p3h2x4x_of_get_dt_setting(dev, node, "tp2367-io-strength", io_strength_settings,
> +				ARRAY_SIZE(io_strength_settings),
> +				&priv->settings.tp2367_io_strength);
> +
> +p3h2x4x_get_tp_of_get_setting(dev, node, priv->settings.tp);
> +}
> +
> +static struct device_node *p3h2x4x_get_dt_p3h2x4x_node(struct device_node *node,
> +		struct device *dev)
> +{
> +	struct device_node *hub_node_no_id = NULL;
> +	struct device_node *hub_node;
> +
> +	for_each_available_child_of_node(node, hub_node) {
> +		if (strstr(hub_node->name, "hub"))
> +			return hub_node;
> +	}
> +	return hub_node_no_id;
> +}
> +
> +static void p3h2x4x_of_default_configuration(struct device *dev)
> +{
> +	struct p3h2x4x *priv = dev_get_drvdata(dev);
> +	int tp_count;
> +
> +	priv->settings.cp0_ldo_en = P3H2x4x_DT_LDO_NOT_DEFINED;
> +	priv->settings.cp1_ldo_en = P3H2x4x_DT_LDO_NOT_DEFINED;
> +	priv->settings.tp0145_ldo_en = P3H2x4x_DT_LDO_NOT_DEFINED;
> +	priv->settings.tp2367_ldo_en = P3H2x4x_DT_LDO_NOT_DEFINED;
> +	priv->settings.cp0_ldo_volt = P3H2x4x_DT_LDO_VOLT_NOT_SET;
> +	priv->settings.cp1_ldo_volt = P3H2x4x_DT_LDO_VOLT_NOT_SET;
> +	priv->settings.tp0145_ldo_volt = P3H2x4x_DT_LDO_VOLT_NOT_SET;
> +	priv->settings.tp2367_ldo_volt = P3H2x4x_DT_LDO_VOLT_NOT_SET;
> +	priv->settings.tp0145_pullup = P3H2x4x_TP_PULLUP_NOT_SET;
> +	priv->settings.tp2367_pullup = P3H2x4x_TP_PULLUP_NOT_SET;
> +	priv->settings.cp0_io_strength = P3H2x4x_IO_STRENGTH_NOT_SET;
> +	priv->settings.cp1_io_strength = P3H2x4x_IO_STRENGTH_NOT_SET;
> +	priv->settings.tp0145_io_strength = P3H2x4x_IO_STRENGTH_NOT_SET;
> +	priv->settings.tp2367_io_strength = P3H2x4x_IO_STRENGTH_NOT_SET;
> +
> +	for (tp_count = 0; tp_count < P3H2x4x_TP_MAX_COUNT; ++tp_count) {
> +		priv->settings.tp[tp_count].mode =  P3H2x4x_TP_MODE_NOT_SET;
> +		priv->settings.tp[tp_count].pullup_en = P3H2x4x_TP_PULLUP_NOT_DEFINED;
> +		priv->settings.tp[tp_count].ibi_en = P3H2x4x_IBI_DISABLED;
> +	}
> +}
> +
> +static int p3h2x4x_device_probe_i3c(struct i3c_device *i3cdev)
> +{
> +	struct device_node *node = NULL;
> +	struct regmap *regmap;
> +	struct device *dev = &i3cdev->dev;
> +	struct p3h2x4x *priv;
> +	char hub_id[64];
> +	int ret, i;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->i3cdev = i3cdev;
> +	priv->driving_master = i3c_dev_get_master(i3cdev->desc);
> +	i3cdev_set_drvdata(i3cdev, priv);
> +	sprintf(hub_id, "i3c-hub-device-%d-%llx", i3c_dev_get_master(i3cdev->desc)->bus.id,
> +		i3cdev->desc->info.pid);
> +	p3h2x4x_of_default_configuration(dev);
> +	regmap = devm_regmap_init_i3c(i3cdev, &p3h2x4x_regmap_config);
> +	if (IS_ERR(regmap)) {
> +		ret = PTR_ERR(regmap);
> +		dev_err(dev, "Failed to register I3C HUB regmap\n");
> +		goto error;


return dev_err_probe(...)

> +	}
> +	priv->regmap = regmap;
> +	priv->is_p3h2x4x_in_i3c = true;
> +
> +	mutex_init(&priv->etx_mutex);
> +
> +	/* Register logic for native SMBus ports */
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++)
> +		mutex_init(&priv->tp_bus[i].port_mutex);
> +
> +	node = p3h2x4x_get_dt_p3h2x4x_node(dev->parent->of_node, dev); /* Find hub node in DT */
> +	if (!node) {
> +		dev_warn(dev, "No DT entry - running with hardware defaults.\n");

needn't this warning, may use dev_dbg

> +	} else {
> +		of_node_get(node);

why need get node here?

> +		p3h2x4x_of_get_p3h2x4x_conf(dev, node);
> +		p3h2x4x_of_get_tp_dt_conf(dev, node);
> +		of_node_put(node);
> +		/* Parse DTS to find data on the SMBus target mode */
> +		p3h2x4x_parse_dt_tp(dev, node, priv);
> +	}
> +
> +	/* Unlock access to protected registers */
> +	ret = regmap_write(priv->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE,
> +						P3H2x4x_REGISTERS_UNLOCK_CODE);
> +	if (ret) {
> +		dev_err(dev, "Failed to unlock HUB's protected registers\n");
> +		goto error;
> +	}
> +
> +	ret = p3h2x4x_configure_hw(dev);
> +	if (ret) {
> +		dev_err(dev, "Failed to configure the HUB\n");
> +		goto error;
> +	}
> +
> +	/* Register logic for native SMBus ports */
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++) {
> +		if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_SMBUS)
> +			ret = p3h2x4x_tp_smbus_algo(priv, i);
> +	}
> +
> +	ret = p3h2x4x_tp_add_downstream_device(priv);
> +	if (ret) {
> +		dev_err(dev, "Failed to add backend device\n");
> +		goto error;
> +	}
> +
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; ++i) {
> +		if (priv->tp_bus[i].dt_available) {
> +			if (priv->settings.tp[i].always_enable)
> +				priv->tp_always_enable_mask =
> +						(priv->tp_always_enable_mask |  BIT(i));
> +
> +			if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_I3C)
> +				priv->tp_i3c_bus_mask = (priv->tp_i3c_bus_mask |  BIT(i));
> +		}
> +	}
> +
> +	/* Register logic for native vertual I3C ports */
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++) {
> +		if ((priv->settings.tp[i].mode == P3H2x4x_TP_MODE_I3C) &&
> +			(!priv->settings.tp[i].always_enable))
> +			ret = p3h2x4x_tp_i3c_algo(priv, i);
> +	}
> +
> +	ret = regmap_write(priv->regmap, P3H2x4x_TP_NET_CON_CONF, priv->tp_always_enable_mask);
> +	if (ret) {
> +		dev_err(dev, "Failed to open Target Port(s)\n");
> +		goto error;
> +	}
> +
> +	ret = i3c_master_do_daa(priv->driving_master);
> +	if (ret)
> +		dev_warn(dev, "Failed to run DAA\n");
> +
> +
> +	/* 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(priv->regmap, P3H2x4x_ONCHIP_TD_AND_SMBUS_AGNT_CONF,
> +						P3H2x4x_TARGET_AGENT_DFT_IBI_CONF_MASK,
> +						P3H2x4x_TARGET_AGENT_DFT_IBI_CONF);
> +	if (ret) {
> +		dev_err(dev, "Failed to P3H2x4x_ONCHIP_TD_AND_SMBUS_AGNT_CONF\n");
> +		goto error;
> +	}
> +
> +	ret = i3c_device_request_ibi(i3cdev, &p3h2x4x_ibireq);
> +	if (ret) {
> +		dev_err(dev, "Failed to request IBI\n");
> +		goto error;
> +	}
> +
> +	ret = i3c_device_enable_ibi(i3cdev);
> +	if (ret) {
> +		dev_err(dev, "Failed to Enable IBI\n");
> +		goto error;
> +	}
> +
> +	/* Lock access to protected registers */
> +	ret = regmap_write(priv->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE,
> +						P3H2x4x_REGISTERS_LOCK_CODE);
> +	if (ret) {
> +		dev_err(dev, "Failed to lock HUB's protected registers\n");
> +		goto error;
> +	}
> +
> +	return 0;
> +
> +error:
> +	devm_kfree(dev, priv);
> +	return ret;
> +}
> +
> +static void p3h2x4x_device_remove_i3c(struct i3c_device *i3cdev)
> +{
> +	struct p3h2x4x *priv = i3cdev_get_drvdata(i3cdev);
> +	struct i2c_adapter *tp_adap;
> +	struct i3c_master_controller *tp_controller;
> +	struct smbus_device *backend = NULL;
> +	int i;
> +
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++) {
> +		tp_adap = &priv->tp_bus[i].smbus_port_adapter;
> +		tp_controller = &priv->tp_bus[i].i3c_port_controller;
> +
> +		if (priv->tp_bus[i].is_registered) {
> +			if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_SMBUS) {
> +				list_for_each_entry(backend,
> +						&priv->tp_bus[i].tp_device_entry,
> +						list) {
> +					i2c_unregister_device(backend->client);
> +					kfree(backend);
> +				}
> +				i2c_del_adapter(tp_adap);
> +			} else if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_I3C) {
> +				i3c_master_unregister(tp_controller);
> +			}
> +		}
> +	}
> +
> +	i3c_device_disable_ibi(i3cdev);
> +	i3c_device_free_ibi(i3cdev);
> +
> +	mutex_destroy(&priv->etx_mutex);
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++)
> +		mutex_destroy(&priv->tp_bus[i].port_mutex);
> +
> +	devm_kfree(&i3cdev->dev, priv);
> +}
> +
> +static int p3h2x4x_device_probe_i2c(struct i2c_client *client)
> +{
> +	struct device_node *node = NULL;
> +	struct regmap *regmap;
> +	struct device *dev = &client->dev;
> +	struct p3h2x4x *priv;
> +	int ret, i;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->i2cdev = client;
> +	i2c_set_clientdata(client, priv);
> +
> +	p3h2x4x_of_default_configuration(dev);
> +
> +	regmap = devm_regmap_init_i2c(client, &p3h2x4x_regmap_config);
> +	if (IS_ERR(regmap)) {
> +		ret = PTR_ERR(regmap);
> +		dev_err(dev, "Failed to register I3C HUB regmap\n");
> +		goto error;
> +	}
> +	priv->regmap = regmap;
> +	priv->is_p3h2x4x_in_i3c = false;
> +
> +	mutex_init(&priv->etx_mutex);
> +
> +	/* Register logic for native SMBus ports */
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++)
> +		mutex_init(&priv->tp_bus[i].port_mutex);
> +
> +	node = p3h2x4x_get_dt_p3h2x4x_node(dev->parent->of_node, dev); /* Find hub node in DT */
> +	if (!node) {
> +		dev_warn(dev, "No DT entry - running with hardware defaults.\n");
> +	} else {
> +		of_node_get(node);
> +		p3h2x4x_of_get_p3h2x4x_conf(dev, node);
> +		p3h2x4x_of_get_tp_dt_conf(dev, node);
> +		of_node_put(node);
> +		/* Parse DTS to find data on the SMBus target mode */
> +		p3h2x4x_parse_dt_tp(dev, node, priv);
> +	}
> +
> +	/* Unlock access to protected registers */
> +	ret = regmap_write(priv->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE,
> +						P3H2x4x_REGISTERS_UNLOCK_CODE);
> +	if (ret) {
> +		dev_err(dev, "Failed to unlock HUB's protected registers\n");
> +		goto error;
> +	}
> +
> +	ret = p3h2x4x_configure_hw(dev);
> +	if (ret) {
> +		dev_err(dev, "Failed to configure the HUB\n");
> +		goto error;
> +	}
> +
> +	/* Register logic for native SMBus ports */
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++) {
> +		if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_SMBUS)
> +			ret = p3h2x4x_tp_smbus_algo(priv, i);
> +	}
> +
> +	ret = p3h2x4x_tp_add_downstream_device(priv);
> +	if (ret) {
> +		dev_err(dev, "Failed to add backend device\n");
> +		goto error;
> +	}
> +
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; ++i) {
> +		if (priv->tp_bus[i].dt_available) {
> +			if (priv->settings.tp[i].always_enable)
> +				priv->tp_always_enable_mask =
> +							(priv->tp_always_enable_mask |  BIT(i));
> +		}
> +	}
> +
> +	ret = regmap_write(priv->regmap, P3H2x4x_TP_NET_CON_CONF, priv->tp_always_enable_mask);
> +	if (ret) {
> +		dev_err(dev, "Failed to open Target Port(s)\n");
> +		goto error;
> +	}
> +
> +	/* Lock access to protected registers */
> +	ret = regmap_write(priv->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE,
> +						P3H2x4x_REGISTERS_LOCK_CODE);
> +	if (ret) {
> +		dev_err(dev, "Failed to lock HUB's protected registers\n");
> +		goto error;
> +	}
> +	return 0;
> +
> +error:
> +	devm_kfree(dev, priv);
> +	return ret;
> +}
> +
> +static void p3h2x4x_device_remove_i2c(struct i2c_client *client)
> +{
> +	struct p3h2x4x *priv = i2c_get_clientdata(client);
> +	struct i2c_adapter *tp_adap;
> +	struct smbus_device *backend = NULL;
> +	int i;
> +
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++) {
> +		tp_adap = &priv->tp_bus[i].smbus_port_adapter;
> +		if (priv->tp_bus[i].is_registered) {
> +			list_for_each_entry(backend, &priv->tp_bus[i].tp_device_entry, list) {
> +				i2c_unregister_device(backend->client);
> +				kfree(backend);
> +			}
> +			i2c_del_adapter(tp_adap);
> +		}
> +	}
> +
> +	mutex_destroy(&priv->etx_mutex);
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++)
> +		mutex_destroy(&priv->tp_bus[i].port_mutex);
> +
> +	devm_kfree(&client->dev, priv);
> +}
> +
> +static struct i3c_driver p3h2x4x_i3c = {
> +	.driver = {
> +		.name = "p3h2x4x_i3c_drv",
> +	},
> +	.id_table = p3h2x4x_i3c_ids,
> +	.probe = p3h2x4x_device_probe_i3c,
> +	.remove = p3h2x4x_device_remove_i3c,
> +};
> +
> +static struct i2c_driver p3h2x4x_i2c = {
> +	.driver = {
> +		.name = "p3h2x4x_i2c_drv",
> +		.of_match_table = p3h2x4x_i2c_of_match,
> +	},
> +	.probe =  p3h2x4x_device_probe_i2c,
> +	.remove = p3h2x4x_device_remove_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 driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_i3c.c b/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_i3c.c
> new file mode 100644
> index 000000000000..c7827c4b6f57
> --- /dev/null
> +++ b/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_i3c.c
> @@ -0,0 +1,353 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2024-2025 NXP
> + * This P3H2x4x driver file contain functions for I3C virtual Bus creation, connect/disconnect
> + * hub network and read/write.
> + */
> +#include "p3h2x4x_i3c_hub.h"
> +
> +static void p3h2x4x_en_p3h2x4x_ntwk_tp(struct tp_bus *bus)
> +{
> +	struct p3h2x4x *priv = bus->priv;
> +	struct device *dev = i3cdev_to_dev(priv->i3cdev);
> +	int ret;
> +
> +	if (priv->settings.tp[bus->tp_port].always_enable)
> +		return;
> +
> +	ret = regmap_write(priv->regmap, P3H2x4x_TP_NET_CON_CONF,
> +				(bus->tp_mask | priv->tp_always_enable_mask));
> +	if (ret)
> +		dev_warn(dev, "Failed to open Target Port(s)\n");
> +}
> +
> +static void p3h2x4x_dis_p3h2x4x_ntwk_tp(struct tp_bus *bus)
> +{
> +	struct p3h2x4x *priv = bus->priv;
> +	struct device *dev = i3cdev_to_dev(priv->i3cdev);
> +	int ret;
> +
> +	if (priv->settings.tp[bus->tp_port].always_enable)
> +		return;
> +
> +	ret = regmap_write(priv->regmap, P3H2x4x_TP_NET_CON_CONF, priv->tp_always_enable_mask);
> +	if (ret)
> +		dev_warn(dev, "Failed to close Target Port(s)\n");
> +}
> +
> +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, i3c_port_controller);
> +}
> +
> +static struct tp_bus *p3h2x4x_bus_from_i2c_desc(struct i2c_dev_desc *desc)
> +{
> +	struct i3c_master_controller *controller = i2c_dev_get_master(desc);
> +
> +	return container_of(controller, struct tp_bus, i3c_port_controller);
> +}
> +
> +static struct i3c_master_controller
> +*get_parent_controller(struct i3c_master_controller *controller)
> +{
> +	struct tp_bus *bus = container_of(controller, struct tp_bus, i3c_port_controller);
> +
> +	return bus->priv->driving_master;
> +}
> +
> +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, i3c_port_controller);
> +
> +	return bus->priv->driving_master;
> +}
> +
> +static struct i3c_master_controller
> +*get_parent_controller_from_i2c_desc(struct i2c_dev_desc *desc)
> +{
> +	struct i3c_master_controller *controller = desc->common.master;
> +	struct tp_bus *bus = container_of(controller, struct tp_bus, i3c_port_controller);
> +
> +	return bus->priv->driving_master;
> +}
> +
> +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, i3c_port_controller);
> +
> +	controller->this = bus->priv->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)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
> +	struct i3c_master_controller *orig_parent;
> +	int ret;
> +
> +	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
> +	ret = parent->ops->attach_i3c_dev(dev);
> +	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
> +	return ret;
> +}
> +
> +static int p3h2x4x_reattach_i3c_dev(struct i3c_dev_desc *dev, u8 old_dyn_addr)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
> +	struct i3c_master_controller *orig_parent;
> +	int ret;
> +
> +	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
> +	ret = parent->ops->reattach_i3c_dev(dev, old_dyn_addr);
> +	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
> +	return ret;
> +}
> +
> +static void p3h2x4x_detach_i3c_dev(struct i3c_dev_desc *dev)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
> +	struct i3c_master_controller *orig_parent;
> +
> +	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
> +	parent->ops->detach_i3c_dev(dev);
> +	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
> +}
> +
> +static int p3h2x4x_do_daa(struct i3c_master_controller *controller)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller(controller);
> +
> +	struct tp_bus *bus = container_of(controller, struct tp_bus, i3c_port_controller);
> +	int ret;
> +
> +	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
> +	down_write(&parent->bus.lock);
> +	ret = parent->ops->do_daa(parent);
> +	up_write(&parent->bus.lock);
> +	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 parent->ops->supports_ccc_cmd(parent, cmd);
> +}
> +
> +static int p3h2x4x_send_ccc_cmd(struct i3c_master_controller *controller,
> +				struct i3c_ccc_cmd *cmd)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller(controller);
> +	struct tp_bus *bus = container_of(controller, struct tp_bus, i3c_port_controller);
> +	int ret;
> +
> +	if (cmd->id == I3C_CCC_RSTDAA(true))
> +		return 0;
> +
> +	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
> +	ret = parent->ops->send_ccc_cmd(parent, cmd);
> +	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
> +
> +	return ret;
> +}
> +
> +static int p3h2x4x_priv_xfers(struct i3c_dev_desc *dev,
> +				struct i3c_priv_xfer *xfers, int nxfers)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
> +	struct i3c_master_controller *orig_parent;
> +	struct tp_bus *bus = p3h2x4x_bus_from_i3c_desc(dev);
> +	int res;
> +
> +	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
> +	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
> +	res = parent->ops->priv_xfers(dev, xfers, nxfers);
> +	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
> +	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
> +
> +	return res;
> +}
> +
> +static int p3h2x4x_attach_i2c_dev(struct i2c_dev_desc *dev)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller_from_i2c_desc(dev);
> +	struct i3c_master_controller *orig_parent;
> +	int ret;
> +
> +	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
> +	ret = parent->ops->attach_i2c_dev(dev);
> +	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
> +	return ret;
> +}
> +
> +static void p3h2x4x_detach_i2c_dev(struct i2c_dev_desc *dev)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller_from_i2c_desc(dev);
> +	struct i3c_master_controller *orig_parent;
> +
> +	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
> +	parent->ops->detach_i2c_dev(dev);
> +	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
> +}
> +
> +static int p3h2x4x_i2c_xfers(struct i2c_dev_desc *dev,
> +				const struct i2c_msg *xfers, int nxfers)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller_from_i2c_desc(dev);
> +	struct tp_bus *bus = p3h2x4x_bus_from_i2c_desc(dev);
> +	struct i3c_master_controller *orig_parent;
> +	int ret;
> +
> +	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
> +	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
> +	ret = parent->ops->i2c_xfers(dev, xfers, nxfers);
> +	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
> +	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
> +	return ret;
> +}
> +
> +static int p3h2x4x_request_ibi(struct i3c_dev_desc *dev,
> +				const struct i3c_ibi_setup *req)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
> +	struct tp_bus *bus = p3h2x4x_bus_from_i3c_desc(dev);
> +	struct i3c_master_controller *orig_parent;
> +	int ret;
> +
> +	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
> +	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
> +	ret = parent->ops->request_ibi(dev, req);
> +	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
> +	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
> +	return ret;
> +}
> +
> +static void p3h2x4x_free_ibi(struct i3c_dev_desc *dev)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
> +	struct tp_bus *bus = p3h2x4x_bus_from_i3c_desc(dev);
> +	struct i3c_master_controller *orig_parent;
> +
> +	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
> +	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
> +	parent->ops->free_ibi(dev);
> +	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
> +	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
> +}
> +
> +static int p3h2x4x_enable_ibi(struct i3c_dev_desc *dev)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
> +	struct tp_bus *bus = p3h2x4x_bus_from_i3c_desc(dev);
> +	struct i3c_master_controller *orig_parent;
> +	int ret;
> +
> +	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
> +	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
> +	ret = parent->ops->enable_ibi(dev);
> +	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
> +	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
> +	return ret;
> +}
> +
> +static int p3h2x4x_disable_ibi(struct i3c_dev_desc *dev)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
> +	struct tp_bus *bus = p3h2x4x_bus_from_i3c_desc(dev);
> +	struct i3c_master_controller *orig_parent;
> +	int ret;
> +
> +	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
> +	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
> +	ret = parent->ops->disable_ibi(dev);
> +	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
> +	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
> +	return ret;
> +}
> +
> +static void p3h2x4x_recycle_ibi_slot(struct i3c_dev_desc *dev,
> +					struct i3c_ibi_slot *slot)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
> +	struct i3c_master_controller *orig_parent;
> +
> +	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
> +	parent->ops->recycle_ibi_slot(dev, slot);
> +	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
> +}
> +
> +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,
> +	.priv_xfers = p3h2x4x_priv_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.
> + * @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 *priv, int tp)
> +{
> +	struct device *dev = i3cdev_to_dev(priv->i3cdev);
> +	int ret;
> +
> +	priv->tp_bus[tp].tp_mask =  BIT(tp);
> +	dev->of_node = priv->tp_bus[tp].of_node;
> +
> +	ret = i3c_master_register(&priv->tp_bus[tp].i3c_port_controller,
> +				dev, &p3h2x4x_i3c_ops, false);
> +	if (ret) {
> +		dev_warn(dev, "Failed to register i3c controller for tp %d\n", tp);
> +		return -EINVAL;
> +	}
> +
> +	priv->tp_bus[tp].is_registered = true;
> +	return 0;
> +}
> diff --git a/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c b/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c
> new file mode 100644
> index 000000000000..8cdbaaf49378
> --- /dev/null
> +++ b/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c
> @@ -0,0 +1,719 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2024-2025 NXP
> + * This P3H2x4x driver file contain functions for SMBus/I2C virtual Bus creation and read/write.
> + */
> +#include "p3h2x4x_i3c_hub.h"
> +
> +static struct device *i2cdev_to_dev(struct i2c_client *i2cdev)
> +{
> +	return &i2cdev->dev;
> +}
> +
> +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 device *dev = i3cdev_to_dev(i3cdev);
> +	struct p3h2x4x *priv = dev_get_drvdata(dev);
> +	struct smbus_device *backend = NULL;
> +
> +	u8 target_buffer_page, flag_clear, rx_data, temp, i;
> +	u8 slave_rx_buffer[P3H2x4x_SMBUS_TARGET_PAYLOAD_SIZE] = { 0 };
> +	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(priv->regmap, P3H2x4x_PAGE_PTR, target_buffer_page);
> +	if (ret)
> +		goto ibi_err;
> +
> +	/* read buffer length */
> +	ret = regmap_read(priv->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(dev, "Received message too big for p3h2x4x buffer\n");
> +		return;
> +	}
> +
> +	/* read slave  address */
> +	ret = regmap_read(priv->regmap, P3H2x4x_TARGET_BUFF_ADDRESS, &slave_address);
> +	if (ret)
> +		goto ibi_err;
> +
> +	/* read data */
> +	if (packet_len) {
> +		ret = regmap_bulk_read(priv->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 */
> +	list_for_each_entry(backend, &priv->tp_bus[tp].tp_device_entry, list) {
> +		if ((slave_address >> 1 == backend->addr) && (priv->is_slave_reg)) {
> +			i2c_slave_event(backend->client, I2C_SLAVE_WRITE_REQUESTED,
> +				(u8 *)&slave_address);
> +
> +			for (i = 0; i < packet_len; i++) {
> +				rx_data = slave_rx_buffer[i];
> +				i2c_slave_event(backend->client, I2C_SLAVE_WRITE_RECEIVED,
> +						&rx_data);
> +			}
> +			i2c_slave_event(backend->client, I2C_SLAVE_STOP, &temp);
> +			break;
> +		}
> +	}
> +
> +ibi_err:
> +	regmap_write(priv->regmap, P3H2x4x_PAGE_PTR, 0x00);
> +	regmap_write(priv->regmap, P3H2x4x_TP0_SMBUS_AGNT_STS + tp, flag_clear);
> +}
> +
> +/**
> + * 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 device *dev = i3cdev_to_dev(i3cdev);
> +	struct p3h2x4x *priv = dev_get_drvdata(dev);
> +
> +	u32 target_port_status, payload_byte_one, payload_byte_two;
> +	u8 i;
> +	int  ret;
> +
> +	payload_byte_one = (*(int *)payload->data);
> +	payload_byte_two = (*(int *)(payload->data + 4));
> +
> +	if (!(payload_byte_one & P3H2x4x_SMBUS_AGENT_EVENT_FLAG_STATUS))
> +		goto err;
> +
> +	mutex_lock(&priv->etx_mutex);
> +	ret = regmap_write(priv->regmap, P3H2x4x_TP_SMBUS_AGNT_IBI_CONFIG, P3H2x4x_ALL_TP_IBI_DIS);
> +	if (ret) {
> +		dev_err(dev, "Failed to Disable IBI\n");
> +		goto err;
> +	}
> +
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; ++i) {
> +		if ((priv->tp_bus[i].is_registered) && ((payload_byte_two >> i) & 0x01)) {
> +			ret = regmap_read(priv->regmap, P3H2x4x_TP0_SMBUS_AGNT_STS + i,
> +					&target_port_status);
> +			if (ret) {
> +				dev_err(dev, "target port read status failed %d\n", ret);
> +				goto err;
> +			}
> +
> +			/* 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(dev, "Overflow, reading buffer zero and one\n");
> +				break;
> +			default:
> +				regmap_write(priv->regmap, P3H2x4x_TP0_SMBUS_AGNT_STS + i,
> +					BUF_RECEIVED_FLAG_TF_MASK);
> +					break;
> +			}
> +		}
> +	}
> +err:
> +	regmap_write(priv->regmap, P3H2x4x_TP_SMBUS_AGNT_IBI_CONFIG, priv->tp_ibi_mask);
> +	mutex_unlock(&priv->etx_mutex);
> +}
> +
> +static int p3h2x4x_read_p3h2x4x_id(struct device *dev)
> +{
> +	struct p3h2x4x *priv = dev_get_drvdata(dev);
> +	u32 reg_val;
> +	int ret;
> +
> +	ret = regmap_read(priv->regmap, P3H2x4x_LDO_AND_CPSEL_STS, &reg_val);
> +	if (ret) {
> +		dev_err(dev, "Failed to read status register\n");
> +		return ret;
> +	}
> +	if (P3H2x4x_CP_SEL_PIN_INPUT_CODE_GET(reg_val) == 3)
> +		return 1;
> +	else
> +		return 0;
> +}
> +
> +
> +static void unlock_port_ext_mutex(struct mutex *ext, struct mutex *port)
> +{
> +	mutex_unlock(ext);
> +	mutex_unlock(port);
> +}
> +
> +static void lock_port_ext_mutex(struct mutex *ext, struct mutex *port)
> +{
> +	mutex_lock(ext);
> +	mutex_lock(port);
> +}
> +
> +static int p3h2x4x_read_smbus_transaction_status(struct p3h2x4x *priv,
> +					u8 target_port_status, u8 *status, u8 data_length)
> +{
> +	unsigned int status_read;
> +	int ret;
> +
> +	mutex_unlock(&priv->etx_mutex);
> +	fsleep(P3H2x4x_SMBUS_400kHz_TRANSFER_TIMEOUT(data_length));
> +	mutex_lock(&priv->etx_mutex);
> +
> +	ret = regmap_read(priv->regmap, target_port_status, &status_read);
> +	if (ret)
> +		return ret;
> +
> +	*status = (u8)status_read;
> +
> +	if ((*status & P3H2x4x_TP_BUFFER_STATUS_MASK) == P3H2x4x_XFER_SUCCESS)
> +		return 0;
> +
> +	dev_err(&priv->i3cdev->dev, "Status read timeout reached\n");
> +	return 0;
> +}
> +
> +/*
> + * 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 *priv,
> +				struct i2c_msg *xfers,
> +				u8 target_port,
> +				u8 nxfers_i, u8 rw, u8 *return_status)
> +{
> +	u8 transaction_type = P3H2x4x_SMBUS_400kHz;
> +	u8 controller_buffer_page = P3H2x4x_CONTROLLER_BUFFER_PAGE + 4 * target_port;
> +	int write_length = xfers[nxfers_i].len;
> +	int read_length = xfers[nxfers_i].len;
> +	u8 target_port_status = P3H2x4x_TP0_SMBUS_AGNT_STS + target_port;
> +	u8 addr = xfers[nxfers_i].addr;
> +	u8 target_port_code = BIT(target_port);
> +	u8 rw_address = 2 * addr;
> +	u8 desc[P3H2x4x_SMBUS_DESCRIPTOR_SIZE] = { 0 };
> +	u8 status;
> +	int ret;
> +
> +	if (rw) {
> +		rw_address |= BIT(0);
> +		write_length = 0;
> +	} else {
> +		read_length = 0;
> +	}
> +
> +	desc[0] = rw_address;
> +	desc[1] = transaction_type;
> +	desc[2] = write_length;
> +	desc[3] = read_length;
> +
> +	ret = regmap_write(priv->regmap, target_port_status,
> +			P3H2x4x_TP_BUFFER_STATUS_MASK);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_write(priv->regmap, P3H2x4x_PAGE_PTR, controller_buffer_page);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_bulk_write(priv->regmap, P3H2x4x_CONTROLLER_AGENT_BUFF,
> +				desc, P3H2x4x_SMBUS_DESCRIPTOR_SIZE);
> +	if (ret)
> +		return ret;
> +
> +	if (!rw) {
> +		ret = regmap_bulk_write(priv->regmap,
> +					P3H2x4x_CONTROLLER_AGENT_BUFF_DATA,
> +					xfers[nxfers_i].buf, xfers[nxfers_i].len);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = regmap_write(priv->regmap, P3H2x4x_TP_SMBUS_AGNT_TRANS_START,
> +			target_port_code);
> +	if (ret)
> +		return ret;
> +
> +	ret = p3h2x4x_read_smbus_transaction_status(priv, target_port_status,
> +				&status, (write_length + read_length));
> +	if (ret)
> +		return ret;
> +
> +	*return_status = status;
> +
> +	if (rw) {
> +		ret = regmap_bulk_read(priv->regmap,
> +				P3H2x4x_CONTROLLER_AGENT_BUFF_DATA,
> +				xfers[nxfers_i].buf, xfers[nxfers_i].len);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = regmap_write(priv->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;
> +	int ret;
> +	u8 return_status;
> +	u8 msg_count;
> +	u8 rw;
> +
> +	struct device *dev;
> +	struct tp_bus *bus =
> +		container_of(adap, struct tp_bus, smbus_port_adapter);
> +	struct p3h2x4x *priv = bus->priv;
> +
> +	if (priv->is_p3h2x4x_in_i3c)
> +		dev = i3cdev_to_dev(priv->i3cdev);
> +	else
> +		dev = i2cdev_to_dev(priv->i2cdev);
> +
> +	lock_port_ext_mutex(&priv->etx_mutex, &bus->port_mutex);
> +
> +	if (priv->is_p3h2x4x_in_i3c) {
> +		ret = i3c_device_disable_ibi(priv->i3cdev);
> +		if (ret) {
> +			dev_err(dev, "Failed to Disable IBI\n");
> +			unlock_port_ext_mutex(&priv->etx_mutex, &bus->port_mutex);
> +			return ret;
> +		}
> +	}
> +
> +	for (msg_count = 0; msg_count < num; msg_count++) {
> +		if (msgs[msg_count].len > P3H2x4x_SMBUS_PAYLOAD_SIZE) {
> +			dev_err(&adap->dev,
> +				"Message nr. %d not sent - length over %d bytes.\n",
> +				msg_count, P3H2x4x_SMBUS_PAYLOAD_SIZE);
> +			continue;
> +		}
> +		rw = msgs[msg_count].flags % 2;
> +
> +		ret = p3h2x4x_tp_i2c_xfer_msg(priv,
> +					msgs,
> +					bus->tp_port,
> +					msg_count, rw, &return_status);
> +
> +		if (ret)
> +			goto error;
> +
> +		if (return_status == P3H2x4x_XFER_SUCCESS)
> +			ret_sum++;
> +	}
> +
> +error:
> +	if (priv->is_p3h2x4x_in_i3c) {
> +		ret =  i3c_device_enable_ibi(priv->i3cdev);
> +		if (ret) {
> +			dev_err(dev, "Failed to Enable IBI\n");
> +			unlock_port_ext_mutex(&priv->etx_mutex, &bus->port_mutex);
> +			return ret;
> +		}
> +	}
> +	unlock_port_ext_mutex(&priv->etx_mutex, &bus->port_mutex);
> +	return ret_sum;
> +}
> +
> +static int p3h2x4x_tp_smbus_xfer_msg(struct p3h2x4x *priv,
> +					u8 target_port,
> +					u8 addr,
> +					u8 rw,
> +					u8 cmd,
> +					int sz,
> +					union i2c_smbus_data *data,
> +					u8 *return_status)
> +{
> +	u8 transaction_type = P3H2x4x_SMBUS_400kHz;
> +	u8 controller_buffer_page = P3H2x4x_CONTROLLER_BUFFER_PAGE + 4 * target_port;
> +	u8 target_port_status = P3H2x4x_TP0_SMBUS_AGNT_STS + target_port;
> +	u8 target_port_code = BIT(target_port);
> +	u8 rw_address = 2 * addr;
> +	u8 desc[P3H2x4x_SMBUS_DESCRIPTOR_SIZE] = { 0 };
> +	u8 status;
> +	int ret, i;
> +	u8 read_length, write_length;
> +	u8 buf[I2C_SMBUS_BLOCK_MAX + 2] = {0};
> +	struct device *dev;
> +
> +	if (priv->is_p3h2x4x_in_i3c)
> +		dev = i3cdev_to_dev(priv->i3cdev);
> +	else
> +		dev = i2cdev_to_dev(priv->i2cdev);
> +
> +	/* Map the size to what the chip understands */
> +	switch (sz) {
> +	case I2C_SMBUS_QUICK:
> +	case I2C_SMBUS_BYTE:
> +		if (rw)	{
> +			buf[0] = data->byte;
> +			read_length = ONE_BYTE_SIZE;
> +			write_length = 0;
> +			rw_address |= BIT(0);
> +		} else {
> +			buf[0] = cmd;
> +			write_length = ONE_BYTE_SIZE;
> +			read_length = 0;
> +		}
> +		break;
> +	case I2C_SMBUS_BYTE_DATA:
> +		if (rw) {   /* read write */
> +			buf[0] = cmd;
> +			write_length = ONE_BYTE_SIZE;
> +			read_length = ONE_BYTE_SIZE;
> +			transaction_type |= BIT(0);
> +		} else {  /* only write */
> +			buf[0] = cmd;
> +			buf[1] = data->byte;
> +			write_length = ONE_BYTE_SIZE + 1;
> +			read_length = 0;
> +		}
> +		break;
> +	case I2C_SMBUS_WORD_DATA:
> +		if (rw) {         /* read write */
> +			buf[0] = cmd;
> +			write_length = ONE_BYTE_SIZE;
> +			read_length = 2;
> +			transaction_type |= BIT(0);
> +		} else {  /* only write */
> +			buf[0] = cmd;
> +			buf[1] = data->word & 0xff;
> +			buf[2] = (data->word & 0xff00) >> 8;
> +			write_length = ONE_BYTE_SIZE + 2;
> +			read_length = 0;
> +		}
> +		break;
> +	case I2C_SMBUS_BLOCK_DATA:
> +		if (rw) {         /* read write */
> +			buf[0] = cmd;
> +			write_length = ONE_BYTE_SIZE;
> +			read_length = data->block[0] + 1;
> +			transaction_type |= BIT(0);
> +		} else {  /* only write */
> +			buf[0] = cmd;
> +			for (i = 0 ; i <= data->block[0]; i++)
> +				buf[i+1] = data->block[i];
> +
> +			write_length = data->block[0] + 2;
> +			read_length = 0;
> +		}
> +		break;
> +	case I2C_SMBUS_I2C_BLOCK_DATA:
> +		if (rw) {         /* read write */
> +			buf[0] = cmd;
> +			write_length = ONE_BYTE_SIZE;
> +			read_length = data->block[0];
> +			transaction_type |= BIT(0);
> +		} else {  /* only write */
> +			buf[0] = cmd;
> +			for (i = 0 ; i < data->block[0]; i++)
> +				buf[i+1] = data->block[i+1];
> +
> +			write_length = data->block[0] + 1;
> +			read_length = 0;
> +		}
> +		break;
> +	default:
> +		dev_warn(dev, "Unsupported transaction %d\n", sz);
> +		break;
> +	}
> +
> +	desc[0] = rw_address;
> +	desc[1] = transaction_type;
> +	desc[2] = write_length;
> +	desc[3] = read_length;
> +
> +	ret = regmap_write(priv->regmap, target_port_status,
> +			P3H2x4x_TP_BUFFER_STATUS_MASK);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_write(priv->regmap, P3H2x4x_PAGE_PTR, controller_buffer_page);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_bulk_write(priv->regmap, P3H2x4x_CONTROLLER_AGENT_BUFF,
> +				desc, P3H2x4x_SMBUS_DESCRIPTOR_SIZE);
> +	if (ret)
> +		return ret;
> +
> +	if (write_length) {
> +		ret = regmap_bulk_write(priv->regmap,
> +				P3H2x4x_CONTROLLER_AGENT_BUFF_DATA,
> +				buf, write_length);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = regmap_write(priv->regmap, P3H2x4x_TP_SMBUS_AGNT_TRANS_START,
> +			target_port_code);
> +	if (ret)
> +		return ret;
> +
> +	ret = p3h2x4x_read_smbus_transaction_status(priv, target_port_status, &status,
> +			(write_length + read_length));
> +	if (ret)
> +		return ret;
> +
> +	*return_status = status;
> +
> +	if (rw) {
> +		switch (sz) {
> +		case I2C_SMBUS_QUICK:
> +		case I2C_SMBUS_BYTE:
> +		case I2C_SMBUS_BYTE_DATA:
> +			{
> +				ret = regmap_bulk_read(priv->regmap,
> +					P3H2x4x_CONTROLLER_AGENT_BUFF_DATA + write_length,
> +					&data->byte, read_length);
> +					break;
> +			}
> +		case I2C_SMBUS_WORD_DATA:
> +			{
> +				ret = regmap_bulk_read(priv->regmap,
> +					P3H2x4x_CONTROLLER_AGENT_BUFF_DATA + write_length,
> +					(u8 *)&data->word, read_length);
> +				break;
> +			}
> +		case I2C_SMBUS_BLOCK_DATA:
> +			{
> +				ret = regmap_bulk_read(priv->regmap,
> +					P3H2x4x_CONTROLLER_AGENT_BUFF_DATA + write_length,
> +					data->block, read_length);
> +				break;
> +			}
> +		case I2C_SMBUS_I2C_BLOCK_DATA:
> +			{
> +				ret = regmap_bulk_read(priv->regmap,
> +					P3H2x4x_CONTROLLER_AGENT_BUFF_DATA + write_length,
> +					data->block + 1, read_length);
> +				break;
> +			}
> +		default:
> +				dev_warn(dev, "Unsupported transaction %d\n", sz);
> +				break;
> +		}
> +
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = regmap_write(priv->regmap, P3H2x4x_PAGE_PTR, 0x00);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static s32 p3h2x4x_tp_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags,
> +		char read_write, u8 command, int size, union i2c_smbus_data *data)
> +{
> +	struct tp_bus *bus =
> +		container_of(adap, struct tp_bus, smbus_port_adapter);
> +
> +	struct p3h2x4x *priv = bus->priv;
> +	struct device *dev;
> +
> +	if (priv->is_p3h2x4x_in_i3c)
> +		dev = i3cdev_to_dev(priv->i3cdev);
> +	else
> +		dev = i2cdev_to_dev(priv->i2cdev);
> +
> +	int ret, ret_status;
> +	u8 return_status;
> +
> +	lock_port_ext_mutex(&priv->etx_mutex, &bus->port_mutex);
> +
> +	if (priv->is_p3h2x4x_in_i3c) {
> +		ret = i3c_device_disable_ibi(priv->i3cdev);
> +		if (ret) {
> +			dev_err(dev, "Failed to Disable IBI\n");
> +			unlock_port_ext_mutex(&priv->etx_mutex, &bus->port_mutex);
> +			return ret;
> +		}
> +	}
> +
> +	ret_status = p3h2x4x_tp_smbus_xfer_msg(priv,
> +				(u8)bus->tp_port,
> +				(u8)addr,
> +				(u8)read_write,
> +				(u8)command,
> +				size,
> +				data,
> +				&return_status);
> +
> +	if (priv->is_p3h2x4x_in_i3c) {
> +		ret = i3c_device_enable_ibi(priv->i3cdev);
> +		if (ret) {
> +			dev_err(dev, "Failed to Enable IBI\n");
> +			unlock_port_ext_mutex(&priv->etx_mutex, &bus->port_mutex);
> +			return ret;
> +		}
> +	}
> +	unlock_port_ext_mutex(&priv->etx_mutex, &bus->port_mutex);
> +
> +	if (ret_status)
> +		return ret_status;
> +
> +	if (return_status == P3H2x4x_XFER_SUCCESS)
> +		return 0;
> +	else
> +		return -EINVAL;
> +}
> +
> +static u32 p3h2x4x_tp_smbus_funcs(struct i2c_adapter *adapter)
> +{
> +	return I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
> +			I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_WORD_DATA |
> +			I2C_FUNC_SMBUS_I2C_BLOCK  | I2C_FUNC_SMBUS_BLOCK_DATA |
> +			I2C_FUNC_I2C;
> +}
> +
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> +static int p3h2x4x_tp_i2c_reg_slave(struct i2c_client *slave)
> +{
> +	struct tp_bus *bus =
> +		container_of(slave->adapter, struct tp_bus, smbus_port_adapter);
> +	struct p3h2x4x *priv = bus->priv;
> +
> +	priv->is_slave_reg = true;
> +
> +	return 0;
> +}
> +static int p3h2x4x_tp_i2c_unreg_slave(struct i2c_client *slave)
> +{
> +	struct tp_bus *bus =
> +		container_of(slave->adapter, struct tp_bus, smbus_port_adapter);
> +	struct p3h2x4x *priv = bus->priv;
> +
> +	priv->is_slave_reg = false;
> +
> +	return 0;
> +}
> +#endif
> +
> +/*
> + * I2C algorithm Structure
> + */
> +static struct i2c_algorithm p3h2x4x_tp_i2c_algorithm = {
> +	.master_xfer    = p3h2x4x_tp_i2c_xfer,
> +	.smbus_xfer = p3h2x4x_tp_smbus_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_add_downstream_device - prove downstream devices for target port who
> + * configured as SMBus.
> + * @priv: p3h2x4x device structure.
> + * Return: 0 in case of success, a negative EINVAL code if the error.
> + */
> +int p3h2x4x_tp_add_downstream_device(struct p3h2x4x *priv)
> +{
> +	struct i2c_board_info smbus_tp_device_info = {0};
> +	struct smbus_device *backend = NULL;
> +	struct tp_bus *bus;
> +	int i;
> +	struct device *dev;
> +
> +	if (priv->is_p3h2x4x_in_i3c)
> +		dev = i3cdev_to_dev(priv->i3cdev);
> +	else
> +		dev = i2cdev_to_dev(priv->i2cdev);
> +
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++) {
> +		bus = &priv->tp_bus[i];
> +		if (!bus->is_registered)
> +			continue;
> +
> +		list_for_each_entry(backend,
> +					&bus->tp_device_entry, list) {
> +
> +			smbus_tp_device_info.addr = backend->addr;
> +			smbus_tp_device_info.flags = I2C_CLIENT_SLAVE;
> +			smbus_tp_device_info.of_node = backend->tp_device_dt_node;
> +			snprintf(smbus_tp_device_info.type, I2C_NAME_SIZE, backend->compatible);
> +			backend->client = i2c_new_client_device(&bus->smbus_port_adapter,
> +						&smbus_tp_device_info);
> +			if (IS_ERR(backend->client)) {
> +				dev_warn(dev, "Error while registering backend\n");
> +				return -EINVAL;
> +			}
> +		}
> +	}
> +	return 0;
> +}
> +
> +/*
> + * p3h2x4x_tp_smbus_algo - add i2c adapter for target port who
> + * 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 *priv, int tp)
> +{
> +	int ret;
> +	struct device *dev;
> +
> +	if (priv->is_p3h2x4x_in_i3c)
> +		dev = i3cdev_to_dev(priv->i3cdev);
> +	else
> +		dev = i2cdev_to_dev(priv->i2cdev);
> +
> +	priv->tp_bus[tp].smbus_port_adapter.owner = THIS_MODULE;
> +	priv->tp_bus[tp].smbus_port_adapter.class = I2C_CLASS_HWMON;
> +	priv->tp_bus[tp].smbus_port_adapter.algo = &p3h2x4x_tp_i2c_algorithm;
> +
> +	sprintf(priv->tp_bus[tp].smbus_port_adapter.name, "p3h2x4x-cp-%d.tp-port-%d",
> +		p3h2x4x_read_p3h2x4x_id(dev), tp);
> +
> +	ret = i2c_add_adapter(&priv->tp_bus[tp].smbus_port_adapter);
> +	if (ret) {
> +		dev_warn(dev, "failled to add adapter for tp %d\n", tp);
> +		return -EINVAL;
> +	}
> +	priv->tp_bus[tp].is_registered = true;
> +
> +	return 0;
> +}
> diff --git a/include/linux/i3c/device.h b/include/linux/i3c/device.h
> index b674f64d0822..5ab199abb653 100644
> --- a/include/linux/i3c/device.h
> +++ b/include/linux/i3c/device.h
> @@ -77,6 +77,7 @@ struct i3c_priv_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
>

-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

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

* Re: [PATCH 1/2] dt-bindings: i3c: Add NXP P3H2x4x i3c-hub support
  2025-02-12 13:22 [PATCH 1/2] dt-bindings: i3c: Add NXP P3H2x4x i3c-hub support Aman Kumar Pandey
                   ` (2 preceding siblings ...)
  2025-02-12 16:23 ` Frank Li
@ 2025-02-12 16:49 ` Krzysztof Kozlowski
  3 siblings, 0 replies; 13+ messages in thread
From: Krzysztof Kozlowski @ 2025-02-12 16:49 UTC (permalink / raw)
  To: Aman Kumar Pandey, linux-kernel, linux-i3c, alexandre.belloni,
	krzk+dt, robh, conor+dt, devicetree
  Cc: vikash.bansal, priyanka.jain, shashank.rebbapragada, Frank.Li

On 12/02/2025 14:22, Aman Kumar Pandey wrote:
> P3H2x4x (P3H2440/P3H2441/P3H2840/P3H2841) is multiport I3C hub
> device which connects to a host CPU via I3C/I2C/SMBus bus on one
> side and to multiple peripheral devices on the other side.
> 
> Signed-off-by: Aman Kumar Pandey <aman.kumarpandey@nxp.com>
> Signed-off-by: Vikash Bansal <vikash.bansal@nxp.com>
> ---
>  .../bindings/i3c/p3h2x4x_i3c_hub.yaml         | 404 ++++++++++++++++++
>  MAINTAINERS                                   |   7 +
>  2 files changed, 411 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/i3c/p3h2x4x_i3c_hub.yaml
> 
> diff --git a/Documentation/devicetree/bindings/i3c/p3h2x4x_i3c_hub.yaml b/Documentation/devicetree/bindings/i3c/p3h2x4x_i3c_hub.yaml
> new file mode 100644
> index 000000000000..33ea524e5432
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/i3c/p3h2x4x_i3c_hub.yaml

Nothing here looks like being even close to existing coding style. Look
how other files are written, including file naming, blank lines, style
of properties and entire layout.

Your current code is not only unreadable but also incorrect. But due to
unreadability, I won't waste time to review.

You should have make internal review of all this.

>  L:	linux-hwmon@vger.kernel.org


Best regards,
Krzysztof

-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

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

* Re: [PATCH 2/2] drivers: i3c: Add driver for NXP P3H2x4x i3c-hub device
  2025-02-12 13:22 ` [PATCH 2/2] drivers: i3c: Add driver for NXP P3H2x4x i3c-hub device Aman Kumar Pandey
  2025-02-12 16:42   ` Frank Li
@ 2025-02-12 16:53   ` Krzysztof Kozlowski
  2025-02-12 18:20   ` Alexandre Belloni
                     ` (5 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Krzysztof Kozlowski @ 2025-02-12 16:53 UTC (permalink / raw)
  To: Aman Kumar Pandey, linux-kernel, linux-i3c, alexandre.belloni,
	krzk+dt, robh, conor+dt, devicetree
  Cc: vikash.bansal, priyanka.jain, shashank.rebbapragada, Frank.Li

On 12/02/2025 14:22, Aman Kumar Pandey wrote:
> +
> +static void p3h2x4x_of_get_tp_dt_conf(struct device *dev,
> +					const struct device_node *node)
> +{
> +	struct p3h2x4x *priv = dev_get_drvdata(dev);
> +	struct device_node *dev_node;
> +	int tp_port;
> +
> +	for_each_available_child_of_node(node, dev_node) {
> +		if (!dev_node->full_name ||
> +			(sscanf(dev_node->full_name, "target-port@%d", &tp_port) != 1))

NAK, undocumented ABI. Also weird code, why are you parsing DT structure
manually? Use proper functions to get the reg.



> +			continue;
> +
> +		if (tp_port < P3H2x4x_TP_MAX_COUNT) {
> +			priv->tp_bus[tp_port].dt_available = true;
> +			priv->tp_bus[tp_port].of_node = dev_node;
> +			priv->tp_bus[tp_port].tp_mask = BIT(tp_port);
> +			priv->tp_bus[tp_port].priv = priv;
> +			priv->tp_bus[tp_port].tp_port = tp_port;
> +		}
> +	}
> +}
> +
> +/* return true when backend node exist */
> +static bool p3h2x4x_is_backend_node_exist(int port, struct p3h2x4x *priv, u32 addr)
> +{
> +	struct smbus_device *backend = NULL;
> +
> +	list_for_each_entry(backend,
> +			&priv->tp_bus[port].tp_device_entry, list) {
> +		if (backend->addr == addr)
> +			return true;
> +	}
> +	return false;
> +}
> +
> +static int p3h2x4x_read_backend_from_p3h2x4x_dts(struct device_node *i3c_node_target,
> +					struct p3h2x4x *priv)
> +{
> +	struct device_node *i3c_node_tp;
> +	const char *compatible;
> +	int tp_port, ret;
> +	u32 addr_dts;
> +	struct smbus_device *backend;
> +
> +	if (sscanf(i3c_node_target->full_name, "target-port@%d", &tp_port) == 0)
> +		return -EINVAL;
> +
> +	if (tp_port > P3H2x4x_TP_MAX_COUNT)
> +		return -ERANGE;
> +
> +	if (tp_port < 0)
> +		return -EINVAL;
> +
> +	INIT_LIST_HEAD(&priv->tp_bus[tp_port].tp_device_entry);
> +
> +	if (priv->settings.tp[tp_port].mode == P3H2x4x_TP_MODE_I3C)
> +		return 0;
> +
> +	for_each_available_child_of_node(i3c_node_target, i3c_node_tp) {
> +
> +		ret = of_property_read_u32(i3c_node_tp, "reg", &addr_dts);
> +		if (ret)
> +			return ret;
> +
> +		if (p3h2x4x_is_backend_node_exist(tp_port, priv, addr_dts))
> +			continue;
> +
> +		ret = of_property_read_string(i3c_node_tp, "compatible", &compatible);
> +		if (ret)
> +			return ret;
> +
> +		backend = kzalloc(sizeof(*backend), GFP_KERNEL);
> +		if (!backend)
> +			return -ENOMEM;
> +
> +		backend->addr = addr_dts;
> +		backend->compatible = compatible;
> +		backend->tp_device_dt_node = i3c_node_tp;
> +		backend->client = NULL;
> +
> +		list_add(&backend->list,
> +			&priv->tp_bus[tp_port].tp_device_entry);
> +	}
> +
> +	return 0;
> +}
> +
> +static void p3h2x4x_parse_dt_tp(struct device *dev,
> +				const struct device_node *i3c_node_hub,
> +				struct p3h2x4x *priv)
> +{
> +	struct device_node *i3c_node_target;
> +	int ret;
> +
> +	for_each_available_child_of_node(i3c_node_hub, i3c_node_target) {
> +		if (!strcmp(i3c_node_target->name, "target-port")) {
> +			ret = p3h2x4x_read_backend_from_p3h2x4x_dts(i3c_node_target, priv);
> +			if (ret)
> +				dev_err(dev, "DTS entry invalid - error %d", ret);
> +		}
> +	}
> +}
> +
> +static int p3h2x4x_get_tp_local_device_dt_setting(struct device *dev,
> +					const struct device_node *node, u32 id)
> +{
> +	u8 i;
> +	u32 local_dev_count, local_dev;
> +	struct p3h2x4x *priv = dev_get_drvdata(dev);
> +
> +	if (!of_get_property(node, "local_dev", &local_dev_count))

Oh no, read DTS conding style before upstreaming such code.


Best regards,
Krzysztof

-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

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

* Re: [PATCH 2/2] drivers: i3c: Add driver for NXP P3H2x4x i3c-hub device
  2025-02-12 13:22 ` [PATCH 2/2] drivers: i3c: Add driver for NXP P3H2x4x i3c-hub device Aman Kumar Pandey
  2025-02-12 16:42   ` Frank Li
  2025-02-12 16:53   ` Krzysztof Kozlowski
@ 2025-02-12 18:20   ` Alexandre Belloni
  2025-02-13 11:49   ` kernel test robot
                     ` (4 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Alexandre Belloni @ 2025-02-12 18:20 UTC (permalink / raw)
  To: Aman Kumar Pandey
  Cc: linux-kernel, linux-i3c, krzk+dt, robh, conor+dt, devicetree,
	vikash.bansal, priyanka.jain, shashank.rebbapragada, Frank.Li

On 12/02/2025 15:22:27+0200, Aman Kumar Pandey wrote:
> P3H2x4x (P3H2440/P3H2441/P3H2840/P3H2841) is multiport I3C hub devices.
> It connects to a host CPU via I3C/I2C/SMBus bus on one side and to multiple
> peripheral devices on the other side.
> P3H2840/ P3H2841 have eight I3C/I2C Target Port.
> P3H2440/ P3H2441 have four I3C/I2C Target Port.
> 
> This driver can support
> 1. i3c/i2c communication between host and hub
> 2. i3c/i2c transparent mode between host and downstream devices.
> 3. Target ports can be configured as I2C/SMBus mode or I3C.
> 4. MCTP devices
> 5. In-band interrupts
> 
> Signed-off-by: Aman Kumar Pandey <aman.kumarpandey@nxp.com>
> Signed-off-by: Vikash Bansal <vikash.bansal@nxp.com>
> ---
>  MAINTAINERS                                  |   1 +
>  drivers/i3c/Kconfig                          |   1 +
>  drivers/i3c/Makefile                         |   1 +
>  drivers/i3c/p3h2x4x/Kconfig                  |  11 +
>  drivers/i3c/p3h2x4x/Makefile                 |   4 +
>  drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub.h        | 454 +++++++++
>  drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_common.c | 941 +++++++++++++++++++
>  drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_i3c.c    | 353 +++++++
>  drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c  | 719 ++++++++++++++
>  include/linux/i3c/device.h                   |   1 +
>  10 files changed, 2486 insertions(+)
>  create mode 100644 drivers/i3c/p3h2x4x/Kconfig
>  create mode 100644 drivers/i3c/p3h2x4x/Makefile
>  create mode 100644 drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub.h
>  create mode 100644 drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_common.c
>  create mode 100644 drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_i3c.c
>  create mode 100644 drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c
> 


Please run checkpatch in strict mode. Also, the directory should be
named hub, I'm not going to have one directory per hub. You must avoid
wildcards in the filenames and compatibles, the device seems to be
P3H2840HN. Also, please check whether your driver is compatible with the
Renesas RG3MxxB12B0 family which I guess it is.

> diff --git a/MAINTAINERS b/MAINTAINERS
> index 20aa3e987ac5..8e4ec4e3656e 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -17173,6 +17173,7 @@ M:	Aman Kumar Pandey <aman.kumarpandey@nxp.com>
>  L:	linux-kernel@vger.kernel.org
>  S:	Maintained
>  F:	Documentation/devicetree/bindings/i3c/p3h2x4x_i3c_hub.yaml
> +F:	i3c/p3h2x4x/*
>  
>  NZXT-KRAKEN2 HARDWARE MONITORING DRIVER
>  M:	Jonas Malaco <jonas@protocubo.io>
> diff --git a/drivers/i3c/Kconfig b/drivers/i3c/Kconfig
> index 30a441506f61..3160437cc8e1 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/p3h2x4x/Kconfig"
>  endif # I3C
> diff --git a/drivers/i3c/Makefile b/drivers/i3c/Makefile
> index 11982efbc6d9..696747cc7f18 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)		+= p3h2x4x/
> diff --git a/drivers/i3c/p3h2x4x/Kconfig b/drivers/i3c/p3h2x4x/Kconfig
> new file mode 100644
> index 000000000000..b8b18342b065
> --- /dev/null
> +++ b/drivers/i3c/p3h2x4x/Kconfig
> @@ -0,0 +1,11 @@
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright 2024-2025 NXP
> +config P3H2X4X_I3C_HUB
> +	tristate "P3H2X4X I3C HUB support"
> +	depends on I3C
> +	select REGMAP_I3C
> +	help
> +		This enables support for NXP P3H244x/P3H284x I3C HUB. Say Y or M here to use
> +		I3C HUB driver to configure I3C HUB device.
> +		I3C HUB driver's probe will trigger when I3C device with DCR
> +		equals to 0xC2 (HUB device) is detected on the bus.
> diff --git a/drivers/i3c/p3h2x4x/Makefile b/drivers/i3c/p3h2x4x/Makefile
> new file mode 100644
> index 000000000000..214a3eeb62f2
> --- /dev/null
> +++ b/drivers/i3c/p3h2x4x/Makefile
> @@ -0,0 +1,4 @@
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright 2024-2025 NXP
> +p3h2x4x_i3c_hub-y := p3h2x4x_i3c_hub_common.o p3h2x4x_i3c_hub_i3c.o p3h2x4x_i3c_hub_smbus.o
> +obj-$(CONFIG_P3H2X4X_I3C_HUB)	+= p3h2x4x_i3c_hub.o
> diff --git a/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub.h b/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub.h
> new file mode 100644
> index 000000000000..e8c627019556
> --- /dev/null
> +++ b/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub.h
> @@ -0,0 +1,454 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright 2024-2025 NXP
> + * This header file contain private device structure definition, Reg address and its bit
> + * mapping etc.
> + */
> +
> +#ifndef P3H2X4X_I3C_HUB_H
> +#define P3H2X4X_I3C_HUB_H
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +#include <linux/i3c/device.h>
> +#include <linux/i3c/master.h>
> +#include <linux/mutex.h>
> +#include <linux/rwsem.h>
> +#include <linux/i2c.h>
> +#include <linux/slab.h>
> +#include <linux/of.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_DEV_REG_PROTECTION_CODE				0x10
> +#define P3H2x4x_REGISTERS_LOCK_CODE				0x00
> +#define P3H2x4x_REGISTERS_UNLOCK_CODE				0x69
> +#define P3H2x4x_CP1_REGISTERS_UNLOCK_CODE			0x6A
> +
> +#define P3H2x4x_CP_CONF						0x11
> +#define P3H2x4x_TP_ENABLE					0x12
> +#define P3H2x4x_TPn_ENABLE(n)					BIT(n)
> +
> +#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)	\
> +		(((x) << 0) & P3H2x4x_TP0145_IO_STRENGTH_MASK)
> +#define P3H2x4x_TP2367_IO_STRENGTH_MASK				GENMASK(3, 2)
> +#define P3H2x4x_TP2367_IO_STRENGTH(x)	\
> +		(((x) << 2) & P3H2x4x_TP2367_IO_STRENGTH_MASK)
> +#define P3H2x4x_CP0_IO_STRENGTH_MASK				GENMASK(5, 4)
> +#define P3H2x4x_CP0_IO_STRENGTH(x)	\
> +		(((x) << 4) & P3H2x4x_CP0_IO_STRENGTH_MASK)
> +#define P3H2x4x_CP1_IO_STRENGTH_MASK				GENMASK(7, 6)
> +#define P3H2x4x_CP1_IO_STRENGTH(x)	\
> +		(((x) << 6) & P3H2x4x_CP1_IO_STRENGTH_MASK)
> +
> +#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)	\
> +		(((x) << 0) & P3H2x4x_CP0_VCCIO_LDO_VOLTAGE_MASK)
> +#define P3H2x4x_CP1_VCCIO_LDO_VOLTAGE_MASK			GENMASK(3, 2)
> +#define P3H2x4x_CP1_VCCIO_LDO_VOLTAGE(x)	\
> +		(((x) << 2) & P3H2x4x_CP1_VCCIO_LDO_VOLTAGE_MASK)
> +#define P3H2x4x_TP0145_VCCIO_LDO_VOLTAGE_MASK			GENMASK(5, 4)
> +#define P3H2x4x_TP0145_VCCIO_LDO_VOLTAGE(x)	\
> +		(((x) << 4) & P3H2x4x_TP0145_VCCIO_LDO_VOLTAGE_MASK)
> +#define P3H2x4x_TP2367_VCCIO_LDO_VOLTAGE_MASK			GENMASK(7, 6)
> +#define P3H2x4x_TP2367_VCCIO_LDO_VOLTAGE(x)	\
> +		(((x) << 6) & P3H2x4x_TP2367_VCCIO_LDO_VOLTAGE_MASK)
> +
> +#define P3H2x4x_TP_IO_MODE_CONF					0x17
> +#define P3H2x4x_TP_SMBUS_AGNT_EN				0x18
> +#define P3H2x4x_TPn_SMBUS_MODE_EN(n)				BIT(n)
> +#define P3H2x4x_TPn_I2C_MODE_EN(n)				BIT(n)
> +
> +#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_TP0145_PULLUP_CONF_MASK				GENMASK(7, 6)
> +#define P3H2x4x_TP0145_PULLUP_CONF(x)	\
> +		(((x) << 6) & P3H2x4x_TP0145_PULLUP_CONF_MASK)
> +#define P3H2x4x_TP2367_PULLUP_CONF_MASK				GENMASK(5, 4)
> +#define P3H2x4x_TP2367_PULLUP_CONF(x)	\
> +		(((x) << 4) & P3H2x4x_TP2367_PULLUP_CONF_MASK)
> +
> +#define P3H2x4x_CP_IBI_CONF					0x1A
> +
> +#define P3H2x4x_TP_SMBUS_AGNT_IBI_CONFIG			0x1B
> +#define P3H2x4x_TPn_IBI_EN(n)					BIT(n)
> +#define P3H2x4x_ALL_TP_IBI_EN					0xFF
> +#define P3H2x4x_ALL_TP_IBI_DIS					0x00
> +
> +#define P3H2x4x_IBI_MDB_CUSTOM					0x1C
> +#define P3H2x4x_JEDEC_CONTEXT_ID				0x1D
> +#define P3H2x4x_TP_GPIO_MODE_EN					0x1E
> +#define P3H2x4x_TPn_GPIO_MODE_EN(n)				BIT(n)
> +
> +/* 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_TPn_NET_CON(n)					BIT(n)
> +
> +#define P3H2x4x_TP_PULLUP_EN					0x53
> +#define P3H2x4x_TPn_PULLUP_EN(n)				BIT(n)
> +
> +#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_DT_LDO_VOLT_NOT_SET				0x04
> +
> +#define P3H2x4x_LDO_DISABLED					0x00
> +#define P3H2x4x_LDO_ENABLED					0x01
> +#define P3H2x4x_DT_LDO_NOT_DEFINED				0x02
> +
> +#define P3H2x4x_IBI_DISABLED					0x00
> +#define P3H2x4x_IBI_ENABLED					0x01
> +#define P3H2x4x_IBI_NOT_DEFINED					0x02
> +
> +/* 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_NOT_SET				0x04
> +
> +#define P3H2x4x_TP_PULLUP_DISABLED				0x00
> +#define P3H2x4x_TP_PULLUP_ENABLED				0x01
> +#define P3H2x4x_TP_PULLUP_NOT_DEFINED				0x02
> +
> +#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_IO_STRENGTH_NOT_SET				0x04
> +
> +#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 P3H2x4x_TP_MODE_NOT_SET					0x04
> +
> +#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_XFER_SUCCESS					0x01
> +#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					10
> +
> +#define P3H2x4x_HUB_ID						0
> +
> +/* Reg config for Regmap */
> +#define P3H2x4x_REG_BITS					8
> +#define P3H2x4x_VAL_BITS					8
> +
> +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_setting {
> +	const char *const name;
> +	const u8 value;
> +};
> +
> +/* IBI enable/disable settings */
> +static const struct p3h2x4x_setting ibi_en_settings[] = {
> +	{ "disabled",	P3H2x4x_IBI_DISABLED },
> +	{ "enabled",	P3H2x4x_IBI_ENABLED },
> +};
> +
> +/* LDO enable/disable settings */
> +static const struct p3h2x4x_setting ldo_en_settings[] = {
> +	{ "disabled",	P3H2x4x_LDO_DISABLED },
> +	{ "enabled",	P3H2x4x_LDO_ENABLED },
> +};
> +
> +/* LDO voltage settings */
> +static const struct p3h2x4x_setting ldo_volt_settings[] = {
> +	{ "1.0V",	P3H2x4x_LDO_VOLT_1_0V },
> +	{ "1.1V",	P3H2x4x_LDO_VOLT_1_1V },
> +	{ "1.2V",	P3H2x4x_LDO_VOLT_1_2V },
> +	{ "1.8V",	P3H2x4x_LDO_VOLT_1_8V },
> +};
> +
> +/* target port pull-up settings */
> +static const struct p3h2x4x_setting pullup_settings[] = {
> +	{ "250R",		P3H2x4x_TP_PULLUP_250R },
> +	{ "500R",		P3H2x4x_TP_PULLUP_500R },
> +	{ "1000R",		P3H2x4x_TP_PULLUP_1000R },
> +	{ "2000R",		P3H2x4x_TP_PULLUP_2000R },
> +};
> +
> +/* target port mode settings */
> +static const struct p3h2x4x_setting tp_mode_settings[] = {
> +	{ "i3c",		P3H2x4x_TP_MODE_I3C },
> +	{ "smbus",		P3H2x4x_TP_MODE_SMBUS },
> +	{ "gpio",		P3H2x4x_TP_MODE_GPIO },
> +	{ "i2c",		P3H2x4x_TP_MODE_I2C },
> +};
> +
> +/* pull-up enable/disable settings */
> +static const struct p3h2x4x_setting tp_pullup_settings[] = {
> +	{ "disabled",	P3H2x4x_TP_PULLUP_DISABLED },
> +	{ "enabled",	P3H2x4x_TP_PULLUP_ENABLED },
> +};
> +
> +/*  IO strenght settings */
> +static const struct p3h2x4x_setting io_strength_settings[] = {
> +	{ "20Ohms",		P3H2x4x_IO_STRENGTH_20_OHM },
> +	{ "30Ohms",		P3H2x4x_IO_STRENGTH_30_OHM },
> +	{ "40Ohms",		P3H2x4x_IO_STRENGTH_40_OHM },
> +	{ "50Ohms",		P3H2x4x_IO_STRENGTH_50_OHM },
> +};
> +
> +struct tp_setting {
> +	u8 mode;
> +	u8 pullup_en;
> +	u8 ibi_en;
> +	bool always_enable;
> +};
> +
> +/*
> + * struct svc_i3c_i2c_dev_data - Device specific data
> + * @index: Index in the master tables corresponding to this device
> + * @ibi: IBI slot index in the master structure
> + * @ibi_pool: IBI pool associated to this device
> + */
> +struct dt_settings {
> +	u8 cp0_ldo_en;
> +	u8 cp1_ldo_en;
> +	u8 tp0145_ldo_en;
> +	u8 tp2367_ldo_en;
> +	u8 cp0_ldo_volt;
> +	u8 cp1_ldo_volt;
> +	u8 tp0145_ldo_volt;
> +	u8 tp2367_ldo_volt;
> +	u8 tp0145_pullup;
> +	u8 tp2367_pullup;
> +	u8 cp0_io_strength;
> +	u8 cp1_io_strength;
> +	u8 tp0145_io_strength;
> +	u8 tp2367_io_strength;
> +	struct tp_setting tp[P3H2x4x_TP_MAX_COUNT];
> +};
> +
> +struct smbus_device {
> +	struct i2c_client *client;
> +	const char *compatible;
> +	int addr;
> +	struct list_head list;
> +	struct device_node *tp_device_dt_node;
> +};
> +
> +struct tp_bus {
> +	bool dt_available;	    /* bus configuration is available in DT. */
> +	bool is_registered;	    /* bus was registered in the framework. */
> +	u8 tp_mask;
> +	u8 tp_port;
> +	u8 local_dev_list[P3H2x4x_TP_LOCAL_DEV];
> +	u8 local_dev_count;
> +	struct i3c_master_controller i3c_port_controller;
> +	struct i2c_adapter smbus_port_adapter;
> +	struct device_node *of_node;
> +	struct p3h2x4x *priv;
> +	struct list_head tp_device_entry;
> +	struct mutex port_mutex;
> +};
> +
> +struct p3h2x4x {
> +	struct i3c_device *i3cdev;
> +	struct i2c_client *i2cdev;
> +	struct i3c_master_controller *driving_master;
> +	struct regmap *regmap;
> +	struct mutex etx_mutex;
> +	struct dt_settings settings;
> +	struct tp_bus tp_bus[P3H2x4x_TP_MAX_COUNT];
> +	/* Offset for reading HUB's register. */
> +	u8 tp_ibi_mask;
> +	u8 tp_i3c_bus_mask;
> +	u8 tp_always_enable_mask;
> +	u8 is_slave_reg;
> +	bool is_p3h2x4x_in_i3c;
> +};
> +
> +/*
> + * p3h2x4x_tp_add_downstream_device - prove downstream devices for target port who
> + * configured as SMBus.
> + * @priv: p3h2x4x device structure.
> + * Return: 0 in case of success, a negative EINVAL code if the error.
> + */
> +int p3h2x4x_tp_add_downstream_device(struct p3h2x4x *priv);
> +
> +/*
> + * p3h2x4x_tp_smbus_algo - add i2c adapter for target port who
> + * 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 *priv, int tp);
> +
> +int p3h2x4x_tp_i3c_algo(struct p3h2x4x *priv, int tp);
> +
> +/*
> + * 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 /* P3H2X4X_I3C_HUB_H */
> diff --git a/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_common.c b/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_common.c
> new file mode 100644
> index 000000000000..74b86a668f12
> --- /dev/null
> +++ b/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_common.c
> @@ -0,0 +1,941 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2024-2025 NXP
> + * This P3H2x4x driver file implements functions for Hub probe and DT parsing.
> + */
> +
> +#include "p3h2x4x_i3c_hub.h"
> +
> +struct i3c_ibi_setup p3h2x4x_ibireq = {
> +		.handler = p3h2x4x_ibi_handler,
> +		.max_payload_len = P3H2x4x_MAX_PAYLOAD_LEN,
> +		.num_slots = P3H2x4x_NUM_SLOTS,
> +};
> +
> +struct regmap_config p3h2x4x_regmap_config = {
> +		.reg_bits = P3H2x4x_REG_BITS,
> +		.val_bits = P3H2x4x_VAL_BITS,
> +	};
> +
> +/* 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", P3H2x4x_HUB_ID },
> +	{ /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(i2c, p3h2x4x_i2c_id_table);
> +
> +static const struct of_device_id  p3h2x4x_i2c_of_match[] = {
> +	{
> +		.compatible = "nxp,p3h2x4x",
> +		.data =  P3H2x4x_HUB_ID,
> +	},
> +	{ /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, p3h2x4x_i2c_of_match);
> +
> +static void p3h2x4x_of_get_dt_setting(struct device *dev,
> +				const struct device_node *node,
> +				const char *setting_name,
> +				const struct p3h2x4x_setting settings[],
> +				const u8 settings_count, u8 *setting_value)
> +{
> +	const char *sval;
> +	int ret;
> +	int i;
> +
> +	ret = of_property_read_string(node, setting_name, &sval);
> +	if (ret) {
> +		if (ret != -EINVAL)
> +			dev_warn(dev, "No setting or invalid setting for %s, err=%i\n",
> +				setting_name, ret);
> +		return;
> +	}
> +
> +	for (i = 0; i < settings_count; ++i) {
> +		const struct p3h2x4x_setting *const setting = &settings[i];
> +
> +		if (!strcmp(setting->name, sval)) {
> +			*setting_value = setting->value;
> +			return;
> +		}
> +	}
> +	dev_warn(dev, "Unknown setting for %s\n", setting_name);
> +}
> +
> +static int p3h2x4x_configure_pullup(struct device *dev)
> +{
> +	struct p3h2x4x *priv = dev_get_drvdata(dev);
> +	u8 mask = 0, value = 0;
> +
> +	if (priv->settings.tp0145_pullup != P3H2x4x_TP_PULLUP_NOT_SET) {
> +		mask |= P3H2x4x_TP0145_PULLUP_CONF_MASK;
> +		value |= P3H2x4x_TP0145_PULLUP_CONF(priv->settings.tp0145_pullup);
> +	}
> +
> +	if (priv->settings.tp2367_pullup != P3H2x4x_TP_PULLUP_NOT_SET) {
> +		mask |= P3H2x4x_TP2367_PULLUP_CONF_MASK;
> +		value |= P3H2x4x_TP2367_PULLUP_CONF(priv->settings.tp2367_pullup);
> +	}
> +
> +	return regmap_update_bits(priv->regmap, P3H2x4x_LDO_AND_PULLUP_CONF,
> +				mask, value);
> +}
> +
> +static int p3h2x4x_configure_ldo(struct device *dev)
> +{
> +	struct p3h2x4x *priv = dev_get_drvdata(dev);
> +	u8 ldo_config_mask = 0, ldo_config_val = 0;
> +	u8 ldo_disable_mask = 0, ldo_en_val = 0;
> +	u32 reg_val;
> +	int ret;
> +	u8 val;
> +
> +	/* Enable or Disable LDO's. If there is no DT entry - disable LDO for safety reasons */
> +	if (priv->settings.cp0_ldo_en == P3H2x4x_LDO_ENABLED)
> +		ldo_en_val |= P3H2x4x_CP0_EN_LDO;
> +	if (priv->settings.cp1_ldo_en == P3H2x4x_LDO_ENABLED)
> +		ldo_en_val |= P3H2x4x_CP1_EN_LDO;
> +	if (priv->settings.tp0145_ldo_en == P3H2x4x_LDO_ENABLED)
> +		ldo_en_val |= P3H2x4x_TP0145_EN_LDO;
> +	if (priv->settings.tp2367_ldo_en == P3H2x4x_LDO_ENABLED)
> +		ldo_en_val |= P3H2x4x_TP2367_EN_LDO;
> +
> +	/* Get current LDOs configuration */
> +	ret = regmap_read(priv->regmap, P3H2x4x_VCCIO_LDO_CONF, &reg_val);
> +	if (ret)
> +		return ret;
> +
> +	/* LDOs Voltage level (Skip if not defined in the DT)
> +	 * Set the mask only if there is a change from current value
> +	 */
> +	if (priv->settings.cp0_ldo_volt != P3H2x4x_DT_LDO_VOLT_NOT_SET) {
> +		val = P3H2x4x_CP0_VCCIO_LDO_VOLTAGE(priv->settings.cp0_ldo_volt);
> +		if ((reg_val & P3H2x4x_CP0_VCCIO_LDO_VOLTAGE_MASK) != val) {
> +			ldo_config_mask |= P3H2x4x_CP0_VCCIO_LDO_VOLTAGE_MASK;
> +			ldo_config_val |= val;
> +
> +			ldo_disable_mask |= P3H2x4x_CP0_EN_LDO;
> +		}
> +	}
> +	if (priv->settings.cp1_ldo_volt != P3H2x4x_DT_LDO_VOLT_NOT_SET) {
> +		val = P3H2x4x_CP1_VCCIO_LDO_VOLTAGE(priv->settings.cp1_ldo_volt);
> +		if ((reg_val & P3H2x4x_CP1_VCCIO_LDO_VOLTAGE_MASK) != val) {
> +			ldo_config_mask |= P3H2x4x_CP1_VCCIO_LDO_VOLTAGE_MASK;
> +			ldo_config_val |= val;
> +
> +			ldo_disable_mask |= P3H2x4x_CP1_EN_LDO;
> +		}
> +	}
> +	if (priv->settings.tp0145_ldo_volt != P3H2x4x_DT_LDO_VOLT_NOT_SET) {
> +		val = P3H2x4x_TP0145_VCCIO_LDO_VOLTAGE(priv->settings.tp0145_ldo_volt);
> +		if ((reg_val & P3H2x4x_TP0145_VCCIO_LDO_VOLTAGE_MASK) != val) {
> +			ldo_config_mask |= P3H2x4x_TP0145_VCCIO_LDO_VOLTAGE_MASK;
> +			ldo_config_val |= val;
> +
> +			ldo_disable_mask |= P3H2x4x_TP0145_EN_LDO;
> +		}
> +	}
> +	if (priv->settings.tp2367_ldo_volt != P3H2x4x_DT_LDO_VOLT_NOT_SET) {
> +		val = P3H2x4x_TP2367_VCCIO_LDO_VOLTAGE(priv->settings.tp2367_ldo_volt);
> +		if ((reg_val & P3H2x4x_TP2367_VCCIO_LDO_VOLTAGE_MASK) != val) {
> +			ldo_config_mask |= P3H2x4x_TP2367_VCCIO_LDO_VOLTAGE_MASK;
> +			ldo_config_val |= val;
> +
> +			ldo_disable_mask |= P3H2x4x_TP2367_EN_LDO;
> +		}
> +	}
> +
> +	/*
> +	 *Update LDO voltage configuration only if value is changed from already existing register
> +	 * value. It is a good practice to disable the LDO's before making any voltage changes.
> +	 * Presence of config mask indicates voltage change to be applied.
> +	 */
> +	if (ldo_config_mask) {
> +		/* Disable LDO's before making voltage changes */
> +		ret = regmap_update_bits(priv->regmap,
> +					P3H2x4x_LDO_AND_PULLUP_CONF,
> +					ldo_disable_mask, 0);
> +		if (ret)
> +			return ret;
> +
> +		/* Update the LDOs configuration */
> +		ret = regmap_update_bits(priv->regmap, P3H2x4x_VCCIO_LDO_CONF,
> +					ldo_config_mask, ldo_config_val);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	/* Update the LDOs Enable/disable register. This will enable only LDOs enabled in DT */
> +	return regmap_update_bits(priv->regmap, P3H2x4x_LDO_AND_PULLUP_CONF,
> +				P3H2x4x_LDO_ENABLE_DISABLE_MASK, ldo_en_val);
> +}
> +
> +static int p3h2x4x_configure_io_strength(struct device *dev)
> +{
> +	struct p3h2x4x *priv = dev_get_drvdata(dev);
> +	u8 mask_all = 0, val_all = 0;
> +	u32 reg_val;
> +	u8 val;
> +	struct dt_settings tmp;
> +	int ret;
> +
> +	/* Get IO strength configuration to figure out what needs to be changed */
> +	ret = regmap_read(priv->regmap, P3H2x4x_IO_STRENGTH, &reg_val);
> +	if (ret)
> +		return ret;
> +
> +	tmp = priv->settings;
> +	if (tmp.cp0_io_strength != P3H2x4x_IO_STRENGTH_NOT_SET) {
> +		val = P3H2x4x_CP0_IO_STRENGTH(tmp.cp0_io_strength);
> +		mask_all |= P3H2x4x_CP0_IO_STRENGTH_MASK;
> +		val_all |= val;
> +	}
> +	if (tmp.cp1_io_strength != P3H2x4x_IO_STRENGTH_NOT_SET) {
> +		val = P3H2x4x_CP1_IO_STRENGTH(tmp.cp1_io_strength);
> +		mask_all |= P3H2x4x_CP1_IO_STRENGTH_MASK;
> +		val_all |= val;
> +	}
> +	if (tmp.tp0145_io_strength != P3H2x4x_IO_STRENGTH_NOT_SET) {
> +		val = P3H2x4x_TP0145_IO_STRENGTH(tmp.tp0145_io_strength);
> +		mask_all |= P3H2x4x_TP0145_IO_STRENGTH_MASK;
> +		val_all |= val;
> +	}
> +	if (tmp.tp2367_io_strength != P3H2x4x_IO_STRENGTH_NOT_SET) {
> +		val = P3H2x4x_TP2367_IO_STRENGTH(tmp.tp2367_io_strength);
> +		mask_all |= P3H2x4x_TP2367_IO_STRENGTH_MASK;
> +		val_all |= val;
> +	}
> +
> +	/* Set IO strength if required */
> +	return regmap_update_bits(priv->regmap, P3H2x4x_IO_STRENGTH, mask_all, val_all);
> +}
> +
> +static int p3h2x4x_configure_tp(struct device *dev)
> +{
> +	struct p3h2x4x *priv = dev_get_drvdata(dev);
> +	u8 pullup_mask = 0, pullup_val = 0;
> +	u8 smbus_mask = 0, smbus_val = 0;
> +	u8 gpio_mask = 0, gpio_val = 0;
> +	u8 i3c_mask = 0, i3c_val = 0;
> +	u8 ibi_mask = 0, ibi_val = 0;
> +	u8 i2c_mask = 0, i2c_val = 0;
> +	int ret;
> +	int i;
> +
> +	/* TBD: Read type of HUB from register P3H2x4x_DEV_INFO_0 to learn target ports count. */
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; ++i) {
> +		if (priv->settings.tp[i].mode != P3H2x4x_TP_MODE_NOT_SET) {
> +			i3c_mask |= P3H2x4x_TPn_NET_CON(i);
> +			smbus_mask |= P3H2x4x_TPn_SMBUS_MODE_EN(i);
> +			gpio_mask |= P3H2x4x_TPn_GPIO_MODE_EN(i);
> +			i2c_mask |= P3H2x4x_TPn_I2C_MODE_EN(i);
> +
> +			if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_I3C)
> +				i3c_val |= P3H2x4x_TPn_NET_CON(i);
> +			else if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_SMBUS)
> +				smbus_val |= P3H2x4x_TPn_SMBUS_MODE_EN(i);
> +			else if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_GPIO)
> +				gpio_val |= P3H2x4x_TPn_GPIO_MODE_EN(i);
> +			else if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_I2C)
> +				i2c_val |= P3H2x4x_TPn_I2C_MODE_EN(i);
> +		}
> +		if (priv->settings.tp[i].pullup_en != P3H2x4x_TP_PULLUP_NOT_DEFINED) {
> +			pullup_mask |= P3H2x4x_TPn_PULLUP_EN(i);
> +
> +			if (priv->settings.tp[i].pullup_en == P3H2x4x_TP_PULLUP_ENABLED)
> +				pullup_val |= P3H2x4x_TPn_PULLUP_EN(i);
> +		}
> +
> +		if (priv->settings.tp[i].ibi_en != P3H2x4x_IBI_NOT_DEFINED) {
> +			ibi_mask |= P3H2x4x_TPn_IBI_EN(i);
> +
> +			if (priv->settings.tp[i].ibi_en == P3H2x4x_IBI_ENABLED)
> +				ibi_val |= P3H2x4x_TPn_IBI_EN(i);
> +		}
> +	}
> +
> +	ret = regmap_update_bits(priv->regmap, P3H2x4x_TP_IO_MODE_CONF, (smbus_mask | i2c_mask),
> +							(smbus_val | i2c_val));
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_update_bits(priv->regmap, P3H2x4x_TP_PULLUP_EN, pullup_mask, pullup_val);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_update_bits(priv->regmap, P3H2x4x_TP_SMBUS_AGNT_IBI_CONFIG, ibi_mask, ibi_val);
> +	if (ret)
> +		return ret;
> +	priv->tp_ibi_mask = ibi_val;
> +
> +	ret = regmap_update_bits(priv->regmap, P3H2x4x_TP_SMBUS_AGNT_EN, smbus_mask, smbus_val);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_update_bits(priv->regmap, P3H2x4x_TP_GPIO_MODE_EN, gpio_mask, gpio_val);
> +	if (ret)
> +		return ret;
> +
> +	/* Request for HUB Network connection in case any TP is configured in I3C mode */
> +	if ((i3c_val) || (i2c_val)) {
> +		ret = regmap_write(priv->regmap, P3H2x4x_CP_MUX_SET,
> +							P3H2x4x_CONTROLLER_PORT_MUX_REQ);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	/* Enable TP here in case TP was configured */
> +	ret = regmap_update_bits(priv->regmap, P3H2x4x_TP_ENABLE,
> +				i3c_mask | smbus_mask | gpio_mask | i2c_mask,
> +				i3c_val | smbus_val | gpio_val | i2c_val);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int p3h2x4x_configure_smbus_local_dev(struct device *dev)
> +{
> +	struct p3h2x4x *priv = dev_get_drvdata(dev);
> +	u8 target_buffer_page, hub_tp;
> +	int ret = 0;
> +
> +	for (hub_tp = 0; hub_tp < P3H2x4x_TP_MAX_COUNT; hub_tp++) {
> +		if ((priv->tp_bus[hub_tp].local_dev_count) &&
> +				(priv->settings.tp[hub_tp].mode == P3H2x4x_TP_MODE_SMBUS)) {
> +			target_buffer_page = P3H2x4x_TARGET_AGENT_LOCAL_DEV + 4 * hub_tp;
> +			ret = regmap_write(priv->regmap, P3H2x4x_PAGE_PTR, target_buffer_page);
> +			if (ret) {
> +				dev_err(dev, "Failed to configure local device settings\n");
> +				break;
> +			}
> +
> +			ret = regmap_bulk_write(priv->regmap,
> +							P3H2x4x_CONTROLLER_AGENT_BUFF,
> +							priv->tp_bus[hub_tp].local_dev_list,
> +							priv->tp_bus[hub_tp].local_dev_count);
> +			if (ret) {
> +				dev_err(dev, "Failed to add local devices\n");
> +				break;
> +			}
> +		}
> +	}
> +	regmap_write(priv->regmap, P3H2x4x_PAGE_PTR, 0x00);
> +	return ret;
> +}
> +
> +static int p3h2x4x_configure_hw(struct device *dev)
> +{
> +	int ret;
> +
> +	ret = p3h2x4x_configure_ldo(dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = p3h2x4x_configure_io_strength(dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = p3h2x4x_configure_pullup(dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = p3h2x4x_configure_smbus_local_dev(dev);
> +	if (ret)
> +		return ret;
> +
> +	return p3h2x4x_configure_tp(dev);
> +}
> +
> +static void p3h2x4x_of_get_tp_dt_conf(struct device *dev,
> +					const struct device_node *node)
> +{
> +	struct p3h2x4x *priv = dev_get_drvdata(dev);
> +	struct device_node *dev_node;
> +	int tp_port;
> +
> +	for_each_available_child_of_node(node, dev_node) {
> +		if (!dev_node->full_name ||
> +			(sscanf(dev_node->full_name, "target-port@%d", &tp_port) != 1))
> +			continue;
> +
> +		if (tp_port < P3H2x4x_TP_MAX_COUNT) {
> +			priv->tp_bus[tp_port].dt_available = true;
> +			priv->tp_bus[tp_port].of_node = dev_node;
> +			priv->tp_bus[tp_port].tp_mask = BIT(tp_port);
> +			priv->tp_bus[tp_port].priv = priv;
> +			priv->tp_bus[tp_port].tp_port = tp_port;
> +		}
> +	}
> +}
> +
> +/* return true when backend node exist */
> +static bool p3h2x4x_is_backend_node_exist(int port, struct p3h2x4x *priv, u32 addr)
> +{
> +	struct smbus_device *backend = NULL;
> +
> +	list_for_each_entry(backend,
> +			&priv->tp_bus[port].tp_device_entry, list) {
> +		if (backend->addr == addr)
> +			return true;
> +	}
> +	return false;
> +}
> +
> +static int p3h2x4x_read_backend_from_p3h2x4x_dts(struct device_node *i3c_node_target,
> +					struct p3h2x4x *priv)
> +{
> +	struct device_node *i3c_node_tp;
> +	const char *compatible;
> +	int tp_port, ret;
> +	u32 addr_dts;
> +	struct smbus_device *backend;
> +
> +	if (sscanf(i3c_node_target->full_name, "target-port@%d", &tp_port) == 0)
> +		return -EINVAL;
> +
> +	if (tp_port > P3H2x4x_TP_MAX_COUNT)
> +		return -ERANGE;
> +
> +	if (tp_port < 0)
> +		return -EINVAL;
> +
> +	INIT_LIST_HEAD(&priv->tp_bus[tp_port].tp_device_entry);
> +
> +	if (priv->settings.tp[tp_port].mode == P3H2x4x_TP_MODE_I3C)
> +		return 0;
> +
> +	for_each_available_child_of_node(i3c_node_target, i3c_node_tp) {
> +
> +		ret = of_property_read_u32(i3c_node_tp, "reg", &addr_dts);
> +		if (ret)
> +			return ret;
> +
> +		if (p3h2x4x_is_backend_node_exist(tp_port, priv, addr_dts))
> +			continue;
> +
> +		ret = of_property_read_string(i3c_node_tp, "compatible", &compatible);
> +		if (ret)
> +			return ret;
> +
> +		backend = kzalloc(sizeof(*backend), GFP_KERNEL);
> +		if (!backend)
> +			return -ENOMEM;
> +
> +		backend->addr = addr_dts;
> +		backend->compatible = compatible;
> +		backend->tp_device_dt_node = i3c_node_tp;
> +		backend->client = NULL;
> +
> +		list_add(&backend->list,
> +			&priv->tp_bus[tp_port].tp_device_entry);
> +	}
> +
> +	return 0;
> +}
> +
> +static void p3h2x4x_parse_dt_tp(struct device *dev,
> +				const struct device_node *i3c_node_hub,
> +				struct p3h2x4x *priv)
> +{
> +	struct device_node *i3c_node_target;
> +	int ret;
> +
> +	for_each_available_child_of_node(i3c_node_hub, i3c_node_target) {
> +		if (!strcmp(i3c_node_target->name, "target-port")) {
> +			ret = p3h2x4x_read_backend_from_p3h2x4x_dts(i3c_node_target, priv);
> +			if (ret)
> +				dev_err(dev, "DTS entry invalid - error %d", ret);
> +		}
> +	}
> +}
> +
> +static int p3h2x4x_get_tp_local_device_dt_setting(struct device *dev,
> +					const struct device_node *node, u32 id)
> +{
> +	u8 i;
> +	u32 local_dev_count, local_dev;
> +	struct p3h2x4x *priv = dev_get_drvdata(dev);
> +
> +	if (!of_get_property(node, "local_dev", &local_dev_count))
> +		return -EINVAL;
> +
> +	local_dev_count = local_dev_count / (sizeof(u32));
> +
> +	if (local_dev_count > P3H2x4x_TP_LOCAL_DEV)
> +		return -ERANGE;
> +
> +	for (i = 0; i < local_dev_count; i++) {
> +		if (of_property_read_u32_index(node, "local_dev", i, &local_dev)) {
> +			priv->tp_bus[id].local_dev_count = 0;
> +			return -EINVAL;
> +		}
> +		priv->tp_bus[id].local_dev_list[i] = (u8)(local_dev*2);
> +	}
> +	priv->tp_bus[id].local_dev_count = local_dev_count;
> +	return 0;
> +}
> +
> +static void p3h2x4x_get_tp_of_get_setting(struct device *dev,
> +					const struct device_node *node,
> +					struct tp_setting tp_setting[])
> +{
> +	struct device_node *tp_node;
> +	u32 id;
> +
> +	for_each_available_child_of_node(node, tp_node) {
> +		if (!tp_node->name || of_node_cmp(tp_node->name, "target-port"))
> +			continue;
> +
> +		if (!tp_node->full_name ||
> +			(sscanf(tp_node->full_name, "target-port@%u", &id) != 1)) {
> +			dev_warn(dev, "Invalid target port node found in DT: %s\n",
> +				tp_node->full_name);
> +			continue;
> +		}
> +
> +		if (id >= P3H2x4x_TP_MAX_COUNT) {
> +			dev_warn(dev, "Invalid target port index found in DT: %i\n", id);
> +			continue;
> +		}
> +		p3h2x4x_of_get_dt_setting(dev, tp_node, "mode", tp_mode_settings,
> +					ARRAY_SIZE(tp_mode_settings),
> +					&tp_setting[id].mode);
> +		p3h2x4x_of_get_dt_setting(dev, tp_node, "pullup_en", tp_pullup_settings,
> +					ARRAY_SIZE(tp_pullup_settings),
> +					&tp_setting[id].pullup_en);
> +		p3h2x4x_of_get_dt_setting(dev, tp_node, "ibi_en", ibi_en_settings,
> +					ARRAY_SIZE(ibi_en_settings),
> +					&tp_setting[id].ibi_en);
> +		tp_setting[id].always_enable =
> +					of_property_read_bool(tp_node, "always-enable");
> +
> +		p3h2x4x_get_tp_local_device_dt_setting(dev, tp_node, id);
> +	}
> +}
> +
> +static void p3h2x4x_of_get_p3h2x4x_conf(struct device *dev,
> +					const struct device_node *node)
> +{
> +	struct p3h2x4x *priv = dev_get_drvdata(dev);
> +
> +	p3h2x4x_of_get_dt_setting(dev, node, "cp0-ldo-en", ldo_en_settings,
> +				ARRAY_SIZE(ldo_en_settings),
> +				&priv->settings.cp0_ldo_en);
> +	p3h2x4x_of_get_dt_setting(dev, node, "cp1-ldo-en", ldo_en_settings,
> +				ARRAY_SIZE(ldo_en_settings),
> +				&priv->settings.cp1_ldo_en);
> +	p3h2x4x_of_get_dt_setting(dev, node, "tp0145-ldo-en", ldo_en_settings,
> +				ARRAY_SIZE(ldo_en_settings),
> +				&priv->settings.tp0145_ldo_en);
> +	p3h2x4x_of_get_dt_setting(dev, node, "tp2367-ldo-en", ldo_en_settings,
> +				ARRAY_SIZE(ldo_en_settings),
> +				&priv->settings.tp2367_ldo_en);
> +	p3h2x4x_of_get_dt_setting(dev, node, "cp0-ldo-volt", ldo_volt_settings,
> +				ARRAY_SIZE(ldo_volt_settings),
> +				&priv->settings.cp0_ldo_volt);
> +	p3h2x4x_of_get_dt_setting(dev, node, "cp1-ldo-volt", ldo_volt_settings,
> +				ARRAY_SIZE(ldo_volt_settings),
> +				&priv->settings.cp1_ldo_volt);
> +	p3h2x4x_of_get_dt_setting(dev, node, "tp0145-ldo-volt", ldo_volt_settings,
> +				ARRAY_SIZE(ldo_volt_settings),
> +				&priv->settings.tp0145_ldo_volt);
> +	p3h2x4x_of_get_dt_setting(dev, node, "tp2367-ldo-volt", ldo_volt_settings,
> +				ARRAY_SIZE(ldo_volt_settings),
> +				&priv->settings.tp2367_ldo_volt);
> +	p3h2x4x_of_get_dt_setting(dev, node, "tp0145-pullup", pullup_settings,
> +				ARRAY_SIZE(pullup_settings),
> +				&priv->settings.tp0145_pullup);
> +	p3h2x4x_of_get_dt_setting(dev, node, "tp2367-pullup", pullup_settings,
> +				ARRAY_SIZE(pullup_settings),
> +				&priv->settings.tp2367_pullup);
> +	p3h2x4x_of_get_dt_setting(dev, node, "cp0-io-strength", io_strength_settings,
> +				ARRAY_SIZE(io_strength_settings),
> +				&priv->settings.cp0_io_strength);
> +	p3h2x4x_of_get_dt_setting(dev, node, "cp1-io-strength", io_strength_settings,
> +				ARRAY_SIZE(io_strength_settings),
> +				&priv->settings.cp1_io_strength);
> +	p3h2x4x_of_get_dt_setting(dev, node, "tp0145-io-strength", io_strength_settings,
> +				ARRAY_SIZE(io_strength_settings),
> +				&priv->settings.tp0145_io_strength);
> +	p3h2x4x_of_get_dt_setting(dev, node, "tp2367-io-strength", io_strength_settings,
> +				ARRAY_SIZE(io_strength_settings),
> +				&priv->settings.tp2367_io_strength);
> +
> +p3h2x4x_get_tp_of_get_setting(dev, node, priv->settings.tp);
> +}
> +
> +static struct device_node *p3h2x4x_get_dt_p3h2x4x_node(struct device_node *node,
> +		struct device *dev)
> +{
> +	struct device_node *hub_node_no_id = NULL;
> +	struct device_node *hub_node;
> +
> +	for_each_available_child_of_node(node, hub_node) {
> +		if (strstr(hub_node->name, "hub"))
> +			return hub_node;
> +	}
> +	return hub_node_no_id;
> +}
> +
> +static void p3h2x4x_of_default_configuration(struct device *dev)
> +{
> +	struct p3h2x4x *priv = dev_get_drvdata(dev);
> +	int tp_count;
> +
> +	priv->settings.cp0_ldo_en = P3H2x4x_DT_LDO_NOT_DEFINED;
> +	priv->settings.cp1_ldo_en = P3H2x4x_DT_LDO_NOT_DEFINED;
> +	priv->settings.tp0145_ldo_en = P3H2x4x_DT_LDO_NOT_DEFINED;
> +	priv->settings.tp2367_ldo_en = P3H2x4x_DT_LDO_NOT_DEFINED;
> +	priv->settings.cp0_ldo_volt = P3H2x4x_DT_LDO_VOLT_NOT_SET;
> +	priv->settings.cp1_ldo_volt = P3H2x4x_DT_LDO_VOLT_NOT_SET;
> +	priv->settings.tp0145_ldo_volt = P3H2x4x_DT_LDO_VOLT_NOT_SET;
> +	priv->settings.tp2367_ldo_volt = P3H2x4x_DT_LDO_VOLT_NOT_SET;
> +	priv->settings.tp0145_pullup = P3H2x4x_TP_PULLUP_NOT_SET;
> +	priv->settings.tp2367_pullup = P3H2x4x_TP_PULLUP_NOT_SET;
> +	priv->settings.cp0_io_strength = P3H2x4x_IO_STRENGTH_NOT_SET;
> +	priv->settings.cp1_io_strength = P3H2x4x_IO_STRENGTH_NOT_SET;
> +	priv->settings.tp0145_io_strength = P3H2x4x_IO_STRENGTH_NOT_SET;
> +	priv->settings.tp2367_io_strength = P3H2x4x_IO_STRENGTH_NOT_SET;
> +
> +	for (tp_count = 0; tp_count < P3H2x4x_TP_MAX_COUNT; ++tp_count) {
> +		priv->settings.tp[tp_count].mode =  P3H2x4x_TP_MODE_NOT_SET;
> +		priv->settings.tp[tp_count].pullup_en = P3H2x4x_TP_PULLUP_NOT_DEFINED;
> +		priv->settings.tp[tp_count].ibi_en = P3H2x4x_IBI_DISABLED;
> +	}
> +}
> +
> +static int p3h2x4x_device_probe_i3c(struct i3c_device *i3cdev)
> +{
> +	struct device_node *node = NULL;
> +	struct regmap *regmap;
> +	struct device *dev = &i3cdev->dev;
> +	struct p3h2x4x *priv;
> +	char hub_id[64];
> +	int ret, i;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->i3cdev = i3cdev;
> +	priv->driving_master = i3c_dev_get_master(i3cdev->desc);
> +	i3cdev_set_drvdata(i3cdev, priv);
> +	sprintf(hub_id, "i3c-hub-device-%d-%llx", i3c_dev_get_master(i3cdev->desc)->bus.id,
> +		i3cdev->desc->info.pid);
> +	p3h2x4x_of_default_configuration(dev);
> +	regmap = devm_regmap_init_i3c(i3cdev, &p3h2x4x_regmap_config);
> +	if (IS_ERR(regmap)) {
> +		ret = PTR_ERR(regmap);
> +		dev_err(dev, "Failed to register I3C HUB regmap\n");
> +		goto error;
> +	}
> +	priv->regmap = regmap;
> +	priv->is_p3h2x4x_in_i3c = true;
> +
> +	mutex_init(&priv->etx_mutex);
> +
> +	/* Register logic for native SMBus ports */
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++)
> +		mutex_init(&priv->tp_bus[i].port_mutex);
> +
> +	node = p3h2x4x_get_dt_p3h2x4x_node(dev->parent->of_node, dev); /* Find hub node in DT */
> +	if (!node) {
> +		dev_warn(dev, "No DT entry - running with hardware defaults.\n");
> +	} else {
> +		of_node_get(node);
> +		p3h2x4x_of_get_p3h2x4x_conf(dev, node);
> +		p3h2x4x_of_get_tp_dt_conf(dev, node);
> +		of_node_put(node);
> +		/* Parse DTS to find data on the SMBus target mode */
> +		p3h2x4x_parse_dt_tp(dev, node, priv);
> +	}
> +
> +	/* Unlock access to protected registers */
> +	ret = regmap_write(priv->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE,
> +						P3H2x4x_REGISTERS_UNLOCK_CODE);
> +	if (ret) {
> +		dev_err(dev, "Failed to unlock HUB's protected registers\n");
> +		goto error;
> +	}
> +
> +	ret = p3h2x4x_configure_hw(dev);
> +	if (ret) {
> +		dev_err(dev, "Failed to configure the HUB\n");
> +		goto error;
> +	}
> +
> +	/* Register logic for native SMBus ports */
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++) {
> +		if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_SMBUS)
> +			ret = p3h2x4x_tp_smbus_algo(priv, i);
> +	}
> +
> +	ret = p3h2x4x_tp_add_downstream_device(priv);
> +	if (ret) {
> +		dev_err(dev, "Failed to add backend device\n");
> +		goto error;
> +	}
> +
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; ++i) {
> +		if (priv->tp_bus[i].dt_available) {
> +			if (priv->settings.tp[i].always_enable)
> +				priv->tp_always_enable_mask =
> +						(priv->tp_always_enable_mask |  BIT(i));
> +
> +			if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_I3C)
> +				priv->tp_i3c_bus_mask = (priv->tp_i3c_bus_mask |  BIT(i));
> +		}
> +	}
> +
> +	/* Register logic for native vertual I3C ports */
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++) {
> +		if ((priv->settings.tp[i].mode == P3H2x4x_TP_MODE_I3C) &&
> +			(!priv->settings.tp[i].always_enable))
> +			ret = p3h2x4x_tp_i3c_algo(priv, i);
> +	}
> +
> +	ret = regmap_write(priv->regmap, P3H2x4x_TP_NET_CON_CONF, priv->tp_always_enable_mask);
> +	if (ret) {
> +		dev_err(dev, "Failed to open Target Port(s)\n");
> +		goto error;
> +	}
> +
> +	ret = i3c_master_do_daa(priv->driving_master);
> +	if (ret)
> +		dev_warn(dev, "Failed to run DAA\n");
> +
> +
> +	/* 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(priv->regmap, P3H2x4x_ONCHIP_TD_AND_SMBUS_AGNT_CONF,
> +						P3H2x4x_TARGET_AGENT_DFT_IBI_CONF_MASK,
> +						P3H2x4x_TARGET_AGENT_DFT_IBI_CONF);
> +	if (ret) {
> +		dev_err(dev, "Failed to P3H2x4x_ONCHIP_TD_AND_SMBUS_AGNT_CONF\n");
> +		goto error;
> +	}
> +
> +	ret = i3c_device_request_ibi(i3cdev, &p3h2x4x_ibireq);
> +	if (ret) {
> +		dev_err(dev, "Failed to request IBI\n");
> +		goto error;
> +	}
> +
> +	ret = i3c_device_enable_ibi(i3cdev);
> +	if (ret) {
> +		dev_err(dev, "Failed to Enable IBI\n");
> +		goto error;
> +	}
> +
> +	/* Lock access to protected registers */
> +	ret = regmap_write(priv->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE,
> +						P3H2x4x_REGISTERS_LOCK_CODE);
> +	if (ret) {
> +		dev_err(dev, "Failed to lock HUB's protected registers\n");
> +		goto error;
> +	}
> +
> +	return 0;
> +
> +error:
> +	devm_kfree(dev, priv);
> +	return ret;
> +}
> +
> +static void p3h2x4x_device_remove_i3c(struct i3c_device *i3cdev)
> +{
> +	struct p3h2x4x *priv = i3cdev_get_drvdata(i3cdev);
> +	struct i2c_adapter *tp_adap;
> +	struct i3c_master_controller *tp_controller;
> +	struct smbus_device *backend = NULL;
> +	int i;
> +
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++) {
> +		tp_adap = &priv->tp_bus[i].smbus_port_adapter;
> +		tp_controller = &priv->tp_bus[i].i3c_port_controller;
> +
> +		if (priv->tp_bus[i].is_registered) {
> +			if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_SMBUS) {
> +				list_for_each_entry(backend,
> +						&priv->tp_bus[i].tp_device_entry,
> +						list) {
> +					i2c_unregister_device(backend->client);
> +					kfree(backend);
> +				}
> +				i2c_del_adapter(tp_adap);
> +			} else if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_I3C) {
> +				i3c_master_unregister(tp_controller);
> +			}
> +		}
> +	}
> +
> +	i3c_device_disable_ibi(i3cdev);
> +	i3c_device_free_ibi(i3cdev);
> +
> +	mutex_destroy(&priv->etx_mutex);
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++)
> +		mutex_destroy(&priv->tp_bus[i].port_mutex);
> +
> +	devm_kfree(&i3cdev->dev, priv);
> +}
> +
> +static int p3h2x4x_device_probe_i2c(struct i2c_client *client)
> +{
> +	struct device_node *node = NULL;
> +	struct regmap *regmap;
> +	struct device *dev = &client->dev;
> +	struct p3h2x4x *priv;
> +	int ret, i;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->i2cdev = client;
> +	i2c_set_clientdata(client, priv);
> +
> +	p3h2x4x_of_default_configuration(dev);
> +
> +	regmap = devm_regmap_init_i2c(client, &p3h2x4x_regmap_config);
> +	if (IS_ERR(regmap)) {
> +		ret = PTR_ERR(regmap);
> +		dev_err(dev, "Failed to register I3C HUB regmap\n");
> +		goto error;
> +	}
> +	priv->regmap = regmap;
> +	priv->is_p3h2x4x_in_i3c = false;
> +
> +	mutex_init(&priv->etx_mutex);
> +
> +	/* Register logic for native SMBus ports */
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++)
> +		mutex_init(&priv->tp_bus[i].port_mutex);
> +
> +	node = p3h2x4x_get_dt_p3h2x4x_node(dev->parent->of_node, dev); /* Find hub node in DT */
> +	if (!node) {
> +		dev_warn(dev, "No DT entry - running with hardware defaults.\n");
> +	} else {
> +		of_node_get(node);
> +		p3h2x4x_of_get_p3h2x4x_conf(dev, node);
> +		p3h2x4x_of_get_tp_dt_conf(dev, node);
> +		of_node_put(node);
> +		/* Parse DTS to find data on the SMBus target mode */
> +		p3h2x4x_parse_dt_tp(dev, node, priv);
> +	}
> +
> +	/* Unlock access to protected registers */
> +	ret = regmap_write(priv->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE,
> +						P3H2x4x_REGISTERS_UNLOCK_CODE);
> +	if (ret) {
> +		dev_err(dev, "Failed to unlock HUB's protected registers\n");
> +		goto error;
> +	}
> +
> +	ret = p3h2x4x_configure_hw(dev);
> +	if (ret) {
> +		dev_err(dev, "Failed to configure the HUB\n");
> +		goto error;
> +	}
> +
> +	/* Register logic for native SMBus ports */
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++) {
> +		if (priv->settings.tp[i].mode == P3H2x4x_TP_MODE_SMBUS)
> +			ret = p3h2x4x_tp_smbus_algo(priv, i);
> +	}
> +
> +	ret = p3h2x4x_tp_add_downstream_device(priv);
> +	if (ret) {
> +		dev_err(dev, "Failed to add backend device\n");
> +		goto error;
> +	}
> +
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; ++i) {
> +		if (priv->tp_bus[i].dt_available) {
> +			if (priv->settings.tp[i].always_enable)
> +				priv->tp_always_enable_mask =
> +							(priv->tp_always_enable_mask |  BIT(i));
> +		}
> +	}
> +
> +	ret = regmap_write(priv->regmap, P3H2x4x_TP_NET_CON_CONF, priv->tp_always_enable_mask);
> +	if (ret) {
> +		dev_err(dev, "Failed to open Target Port(s)\n");
> +		goto error;
> +	}
> +
> +	/* Lock access to protected registers */
> +	ret = regmap_write(priv->regmap, P3H2x4x_DEV_REG_PROTECTION_CODE,
> +						P3H2x4x_REGISTERS_LOCK_CODE);
> +	if (ret) {
> +		dev_err(dev, "Failed to lock HUB's protected registers\n");
> +		goto error;
> +	}
> +	return 0;
> +
> +error:
> +	devm_kfree(dev, priv);
> +	return ret;
> +}
> +
> +static void p3h2x4x_device_remove_i2c(struct i2c_client *client)
> +{
> +	struct p3h2x4x *priv = i2c_get_clientdata(client);
> +	struct i2c_adapter *tp_adap;
> +	struct smbus_device *backend = NULL;
> +	int i;
> +
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++) {
> +		tp_adap = &priv->tp_bus[i].smbus_port_adapter;
> +		if (priv->tp_bus[i].is_registered) {
> +			list_for_each_entry(backend, &priv->tp_bus[i].tp_device_entry, list) {
> +				i2c_unregister_device(backend->client);
> +				kfree(backend);
> +			}
> +			i2c_del_adapter(tp_adap);
> +		}
> +	}
> +
> +	mutex_destroy(&priv->etx_mutex);
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++)
> +		mutex_destroy(&priv->tp_bus[i].port_mutex);
> +
> +	devm_kfree(&client->dev, priv);
> +}
> +
> +static struct i3c_driver p3h2x4x_i3c = {
> +	.driver = {
> +		.name = "p3h2x4x_i3c_drv",
> +	},
> +	.id_table = p3h2x4x_i3c_ids,
> +	.probe = p3h2x4x_device_probe_i3c,
> +	.remove = p3h2x4x_device_remove_i3c,
> +};
> +
> +static struct i2c_driver p3h2x4x_i2c = {
> +	.driver = {
> +		.name = "p3h2x4x_i2c_drv",
> +		.of_match_table = p3h2x4x_i2c_of_match,
> +	},
> +	.probe =  p3h2x4x_device_probe_i2c,
> +	.remove = p3h2x4x_device_remove_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 driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_i3c.c b/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_i3c.c
> new file mode 100644
> index 000000000000..c7827c4b6f57
> --- /dev/null
> +++ b/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_i3c.c
> @@ -0,0 +1,353 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2024-2025 NXP
> + * This P3H2x4x driver file contain functions for I3C virtual Bus creation, connect/disconnect
> + * hub network and read/write.
> + */
> +#include "p3h2x4x_i3c_hub.h"
> +
> +static void p3h2x4x_en_p3h2x4x_ntwk_tp(struct tp_bus *bus)
> +{
> +	struct p3h2x4x *priv = bus->priv;
> +	struct device *dev = i3cdev_to_dev(priv->i3cdev);
> +	int ret;
> +
> +	if (priv->settings.tp[bus->tp_port].always_enable)
> +		return;
> +
> +	ret = regmap_write(priv->regmap, P3H2x4x_TP_NET_CON_CONF,
> +				(bus->tp_mask | priv->tp_always_enable_mask));
> +	if (ret)
> +		dev_warn(dev, "Failed to open Target Port(s)\n");
> +}
> +
> +static void p3h2x4x_dis_p3h2x4x_ntwk_tp(struct tp_bus *bus)
> +{
> +	struct p3h2x4x *priv = bus->priv;
> +	struct device *dev = i3cdev_to_dev(priv->i3cdev);
> +	int ret;
> +
> +	if (priv->settings.tp[bus->tp_port].always_enable)
> +		return;
> +
> +	ret = regmap_write(priv->regmap, P3H2x4x_TP_NET_CON_CONF, priv->tp_always_enable_mask);
> +	if (ret)
> +		dev_warn(dev, "Failed to close Target Port(s)\n");
> +}
> +
> +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, i3c_port_controller);
> +}
> +
> +static struct tp_bus *p3h2x4x_bus_from_i2c_desc(struct i2c_dev_desc *desc)
> +{
> +	struct i3c_master_controller *controller = i2c_dev_get_master(desc);
> +
> +	return container_of(controller, struct tp_bus, i3c_port_controller);
> +}
> +
> +static struct i3c_master_controller
> +*get_parent_controller(struct i3c_master_controller *controller)
> +{
> +	struct tp_bus *bus = container_of(controller, struct tp_bus, i3c_port_controller);
> +
> +	return bus->priv->driving_master;
> +}
> +
> +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, i3c_port_controller);
> +
> +	return bus->priv->driving_master;
> +}
> +
> +static struct i3c_master_controller
> +*get_parent_controller_from_i2c_desc(struct i2c_dev_desc *desc)
> +{
> +	struct i3c_master_controller *controller = desc->common.master;
> +	struct tp_bus *bus = container_of(controller, struct tp_bus, i3c_port_controller);
> +
> +	return bus->priv->driving_master;
> +}
> +
> +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, i3c_port_controller);
> +
> +	controller->this = bus->priv->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)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
> +	struct i3c_master_controller *orig_parent;
> +	int ret;
> +
> +	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
> +	ret = parent->ops->attach_i3c_dev(dev);
> +	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
> +	return ret;
> +}
> +
> +static int p3h2x4x_reattach_i3c_dev(struct i3c_dev_desc *dev, u8 old_dyn_addr)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
> +	struct i3c_master_controller *orig_parent;
> +	int ret;
> +
> +	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
> +	ret = parent->ops->reattach_i3c_dev(dev, old_dyn_addr);
> +	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
> +	return ret;
> +}
> +
> +static void p3h2x4x_detach_i3c_dev(struct i3c_dev_desc *dev)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
> +	struct i3c_master_controller *orig_parent;
> +
> +	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
> +	parent->ops->detach_i3c_dev(dev);
> +	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
> +}
> +
> +static int p3h2x4x_do_daa(struct i3c_master_controller *controller)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller(controller);
> +
> +	struct tp_bus *bus = container_of(controller, struct tp_bus, i3c_port_controller);
> +	int ret;
> +
> +	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
> +	down_write(&parent->bus.lock);
> +	ret = parent->ops->do_daa(parent);
> +	up_write(&parent->bus.lock);
> +	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 parent->ops->supports_ccc_cmd(parent, cmd);
> +}
> +
> +static int p3h2x4x_send_ccc_cmd(struct i3c_master_controller *controller,
> +				struct i3c_ccc_cmd *cmd)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller(controller);
> +	struct tp_bus *bus = container_of(controller, struct tp_bus, i3c_port_controller);
> +	int ret;
> +
> +	if (cmd->id == I3C_CCC_RSTDAA(true))
> +		return 0;
> +
> +	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
> +	ret = parent->ops->send_ccc_cmd(parent, cmd);
> +	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
> +
> +	return ret;
> +}
> +
> +static int p3h2x4x_priv_xfers(struct i3c_dev_desc *dev,
> +				struct i3c_priv_xfer *xfers, int nxfers)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
> +	struct i3c_master_controller *orig_parent;
> +	struct tp_bus *bus = p3h2x4x_bus_from_i3c_desc(dev);
> +	int res;
> +
> +	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
> +	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
> +	res = parent->ops->priv_xfers(dev, xfers, nxfers);
> +	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
> +	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
> +
> +	return res;
> +}
> +
> +static int p3h2x4x_attach_i2c_dev(struct i2c_dev_desc *dev)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller_from_i2c_desc(dev);
> +	struct i3c_master_controller *orig_parent;
> +	int ret;
> +
> +	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
> +	ret = parent->ops->attach_i2c_dev(dev);
> +	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
> +	return ret;
> +}
> +
> +static void p3h2x4x_detach_i2c_dev(struct i2c_dev_desc *dev)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller_from_i2c_desc(dev);
> +	struct i3c_master_controller *orig_parent;
> +
> +	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
> +	parent->ops->detach_i2c_dev(dev);
> +	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
> +}
> +
> +static int p3h2x4x_i2c_xfers(struct i2c_dev_desc *dev,
> +				const struct i2c_msg *xfers, int nxfers)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller_from_i2c_desc(dev);
> +	struct tp_bus *bus = p3h2x4x_bus_from_i2c_desc(dev);
> +	struct i3c_master_controller *orig_parent;
> +	int ret;
> +
> +	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
> +	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
> +	ret = parent->ops->i2c_xfers(dev, xfers, nxfers);
> +	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
> +	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
> +	return ret;
> +}
> +
> +static int p3h2x4x_request_ibi(struct i3c_dev_desc *dev,
> +				const struct i3c_ibi_setup *req)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
> +	struct tp_bus *bus = p3h2x4x_bus_from_i3c_desc(dev);
> +	struct i3c_master_controller *orig_parent;
> +	int ret;
> +
> +	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
> +	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
> +	ret = parent->ops->request_ibi(dev, req);
> +	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
> +	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
> +	return ret;
> +}
> +
> +static void p3h2x4x_free_ibi(struct i3c_dev_desc *dev)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
> +	struct tp_bus *bus = p3h2x4x_bus_from_i3c_desc(dev);
> +	struct i3c_master_controller *orig_parent;
> +
> +	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
> +	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
> +	parent->ops->free_ibi(dev);
> +	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
> +	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
> +}
> +
> +static int p3h2x4x_enable_ibi(struct i3c_dev_desc *dev)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
> +	struct tp_bus *bus = p3h2x4x_bus_from_i3c_desc(dev);
> +	struct i3c_master_controller *orig_parent;
> +	int ret;
> +
> +	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
> +	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
> +	ret = parent->ops->enable_ibi(dev);
> +	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
> +	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
> +	return ret;
> +}
> +
> +static int p3h2x4x_disable_ibi(struct i3c_dev_desc *dev)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
> +	struct tp_bus *bus = p3h2x4x_bus_from_i3c_desc(dev);
> +	struct i3c_master_controller *orig_parent;
> +	int ret;
> +
> +	p3h2x4x_en_p3h2x4x_ntwk_tp(bus);
> +	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
> +	ret = parent->ops->disable_ibi(dev);
> +	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
> +	p3h2x4x_dis_p3h2x4x_ntwk_tp(bus);
> +	return ret;
> +}
> +
> +static void p3h2x4x_recycle_ibi_slot(struct i3c_dev_desc *dev,
> +					struct i3c_ibi_slot *slot)
> +{
> +	struct i3c_master_controller *parent = get_parent_controller_from_i3c_desc(dev);
> +	struct i3c_master_controller *orig_parent;
> +
> +	orig_parent = update_i3c_i2c_desc_parent(&dev->common, parent);
> +	parent->ops->recycle_ibi_slot(dev, slot);
> +	restore_i3c_i2c_desc_parent(&dev->common, orig_parent);
> +}
> +
> +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,
> +	.priv_xfers = p3h2x4x_priv_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.
> + * @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 *priv, int tp)
> +{
> +	struct device *dev = i3cdev_to_dev(priv->i3cdev);
> +	int ret;
> +
> +	priv->tp_bus[tp].tp_mask =  BIT(tp);
> +	dev->of_node = priv->tp_bus[tp].of_node;
> +
> +	ret = i3c_master_register(&priv->tp_bus[tp].i3c_port_controller,
> +				dev, &p3h2x4x_i3c_ops, false);
> +	if (ret) {
> +		dev_warn(dev, "Failed to register i3c controller for tp %d\n", tp);
> +		return -EINVAL;
> +	}
> +
> +	priv->tp_bus[tp].is_registered = true;
> +	return 0;
> +}
> diff --git a/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c b/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c
> new file mode 100644
> index 000000000000..8cdbaaf49378
> --- /dev/null
> +++ b/drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c
> @@ -0,0 +1,719 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2024-2025 NXP
> + * This P3H2x4x driver file contain functions for SMBus/I2C virtual Bus creation and read/write.
> + */
> +#include "p3h2x4x_i3c_hub.h"
> +
> +static struct device *i2cdev_to_dev(struct i2c_client *i2cdev)
> +{
> +	return &i2cdev->dev;
> +}
> +
> +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 device *dev = i3cdev_to_dev(i3cdev);
> +	struct p3h2x4x *priv = dev_get_drvdata(dev);
> +	struct smbus_device *backend = NULL;
> +
> +	u8 target_buffer_page, flag_clear, rx_data, temp, i;
> +	u8 slave_rx_buffer[P3H2x4x_SMBUS_TARGET_PAYLOAD_SIZE] = { 0 };
> +	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(priv->regmap, P3H2x4x_PAGE_PTR, target_buffer_page);
> +	if (ret)
> +		goto ibi_err;
> +
> +	/* read buffer length */
> +	ret = regmap_read(priv->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(dev, "Received message too big for p3h2x4x buffer\n");
> +		return;
> +	}
> +
> +	/* read slave  address */
> +	ret = regmap_read(priv->regmap, P3H2x4x_TARGET_BUFF_ADDRESS, &slave_address);
> +	if (ret)
> +		goto ibi_err;
> +
> +	/* read data */
> +	if (packet_len) {
> +		ret = regmap_bulk_read(priv->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 */
> +	list_for_each_entry(backend, &priv->tp_bus[tp].tp_device_entry, list) {
> +		if ((slave_address >> 1 == backend->addr) && (priv->is_slave_reg)) {
> +			i2c_slave_event(backend->client, I2C_SLAVE_WRITE_REQUESTED,
> +				(u8 *)&slave_address);
> +
> +			for (i = 0; i < packet_len; i++) {
> +				rx_data = slave_rx_buffer[i];
> +				i2c_slave_event(backend->client, I2C_SLAVE_WRITE_RECEIVED,
> +						&rx_data);
> +			}
> +			i2c_slave_event(backend->client, I2C_SLAVE_STOP, &temp);
> +			break;
> +		}
> +	}
> +
> +ibi_err:
> +	regmap_write(priv->regmap, P3H2x4x_PAGE_PTR, 0x00);
> +	regmap_write(priv->regmap, P3H2x4x_TP0_SMBUS_AGNT_STS + tp, flag_clear);
> +}
> +
> +/**
> + * 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 device *dev = i3cdev_to_dev(i3cdev);
> +	struct p3h2x4x *priv = dev_get_drvdata(dev);
> +
> +	u32 target_port_status, payload_byte_one, payload_byte_two;
> +	u8 i;
> +	int  ret;
> +
> +	payload_byte_one = (*(int *)payload->data);
> +	payload_byte_two = (*(int *)(payload->data + 4));
> +
> +	if (!(payload_byte_one & P3H2x4x_SMBUS_AGENT_EVENT_FLAG_STATUS))
> +		goto err;
> +
> +	mutex_lock(&priv->etx_mutex);
> +	ret = regmap_write(priv->regmap, P3H2x4x_TP_SMBUS_AGNT_IBI_CONFIG, P3H2x4x_ALL_TP_IBI_DIS);
> +	if (ret) {
> +		dev_err(dev, "Failed to Disable IBI\n");
> +		goto err;
> +	}
> +
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; ++i) {
> +		if ((priv->tp_bus[i].is_registered) && ((payload_byte_two >> i) & 0x01)) {
> +			ret = regmap_read(priv->regmap, P3H2x4x_TP0_SMBUS_AGNT_STS + i,
> +					&target_port_status);
> +			if (ret) {
> +				dev_err(dev, "target port read status failed %d\n", ret);
> +				goto err;
> +			}
> +
> +			/* 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(dev, "Overflow, reading buffer zero and one\n");
> +				break;
> +			default:
> +				regmap_write(priv->regmap, P3H2x4x_TP0_SMBUS_AGNT_STS + i,
> +					BUF_RECEIVED_FLAG_TF_MASK);
> +					break;
> +			}
> +		}
> +	}
> +err:
> +	regmap_write(priv->regmap, P3H2x4x_TP_SMBUS_AGNT_IBI_CONFIG, priv->tp_ibi_mask);
> +	mutex_unlock(&priv->etx_mutex);
> +}
> +
> +static int p3h2x4x_read_p3h2x4x_id(struct device *dev)
> +{
> +	struct p3h2x4x *priv = dev_get_drvdata(dev);
> +	u32 reg_val;
> +	int ret;
> +
> +	ret = regmap_read(priv->regmap, P3H2x4x_LDO_AND_CPSEL_STS, &reg_val);
> +	if (ret) {
> +		dev_err(dev, "Failed to read status register\n");
> +		return ret;
> +	}
> +	if (P3H2x4x_CP_SEL_PIN_INPUT_CODE_GET(reg_val) == 3)
> +		return 1;
> +	else
> +		return 0;
> +}
> +
> +
> +static void unlock_port_ext_mutex(struct mutex *ext, struct mutex *port)
> +{
> +	mutex_unlock(ext);
> +	mutex_unlock(port);
> +}
> +
> +static void lock_port_ext_mutex(struct mutex *ext, struct mutex *port)
> +{
> +	mutex_lock(ext);
> +	mutex_lock(port);
> +}
> +
> +static int p3h2x4x_read_smbus_transaction_status(struct p3h2x4x *priv,
> +					u8 target_port_status, u8 *status, u8 data_length)
> +{
> +	unsigned int status_read;
> +	int ret;
> +
> +	mutex_unlock(&priv->etx_mutex);
> +	fsleep(P3H2x4x_SMBUS_400kHz_TRANSFER_TIMEOUT(data_length));
> +	mutex_lock(&priv->etx_mutex);
> +
> +	ret = regmap_read(priv->regmap, target_port_status, &status_read);
> +	if (ret)
> +		return ret;
> +
> +	*status = (u8)status_read;
> +
> +	if ((*status & P3H2x4x_TP_BUFFER_STATUS_MASK) == P3H2x4x_XFER_SUCCESS)
> +		return 0;
> +
> +	dev_err(&priv->i3cdev->dev, "Status read timeout reached\n");
> +	return 0;
> +}
> +
> +/*
> + * 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 *priv,
> +				struct i2c_msg *xfers,
> +				u8 target_port,
> +				u8 nxfers_i, u8 rw, u8 *return_status)
> +{
> +	u8 transaction_type = P3H2x4x_SMBUS_400kHz;
> +	u8 controller_buffer_page = P3H2x4x_CONTROLLER_BUFFER_PAGE + 4 * target_port;
> +	int write_length = xfers[nxfers_i].len;
> +	int read_length = xfers[nxfers_i].len;
> +	u8 target_port_status = P3H2x4x_TP0_SMBUS_AGNT_STS + target_port;
> +	u8 addr = xfers[nxfers_i].addr;
> +	u8 target_port_code = BIT(target_port);
> +	u8 rw_address = 2 * addr;
> +	u8 desc[P3H2x4x_SMBUS_DESCRIPTOR_SIZE] = { 0 };
> +	u8 status;
> +	int ret;
> +
> +	if (rw) {
> +		rw_address |= BIT(0);
> +		write_length = 0;
> +	} else {
> +		read_length = 0;
> +	}
> +
> +	desc[0] = rw_address;
> +	desc[1] = transaction_type;
> +	desc[2] = write_length;
> +	desc[3] = read_length;
> +
> +	ret = regmap_write(priv->regmap, target_port_status,
> +			P3H2x4x_TP_BUFFER_STATUS_MASK);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_write(priv->regmap, P3H2x4x_PAGE_PTR, controller_buffer_page);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_bulk_write(priv->regmap, P3H2x4x_CONTROLLER_AGENT_BUFF,
> +				desc, P3H2x4x_SMBUS_DESCRIPTOR_SIZE);
> +	if (ret)
> +		return ret;
> +
> +	if (!rw) {
> +		ret = regmap_bulk_write(priv->regmap,
> +					P3H2x4x_CONTROLLER_AGENT_BUFF_DATA,
> +					xfers[nxfers_i].buf, xfers[nxfers_i].len);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = regmap_write(priv->regmap, P3H2x4x_TP_SMBUS_AGNT_TRANS_START,
> +			target_port_code);
> +	if (ret)
> +		return ret;
> +
> +	ret = p3h2x4x_read_smbus_transaction_status(priv, target_port_status,
> +				&status, (write_length + read_length));
> +	if (ret)
> +		return ret;
> +
> +	*return_status = status;
> +
> +	if (rw) {
> +		ret = regmap_bulk_read(priv->regmap,
> +				P3H2x4x_CONTROLLER_AGENT_BUFF_DATA,
> +				xfers[nxfers_i].buf, xfers[nxfers_i].len);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = regmap_write(priv->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;
> +	int ret;
> +	u8 return_status;
> +	u8 msg_count;
> +	u8 rw;
> +
> +	struct device *dev;
> +	struct tp_bus *bus =
> +		container_of(adap, struct tp_bus, smbus_port_adapter);
> +	struct p3h2x4x *priv = bus->priv;
> +
> +	if (priv->is_p3h2x4x_in_i3c)
> +		dev = i3cdev_to_dev(priv->i3cdev);
> +	else
> +		dev = i2cdev_to_dev(priv->i2cdev);
> +
> +	lock_port_ext_mutex(&priv->etx_mutex, &bus->port_mutex);
> +
> +	if (priv->is_p3h2x4x_in_i3c) {
> +		ret = i3c_device_disable_ibi(priv->i3cdev);
> +		if (ret) {
> +			dev_err(dev, "Failed to Disable IBI\n");
> +			unlock_port_ext_mutex(&priv->etx_mutex, &bus->port_mutex);
> +			return ret;
> +		}
> +	}
> +
> +	for (msg_count = 0; msg_count < num; msg_count++) {
> +		if (msgs[msg_count].len > P3H2x4x_SMBUS_PAYLOAD_SIZE) {
> +			dev_err(&adap->dev,
> +				"Message nr. %d not sent - length over %d bytes.\n",
> +				msg_count, P3H2x4x_SMBUS_PAYLOAD_SIZE);
> +			continue;
> +		}
> +		rw = msgs[msg_count].flags % 2;
> +
> +		ret = p3h2x4x_tp_i2c_xfer_msg(priv,
> +					msgs,
> +					bus->tp_port,
> +					msg_count, rw, &return_status);
> +
> +		if (ret)
> +			goto error;
> +
> +		if (return_status == P3H2x4x_XFER_SUCCESS)
> +			ret_sum++;
> +	}
> +
> +error:
> +	if (priv->is_p3h2x4x_in_i3c) {
> +		ret =  i3c_device_enable_ibi(priv->i3cdev);
> +		if (ret) {
> +			dev_err(dev, "Failed to Enable IBI\n");
> +			unlock_port_ext_mutex(&priv->etx_mutex, &bus->port_mutex);
> +			return ret;
> +		}
> +	}
> +	unlock_port_ext_mutex(&priv->etx_mutex, &bus->port_mutex);
> +	return ret_sum;
> +}
> +
> +static int p3h2x4x_tp_smbus_xfer_msg(struct p3h2x4x *priv,
> +					u8 target_port,
> +					u8 addr,
> +					u8 rw,
> +					u8 cmd,
> +					int sz,
> +					union i2c_smbus_data *data,
> +					u8 *return_status)
> +{
> +	u8 transaction_type = P3H2x4x_SMBUS_400kHz;
> +	u8 controller_buffer_page = P3H2x4x_CONTROLLER_BUFFER_PAGE + 4 * target_port;
> +	u8 target_port_status = P3H2x4x_TP0_SMBUS_AGNT_STS + target_port;
> +	u8 target_port_code = BIT(target_port);
> +	u8 rw_address = 2 * addr;
> +	u8 desc[P3H2x4x_SMBUS_DESCRIPTOR_SIZE] = { 0 };
> +	u8 status;
> +	int ret, i;
> +	u8 read_length, write_length;
> +	u8 buf[I2C_SMBUS_BLOCK_MAX + 2] = {0};
> +	struct device *dev;
> +
> +	if (priv->is_p3h2x4x_in_i3c)
> +		dev = i3cdev_to_dev(priv->i3cdev);
> +	else
> +		dev = i2cdev_to_dev(priv->i2cdev);
> +
> +	/* Map the size to what the chip understands */
> +	switch (sz) {
> +	case I2C_SMBUS_QUICK:
> +	case I2C_SMBUS_BYTE:
> +		if (rw)	{
> +			buf[0] = data->byte;
> +			read_length = ONE_BYTE_SIZE;
> +			write_length = 0;
> +			rw_address |= BIT(0);
> +		} else {
> +			buf[0] = cmd;
> +			write_length = ONE_BYTE_SIZE;
> +			read_length = 0;
> +		}
> +		break;
> +	case I2C_SMBUS_BYTE_DATA:
> +		if (rw) {   /* read write */
> +			buf[0] = cmd;
> +			write_length = ONE_BYTE_SIZE;
> +			read_length = ONE_BYTE_SIZE;
> +			transaction_type |= BIT(0);
> +		} else {  /* only write */
> +			buf[0] = cmd;
> +			buf[1] = data->byte;
> +			write_length = ONE_BYTE_SIZE + 1;
> +			read_length = 0;
> +		}
> +		break;
> +	case I2C_SMBUS_WORD_DATA:
> +		if (rw) {         /* read write */
> +			buf[0] = cmd;
> +			write_length = ONE_BYTE_SIZE;
> +			read_length = 2;
> +			transaction_type |= BIT(0);
> +		} else {  /* only write */
> +			buf[0] = cmd;
> +			buf[1] = data->word & 0xff;
> +			buf[2] = (data->word & 0xff00) >> 8;
> +			write_length = ONE_BYTE_SIZE + 2;
> +			read_length = 0;
> +		}
> +		break;
> +	case I2C_SMBUS_BLOCK_DATA:
> +		if (rw) {         /* read write */
> +			buf[0] = cmd;
> +			write_length = ONE_BYTE_SIZE;
> +			read_length = data->block[0] + 1;
> +			transaction_type |= BIT(0);
> +		} else {  /* only write */
> +			buf[0] = cmd;
> +			for (i = 0 ; i <= data->block[0]; i++)
> +				buf[i+1] = data->block[i];
> +
> +			write_length = data->block[0] + 2;
> +			read_length = 0;
> +		}
> +		break;
> +	case I2C_SMBUS_I2C_BLOCK_DATA:
> +		if (rw) {         /* read write */
> +			buf[0] = cmd;
> +			write_length = ONE_BYTE_SIZE;
> +			read_length = data->block[0];
> +			transaction_type |= BIT(0);
> +		} else {  /* only write */
> +			buf[0] = cmd;
> +			for (i = 0 ; i < data->block[0]; i++)
> +				buf[i+1] = data->block[i+1];
> +
> +			write_length = data->block[0] + 1;
> +			read_length = 0;
> +		}
> +		break;
> +	default:
> +		dev_warn(dev, "Unsupported transaction %d\n", sz);
> +		break;
> +	}
> +
> +	desc[0] = rw_address;
> +	desc[1] = transaction_type;
> +	desc[2] = write_length;
> +	desc[3] = read_length;
> +
> +	ret = regmap_write(priv->regmap, target_port_status,
> +			P3H2x4x_TP_BUFFER_STATUS_MASK);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_write(priv->regmap, P3H2x4x_PAGE_PTR, controller_buffer_page);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_bulk_write(priv->regmap, P3H2x4x_CONTROLLER_AGENT_BUFF,
> +				desc, P3H2x4x_SMBUS_DESCRIPTOR_SIZE);
> +	if (ret)
> +		return ret;
> +
> +	if (write_length) {
> +		ret = regmap_bulk_write(priv->regmap,
> +				P3H2x4x_CONTROLLER_AGENT_BUFF_DATA,
> +				buf, write_length);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = regmap_write(priv->regmap, P3H2x4x_TP_SMBUS_AGNT_TRANS_START,
> +			target_port_code);
> +	if (ret)
> +		return ret;
> +
> +	ret = p3h2x4x_read_smbus_transaction_status(priv, target_port_status, &status,
> +			(write_length + read_length));
> +	if (ret)
> +		return ret;
> +
> +	*return_status = status;
> +
> +	if (rw) {
> +		switch (sz) {
> +		case I2C_SMBUS_QUICK:
> +		case I2C_SMBUS_BYTE:
> +		case I2C_SMBUS_BYTE_DATA:
> +			{
> +				ret = regmap_bulk_read(priv->regmap,
> +					P3H2x4x_CONTROLLER_AGENT_BUFF_DATA + write_length,
> +					&data->byte, read_length);
> +					break;
> +			}
> +		case I2C_SMBUS_WORD_DATA:
> +			{
> +				ret = regmap_bulk_read(priv->regmap,
> +					P3H2x4x_CONTROLLER_AGENT_BUFF_DATA + write_length,
> +					(u8 *)&data->word, read_length);
> +				break;
> +			}
> +		case I2C_SMBUS_BLOCK_DATA:
> +			{
> +				ret = regmap_bulk_read(priv->regmap,
> +					P3H2x4x_CONTROLLER_AGENT_BUFF_DATA + write_length,
> +					data->block, read_length);
> +				break;
> +			}
> +		case I2C_SMBUS_I2C_BLOCK_DATA:
> +			{
> +				ret = regmap_bulk_read(priv->regmap,
> +					P3H2x4x_CONTROLLER_AGENT_BUFF_DATA + write_length,
> +					data->block + 1, read_length);
> +				break;
> +			}
> +		default:
> +				dev_warn(dev, "Unsupported transaction %d\n", sz);
> +				break;
> +		}
> +
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = regmap_write(priv->regmap, P3H2x4x_PAGE_PTR, 0x00);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static s32 p3h2x4x_tp_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags,
> +		char read_write, u8 command, int size, union i2c_smbus_data *data)
> +{
> +	struct tp_bus *bus =
> +		container_of(adap, struct tp_bus, smbus_port_adapter);
> +
> +	struct p3h2x4x *priv = bus->priv;
> +	struct device *dev;
> +
> +	if (priv->is_p3h2x4x_in_i3c)
> +		dev = i3cdev_to_dev(priv->i3cdev);
> +	else
> +		dev = i2cdev_to_dev(priv->i2cdev);
> +
> +	int ret, ret_status;
> +	u8 return_status;
> +
> +	lock_port_ext_mutex(&priv->etx_mutex, &bus->port_mutex);
> +
> +	if (priv->is_p3h2x4x_in_i3c) {
> +		ret = i3c_device_disable_ibi(priv->i3cdev);
> +		if (ret) {
> +			dev_err(dev, "Failed to Disable IBI\n");
> +			unlock_port_ext_mutex(&priv->etx_mutex, &bus->port_mutex);
> +			return ret;
> +		}
> +	}
> +
> +	ret_status = p3h2x4x_tp_smbus_xfer_msg(priv,
> +				(u8)bus->tp_port,
> +				(u8)addr,
> +				(u8)read_write,
> +				(u8)command,
> +				size,
> +				data,
> +				&return_status);
> +
> +	if (priv->is_p3h2x4x_in_i3c) {
> +		ret = i3c_device_enable_ibi(priv->i3cdev);
> +		if (ret) {
> +			dev_err(dev, "Failed to Enable IBI\n");
> +			unlock_port_ext_mutex(&priv->etx_mutex, &bus->port_mutex);
> +			return ret;
> +		}
> +	}
> +	unlock_port_ext_mutex(&priv->etx_mutex, &bus->port_mutex);
> +
> +	if (ret_status)
> +		return ret_status;
> +
> +	if (return_status == P3H2x4x_XFER_SUCCESS)
> +		return 0;
> +	else
> +		return -EINVAL;
> +}
> +
> +static u32 p3h2x4x_tp_smbus_funcs(struct i2c_adapter *adapter)
> +{
> +	return I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
> +			I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_WORD_DATA |
> +			I2C_FUNC_SMBUS_I2C_BLOCK  | I2C_FUNC_SMBUS_BLOCK_DATA |
> +			I2C_FUNC_I2C;
> +}
> +
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> +static int p3h2x4x_tp_i2c_reg_slave(struct i2c_client *slave)
> +{
> +	struct tp_bus *bus =
> +		container_of(slave->adapter, struct tp_bus, smbus_port_adapter);
> +	struct p3h2x4x *priv = bus->priv;
> +
> +	priv->is_slave_reg = true;
> +
> +	return 0;
> +}
> +static int p3h2x4x_tp_i2c_unreg_slave(struct i2c_client *slave)
> +{
> +	struct tp_bus *bus =
> +		container_of(slave->adapter, struct tp_bus, smbus_port_adapter);
> +	struct p3h2x4x *priv = bus->priv;
> +
> +	priv->is_slave_reg = false;
> +
> +	return 0;
> +}
> +#endif
> +
> +/*
> + * I2C algorithm Structure
> + */
> +static struct i2c_algorithm p3h2x4x_tp_i2c_algorithm = {
> +	.master_xfer    = p3h2x4x_tp_i2c_xfer,
> +	.smbus_xfer = p3h2x4x_tp_smbus_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_add_downstream_device - prove downstream devices for target port who
> + * configured as SMBus.
> + * @priv: p3h2x4x device structure.
> + * Return: 0 in case of success, a negative EINVAL code if the error.
> + */
> +int p3h2x4x_tp_add_downstream_device(struct p3h2x4x *priv)
> +{
> +	struct i2c_board_info smbus_tp_device_info = {0};
> +	struct smbus_device *backend = NULL;
> +	struct tp_bus *bus;
> +	int i;
> +	struct device *dev;
> +
> +	if (priv->is_p3h2x4x_in_i3c)
> +		dev = i3cdev_to_dev(priv->i3cdev);
> +	else
> +		dev = i2cdev_to_dev(priv->i2cdev);
> +
> +	for (i = 0; i < P3H2x4x_TP_MAX_COUNT; i++) {
> +		bus = &priv->tp_bus[i];
> +		if (!bus->is_registered)
> +			continue;
> +
> +		list_for_each_entry(backend,
> +					&bus->tp_device_entry, list) {
> +
> +			smbus_tp_device_info.addr = backend->addr;
> +			smbus_tp_device_info.flags = I2C_CLIENT_SLAVE;
> +			smbus_tp_device_info.of_node = backend->tp_device_dt_node;
> +			snprintf(smbus_tp_device_info.type, I2C_NAME_SIZE, backend->compatible);
> +			backend->client = i2c_new_client_device(&bus->smbus_port_adapter,
> +						&smbus_tp_device_info);
> +			if (IS_ERR(backend->client)) {
> +				dev_warn(dev, "Error while registering backend\n");
> +				return -EINVAL;
> +			}
> +		}
> +	}
> +	return 0;
> +}
> +
> +/*
> + * p3h2x4x_tp_smbus_algo - add i2c adapter for target port who
> + * 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 *priv, int tp)
> +{
> +	int ret;
> +	struct device *dev;
> +
> +	if (priv->is_p3h2x4x_in_i3c)
> +		dev = i3cdev_to_dev(priv->i3cdev);
> +	else
> +		dev = i2cdev_to_dev(priv->i2cdev);
> +
> +	priv->tp_bus[tp].smbus_port_adapter.owner = THIS_MODULE;
> +	priv->tp_bus[tp].smbus_port_adapter.class = I2C_CLASS_HWMON;
> +	priv->tp_bus[tp].smbus_port_adapter.algo = &p3h2x4x_tp_i2c_algorithm;
> +
> +	sprintf(priv->tp_bus[tp].smbus_port_adapter.name, "p3h2x4x-cp-%d.tp-port-%d",
> +		p3h2x4x_read_p3h2x4x_id(dev), tp);
> +
> +	ret = i2c_add_adapter(&priv->tp_bus[tp].smbus_port_adapter);
> +	if (ret) {
> +		dev_warn(dev, "failled to add adapter for tp %d\n", tp);
> +		return -EINVAL;
> +	}
> +	priv->tp_bus[tp].is_registered = true;
> +
> +	return 0;
> +}
> diff --git a/include/linux/i3c/device.h b/include/linux/i3c/device.h
> index b674f64d0822..5ab199abb653 100644
> --- a/include/linux/i3c/device.h
> +++ b/include/linux/i3c/device.h
> @@ -77,6 +77,7 @@ struct i3c_priv_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
> 

-- 
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

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

* Re: [PATCH 2/2] drivers: i3c: Add driver for NXP P3H2x4x i3c-hub device
  2025-02-12 13:22 ` [PATCH 2/2] drivers: i3c: Add driver for NXP P3H2x4x i3c-hub device Aman Kumar Pandey
                     ` (2 preceding siblings ...)
  2025-02-12 18:20   ` Alexandre Belloni
@ 2025-02-13 11:49   ` kernel test robot
  2025-02-13 17:46   ` kernel test robot
                     ` (3 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: kernel test robot @ 2025-02-13 11:49 UTC (permalink / raw)
  To: Aman Kumar Pandey, linux-kernel, linux-i3c, alexandre.belloni,
	krzk+dt, robh, conor+dt, devicetree
  Cc: llvm, oe-kbuild-all, vikash.bansal, priyanka.jain,
	shashank.rebbapragada, Frank.Li, Aman Kumar Pandey

Hi Aman,

kernel test robot noticed the following build warnings:

[auto build test WARNING on robh/for-next]
[also build test WARNING on linus/master v6.14-rc2 next-20250213]
[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/drivers-i3c-Add-driver-for-NXP-P3H2x4x-i3c-hub-device/20250212-213659
base:   https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
patch link:    https://lore.kernel.org/r/20250212132227.1348374-2-aman.kumarpandey%40nxp.com
patch subject: [PATCH 2/2] drivers: i3c: Add driver for NXP P3H2x4x i3c-hub device
config: s390-allmodconfig (https://download.01.org/0day-ci/archive/20250213/202502131947.VmPYnXuz-lkp@intel.com/config)
compiler: clang version 19.1.3 (https://github.com/llvm/llvm-project ab51eccf88f5321e7c60591c5546b254b6afab99)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250213/202502131947.VmPYnXuz-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/202502131947.VmPYnXuz-lkp@intel.com/

All warnings (new ones prefixed by >>):

   In file included from drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c:6:
   In file included from drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub.h:12:
   In file included from include/linux/module.h:19:
   In file included from include/linux/elf.h:6:
   In file included from arch/s390/include/asm/elf.h:181:
   In file included from arch/s390/include/asm/mmu_context.h:11:
   In file included from arch/s390/include/asm/pgalloc.h:18:
   In file included from include/linux/mm.h:2224:
   include/linux/vmstat.h:504:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     504 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     505 |                            item];
         |                            ~~~~
   include/linux/vmstat.h:511:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     511 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     512 |                            NR_VM_NUMA_EVENT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~~
   include/linux/vmstat.h:524:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     524 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     525 |                            NR_VM_NUMA_EVENT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~~
>> drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c:52:7: warning: variable 'flag_clear' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
      52 |                 if (ret)
         |                     ^~~
   drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c:80:62: note: uninitialized use occurs here
      80 |         regmap_write(priv->regmap, P3H2x4x_TP0_SMBUS_AGNT_STS + tp, flag_clear);
         |                                                                     ^~~~~~~~~~
   drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c:52:3: note: remove the 'if' if its condition is always false
      52 |                 if (ret)
         |                 ^~~~~~~~
      53 |                         goto ibi_err;
         |                         ~~~~~~~~~~~~
   drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c:45:6: warning: variable 'flag_clear' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
      45 |         if (ret)
         |             ^~~
   drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c:80:62: note: uninitialized use occurs here
      80 |         regmap_write(priv->regmap, P3H2x4x_TP0_SMBUS_AGNT_STS + tp, flag_clear);
         |                                                                     ^~~~~~~~~~
   drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c:45:2: note: remove the 'if' if its condition is always false
      45 |         if (ret)
         |         ^~~~~~~~
      46 |                 goto ibi_err;
         |                 ~~~~~~~~~~~~
   drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c:32:6: warning: variable 'flag_clear' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
      32 |         if (ret)
         |             ^~~
   drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c:80:62: note: uninitialized use occurs here
      80 |         regmap_write(priv->regmap, P3H2x4x_TP0_SMBUS_AGNT_STS + tp, flag_clear);
         |                                                                     ^~~~~~~~~~
   drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c:32:2: note: remove the 'if' if its condition is always false
      32 |         if (ret)
         |         ^~~~~~~~
      33 |                 goto ibi_err;
         |                 ~~~~~~~~~~~~
   drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c:27:6: warning: variable 'flag_clear' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
      27 |         if (ret)
         |             ^~~
   drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c:80:62: note: uninitialized use occurs here
      80 |         regmap_write(priv->regmap, P3H2x4x_TP0_SMBUS_AGNT_STS + tp, flag_clear);
         |                                                                     ^~~~~~~~~~
   drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c:27:2: note: remove the 'if' if its condition is always false
      27 |         if (ret)
         |         ^~~~~~~~
      28 |                 goto ibi_err;
         |                 ~~~~~~~~~~~~
   drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c:20:35: note: initialize the variable 'flag_clear' to silence this warning
      20 |         u8 target_buffer_page, flag_clear, rx_data, temp, i;
         |                                          ^
         |                                           = '\0'
>> drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c:453:2: warning: variable 'write_length' is used uninitialized whenever switch default is taken [-Wsometimes-uninitialized]
     453 |         default:
         |         ^~~~~~~
   drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c:460:12: note: uninitialized use occurs here
     460 |         desc[2] = write_length;
         |                   ^~~~~~~~~~~~
   drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c:372:30: note: initialize the variable 'write_length' to silence this warning
     372 |         u8 read_length, write_length;
         |                                     ^
         |                                      = '\0'
>> drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c:453:2: warning: variable 'read_length' is used uninitialized whenever switch default is taken [-Wsometimes-uninitialized]
     453 |         default:
         |         ^~~~~~~
   drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c:461:12: note: uninitialized use occurs here
     461 |         desc[3] = read_length;
         |                   ^~~~~~~~~~~
   drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c:372:16: note: initialize the variable 'read_length' to silence this warning
     372 |         u8 read_length, write_length;
         |                       ^
         |                        = '\0'
   9 warnings generated.


vim +52 drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_smbus.c

    12	
    13	static void p3h2x4x_read_smbus_agent_rx_buf(struct i3c_device *i3cdev, enum p3h2x4x_rcv_buf rfbuf,
    14									enum p3h2x4x_tp tp, bool is_of)
    15	{
    16		struct device *dev = i3cdev_to_dev(i3cdev);
    17		struct p3h2x4x *priv = dev_get_drvdata(dev);
    18		struct smbus_device *backend = NULL;
    19	
    20		u8 target_buffer_page, flag_clear, rx_data, temp, i;
    21		u8 slave_rx_buffer[P3H2x4x_SMBUS_TARGET_PAYLOAD_SIZE] = { 0 };
    22		u32 packet_len, slave_address, ret;
    23	
    24		target_buffer_page = (((rfbuf) ? P3H2x4x_TARGET_BUFF_1_PAGE : P3H2x4x_TARGET_BUFF_0_PAGE)
    25								+  (P3H2x4x_NO_PAGE_PER_TP * tp));
    26		ret = regmap_write(priv->regmap, P3H2x4x_PAGE_PTR, target_buffer_page);
    27		if (ret)
    28			goto ibi_err;
    29	
    30		/* read buffer length */
    31		ret = regmap_read(priv->regmap, P3H2x4x_TARGET_BUFF_LENGTH, &packet_len);
    32		if (ret)
    33			goto ibi_err;
    34	
    35		if (packet_len)
    36			packet_len = packet_len - 1;
    37	
    38		if (packet_len > P3H2x4x_SMBUS_TARGET_PAYLOAD_SIZE) {
    39			dev_err(dev, "Received message too big for p3h2x4x buffer\n");
    40			return;
    41		}
    42	
    43		/* read slave  address */
    44		ret = regmap_read(priv->regmap, P3H2x4x_TARGET_BUFF_ADDRESS, &slave_address);
    45		if (ret)
    46			goto ibi_err;
    47	
    48		/* read data */
    49		if (packet_len) {
    50			ret = regmap_bulk_read(priv->regmap, P3H2x4x_TARGET_BUFF_DATA,
    51					slave_rx_buffer, packet_len);
  > 52			if (ret)
    53				goto ibi_err;
    54		}
    55	
    56		if (is_of)
    57			flag_clear = BUF_RECEIVED_FLAG_TF_MASK;
    58		else
    59			flag_clear = (((rfbuf == RCV_BUF_0) ? P3H2x4x_TARGET_BUF_0_RECEIVE :
    60						P3H2x4x_TARGET_BUF_1_RECEIVE));
    61	
    62		/* notify slave driver about received data */
    63		list_for_each_entry(backend, &priv->tp_bus[tp].tp_device_entry, list) {
    64			if ((slave_address >> 1 == backend->addr) && (priv->is_slave_reg)) {
    65				i2c_slave_event(backend->client, I2C_SLAVE_WRITE_REQUESTED,
    66					(u8 *)&slave_address);
    67	
    68				for (i = 0; i < packet_len; i++) {
    69					rx_data = slave_rx_buffer[i];
    70					i2c_slave_event(backend->client, I2C_SLAVE_WRITE_RECEIVED,
    71							&rx_data);
    72				}
    73				i2c_slave_event(backend->client, I2C_SLAVE_STOP, &temp);
    74				break;
    75			}
    76		}
    77	
    78	ibi_err:
    79		regmap_write(priv->regmap, P3H2x4x_PAGE_PTR, 0x00);
    80		regmap_write(priv->regmap, P3H2x4x_TP0_SMBUS_AGNT_STS + tp, flag_clear);
    81	}
    82	

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

-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

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

* Re: [PATCH 2/2] drivers: i3c: Add driver for NXP P3H2x4x i3c-hub device
  2025-02-12 13:22 ` [PATCH 2/2] drivers: i3c: Add driver for NXP P3H2x4x i3c-hub device Aman Kumar Pandey
                     ` (3 preceding siblings ...)
  2025-02-13 11:49   ` kernel test robot
@ 2025-02-13 17:46   ` kernel test robot
  2025-02-14  4:21   ` kernel test robot
                     ` (2 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: kernel test robot @ 2025-02-13 17:46 UTC (permalink / raw)
  To: Aman Kumar Pandey, linux-kernel, linux-i3c, alexandre.belloni,
	krzk+dt, robh, conor+dt, devicetree
  Cc: oe-kbuild-all, vikash.bansal, priyanka.jain,
	shashank.rebbapragada, Frank.Li, Aman Kumar Pandey

Hi Aman,

kernel test robot noticed the following build warnings:

[auto build test WARNING on robh/for-next]
[also build test WARNING on linus/master v6.14-rc2 next-20250213]
[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/drivers-i3c-Add-driver-for-NXP-P3H2x4x-i3c-hub-device/20250212-213659
base:   https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
patch link:    https://lore.kernel.org/r/20250212132227.1348374-2-aman.kumarpandey%40nxp.com
patch subject: [PATCH 2/2] drivers: i3c: Add driver for NXP P3H2x4x i3c-hub device
config: loongarch-allmodconfig (https://download.01.org/0day-ci/archive/20250214/202502140107.5TZoA3GU-lkp@intel.com/config)
compiler: loongarch64-linux-gcc (GCC) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250214/202502140107.5TZoA3GU-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/202502140107.5TZoA3GU-lkp@intel.com/

All warnings (new ones prefixed by >>):

   In file included from drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_i3c.c:7:
>> drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub.h:351:37: warning: 'io_strength_settings' defined but not used [-Wunused-const-variable=]
     351 | static const struct p3h2x4x_setting io_strength_settings[] = {
         |                                     ^~~~~~~~~~~~~~~~~~~~
>> drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub.h:345:37: warning: 'tp_pullup_settings' defined but not used [-Wunused-const-variable=]
     345 | static const struct p3h2x4x_setting tp_pullup_settings[] = {
         |                                     ^~~~~~~~~~~~~~~~~~
>> drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub.h:337:37: warning: 'tp_mode_settings' defined but not used [-Wunused-const-variable=]
     337 | static const struct p3h2x4x_setting tp_mode_settings[] = {
         |                                     ^~~~~~~~~~~~~~~~
>> drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub.h:329:37: warning: 'pullup_settings' defined but not used [-Wunused-const-variable=]
     329 | static const struct p3h2x4x_setting pullup_settings[] = {
         |                                     ^~~~~~~~~~~~~~~
>> drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub.h:321:37: warning: 'ldo_volt_settings' defined but not used [-Wunused-const-variable=]
     321 | static const struct p3h2x4x_setting ldo_volt_settings[] = {
         |                                     ^~~~~~~~~~~~~~~~~
>> drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub.h:315:37: warning: 'ldo_en_settings' defined but not used [-Wunused-const-variable=]
     315 | static const struct p3h2x4x_setting ldo_en_settings[] = {
         |                                     ^~~~~~~~~~~~~~~
>> drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub.h:309:37: warning: 'ibi_en_settings' defined but not used [-Wunused-const-variable=]
     309 | static const struct p3h2x4x_setting ibi_en_settings[] = {
         |                                     ^~~~~~~~~~~~~~~


vim +/io_strength_settings +351 drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub.h

   307	
   308	/* IBI enable/disable settings */
 > 309	static const struct p3h2x4x_setting ibi_en_settings[] = {
   310		{ "disabled",	P3H2x4x_IBI_DISABLED },
   311		{ "enabled",	P3H2x4x_IBI_ENABLED },
   312	};
   313	
   314	/* LDO enable/disable settings */
 > 315	static const struct p3h2x4x_setting ldo_en_settings[] = {
   316		{ "disabled",	P3H2x4x_LDO_DISABLED },
   317		{ "enabled",	P3H2x4x_LDO_ENABLED },
   318	};
   319	
   320	/* LDO voltage settings */
 > 321	static const struct p3h2x4x_setting ldo_volt_settings[] = {
   322		{ "1.0V",	P3H2x4x_LDO_VOLT_1_0V },
   323		{ "1.1V",	P3H2x4x_LDO_VOLT_1_1V },
   324		{ "1.2V",	P3H2x4x_LDO_VOLT_1_2V },
   325		{ "1.8V",	P3H2x4x_LDO_VOLT_1_8V },
   326	};
   327	
   328	/* target port pull-up settings */
 > 329	static const struct p3h2x4x_setting pullup_settings[] = {
   330		{ "250R",		P3H2x4x_TP_PULLUP_250R },
   331		{ "500R",		P3H2x4x_TP_PULLUP_500R },
   332		{ "1000R",		P3H2x4x_TP_PULLUP_1000R },
   333		{ "2000R",		P3H2x4x_TP_PULLUP_2000R },
   334	};
   335	
   336	/* target port mode settings */
 > 337	static const struct p3h2x4x_setting tp_mode_settings[] = {
   338		{ "i3c",		P3H2x4x_TP_MODE_I3C },
   339		{ "smbus",		P3H2x4x_TP_MODE_SMBUS },
   340		{ "gpio",		P3H2x4x_TP_MODE_GPIO },
   341		{ "i2c",		P3H2x4x_TP_MODE_I2C },
   342	};
   343	
   344	/* pull-up enable/disable settings */
 > 345	static const struct p3h2x4x_setting tp_pullup_settings[] = {
   346		{ "disabled",	P3H2x4x_TP_PULLUP_DISABLED },
   347		{ "enabled",	P3H2x4x_TP_PULLUP_ENABLED },
   348	};
   349	
   350	/*  IO strenght settings */
 > 351	static const struct p3h2x4x_setting io_strength_settings[] = {
   352		{ "20Ohms",		P3H2x4x_IO_STRENGTH_20_OHM },
   353		{ "30Ohms",		P3H2x4x_IO_STRENGTH_30_OHM },
   354		{ "40Ohms",		P3H2x4x_IO_STRENGTH_40_OHM },
   355		{ "50Ohms",		P3H2x4x_IO_STRENGTH_50_OHM },
   356	};
   357	

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

-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

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

* Re: [PATCH 2/2] drivers: i3c: Add driver for NXP P3H2x4x i3c-hub device
  2025-02-12 13:22 ` [PATCH 2/2] drivers: i3c: Add driver for NXP P3H2x4x i3c-hub device Aman Kumar Pandey
                     ` (4 preceding siblings ...)
  2025-02-13 17:46   ` kernel test robot
@ 2025-02-14  4:21   ` kernel test robot
  2025-02-16 15:17   ` Dan Carpenter
  2025-02-19  4:52   ` kernel test robot
  7 siblings, 0 replies; 13+ messages in thread
From: kernel test robot @ 2025-02-14  4:21 UTC (permalink / raw)
  To: Aman Kumar Pandey, linux-kernel, linux-i3c, alexandre.belloni,
	krzk+dt, robh, conor+dt, devicetree
  Cc: oe-kbuild-all, vikash.bansal, priyanka.jain,
	shashank.rebbapragada, Frank.Li, Aman Kumar Pandey

Hi Aman,

kernel test robot noticed the following build warnings:

[auto build test WARNING on robh/for-next]
[also build test WARNING on linus/master v6.14-rc2 next-20250213]
[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/drivers-i3c-Add-driver-for-NXP-P3H2x4x-i3c-hub-device/20250212-213659
base:   https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
patch link:    https://lore.kernel.org/r/20250212132227.1348374-2-aman.kumarpandey%40nxp.com
patch subject: [PATCH 2/2] drivers: i3c: Add driver for NXP P3H2x4x i3c-hub device
config: i386-randconfig-r111-20250214 (https://download.01.org/0day-ci/archive/20250214/202502141155.osJiWAHL-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250214/202502141155.osJiWAHL-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/202502141155.osJiWAHL-lkp@intel.com/

sparse warnings: (new ones prefixed by >>)
>> drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_common.c:9:22: sparse: sparse: symbol 'p3h2x4x_ibireq' was not declared. Should it be static?
>> drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_common.c:15:22: sparse: sparse: symbol 'p3h2x4x_regmap_config' was not declared. Should it be static?
>> drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_common.c:37:26: sparse: sparse: Using plain integer as NULL pointer
   drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_common.c: note: in included file (through include/linux/module.h, drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub.h):
   include/linux/list.h:83:21: sparse: sparse: self-comparison always evaluates to true

vim +/p3h2x4x_ibireq +9 drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_common.c

     8	
   > 9	struct i3c_ibi_setup p3h2x4x_ibireq = {
    10			.handler = p3h2x4x_ibi_handler,
    11			.max_payload_len = P3H2x4x_MAX_PAYLOAD_LEN,
    12			.num_slots = P3H2x4x_NUM_SLOTS,
    13	};
    14	
  > 15	struct regmap_config p3h2x4x_regmap_config = {
    16			.reg_bits = P3H2x4x_REG_BITS,
    17			.val_bits = P3H2x4x_VAL_BITS,
    18		};
    19	
    20	/* p3h2x4x ids (i3c) */
    21	static const struct i3c_device_id p3h2x4x_i3c_ids[] = {
    22		I3C_CLASS(I3C_DCR_HUB, NULL),
    23		{ /* sentinel */ },
    24	};
    25	MODULE_DEVICE_TABLE(i3c, p3h2x4x_i3c_ids);
    26	
    27	/* p3h2x4x ids (i2c) */
    28	static const struct i2c_device_id p3h2x4x_i2c_id_table[] = {
    29		{ "nxp-i3c-hub", P3H2x4x_HUB_ID },
    30		{ /* sentinel */ },
    31	};
    32	MODULE_DEVICE_TABLE(i2c, p3h2x4x_i2c_id_table);
    33	
    34	static const struct of_device_id  p3h2x4x_i2c_of_match[] = {
    35		{
    36			.compatible = "nxp,p3h2x4x",
  > 37			.data =  P3H2x4x_HUB_ID,
    38		},
    39		{ /* sentinel */ },
    40	};
    41	MODULE_DEVICE_TABLE(of, p3h2x4x_i2c_of_match);
    42	

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

-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

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

* Re: [PATCH 2/2] drivers: i3c: Add driver for NXP P3H2x4x i3c-hub device
  2025-02-12 13:22 ` [PATCH 2/2] drivers: i3c: Add driver for NXP P3H2x4x i3c-hub device Aman Kumar Pandey
                     ` (5 preceding siblings ...)
  2025-02-14  4:21   ` kernel test robot
@ 2025-02-16 15:17   ` Dan Carpenter
  2025-02-19  4:52   ` kernel test robot
  7 siblings, 0 replies; 13+ messages in thread
From: Dan Carpenter @ 2025-02-16 15:17 UTC (permalink / raw)
  To: oe-kbuild, Aman Kumar Pandey, linux-kernel, linux-i3c,
	alexandre.belloni, krzk+dt, robh, conor+dt, devicetree
  Cc: lkp, oe-kbuild-all, vikash.bansal, priyanka.jain,
	shashank.rebbapragada, Frank.Li, Aman Kumar Pandey

Hi Aman,

kernel test robot noticed the following build warnings:

https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Aman-Kumar-Pandey/drivers-i3c-Add-driver-for-NXP-P3H2x4x-i3c-hub-device/20250212-213659
base:   https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
patch link:    https://lore.kernel.org/r/20250212132227.1348374-2-aman.kumarpandey%40nxp.com
patch subject: [PATCH 2/2] drivers: i3c: Add driver for NXP P3H2x4x i3c-hub device
config: i386-randconfig-r073-20250215 (https://download.01.org/0day-ci/archive/20250215/202502150815.xfIJk1kS-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0

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>
| Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
| Closes: https://lore.kernel.org/r/202502150815.xfIJk1kS-lkp@intel.com/

smatch warnings:
drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_common.c:412 p3h2x4x_read_backend_from_p3h2x4x_dts() error: buffer overflow 'priv->settings.tp' 8 <= 8 user_rl='0-8'
drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_common.c:572 p3h2x4x_of_get_p3h2x4x_conf() warn: inconsistent indenting
drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_common.c:771 p3h2x4x_device_remove_i3c() error: dereferencing freed memory 'backend' (line 775)
drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_common.c:902 p3h2x4x_device_remove_i2c() error: dereferencing freed memory 'backend' (line 904)

vim +370 drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub_common.c

5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  392  static int p3h2x4x_read_backend_from_p3h2x4x_dts(struct device_node *i3c_node_target,
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  393  					struct p3h2x4x *priv)
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  394  {
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  395  	struct device_node *i3c_node_tp;
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  396  	const char *compatible;
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  397  	int tp_port, ret;
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  398  	u32 addr_dts;
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  399  	struct smbus_device *backend;
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  400  
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  401  	if (sscanf(i3c_node_target->full_name, "target-port@%d", &tp_port) == 0)
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  402  		return -EINVAL;
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  403  
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  404  	if (tp_port > P3H2x4x_TP_MAX_COUNT)

Change > to >=.

5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  405  		return -ERANGE;
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  406  
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  407  	if (tp_port < 0)
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  408  		return -EINVAL;
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  409  
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12 @410  	INIT_LIST_HEAD(&priv->tp_bus[tp_port].tp_device_entry);
                                                                                     ^^^^^^^
Off by one.

5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  411  
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12 @412  	if (priv->settings.tp[tp_port].mode == P3H2x4x_TP_MODE_I3C)
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  413  		return 0;
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  414  
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  415  	for_each_available_child_of_node(i3c_node_target, i3c_node_tp) {
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  416  
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  417  		ret = of_property_read_u32(i3c_node_tp, "reg", &addr_dts);
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  418  		if (ret)
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  419  			return ret;
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  420  
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  421  		if (p3h2x4x_is_backend_node_exist(tp_port, priv, addr_dts))
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  422  			continue;
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  423  
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  424  		ret = of_property_read_string(i3c_node_tp, "compatible", &compatible);
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  425  		if (ret)
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  426  			return ret;
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  427  
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  428  		backend = kzalloc(sizeof(*backend), GFP_KERNEL);
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  429  		if (!backend)
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  430  			return -ENOMEM;
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  431  
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  432  		backend->addr = addr_dts;
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  433  		backend->compatible = compatible;
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  434  		backend->tp_device_dt_node = i3c_node_tp;
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  435  		backend->client = NULL;
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  436  
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  437  		list_add(&backend->list,
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  438  			&priv->tp_bus[tp_port].tp_device_entry);
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  439  	}
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  440  
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  441  	return 0;
5185c1dfec77e7 Aman Kumar Pandey 2025-02-12  442  }

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


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

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

* Re: [PATCH 2/2] drivers: i3c: Add driver for NXP P3H2x4x i3c-hub device
  2025-02-12 13:22 ` [PATCH 2/2] drivers: i3c: Add driver for NXP P3H2x4x i3c-hub device Aman Kumar Pandey
                     ` (6 preceding siblings ...)
  2025-02-16 15:17   ` Dan Carpenter
@ 2025-02-19  4:52   ` kernel test robot
  7 siblings, 0 replies; 13+ messages in thread
From: kernel test robot @ 2025-02-19  4:52 UTC (permalink / raw)
  To: Aman Kumar Pandey, linux-kernel, linux-i3c, alexandre.belloni,
	krzk+dt, robh, conor+dt, devicetree
  Cc: llvm, oe-kbuild-all, vikash.bansal, priyanka.jain,
	shashank.rebbapragada, Frank.Li, Aman Kumar Pandey

Hi Aman,

kernel test robot noticed the following build errors:

[auto build test ERROR on robh/for-next]
[also build test ERROR on linus/master v6.14-rc3 next-20250218]
[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/drivers-i3c-Add-driver-for-NXP-P3H2x4x-i3c-hub-device/20250212-213659
base:   https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
patch link:    https://lore.kernel.org/r/20250212132227.1348374-2-aman.kumarpandey%40nxp.com
patch subject: [PATCH 2/2] drivers: i3c: Add driver for NXP P3H2x4x i3c-hub device
config: i386-randconfig-006-20250219 (https://download.01.org/0day-ci/archive/20250219/202502191226.OQpregx1-lkp@intel.com/config)
compiler: clang version 19.1.3 (https://github.com/llvm/llvm-project ab51eccf88f5321e7c60591c5546b254b6afab99)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250219/202502191226.OQpregx1-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/202502191226.OQpregx1-lkp@intel.com/

All errors (new ones prefixed by >>, old ones prefixed by <<):

WARNING: modpost: missing MODULE_DESCRIPTION() in kernel/bpf/preload/bpf_preload.o
>> ERROR: modpost: "i2c_slave_event" [drivers/i3c/p3h2x4x/p3h2x4x_i3c_hub.ko] undefined!

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

-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

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

end of thread, other threads:[~2025-02-19  4:52 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-02-12 13:22 [PATCH 1/2] dt-bindings: i3c: Add NXP P3H2x4x i3c-hub support Aman Kumar Pandey
2025-02-12 13:22 ` [PATCH 2/2] drivers: i3c: Add driver for NXP P3H2x4x i3c-hub device Aman Kumar Pandey
2025-02-12 16:42   ` Frank Li
2025-02-12 16:53   ` Krzysztof Kozlowski
2025-02-12 18:20   ` Alexandre Belloni
2025-02-13 11:49   ` kernel test robot
2025-02-13 17:46   ` kernel test robot
2025-02-14  4:21   ` kernel test robot
2025-02-16 15:17   ` Dan Carpenter
2025-02-19  4:52   ` kernel test robot
2025-02-12 15:25 ` [PATCH 1/2] dt-bindings: i3c: Add NXP P3H2x4x i3c-hub support Rob Herring (Arm)
2025-02-12 16:23 ` Frank Li
2025-02-12 16:49 ` Krzysztof Kozlowski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).