mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/4] usb: typec: add CIX Type-C PD controller driver
@ 2026-11-07  1:10 Gary Yang
  2026-11-07  1:10 ` [PATCH 1/4] dt-bindings: usb: add Realtek RTS5453H Type-C port controller Gary Yang
                   ` (3 more replies)
  0 siblings, 4 replies; 10+ messages in thread
From: Gary Yang @ 2026-11-07  1:10 UTC (permalink / raw)
  To: Heikki Krogerus, Greg Kroah-Hartman, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Fugang Duan
  Cc: linux-usb, devicetree, linux-arm-kernel, linux-kernel,
	cix-kernel-upstream, gary.yang

This series adds a Type-C PD controller driver for CIX platforms and
enables it on the Radxa Orion O6.

The first supported controller is the Realtek RTS5453H, a stand-alone
port controller: the CC and USB Power Delivery state machines run in
its firmware and are configured through vendor registers rather than
the TCPC register interface, so it cannot be driven by the tcpm
framework. The driver translates the controller status registers into
Type-C class notifications and drives the platform PHY's orientation
switch and mode mux.

The driver is named after the CIX platform integration rather than the
RTS5453H so that further register-compatible PD controllers can be
supported without renaming it.

Patch 1 adds the devicetree binding, patch 2 adds the driver and its
MAINTAINERS entry, patch 3 fixes the I2C bus #address-cells/#size-cells
on Sky1 so that child devices can be described on the buses, and patch 4
adds the two controllers and the I2C1 pinmux to the Orion O6 board.

Orientation switching and USB/DisplayPort lane muxing are performed
by the platform combo PHY. That PHY driver is not upstream yet, so
the DTS does not link the connector to it in this series; the ports
are still registered and their state is reported through the Type-C
class.

Tested on a Radxa Orion O6 (Sky1): attach/detach, USB-only and DP-only
alt modes, cable orientation detection and HPD reporting all work.

Gary Yang (4):
  dt-bindings: usb: add Realtek RTS5453H Type-C port controller
  usb: typec: add CIX Type-C PD controller driver
  arm64: dts: cix: fix I2C bus address and size cells on Sky1
  arm64: dts: cix: add RTS5453 PD controllers to Orion O6

 .../bindings/usb/realtek,rts5453h.yaml        | 126 +++
 MAINTAINERS                                   |   7 +
 arch/arm64/boot/dts/cix/sky1-orion-o6.dts     |  51 ++
 arch/arm64/boot/dts/cix/sky1.dtsi             |  16 +
 drivers/usb/typec/Kconfig                     |  11 +
 drivers/usb/typec/Makefile                    |   1 +
 drivers/usb/typec/cix_pd.c                    | 777 ++++++++++++++++++
 7 files changed, 989 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/usb/realtek,rts5453h.yaml
 create mode 100644 drivers/usb/typec/cix_pd.c


base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
-- 
2.50.1


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

* [PATCH 1/4] dt-bindings: usb: add Realtek RTS5453H Type-C port controller
  2026-11-07  1:10 [PATCH 0/4] usb: typec: add CIX Type-C PD controller driver Gary Yang
@ 2026-11-07  1:10 ` Gary Yang
  2026-11-07 10:54   ` Krzysztof Kozlowski
  2026-11-07  1:10 ` [PATCH 2/4] usb: typec: add CIX Type-C PD controller driver Gary Yang
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 10+ messages in thread
From: Gary Yang @ 2026-11-07  1:10 UTC (permalink / raw)
  To: Heikki Krogerus, Greg Kroah-Hartman, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Fugang Duan
  Cc: linux-usb, devicetree, linux-arm-kernel, linux-kernel,
	cix-kernel-upstream, gary.yang

Document the Realtek RTS5453H stand-alone USB Type-C port controller.
The controller is connected over I2C and manages a single USB Type-C
connector, described with the generic USB connector binding.

The connector's OF graph links to the Type-C orientation switch and
mode mux provided by the platform PHY.

Signed-off-by: Gary Yang <gary.yang@cixtech.com>
---
 .../bindings/usb/realtek,rts5453h.yaml        | 126 ++++++++++++++++++
 1 file changed, 126 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/usb/realtek,rts5453h.yaml

diff --git a/Documentation/devicetree/bindings/usb/realtek,rts5453h.yaml b/Documentation/devicetree/bindings/usb/realtek,rts5453h.yaml
new file mode 100644
index 000000000000..535aa2379427
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/realtek,rts5453h.yaml
@@ -0,0 +1,126 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/usb/realtek,rts5453h.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Realtek RTS5453H USB Type-C port controller
+
+description: |
+  The RTS5453H is a stand-alone USB Type-C port controller with USB
+  Power Delivery support. It is controlled over I2C and reports
+  changes of the port state through an interrupt line.
+
+  Orientation switching and USB/DisplayPort lane muxing are performed by
+  the platform PHY. The controller finds the Type-C switch and mux
+  through the OF graph of its connector node.
+
+maintainers:
+  - Gary Yang <gary.yang@cixtech.com>
+
+properties:
+  compatible:
+    const: realtek,rts5453h
+
+  reg:
+    maxItems: 1
+
+  '#address-cells':
+    const: 1
+
+  '#size-cells':
+    const: 0
+
+  interrupts:
+    maxItems: 1
+
+patternProperties:
+  '^connector@[0-9a-f]+$':
+    type: object
+    $ref: /schemas/connector/usb-connector.yaml#
+    unevaluatedProperties: false
+    description:
+      The USB Type-C connector managed by this controller.
+
+    required:
+      - reg
+      - data-role
+      - power-role
+      - try-power-role
+
+required:
+  - compatible
+  - reg
+  - '#address-cells'
+  - '#size-cells'
+  - interrupts
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        typec@30 {
+            compatible = "realtek,rts5453h";
+            reg = <0x30>;
+            #address-cells = <1>;
+            #size-cells = <0>;
+            interrupt-parent = <&gpio0>;
+            interrupts = <8 IRQ_TYPE_LEVEL_LOW>;
+
+            connector@0 {
+                compatible = "usb-c-connector";
+                reg = <0>;
+                data-role = "host";
+                power-role = "source";
+                try-power-role = "source";
+
+                ports {
+                    #address-cells = <1>;
+                    #size-cells = <0>;
+
+                    port@0 {
+                        reg = <0>;
+
+                        typec_hs_ep: endpoint {
+                            remote-endpoint = <&usb_hs_ep>;
+                        };
+                    };
+
+                    port@1 {
+                        reg = <1>;
+
+                        typec_ss_ep: endpoint {
+                            remote-endpoint = <&usb_ss_ep>;
+                        };
+                    };
+                };
+            };
+        };
+    };
+
+    usb {
+        usb-role-switch;
+
+        port {
+            usb_hs_ep: endpoint {
+                remote-endpoint = <&typec_hs_ep>;
+            };
+        };
+    };
+
+    usb-mux {
+        orientation-switch;
+        mode-switch;
+
+        port {
+            usb_ss_ep: endpoint {
+                remote-endpoint = <&typec_ss_ep>;
+            };
+        };
+    };
-- 
2.50.1


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

* [PATCH 2/4] usb: typec: add CIX Type-C PD controller driver
  2026-11-07  1:10 [PATCH 0/4] usb: typec: add CIX Type-C PD controller driver Gary Yang
  2026-11-07  1:10 ` [PATCH 1/4] dt-bindings: usb: add Realtek RTS5453H Type-C port controller Gary Yang
@ 2026-11-07  1:10 ` Gary Yang
  2026-11-07 10:56   ` Krzysztof Kozlowski
  2026-11-07 13:11   ` kernel test robot
  2026-11-07  1:10 ` [PATCH 3/4] arm64: dts: cix: fix I2C bus address and size cells on Sky1 Gary Yang
  2026-11-07  1:10 ` [PATCH 4/4] arm64: dts: cix: add RTS5453 PD controllers to Orion O6 Gary Yang
  3 siblings, 2 replies; 10+ messages in thread
From: Gary Yang @ 2026-11-07  1:10 UTC (permalink / raw)
  To: Heikki Krogerus, Greg Kroah-Hartman, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Fugang Duan
  Cc: linux-usb, devicetree, linux-arm-kernel, linux-kernel,
	cix-kernel-upstream, gary.yang

Add a driver for the CIX Type-C PD controller, which is connected over
I2C and reports power, data and plug orientation changes through a
level-triggered interrupt. The Realtek RTS5453H is the first supported
device.

The controller runs the Type-C and PD state machines in firmware and
exposes the current port status over I2C. The driver translates that
into Type-C class notifications, driving the orientation switch and
mode mux registered by the platform PHY for the DisplayPort alternate
mode. A USB role switch is registered so that the USB controller can
follow the negotiated data role.

Several controller instances can share a single interrupt line, so each
instance registers a handler on it with IRQF_SHARED and only accesses
its own registers.

The driver is named after the CIX platform integration rather than the
RTS5453H so that further register-compatible PD controllers can be
supported without renaming it.

Signed-off-by: Gary Yang <gary.yang@cixtech.com>
---
 MAINTAINERS                |   7 +
 drivers/usb/typec/Kconfig  |  11 +
 drivers/usb/typec/Makefile |   1 +
 drivers/usb/typec/cix_pd.c | 777 +++++++++++++++++++++++++++++++++++++
 4 files changed, 796 insertions(+)
 create mode 100644 drivers/usb/typec/cix_pd.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 93abbb315065..bd99313bf91d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6522,6 +6522,13 @@ M:	Satish Kharat <satishkh@cisco.com>
 S:	Supported
 F:	drivers/infiniband/hw/usnic/
 
+CIX USB TYPE-C PD CONTROLLER
+M:	Gary Yang <gary.yang@cixtech.com>
+L:	linux-usb@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/usb/realtek,rts5453h.yaml
+F:	drivers/usb/typec/cix_pd.c
+
 CLANG CONTEXT ANALYSIS
 M:	Marco Elver <elver@google.com>
 R:	Bart Van Assche <bvanassche@acm.org>
diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
index 2f80c2792dbd..b81a0d946d00 100644
--- a/drivers/usb/typec/Kconfig
+++ b/drivers/usb/typec/Kconfig
@@ -110,6 +110,17 @@ config TYPEC_WUSB3801
 	  If you choose to build this driver as a dynamically linked module, the
 	  module will be called wusb3801.ko.
 
+config TYPEC_CIX_PD
+	tristate "CIX Type-C PD controller driver"
+	depends on I2C
+	select REGMAP_I2C
+	help
+	  Say Y or M here if your system has a CIX Type-C PD controller.
+	  This driver currently supports the Realtek RTS5453H controller.
+
+	  If you choose to build this driver as a dynamically linked module, the
+	  module will be called cix_pd.ko.
+
 source "drivers/usb/typec/mux/Kconfig"
 
 source "drivers/usb/typec/altmodes/Kconfig"
diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index 8a6a1c663eb6..bb3f3139fd7e 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -11,4 +11,5 @@ obj-$(CONFIG_TYPEC_HD3SS3220)	+= hd3ss3220.o
 obj-$(CONFIG_TYPEC_STUSB160X) 	+= stusb160x.o
 obj-$(CONFIG_TYPEC_RT1719)	+= rt1719.o
 obj-$(CONFIG_TYPEC_WUSB3801)	+= wusb3801.o
+obj-$(CONFIG_TYPEC_CIX_PD)	+= cix_pd.o
 obj-$(CONFIG_TYPEC)		+= mux/
diff --git a/drivers/usb/typec/cix_pd.c b/drivers/usb/typec/cix_pd.c
new file mode 100644
index 000000000000..b65e1a4760b7
--- /dev/null
+++ b/drivers/usb/typec/cix_pd.c
@@ -0,0 +1,777 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Cix Technology Group Co., Ltd.
+ *
+ * Driver for the CIX Type-C PD controller.
+ */
+
+#include <linux/bits.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/kstrtox.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/usb/pd.h>
+#include <linux/usb/typec.h>
+#include <linux/usb/typec_altmode.h>
+#include <linux/usb/typec_dp.h>
+#include <linux/usb/typec_mux.h>
+#include <linux/usb/typec_tbt.h>
+#include <linux/usb/role.h>
+
+#define RTS_DATA_CONTROL		0x50
+#define I2C_INT_ACK_BIT			BIT(2)
+#define HPD_IRQ_ACK			BIT(5)
+#define RTS_DATA_STATUS			0x5F
+/* byte 1 */
+#define CONNECTION_ORIENTATION		BIT(1)
+#define USB2_CONNECTION			BIT(4)
+#define USB3_2_CONNECTION		BIT(5)
+#define USB_DATA_ROLE			BIT(7)
+/* byte 2 */
+#define DP_CONNECTION			BIT(0)
+#define DP_SOURCE_SINK			BIT(1)
+#define DEBUG_ACCESSORY_ATTACHED	BIT(4)
+#define HPD_IRQ				BIT(6)
+#define HPD_STATE			BIT(7)
+/* byte 3 */
+#define POWER_SOURCE			BIT(0)
+#define POWER_SINK			BIT(1)
+#define POWER_MASK			3
+
+#define DATA_STATUS_COUNT		6
+
+struct cix_pd {
+	struct device *dev;
+	struct regmap *regmap;
+	struct mutex lock; /* device lock */
+	struct mutex irq_lock; /* protects IRQ state transitions */
+	int id;
+	int irq;
+	u8 data_status[DATA_STATUS_COUNT];
+	struct typec_displayport_data dp_data;
+	struct typec_mux_state state;
+	struct typec_capability caps;
+	struct typec_mux *mux;
+	struct usb_role_switch *role_sw;
+	struct typec_altmode *port_altmode;
+	struct typec_altmode *partner_altmode;
+	struct typec_port *port;
+	enum usb_role role;
+	unsigned long mode;
+	u32 hpd_status;
+	struct typec_partner	*partner;
+	struct typec_partner_desc desc;
+	bool pd_int_disabled;
+	bool irq_requested;
+	bool irq_disabled;
+	bool suspended;
+	bool wake_enabled;
+};
+
+static int cix_pd_block_read(struct cix_pd *rts, u8 reg, void *val, size_t len)
+{
+	return regmap_bulk_read(rts->regmap, reg, val, len);
+}
+
+static int cix_pd_block_write(struct cix_pd *rts, u8 reg,
+			      const void *val, size_t len)
+{
+	return regmap_bulk_write(rts->regmap, reg, val, len);
+}
+
+static const struct regmap_config cix_pd_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = 0x7F,
+};
+
+static const char *const dp_only_mode = "Alt Mode:DP";
+static const char *const dp_usb_mode = "Alt Mode:DP + USB";
+static const char *const usb_mode = "Alt Mode:USB";
+
+static const char *const power_source = "power source";
+static const char *const power_sink = "power sink";
+
+static const char *const orientation_normal = "orientation normal";
+static const char *const orientation_reverse = "orientation reverse";
+
+static const char *const data_host = "usb host";
+static const char *const data_device = "usb device";
+
+static const char *cix_pd_pwr_role_str(const struct cix_pd *rts)
+{
+	switch (rts->data_status[3] & POWER_MASK) {
+	case POWER_SOURCE:
+		return power_source;
+	case POWER_SINK:
+		return power_sink;
+	default:
+		return "unknown";
+	}
+}
+
+static const struct typec_altmode_desc cix_pd_dp_altmode = {
+	.svid = USB_TYPEC_DP_SID,
+	.mode = USB_TYPEC_DP_MODE,
+};
+
+static int cix_pd_typec_port_update(struct cix_pd *typec)
+{
+	struct typec_displayport_data *dp_data = &typec->dp_data;
+	enum typec_orientation orientation;
+	enum usb_role role;
+	bool connected;
+	int ret = 0;
+
+	if (!(typec->data_status[1] & USB3_2_CONNECTION) &&
+	    !(typec->data_status[2] & DP_CONNECTION))
+		orientation = TYPEC_ORIENTATION_NONE;
+	else if (typec->data_status[1] & CONNECTION_ORIENTATION)
+		orientation = TYPEC_ORIENTATION_REVERSE;
+	else
+		orientation = TYPEC_ORIENTATION_NORMAL;
+
+	ret = typec_set_orientation(typec->port, orientation);
+	if (ret)
+		return ret;
+
+	if (!(typec->data_status[1] & USB3_2_CONNECTION) &&
+	    !(typec->data_status[1] & USB2_CONNECTION))
+		role = USB_ROLE_NONE;
+	else if (typec->data_status[1] & USB_DATA_ROLE)
+		role = USB_ROLE_DEVICE;
+	else
+		role = USB_ROLE_HOST;
+
+	if (typec->data_status[2] & DEBUG_ACCESSORY_ATTACHED)
+		dev_err(typec->dev,
+			"debug accessory mode not supported, try another usb cable\n");
+
+	/*
+	 * Update the partner before building the mux state so that the DP
+	 * alternate mode handed to the mux is always a registered one.
+	 * Debug accessories are treated as not connected, since they are
+	 * not supported.
+	 */
+	connected = !(typec->data_status[2] & DEBUG_ACCESSORY_ATTACHED) &&
+		    ((typec->data_status[1] & (USB2_CONNECTION | USB3_2_CONNECTION)) ||
+		     (typec->data_status[2] & DP_CONNECTION));
+	if (!connected) {
+		if (typec->partner) {
+			dev_info(typec->dev, "typec port(%d): unregister partner\n",
+				 typec->id);
+			typec_unregister_altmode(typec->partner_altmode);
+			typec->partner_altmode = NULL;
+			typec_unregister_partner(typec->partner);
+			typec->partner = NULL;
+		}
+	} else {
+		if (!typec->partner) {
+			dev_info(typec->dev, "typec port(%d): register partner\n",
+				 typec->id);
+			typec->partner = typec_register_partner(typec->port,
+								&typec->desc);
+			if (IS_ERR(typec->partner)) {
+				dev_err(typec->dev,
+					"typec port(%d): failed to register partner: %ld\n",
+					typec->id, PTR_ERR(typec->partner));
+				typec->partner = NULL;
+			}
+		}
+
+		if (typec->partner && !typec->partner_altmode) {
+			typec->partner_altmode =
+				typec_partner_register_altmode(typec->partner,
+							       &cix_pd_dp_altmode);
+			if (IS_ERR(typec->partner_altmode)) {
+				dev_err(typec->dev,
+					"typec port(%d): failed to register DP altmode: %ld\n",
+					typec->id,
+					PTR_ERR(typec->partner_altmode));
+				typec->partner_altmode = NULL;
+			}
+		}
+	}
+
+	if ((typec->data_status[1] & USB3_2_CONNECTION) &&
+	    (typec->data_status[2] & DP_CONNECTION)) {
+		/* usb + dp */
+		dp_data->status = DP_STATUS_ENABLED;
+		if (typec->data_status[2] & HPD_STATE)
+			dp_data->status |= DP_STATUS_HPD_STATE;
+		if (typec->data_status[2] & HPD_IRQ)
+			dp_data->status |= DP_STATUS_IRQ_HPD;
+
+		typec->state.data = dp_data;
+		typec->state.mode = TYPEC_DP_STATE_D;
+		typec->state.alt = typec->partner_altmode;
+	} else if (typec->data_status[2] & DP_CONNECTION) {
+		/* dp only */
+		dp_data->status = DP_STATUS_ENABLED;
+		if (typec->data_status[2] & HPD_STATE)
+			dp_data->status |= DP_STATUS_HPD_STATE;
+		if (typec->data_status[2] & HPD_IRQ)
+			dp_data->status |= DP_STATUS_IRQ_HPD;
+		typec->state.data = dp_data;
+		typec->state.mode = TYPEC_DP_STATE_E;
+		typec->state.alt = typec->partner_altmode;
+	} else if (typec->data_status[1] & USB3_2_CONNECTION) {
+		/* usb only */
+		dp_data->status = 0;
+		typec->state.mode = TYPEC_STATE_USB;
+		typec->state.alt = NULL;
+		typec->state.data = NULL;
+	} else {
+		/* disconnect */
+		dp_data->status = 0;
+		typec->state.mode = TYPEC_STATE_SAFE;
+		typec->state.alt = NULL;
+		typec->state.data = NULL;
+	}
+
+	ret = typec_mux_set(typec->mux, &typec->state);
+	if (ret)
+		return ret;
+
+	if ((typec->data_status[3] & POWER_MASK) == POWER_SOURCE) {
+		typec_set_pwr_role(typec->port, TYPEC_SOURCE);
+	} else if ((typec->data_status[3] & POWER_MASK) == POWER_SINK) {
+		typec_set_pwr_role(typec->port, TYPEC_SINK);
+	} else {
+		dev_dbg(typec->dev, "typec port(%d): unknown power state %d\n",
+			typec->id, typec->data_status[3]);
+	}
+
+	if (typec->role != role) {
+		typec->role = role;
+		ret = usb_role_switch_set_role(typec->role_sw, role);
+		if (role == USB_ROLE_DEVICE)
+			typec_set_data_role(typec->port, TYPEC_DEVICE);
+		else if (role == USB_ROLE_HOST)
+			typec_set_data_role(typec->port, TYPEC_HOST);
+	}
+
+	if (typec->mode == typec->state.mode &&
+	    typec->hpd_status == dp_data->status)
+		return ret;
+
+	if (typec->state.mode == TYPEC_DP_STATE_D)
+		dev_info(typec->dev,
+			 "typec port(%d): alt mode(%s) orientation(%s), usb_data_role(%s), power role(%s), hpd_state(%d)\n",
+			 typec->id, dp_usb_mode,
+			 orientation == TYPEC_ORIENTATION_REVERSE ?
+			 orientation_reverse : orientation_normal,
+			 role == USB_ROLE_DEVICE ? data_device : data_host,
+			 cix_pd_pwr_role_str(typec),
+			 (int)(dp_data->status & DP_STATUS_HPD_STATE));
+	else if (typec->state.mode == TYPEC_DP_STATE_E)
+		dev_info(typec->dev,
+			 "typec port(%d): alt mode(%s) orientation(%s), power role(%s), hpd_state(%d)\n",
+			 typec->id, dp_only_mode,
+			 orientation == TYPEC_ORIENTATION_REVERSE ?
+			 orientation_reverse : orientation_normal,
+			 cix_pd_pwr_role_str(typec),
+			 (int)(dp_data->status & DP_STATUS_HPD_STATE));
+	else if (typec->state.mode == TYPEC_STATE_USB)
+		dev_info(typec->dev,
+			 "typec port(%d): alt mode(%s) orientation(%s), power role(%s), usb_data_role(%s)\n",
+			 typec->id, usb_mode,
+			 orientation == TYPEC_ORIENTATION_REVERSE ?
+			 orientation_reverse : orientation_normal,
+			 cix_pd_pwr_role_str(typec),
+			 role == USB_ROLE_DEVICE ? data_device : data_host);
+	else if (typec->state.mode == TYPEC_STATE_SAFE)
+		dev_info(typec->dev, "typec port(%d): disconnect state\n",
+			 typec->id);
+
+	typec->mode = typec->state.mode;
+	typec->hpd_status = dp_data->status;
+
+	return ret;
+}
+
+static void cix_pd_typec_init_state(struct cix_pd *typec)
+{
+	typec->state.alt = NULL;
+	typec->state.mode = TYPEC_STATE_USB;
+	typec->state.data = NULL;
+	typec->partner = NULL;
+	typec->desc.accessory = TYPEC_ACCESSORY_NONE;
+	/*
+	 * The data status registers only report the source/sink state, not
+	 * whether USB PD is being used, so the partner is not advertised as
+	 * PD capable.
+	 */
+	typec->desc.usb_pd = 0;
+}
+
+static int cix_pd_event_handler(struct cix_pd *rts, u8 clear_irq)
+{
+	int ret, err = 0;
+	u8 data_control[3] = {0};
+
+	mutex_lock(&rts->lock);
+	if (clear_irq) {
+		data_control[0] = 2;
+		data_control[1] = I2C_INT_ACK_BIT;
+		data_control[2] = 0;
+
+		ret = cix_pd_block_write(rts, RTS_DATA_CONTROL, data_control, 3);
+		if (ret)
+			goto i2c_err;
+	}
+
+	ret = cix_pd_block_read(rts, RTS_DATA_STATUS, rts->data_status, 6);
+	if (ret)
+		goto i2c_err;
+
+	dev_dbg(rts->dev,
+		"typec port(%d): data status = 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
+		rts->id, rts->data_status[0], rts->data_status[1],
+		rts->data_status[2], rts->data_status[3], rts->data_status[4],
+		rts->data_status[5]);
+
+	err = cix_pd_typec_port_update(rts);
+	if (err)
+		dev_err(rts->dev, "typec port(%d): failed to update port: %d\n",
+			rts->id, err);
+
+	if (rts->data_status[2] & HPD_IRQ) {
+		data_control[0] = 2;
+		data_control[1] = 0;
+		data_control[2] = HPD_IRQ_ACK;
+		ret = cix_pd_block_write(rts, RTS_DATA_CONTROL, data_control, 3);
+		if (ret)
+			goto i2c_err;
+	}
+
+	mutex_unlock(&rts->lock);
+	return err;
+
+i2c_err:
+	/*
+	 * Report the controller as unresponsive; the IRQ handler turns this
+	 * into IRQ_NONE so a stuck line is throttled by the IRQ core.
+	 */
+	dev_err(rts->dev, "typec port(%d): i2c transfer error = %d\n",
+		rts->id, ret);
+	mutex_unlock(&rts->lock);
+	return -ESHUTDOWN;
+}
+
+static int cix_pd_typec_parse_port_props(struct typec_capability *cap,
+					 struct fwnode_handle *fwnode,
+					 struct device *dev)
+{
+	const char *buf;
+	int ret;
+
+	memset(cap, 0, sizeof(*cap));
+	ret = fwnode_property_read_string(fwnode, "power-role", &buf);
+	if (ret) {
+		dev_err(dev, "power-role not found: %d\n", ret);
+		return ret;
+	}
+	ret = typec_find_port_power_role(buf);
+	if (ret < 0)
+		return ret;
+	cap->type = ret;
+	ret = fwnode_property_read_string(fwnode, "data-role", &buf);
+	if (ret) {
+		dev_err(dev, "data-role not found: %d\n", ret);
+		return ret;
+	}
+	ret = typec_find_port_data_role(buf);
+	if (ret < 0)
+		return ret;
+	cap->data = ret;
+	ret = fwnode_property_read_string(fwnode, "try-power-role", &buf);
+	if (ret) {
+		dev_err(dev, "try-power-role not found: %d\n", ret);
+		return ret;
+	}
+	ret = typec_find_power_role(buf);
+	if (ret < 0)
+		return ret;
+	cap->prefer_role = ret;
+	cap->fwnode = fwnode_handle_get(fwnode);
+	cap->orientation_aware = 1;
+	return 0;
+}
+
+static int cix_pd_typec_get_handles(struct cix_pd *typec,
+				    struct fwnode_handle *fwnode,
+				    struct device *dev)
+{
+	typec->mux = fwnode_typec_mux_get(fwnode);
+	if (IS_ERR(typec->mux))
+		return dev_err_probe(dev, PTR_ERR(typec->mux),
+				     "failed to get mux\n");
+
+	typec->role_sw = fwnode_usb_role_switch_get(fwnode);
+	if (IS_ERR(typec->role_sw)) {
+		typec_mux_put(typec->mux);
+		typec->mux = NULL;
+		return dev_err_probe(dev, PTR_ERR(typec->role_sw),
+				     "failed to get USB role switch\n");
+	}
+
+	if (!typec->mux || !typec->role_sw)
+		dev_dbg(dev, "mux or role switch not found\n");
+
+	return 0;
+}
+
+static void cix_pd_unregister_ports(struct cix_pd *typec)
+{
+	if (!IS_ERR_OR_NULL(typec->port)) {
+		typec->state.alt = NULL;
+		typec->state.mode = TYPEC_STATE_USB;
+		typec->state.data = NULL;
+		usb_role_switch_set_role(typec->role_sw, USB_ROLE_NONE);
+		typec_set_orientation(typec->port, TYPEC_ORIENTATION_NONE);
+		typec_mux_set(typec->mux, &typec->state);
+
+		typec_unregister_altmode(typec->partner_altmode);
+		typec->partner_altmode = NULL;
+		typec_unregister_partner(typec->partner);
+		typec->partner = NULL;
+		typec_unregister_altmode(typec->port_altmode);
+		typec->port_altmode = NULL;
+
+		typec_unregister_port(typec->port);
+		typec->port = NULL;
+	}
+
+	usb_role_switch_put(typec->role_sw);
+	typec->role_sw = NULL;
+	typec_mux_put(typec->mux);
+	typec->mux = NULL;
+	fwnode_handle_put(typec->caps.fwnode);
+	typec->caps.fwnode = NULL;
+}
+
+static irqreturn_t cix_pd_irq_handle(int irq, void *data)
+{
+	struct cix_pd *rts = data;
+
+	if (cix_pd_event_handler(rts, 1) == -ESHUTDOWN)
+		return IRQ_NONE;
+
+	return IRQ_HANDLED;
+}
+
+static int cix_pd_init_ports(struct cix_pd *typec)
+{
+	struct device *dev = typec->dev;
+	struct typec_altmode_desc desc = cix_pd_dp_altmode;
+	int ret, nports;
+
+	nports = device_get_child_node_count(dev);
+	if (nports == 0) {
+		dev_err(dev, "No port entries found.\n");
+		return -ENODEV;
+	}
+
+	if (nports > 1) {
+		dev_err(dev, "More ports listed than can be supported.\n");
+		return -EINVAL;
+	}
+
+	device_for_each_child_node_scoped(dev, fwnode) {
+		ret = fwnode_property_read_u32(fwnode, "reg", &typec->id);
+		if (ret)
+			goto unregister_ports;
+
+		ret = cix_pd_typec_parse_port_props(&typec->caps, fwnode,
+						    dev);
+		if (ret)
+			goto unregister_ports;
+
+		typec->port = typec_register_port(dev, &typec->caps);
+		if (IS_ERR(typec->port)) {
+			ret = PTR_ERR(typec->port);
+			typec->port = NULL;
+			dev_err(dev, "Failed to register port: %d\n", ret);
+			goto unregister_ports;
+		}
+
+		desc.roles = typec->caps.data;
+		typec->port_altmode = typec_port_register_altmode(typec->port,
+								  &desc);
+		if (IS_ERR(typec->port_altmode)) {
+			ret = PTR_ERR(typec->port_altmode);
+			typec->port_altmode = NULL;
+			dev_err(dev, "Failed to register DP altmode: %d\n", ret);
+			goto unregister_ports;
+		}
+
+		ret = cix_pd_typec_get_handles(typec, fwnode, dev);
+		if (ret)
+			goto unregister_ports;
+	}
+
+	return 0;
+
+unregister_ports:
+	cix_pd_unregister_ports(typec);
+	return ret;
+}
+
+/*
+ * The IRQ is disabled when either the user asked for it or the device is
+ * suspended. Keep the disable/enable calls balanced and idempotent.
+ */
+static void cix_pd_sync_irq(struct cix_pd *rts)
+{
+	bool disable = rts->pd_int_disabled || rts->suspended;
+
+	if (!rts->irq_requested || rts->irq_disabled == disable)
+		return;
+
+	rts->irq_disabled = disable;
+	if (disable)
+		disable_irq(rts->irq);
+	else
+		enable_irq(rts->irq);
+}
+
+static ssize_t disable_pd_interrupt_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct cix_pd *rts = i2c_get_clientdata(client);
+	const char *p;
+
+	if (rts->pd_int_disabled)
+		p = "disabled";
+	else
+		p = "enabled";
+
+	return sysfs_emit(buf, "%s\n", p);
+}
+
+static ssize_t disable_pd_interrupt_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct cix_pd *rts = i2c_get_clientdata(client);
+	bool value;
+	int ret;
+
+	ret = kstrtobool(buf, &value);
+	if (ret)
+		return ret;
+
+	mutex_lock(&rts->irq_lock);
+	rts->pd_int_disabled = value;
+	cix_pd_sync_irq(rts);
+	mutex_unlock(&rts->irq_lock);
+
+	dev_alert(dev, "Only for USB Compliance test, PD int is %s\n",
+		  value ? "disabled" : "enabled");
+
+	return count;
+}
+
+DEVICE_ATTR_RW(disable_pd_interrupt);
+
+static int cix_pd_probe(struct i2c_client *client)
+{
+	struct cix_pd *rts;
+	int ret;
+
+	dev_dbg(&client->dev, "IRQ %d supplied\n", client->irq);
+	rts = devm_kzalloc(&client->dev, sizeof(*rts), GFP_KERNEL);
+	if (!rts)
+		return -ENOMEM;
+
+	rts->dev = &client->dev;
+	rts->irq = client->irq;
+	mutex_init(&rts->lock);
+	mutex_init(&rts->irq_lock);
+	cix_pd_typec_init_state(rts);
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+		return dev_err_probe(&client->dev, -ENODEV,
+				     "i2c check functionality failed\n");
+
+	rts->regmap = devm_regmap_init_i2c(client, &cix_pd_regmap_config);
+	if (IS_ERR(rts->regmap))
+		return dev_err_probe(&client->dev, PTR_ERR(rts->regmap),
+				     "failed to init regmap\n");
+
+	i2c_set_clientdata(client, rts);
+
+	ret = cix_pd_init_ports(rts);
+	if (ret)
+		return ret;
+
+	ret = cix_pd_event_handler(rts, 0);
+	if (ret) {
+		dev_err(&client->dev, "failed to read initial status: %d\n", ret);
+		if (ret == -ESHUTDOWN)
+			ret = -EIO;
+		goto err_unregister_ports;
+	}
+
+	if (client->irq > 0) {
+		irq_set_status_flags(client->irq, IRQ_DISABLE_UNLAZY);
+		ret = request_threaded_irq(client->irq, NULL, cix_pd_irq_handle,
+					   IRQF_SHARED | IRQF_ONESHOT,
+					   dev_name(&client->dev), rts);
+		if (ret) {
+			dev_err(&client->dev, "failed to request irq: %d\n", ret);
+			goto err_unregister_ports;
+		}
+		rts->irq_requested = true;
+	}
+
+	ret = device_create_file(&client->dev, &dev_attr_disable_pd_interrupt);
+	if (ret)
+		dev_warn(&client->dev,
+			 "Failed to create sysfs attribute, ret = %d\n", ret);
+
+	device_set_wakeup_capable(&client->dev, true);
+
+	return 0;
+
+err_unregister_ports:
+	cix_pd_unregister_ports(rts);
+	return ret;
+}
+
+static void cix_pd_remove(struct i2c_client *client)
+{
+	struct cix_pd *rts = i2c_get_clientdata(client);
+
+	device_remove_file(&client->dev, &dev_attr_disable_pd_interrupt);
+
+	mutex_lock(&rts->irq_lock);
+	if (rts->irq_requested) {
+		rts->suspended = true;
+		cix_pd_sync_irq(rts);
+		if (rts->wake_enabled) {
+			disable_irq_wake(rts->irq);
+			rts->wake_enabled = false;
+		}
+		free_irq(rts->irq, rts);
+		rts->irq_requested = false;
+	}
+	mutex_unlock(&rts->irq_lock);
+
+	cix_pd_unregister_ports(rts);
+}
+
+static const struct of_device_id cix_pd_of_match[] = {
+	{ .compatible = "realtek,rts5453h", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, cix_pd_of_match);
+
+static const struct i2c_device_id cix_pd_id[] = {
+	{ "rts5453h" },
+	{}
+};
+
+static int __maybe_unused cix_pd_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct cix_pd *rts = i2c_get_clientdata(client);
+
+	mutex_lock(&rts->irq_lock);
+	if (rts->irq_requested) {
+		rts->suspended = true;
+		cix_pd_sync_irq(rts);
+		if (device_may_wakeup(dev)) {
+			enable_irq_wake(rts->irq);
+			rts->wake_enabled = true;
+		}
+	}
+	mutex_unlock(&rts->irq_lock);
+
+	return 0;
+}
+
+static int __maybe_unused cix_pd_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct cix_pd *rts = i2c_get_clientdata(client);
+
+	mutex_lock(&rts->irq_lock);
+	if (rts->irq_requested) {
+		if (rts->wake_enabled) {
+			disable_irq_wake(rts->irq);
+			rts->wake_enabled = false;
+		}
+		rts->suspended = false;
+		cix_pd_sync_irq(rts);
+	}
+	mutex_unlock(&rts->irq_lock);
+
+	return 0;
+}
+
+static int __maybe_unused cix_pd_restore(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct cix_pd *rts = i2c_get_clientdata(client);
+	int ret;
+
+	ret = cix_pd_resume(dev);
+	if (ret)
+		return ret;
+
+	/* Re-trigger PD event if PD event occurs during std */
+	ret = cix_pd_event_handler(rts, 0);
+	if (ret)
+		dev_err(&client->dev, "typec event handling error %d\n", ret);
+
+	return ret;
+}
+
+static void cix_pd_shutdown(struct i2c_client *client)
+{
+	struct cix_pd *rts = i2c_get_clientdata(client);
+
+	mutex_lock(&rts->irq_lock);
+	rts->suspended = true;
+	cix_pd_sync_irq(rts);
+	mutex_unlock(&rts->irq_lock);
+}
+
+static const struct dev_pm_ops cix_pd_pm_ops = {
+	.suspend = pm_sleep_ptr(cix_pd_suspend),
+	.resume = pm_sleep_ptr(cix_pd_resume),
+	.freeze = pm_sleep_ptr(cix_pd_suspend),
+	.thaw = pm_sleep_ptr(cix_pd_resume),
+	.restore = pm_sleep_ptr(cix_pd_restore),
+};
+
+MODULE_DEVICE_TABLE(i2c, cix_pd_id);
+static struct i2c_driver cix_pd_i2c_driver = {
+	.driver = {
+		.name = "rts5453h",
+		.of_match_table = cix_pd_of_match,
+		.pm = pm_ptr(&cix_pd_pm_ops),
+	},
+	.id_table = cix_pd_id,
+	.probe = cix_pd_probe,
+	.remove = cix_pd_remove,
+	.shutdown = cix_pd_shutdown,
+};
+
+module_i2c_driver(cix_pd_i2c_driver);
+MODULE_DESCRIPTION("CIX Type-C PD controller driver");
+MODULE_AUTHOR("Chao Zeng <Chao.Zeng@cixtech.com>");
+MODULE_AUTHOR("Gary Yang <gary.yang@cixtech.com>");
+MODULE_LICENSE("GPL");
-- 
2.50.1


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

* [PATCH 3/4] arm64: dts: cix: fix I2C bus address and size cells on Sky1
  2026-11-07  1:10 [PATCH 0/4] usb: typec: add CIX Type-C PD controller driver Gary Yang
  2026-11-07  1:10 ` [PATCH 1/4] dt-bindings: usb: add Realtek RTS5453H Type-C port controller Gary Yang
  2026-11-07  1:10 ` [PATCH 2/4] usb: typec: add CIX Type-C PD controller driver Gary Yang
@ 2026-11-07  1:10 ` Gary Yang
  2026-11-07 10:53   ` Krzysztof Kozlowski
  2026-11-07  1:10 ` [PATCH 4/4] arm64: dts: cix: add RTS5453 PD controllers to Orion O6 Gary Yang
  3 siblings, 1 reply; 10+ messages in thread
From: Gary Yang @ 2026-11-07  1:10 UTC (permalink / raw)
  To: Heikki Krogerus, Greg Kroah-Hartman, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Fugang Duan
  Cc: linux-usb, devicetree, linux-arm-kernel, linux-kernel,
	cix-kernel-upstream, gary.yang, stable

The I2C controller nodes inherit #address-cells = <2> and #size-cells
= <2> from the soc@0 bus, but I2C child devices always use a
single-cell address and no size. With the inherited values every
child node triggers a reg_format warning, and a device described on
the bus would be decoded with the wrong address layout.

Set #address-cells = <1> and #size-cells = <0> on all eight I2C
controllers, matching the I3C nodes that already do so.

Fixes: fad32e8ac460 ("arm64: dts: cix: add DT nodes for all I2C and I3C ports for sky1")
Cc: stable@vger.kernel.org
Signed-off-by: Gary Yang <gary.yang@cixtech.com>
---
 arch/arm64/boot/dts/cix/sky1.dtsi | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/arch/arm64/boot/dts/cix/sky1.dtsi b/arch/arm64/boot/dts/cix/sky1.dtsi
index 0a820f45feb9..a18e8fd43fcf 100644
--- a/arch/arm64/boot/dts/cix/sky1.dtsi
+++ b/arch/arm64/boot/dts/cix/sky1.dtsi
@@ -290,6 +290,8 @@ soc@0 {
 		i2c0: i2c@4010000 {
 			compatible = "cdns,i2c-r1p14";
 			reg = <0x0 0x04010000 0x0 0x10000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
 			clock-frequency = <400000>;
 			clocks = <&scmi_clk CLK_TREE_FCH_I2C0_APB>;
 			interrupts = <GIC_SPI 286 IRQ_TYPE_LEVEL_HIGH 0>;
@@ -299,6 +301,8 @@ i2c0: i2c@4010000 {
 		i2c1: i2c@4020000 {
 			compatible = "cdns,i2c-r1p14";
 			reg = <0x0 0x04020000 0x0 0x10000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
 			clock-frequency = <400000>;
 			clocks = <&scmi_clk CLK_TREE_FCH_I2C1_APB>;
 			interrupts = <GIC_SPI 287 IRQ_TYPE_LEVEL_HIGH 0>;
@@ -308,6 +312,8 @@ i2c1: i2c@4020000 {
 		i2c2: i2c@4030000 {
 			compatible = "cdns,i2c-r1p14";
 			reg = <0x0 0x04030000 0x0 0x10000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
 			clock-frequency = <400000>;
 			clocks = <&scmi_clk CLK_TREE_FCH_I2C2_APB>;
 			interrupts = <GIC_SPI 288 IRQ_TYPE_LEVEL_HIGH 0>;
@@ -317,6 +323,8 @@ i2c2: i2c@4030000 {
 		i2c3: i2c@4040000 {
 			compatible = "cdns,i2c-r1p14";
 			reg = <0x0 0x04040000 0x0 0x10000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
 			clock-frequency = <400000>;
 			clocks = <&scmi_clk CLK_TREE_FCH_I2C3_APB>;
 			interrupts = <GIC_SPI 289 IRQ_TYPE_LEVEL_HIGH 0>;
@@ -326,6 +334,8 @@ i2c3: i2c@4040000 {
 		i2c4: i2c@4050000 {
 			compatible = "cdns,i2c-r1p14";
 			reg = <0x0 0x04050000 0x0 0x10000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
 			clock-frequency = <400000>;
 			clocks = <&scmi_clk CLK_TREE_FCH_I2C4_APB>;
 			interrupts = <GIC_SPI 290 IRQ_TYPE_LEVEL_HIGH 0>;
@@ -335,6 +345,8 @@ i2c4: i2c@4050000 {
 		i2c5: i2c@4060000 {
 			compatible = "cdns,i2c-r1p14";
 			reg = <0x0 0x04060000 0x0 0x10000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
 			clock-frequency = <400000>;
 			clocks = <&scmi_clk CLK_TREE_FCH_I2C5_APB>;
 			interrupts = <GIC_SPI 291 IRQ_TYPE_LEVEL_HIGH 0>;
@@ -344,6 +356,8 @@ i2c5: i2c@4060000 {
 		i2c6: i2c@4070000 {
 			compatible = "cdns,i2c-r1p14";
 			reg = <0x0 0x04070000 0x0 0x10000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
 			clock-frequency = <400000>;
 			clocks = <&scmi_clk CLK_TREE_FCH_I2C6_APB>;
 			interrupts = <GIC_SPI 292 IRQ_TYPE_LEVEL_HIGH 0>;
@@ -353,6 +367,8 @@ i2c6: i2c@4070000 {
 		i2c7: i2c@4080000 {
 			compatible = "cdns,i2c-r1p14";
 			reg = <0x0 0x04080000 0x0 0x10000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
 			clock-frequency = <400000>;
 			clocks = <&scmi_clk CLK_TREE_FCH_I2C7_APB>;
 			interrupts = <GIC_SPI 293 IRQ_TYPE_LEVEL_HIGH 0>;
-- 
2.50.1


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

* [PATCH 4/4] arm64: dts: cix: add RTS5453 PD controllers to Orion O6
  2026-11-07  1:10 [PATCH 0/4] usb: typec: add CIX Type-C PD controller driver Gary Yang
                   ` (2 preceding siblings ...)
  2026-11-07  1:10 ` [PATCH 3/4] arm64: dts: cix: fix I2C bus address and size cells on Sky1 Gary Yang
@ 2026-11-07  1:10 ` Gary Yang
  2026-11-07 10:53   ` Krzysztof Kozlowski
  3 siblings, 1 reply; 10+ messages in thread
From: Gary Yang @ 2026-11-07  1:10 UTC (permalink / raw)
  To: Heikki Krogerus, Greg Kroah-Hartman, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Fugang Duan
  Cc: linux-usb, devicetree, linux-arm-kernel, linux-kernel,
	cix-kernel-upstream, gary.yang

Describe the two Realtek RTS5453H port controllers on the Orion O6
I2C1 bus and add the I2C1 pin configuration.

The controllers route orientation and DisplayPort mode changes
through the USB/DP combo PHY lane switches, but that PHY support is
not upstream yet, so the connector graph endpoints are left out for
now. The Type-C ports are still registered and reported, only the
lane switching is deferred until the PHY nodes are available.

Both controllers report through the same level-triggered interrupt
line, which the driver handles with a shared interrupt. The I2C bus
runs at 100 kHz because the RTS5453H I2C interface does not support
higher rates.

Signed-off-by: Gary Yang <gary.yang@cixtech.com>
---
 arch/arm64/boot/dts/cix/sky1-orion-o6.dts | 51 +++++++++++++++++++++++
 1 file changed, 51 insertions(+)

diff --git a/arch/arm64/boot/dts/cix/sky1-orion-o6.dts b/arch/arm64/boot/dts/cix/sky1-orion-o6.dts
index e39c87774c12..53c66ab30679 100644
--- a/arch/arm64/boot/dts/cix/sky1-orion-o6.dts
+++ b/arch/arm64/boot/dts/cix/sky1-orion-o6.dts
@@ -52,6 +52,48 @@ &fch_gpio3 {
 	status = "okay";
 };
 
+&i2c1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_fch_i2c1>;
+	clock-frequency = <100000>;
+
+	status = "okay";
+
+	typec@30 {
+		compatible = "realtek,rts5453h";
+		reg = <0x30>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupt-parent = <&s5_gpio0>;
+		interrupts = <8 IRQ_TYPE_LEVEL_LOW>;
+
+		connector@0 {
+			compatible = "usb-c-connector";
+			reg = <0>;
+			data-role = "host";
+			power-role = "source";
+			try-power-role = "source";
+		};
+	};
+
+	typec@31 {
+		compatible = "realtek,rts5453h";
+		reg = <0x31>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupt-parent = <&s5_gpio0>;
+		interrupts = <8 IRQ_TYPE_LEVEL_LOW>;
+
+		connector@2 {
+			compatible = "usb-c-connector";
+			reg = <2>;
+			data-role = "host";
+			power-role = "source";
+			try-power-role = "source";
+		};
+	};
+};
+
 &iomuxc {
 	pinctrl-names = "default";
 	pinctrl-0 = <&pinctrl_hog>;
@@ -66,6 +108,15 @@ pins {
 			drive-strength = <8>;
 		};
 	};
+
+	pinctrl_fch_i2c1: i2c1-cfg {
+		pins {
+			pinmux = <CIX_PAD_I2C1_CLK_FUNC_I2C1_CLK>,
+				<CIX_PAD_I2C1_SDA_FUNC_I2C1_SDA>;
+			bias-pull-up;
+			drive-strength = <12>;
+		};
+	};
 };
 
 &iomuxc_s5 {
-- 
2.50.1


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

* Re: [PATCH 3/4] arm64: dts: cix: fix I2C bus address and size cells on Sky1
  2026-11-07  1:10 ` [PATCH 3/4] arm64: dts: cix: fix I2C bus address and size cells on Sky1 Gary Yang
@ 2026-11-07 10:53   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 10+ messages in thread
From: Krzysztof Kozlowski @ 2026-11-07 10:53 UTC (permalink / raw)
  To: Gary Yang, Heikki Krogerus, Greg Kroah-Hartman, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Fugang Duan
  Cc: linux-usb, devicetree, linux-arm-kernel, linux-kernel,
	cix-kernel-upstream, stable

On 07/11/2026 02:10, Gary Yang wrote:
> The I2C controller nodes inherit #address-cells = <2> and #size-cells
> = <2> from the soc@0 bus, but I2C child devices always use a
> single-cell address and no size. With the inherited values every
> child node triggers a reg_format warning, and a device described on
> the bus would be decoded with the wrong address layout.
> 
> Set #address-cells = <1> and #size-cells = <0> on all eight I2C
> controllers, matching the I3C nodes that already do so.
> 
> Fixes: fad32e8ac460 ("arm64: dts: cix: add DT nodes for all I2C and I3C ports for sky1")
> Cc: stable@vger.kernel.org
> Signed-off-by: Gary Yang <gary.yang@cixtech.com>

1. Fixes cannot depend on the driver.

2. Also, explain what is the bug here. Your commit msg has absolutely no
indication of the bug.

3. DTS cannot be combined in this patchset and must be split. Don't mix
patches from independent subsystems when you send something explicitly
targeting only one subsystem.
See also: Documentation/devicetree/bindings/submitting-patches.rst


> ---
>  arch/arm64/boot/dts/cix/sky1.dtsi | 16 ++++++++++++++++
>  1 file changed, 16 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/cix/sky1.dtsi b/arch/arm64/boot/dts/cix/sky1.dtsi
> index 0a820f45feb9..a18e8fd43fcf 100644
> --- a/arch/arm64/boot/dts/cix/sky1.dtsi
> +++ b/arch/arm64/boot/dts/cix/sky1.dtsi
> @@ -290,6 +290,8 @@ soc@0 {
>  		i2c0: i2c@4010000 {
>  			compatible = "cdns,i2c-r1p14";
>  			reg = <0x0 0x04010000 0x0 0x10000>;
> +			#address-cells = <1>;
> +			#size-cells = <0>;

How is this a fix? Really.



Best regards,
Krzysztof

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

* Re: [PATCH 4/4] arm64: dts: cix: add RTS5453 PD controllers to Orion O6
  2026-11-07  1:10 ` [PATCH 4/4] arm64: dts: cix: add RTS5453 PD controllers to Orion O6 Gary Yang
@ 2026-11-07 10:53   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 10+ messages in thread
From: Krzysztof Kozlowski @ 2026-11-07 10:53 UTC (permalink / raw)
  To: Gary Yang, Heikki Krogerus, Greg Kroah-Hartman, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Fugang Duan
  Cc: linux-usb, devicetree, linux-arm-kernel, linux-kernel,
	cix-kernel-upstream

On 07/11/2026 02:10, Gary Yang wrote:
> Describe the two Realtek RTS5453H port controllers on the Orion O6
> I2C1 bus and add the I2C1 pin configuration.
> 
> The controllers route orientation and DisplayPort mode changes
> through the USB/DP combo PHY lane switches, but that PHY support is
> not upstream yet, so the connector graph endpoints are left out for
> now. The Type-C ports are still registered and reported, only the
> lane switching is deferred until the PHY nodes are available.
> 
> Both controllers report through the same level-triggered interrupt
> line, which the driver handles with a shared interrupt. The I2C bus
> runs at 100 kHz because the RTS5453H I2C interface does not support
> higher rates.
> 
> Signed-off-by: Gary Yang <gary.yang@cixtech.com>
> ---
>  arch/arm64/boot/dts/cix/sky1-orion-o6.dts | 51 +++++++++++++++++++++++
>  1 file changed, 51 insertions(+)

DTS cannot be combined in this patchset and must be split. Don't mix
patches from independent subsystems when you send something explicitly
targeting only one subsystem.
See also: Documentation/devicetree/bindings/submitting-patches.rst


Best regards,
Krzysztof

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

* Re: [PATCH 1/4] dt-bindings: usb: add Realtek RTS5453H Type-C port controller
  2026-11-07  1:10 ` [PATCH 1/4] dt-bindings: usb: add Realtek RTS5453H Type-C port controller Gary Yang
@ 2026-11-07 10:54   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 10+ messages in thread
From: Krzysztof Kozlowski @ 2026-11-07 10:54 UTC (permalink / raw)
  To: Gary Yang, Heikki Krogerus, Greg Kroah-Hartman, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Fugang Duan
  Cc: linux-usb, devicetree, linux-arm-kernel, linux-kernel,
	cix-kernel-upstream

On 07/11/2026 02:10, Gary Yang wrote:
> Document the Realtek RTS5453H stand-alone USB Type-C port controller.
> The controller is connected over I2C and manages a single USB Type-C
> connector, described with the generic USB connector binding.
> 
> The connector's OF graph links to the Type-C orientation switch and
> mode mux provided by the platform PHY.
> 
> Signed-off-by: Gary Yang <gary.yang@cixtech.com>
> ---
>  .../bindings/usb/realtek,rts5453h.yaml        | 126 ++++++++++++++++++
>  1 file changed, 126 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/usb/realtek,rts5453h.yaml
> 

Valid comments from Sashiko.

Best regards,
Krzysztof

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

* Re: [PATCH 2/4] usb: typec: add CIX Type-C PD controller driver
  2026-11-07  1:10 ` [PATCH 2/4] usb: typec: add CIX Type-C PD controller driver Gary Yang
@ 2026-11-07 10:56   ` Krzysztof Kozlowski
  2026-11-07 13:11   ` kernel test robot
  1 sibling, 0 replies; 10+ messages in thread
From: Krzysztof Kozlowski @ 2026-11-07 10:56 UTC (permalink / raw)
  To: Gary Yang, Heikki Krogerus, Greg Kroah-Hartman, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Fugang Duan
  Cc: linux-usb, devicetree, linux-arm-kernel, linux-kernel,
	cix-kernel-upstream

On 07/11/2026 02:10, Gary Yang wrote:
>  CLANG CONTEXT ANALYSIS
>  M:	Marco Elver <elver@google.com>
>  R:	Bart Van Assche <bvanassche@acm.org>
> diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
> index 2f80c2792dbd..b81a0d946d00 100644
> --- a/drivers/usb/typec/Kconfig
> +++ b/drivers/usb/typec/Kconfig
> @@ -110,6 +110,17 @@ config TYPEC_WUSB3801
>  	  If you choose to build this driver as a dynamically linked module, the
>  	  module will be called wusb3801.ko.
>  
> +config TYPEC_CIX_PD
> +	tristate "CIX Type-C PD controller driver"
> +	depends on I2C

depends on your ARCH || COMPILE_TEST

> +	select REGMAP_I2C
> +	help
> +	  Say Y or M here if your system has a CIX Type-C PD controller.
> +	  This driver currently supports the Realtek RTS5453H controller.
> +
> +	  If you choose to build this driver as a dynamically linked module, the
> +	  module will be called cix_pd.ko.


...


> +static int cix_pd_init_ports(struct cix_pd *typec)
> +{
> +	struct device *dev = typec->dev;
> +	struct typec_altmode_desc desc = cix_pd_dp_altmode;
> +	int ret, nports;
> +
> +	nports = device_get_child_node_count(dev);
> +	if (nports == 0) {
> +		dev_err(dev, "No port entries found.\n");
> +		return -ENODEV;
> +	}
> +
> +	if (nports > 1) {
> +		dev_err(dev, "More ports listed than can be supported.\n");
> +		return -EINVAL;
> +	}
> +
> +	device_for_each_child_node_scoped(dev, fwnode) {
> +		ret = fwnode_property_read_u32(fwnode, "reg", &typec->id);
> +		if (ret)
> +			goto unregister_ports;
> +
> +		ret = cix_pd_typec_parse_port_props(&typec->caps, fwnode,
> +						    dev);
> +		if (ret)
> +			goto unregister_ports;
> +
> +		typec->port = typec_register_port(dev, &typec->caps);
> +		if (IS_ERR(typec->port)) {
> +			ret = PTR_ERR(typec->port);
> +			typec->port = NULL;
> +			dev_err(dev, "Failed to register port: %d\n", ret);
> +			goto unregister_ports;
> +		}
> +
> +		desc.roles = typec->caps.data;
> +		typec->port_altmode = typec_port_register_altmode(typec->port,
> +								  &desc);
> +		if (IS_ERR(typec->port_altmode)) {
> +			ret = PTR_ERR(typec->port_altmode);
> +			typec->port_altmode = NULL;
> +			dev_err(dev, "Failed to register DP altmode: %d\n", ret);
> +			goto unregister_ports;
> +		}
> +
> +		ret = cix_pd_typec_get_handles(typec, fwnode, dev);
> +		if (ret)
> +			goto unregister_ports;
> +	}
> +
> +	return 0;
> +
> +unregister_ports:
> +	cix_pd_unregister_ports(typec);
> +	return ret;
> +}
> +
> +/*
> + * The IRQ is disabled when either the user asked for it or the device is
> + * suspended. Keep the disable/enable calls balanced and idempotent.
> + */
> +static void cix_pd_sync_irq(struct cix_pd *rts)
> +{
> +	bool disable = rts->pd_int_disabled || rts->suspended;
> +
> +	if (!rts->irq_requested || rts->irq_disabled == disable)
> +		return;
> +
> +	rts->irq_disabled = disable;
> +	if (disable)
> +		disable_irq(rts->irq);
> +	else
> +		enable_irq(rts->irq);
> +}
> +
> +static ssize_t disable_pd_interrupt_show(struct device *dev,
> +					 struct device_attribute *attr,
> +					 char *buf)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct cix_pd *rts = i2c_get_clientdata(client);
> +	const char *p;
> +
> +	if (rts->pd_int_disabled)
> +		p = "disabled";
> +	else
> +		p = "enabled";
> +
> +	return sysfs_emit(buf, "%s\n", p);
> +}
> +
> +static ssize_t disable_pd_interrupt_store(struct device *dev,
> +					  struct device_attribute *attr,
> +					  const char *buf, size_t count)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct cix_pd *rts = i2c_get_clientdata(client);
> +	bool value;
> +	int ret;
> +
> +	ret = kstrtobool(buf, &value);
> +	if (ret)
> +		return ret;
> +
> +	mutex_lock(&rts->irq_lock);
> +	rts->pd_int_disabled = value;
> +	cix_pd_sync_irq(rts);
> +	mutex_unlock(&rts->irq_lock);
> +
> +	dev_alert(dev, "Only for USB Compliance test, PD int is %s\n",
> +		  value ? "disabled" : "enabled");

What?

> +
> +	return count;
> +}
> +
> +DEVICE_ATTR_RW(disable_pd_interrupt);

You need to document the ABI. But really, we do not enable/disable
interrupts through custom sysfs hooks. Drop.

> +
> +static int cix_pd_probe(struct i2c_client *client)
> +{
> +	struct cix_pd *rts;
> +	int ret;
> +
> +	dev_dbg(&client->dev, "IRQ %d supplied\n", client->irq);

Drop

> +	rts = devm_kzalloc(&client->dev, sizeof(*rts), GFP_KERNEL);
> +	if (!rts)
> +		return -ENOMEM;
> +
> +	rts->dev = &client->dev;
> +	rts->irq = client->irq;
> +	mutex_init(&rts->lock);
> +	mutex_init(&rts->irq_lock);
> +	cix_pd_typec_init_state(rts);
> +
> +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
> +		return dev_err_probe(&client->dev, -ENODEV,
> +				     "i2c check functionality failed\n");
> +
> +	rts->regmap = devm_regmap_init_i2c(client, &cix_pd_regmap_config);
> +	if (IS_ERR(rts->regmap))
> +		return dev_err_probe(&client->dev, PTR_ERR(rts->regmap),
> +				     "failed to init regmap\n");
> +
> +	i2c_set_clientdata(client, rts);
> +
> +	ret = cix_pd_init_ports(rts);
> +	if (ret)
> +		return ret;
> +
> +	ret = cix_pd_event_handler(rts, 0);
> +	if (ret) {
> +		dev_err(&client->dev, "failed to read initial status: %d\n", ret);
> +		if (ret == -ESHUTDOWN)
> +			ret = -EIO;
> +		goto err_unregister_ports;
> +	}
> +
> +	if (client->irq > 0) {
> +		irq_set_status_flags(client->irq, IRQ_DISABLE_UNLAZY);
> +		ret = request_threaded_irq(client->irq, NULL, cix_pd_irq_handle,
> +					   IRQF_SHARED | IRQF_ONESHOT,
> +					   dev_name(&client->dev), rts);
> +		if (ret) {
> +			dev_err(&client->dev, "failed to request irq: %d\n", ret);
> +			goto err_unregister_ports;
> +		}
> +		rts->irq_requested = true;
> +	}
> +
> +	ret = device_create_file(&client->dev, &dev_attr_disable_pd_interrupt);
> +	if (ret)
> +		dev_warn(&client->dev,
> +			 "Failed to create sysfs attribute, ret = %d\n", ret);
> +
> +	device_set_wakeup_capable(&client->dev, true);
> +
> +	return 0;
> +
> +err_unregister_ports:
> +	cix_pd_unregister_ports(rts);
> +	return ret;
> +}
> +
> +static void cix_pd_remove(struct i2c_client *client)
> +{
> +	struct cix_pd *rts = i2c_get_clientdata(client);
> +
> +	device_remove_file(&client->dev, &dev_attr_disable_pd_interrupt);
> +
> +	mutex_lock(&rts->irq_lock);
> +	if (rts->irq_requested) {
> +		rts->suspended = true;
> +		cix_pd_sync_irq(rts);
> +		if (rts->wake_enabled) {
> +			disable_irq_wake(rts->irq);
> +			rts->wake_enabled = false;
> +		}
> +		free_irq(rts->irq, rts);
> +		rts->irq_requested = false;
> +	}
> +	mutex_unlock(&rts->irq_lock);
> +
> +	cix_pd_unregister_ports(rts);
> +}
> +
> +static const struct of_device_id cix_pd_of_match[] = {
> +	{ .compatible = "realtek,rts5453h", },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, cix_pd_of_match);
> +
> +static const struct i2c_device_id cix_pd_id[] = {
> +	{ "rts5453h" },
> +	{}
> +};
> +
> +static int __maybe_unused cix_pd_suspend(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct cix_pd *rts = i2c_get_clientdata(client);
> +
> +	mutex_lock(&rts->irq_lock);
> +	if (rts->irq_requested) {
> +		rts->suspended = true;
> +		cix_pd_sync_irq(rts);
> +		if (device_may_wakeup(dev)) {
> +			enable_irq_wake(rts->irq);
> +			rts->wake_enabled = true;
> +		}
> +	}
> +	mutex_unlock(&rts->irq_lock);
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused cix_pd_resume(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct cix_pd *rts = i2c_get_clientdata(client);
> +
> +	mutex_lock(&rts->irq_lock);
> +	if (rts->irq_requested) {
> +		if (rts->wake_enabled) {
> +			disable_irq_wake(rts->irq);
> +			rts->wake_enabled = false;
> +		}
> +		rts->suspended = false;
> +		cix_pd_sync_irq(rts);
> +	}
> +	mutex_unlock(&rts->irq_lock);
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused cix_pd_restore(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct cix_pd *rts = i2c_get_clientdata(client);
> +	int ret;
> +
> +	ret = cix_pd_resume(dev);
> +	if (ret)
> +		return ret;
> +
> +	/* Re-trigger PD event if PD event occurs during std */
> +	ret = cix_pd_event_handler(rts, 0);
> +	if (ret)
> +		dev_err(&client->dev, "typec event handling error %d\n", ret);
> +
> +	return ret;
> +}
> +
> +static void cix_pd_shutdown(struct i2c_client *client)
> +{
> +	struct cix_pd *rts = i2c_get_clientdata(client);
> +
> +	mutex_lock(&rts->irq_lock);
> +	rts->suspended = true;
> +	cix_pd_sync_irq(rts);
> +	mutex_unlock(&rts->irq_lock);
> +}
> +
> +static const struct dev_pm_ops cix_pd_pm_ops = {
> +	.suspend = pm_sleep_ptr(cix_pd_suspend),
> +	.resume = pm_sleep_ptr(cix_pd_resume),
> +	.freeze = pm_sleep_ptr(cix_pd_suspend),
> +	.thaw = pm_sleep_ptr(cix_pd_resume),
> +	.restore = pm_sleep_ptr(cix_pd_restore),
> +};
> +
> +MODULE_DEVICE_TABLE(i2c, cix_pd_id);

This is placed next to the table.

> +static struct i2c_driver cix_pd_i2c_driver = {
> +	.driver = {
> +		.name = "rts5453h",
> +		.of_match_table = cix_pd_of_match,
> +		.pm = pm_ptr(&cix_pd_pm_ops),
> +	},
> +	.id_table = cix_pd_id,
> +	.probe = cix_pd_probe,
> +	.remove = cix_pd_remove,
> +	.shutdown = cix_pd_shutdown,
> +};
> +
> +module_i2c_driver(cix_pd_i2c_driver);
> +MODULE_DESCRIPTION("CIX Type-C PD controller driver");
> +MODULE_AUTHOR("Chao Zeng <Chao.Zeng@cixtech.com>");
> +MODULE_AUTHOR("Gary Yang <gary.yang@cixtech.com>");
> +MODULE_LICENSE("GPL");


Best regards,
Krzysztof

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

* Re: [PATCH 2/4] usb: typec: add CIX Type-C PD controller driver
  2026-11-07  1:10 ` [PATCH 2/4] usb: typec: add CIX Type-C PD controller driver Gary Yang
  2026-11-07 10:56   ` Krzysztof Kozlowski
@ 2026-11-07 13:11   ` kernel test robot
  1 sibling, 0 replies; 10+ messages in thread
From: kernel test robot @ 2026-11-07 13:11 UTC (permalink / raw)
  To: Gary Yang, Heikki Krogerus, Greg Kroah-Hartman, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Fugang Duan
  Cc: llvm, oe-kbuild-all, linux-usb, devicetree, linux-arm-kernel,
	linux-kernel, cix-kernel-upstream, gary.yang

Hi Gary,

kernel test robot noticed the following build errors:

[auto build test ERROR on 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4]

url:    https://github.com/intel-lab-lkp/linux/commits/Gary-Yang/dt-bindings-usb-add-Realtek-RTS5453H-Type-C-port-controller/20261107-091009
base:   6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
patch link:    https://lore.kernel.org/r/20261107011012.3655873-3-gary.yang%40cixtech.com
patch subject: [PATCH 2/4] usb: typec: add CIX Type-C PD controller driver
config: sparc64-allmodconfig (https://download.01.org/0day-ci/archive/20261107/202611072135.EulyUBtA-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20261107/202611072135.EulyUBtA-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/202611072135.EulyUBtA-lkp@intel.com/

All errors (new ones prefixed by >>):

>> drivers/usb/typec/cix_pd.c:628:3: error: call to undeclared function 'irq_set_status_flags'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
     628 |                 irq_set_status_flags(client->irq, IRQ_DISABLE_UNLAZY);
         |                 ^
>> drivers/usb/typec/cix_pd.c:628:37: error: use of undeclared identifier 'IRQ_DISABLE_UNLAZY'
     628 |                 irq_set_status_flags(client->irq, IRQ_DISABLE_UNLAZY);
         |                                                   ^
   2 errors generated.


vim +/irq_set_status_flags +628 drivers/usb/typec/cix_pd.c

   587	
   588	static int cix_pd_probe(struct i2c_client *client)
   589	{
   590		struct cix_pd *rts;
   591		int ret;
   592	
   593		dev_dbg(&client->dev, "IRQ %d supplied\n", client->irq);
   594		rts = devm_kzalloc(&client->dev, sizeof(*rts), GFP_KERNEL);
   595		if (!rts)
   596			return -ENOMEM;
   597	
   598		rts->dev = &client->dev;
   599		rts->irq = client->irq;
   600		mutex_init(&rts->lock);
   601		mutex_init(&rts->irq_lock);
   602		cix_pd_typec_init_state(rts);
   603	
   604		if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
   605			return dev_err_probe(&client->dev, -ENODEV,
   606					     "i2c check functionality failed\n");
   607	
   608		rts->regmap = devm_regmap_init_i2c(client, &cix_pd_regmap_config);
   609		if (IS_ERR(rts->regmap))
   610			return dev_err_probe(&client->dev, PTR_ERR(rts->regmap),
   611					     "failed to init regmap\n");
   612	
   613		i2c_set_clientdata(client, rts);
   614	
   615		ret = cix_pd_init_ports(rts);
   616		if (ret)
   617			return ret;
   618	
   619		ret = cix_pd_event_handler(rts, 0);
   620		if (ret) {
   621			dev_err(&client->dev, "failed to read initial status: %d\n", ret);
   622			if (ret == -ESHUTDOWN)
   623				ret = -EIO;
   624			goto err_unregister_ports;
   625		}
   626	
   627		if (client->irq > 0) {
 > 628			irq_set_status_flags(client->irq, IRQ_DISABLE_UNLAZY);
   629			ret = request_threaded_irq(client->irq, NULL, cix_pd_irq_handle,
   630						   IRQF_SHARED | IRQF_ONESHOT,
   631						   dev_name(&client->dev), rts);
   632			if (ret) {
   633				dev_err(&client->dev, "failed to request irq: %d\n", ret);
   634				goto err_unregister_ports;
   635			}
   636			rts->irq_requested = true;
   637		}
   638	
   639		ret = device_create_file(&client->dev, &dev_attr_disable_pd_interrupt);
   640		if (ret)
   641			dev_warn(&client->dev,
   642				 "Failed to create sysfs attribute, ret = %d\n", ret);
   643	
   644		device_set_wakeup_capable(&client->dev, true);
   645	
   646		return 0;
   647	
   648	err_unregister_ports:
   649		cix_pd_unregister_ports(rts);
   650		return ret;
   651	}
   652	

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

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

end of thread, other threads:[~2026-11-07 13:12 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-07  1:10 [PATCH 0/4] usb: typec: add CIX Type-C PD controller driver Gary Yang
2026-11-07  1:10 ` [PATCH 1/4] dt-bindings: usb: add Realtek RTS5453H Type-C port controller Gary Yang
2026-11-07 10:54   ` Krzysztof Kozlowski
2026-11-07  1:10 ` [PATCH 2/4] usb: typec: add CIX Type-C PD controller driver Gary Yang
2026-11-07 10:56   ` Krzysztof Kozlowski
2026-11-07 13:11   ` kernel test robot
2026-11-07  1:10 ` [PATCH 3/4] arm64: dts: cix: fix I2C bus address and size cells on Sky1 Gary Yang
2026-11-07 10:53   ` Krzysztof Kozlowski
2026-11-07  1:10 ` [PATCH 4/4] arm64: dts: cix: add RTS5453 PD controllers to Orion O6 Gary Yang
2026-11-07 10:53   ` Krzysztof Kozlowski

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

all inboxes | Powered by JetHome®