Linux USB
 help / color / mirror / Atom feed
* [PATCH v2 0/2] Add UCSI I2C transport driver for ITE885x USB-C controllers
@ 2026-08-25 20:14 Edward Blair
  2026-08-25 20:14 ` [PATCH v2 1/2] ACPI: x86: hide duplicate RhProxy node for ITE UCSI controllers Edward Blair
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Edward Blair @ 2026-08-25 20:14 UTC (permalink / raw)
  To: linux-acpi, linux-usb
  Cc: rafael, lenb, heikki.krogerus, mika.westerberg, gregkh,
	linux-kernel, Edward Blair

Some ASUS desktop boards describe the same ITE USB-C controller twice
in ACPI: as an MSFT8000 Resource Hub Proxy and as an ITE885x device at
the same I2C address. If the RhProxy node claims the address first, the
ITE device cannot be registered. Only the ITE node provides the
interrupt required by this transport.

Patch 1 marks the RhProxy node absent only when its I2C resource exactly
matches a supported ITE node which also has an interrupt.

Patch 2 adds the ITE885x UCSI transport. These controllers use
ITE-specific offsets for CCI, MESSAGE_IN, CONTROL and interrupt
handling. They do not expose a VERSION register or accept PPM_RESET
over I2C.

The register layout and reset behaviour were checked against the ITE
Windows driver supplied for this board.

Tested on an ASUS ROG Strix Z790-E Gaming WiFi with ITE8853:

- Built and booted on x86_64.
- ucsi_ite bound to ITE8853:00 without the previous -EBUSY failure.
- /sys/class/typec/port0 was registered.
- USB-C attachment detection worked.
- No UCSI, ITE or Type-C initialization errors were logged.

Suspend/resume and the ITE8800 through ITE8805 IDs have not been tested.

Changes since v1:

- Move the duplicate-device quirk from the I2C core to the ACPI x86
  status-override code, as suggested by Mika and Heikki.
- Correctly identify MSFT8000 as a Resource Hub Proxy.
- Restrict the quirk to exact matching I2C resources.
- Use devm_request_threaded_irq(), as suggested by Heikki.
- Rebase onto Linux 7.2 and update for the current UCSI API.
- Read and cache complete UCSI events before acknowledging them.
- Match the vendor transport's MESSAGE_IN and PPM_RESET behaviour.

Link: https://lore.kernel.org/all/20260314013157.7181-1-edward.blair@gmail.com/

Edward Blair (2):
  ACPI: x86: hide duplicate RhProxy node for ITE UCSI controllers
  usb: typec: ucsi: add ITE885x I2C transport driver

 drivers/acpi/x86/utils.c          | 151 ++++++++++++
 drivers/usb/typec/ucsi/Kconfig    |  11 +
 drivers/usb/typec/ucsi/Makefile   |   1 +
 drivers/usb/typec/ucsi/ucsi_ite.c | 395 ++++++++++++++++++++++++++++++
 4 files changed, 558 insertions(+)
 create mode 100644 drivers/usb/typec/ucsi/ucsi_ite.c


base-commit: 66498c75b4f8017f62d720d9b59675bdf3abce91
-- 
2.55.0

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

* [PATCH v2 1/2] ACPI: x86: hide duplicate RhProxy node for ITE UCSI controllers
  2026-08-25 20:14 [PATCH v2 0/2] Add UCSI I2C transport driver for ITE885x USB-C controllers Edward Blair
@ 2026-08-25 20:14 ` Edward Blair
  2026-08-25 20:14 ` [PATCH v2 2/2] usb: typec: ucsi: add ITE885x I2C transport driver Edward Blair
  2026-08-27 22:30 ` [PATCH v2 0/2] Add UCSI I2C transport driver for ITE885x USB-C controllers Armin Wolf
  2 siblings, 0 replies; 5+ messages in thread
From: Edward Blair @ 2026-08-25 20:14 UTC (permalink / raw)
  To: linux-acpi, linux-usb
  Cc: rafael, lenb, heikki.krogerus, mika.westerberg, gregkh,
	linux-kernel, Edward Blair

Some desktop firmware describes the same ITE UCSI controller both as an
MSFT8000 Resource Hub Proxy device and as an ITE885x device. Both nodes
point to the same I2C address, so whichever is enumerated first prevents
the other from registering. Only the ITE node provides the interrupt
needed by the UCSI transport.

Override the RhProxy node status only when it contains exactly one I2C
resource, contains no other resources, and a present ITE UCSI node has
exactly one I2C resource, a usable interrupt, and matching controller,
address, speed, slave mode, connection sharing and address mode. Keeping
this in the ACPI status quirk layer handles initial enumeration and
reconfiguration without affecting unrelated MSFT8000 devices.

Signed-off-by: Edward Blair <edward.blair@gmail.com>
---
 drivers/acpi/x86/utils.c | 151 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 151 insertions(+)

diff --git a/drivers/acpi/x86/utils.c b/drivers/acpi/x86/utils.c
index 418951639..8601b9713 100644
--- a/drivers/acpi/x86/utils.c
+++ b/drivers/acpi/x86/utils.c
@@ -67,6 +67,144 @@ struct override_status_id {
 #define NOT_PRESENT_ENTRY_PATH(path, cpu_vfm, dmi...) \
 	ENTRY(0, "", NULL, path, cpu_vfm, dmi)
 
+struct acpi_i2c_resource_info {
+	acpi_handle device;
+	acpi_handle controller;
+	u32 connection_speed;
+	unsigned int i2c_resources;
+	unsigned int other_resources;
+	u16 address;
+	u8 slave_mode;
+	u8 connection_sharing;
+	u8 access_mode;
+	bool has_interrupt;
+	bool found;
+};
+
+static acpi_status acpi_get_i2c_resource(struct acpi_resource *ares,
+					 void *context)
+{
+	struct acpi_i2c_resource_info *info = context;
+	struct acpi_resource_i2c_serialbus *sb;
+
+	if (ares->type == ACPI_RESOURCE_TYPE_END_TAG)
+		return AE_OK;
+
+	if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) {
+		if ((ares->type == ACPI_RESOURCE_TYPE_IRQ &&
+		     ares->data.irq.interrupt_count) ||
+		    (ares->type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ &&
+		     ares->data.extended_irq.interrupt_count) ||
+		    (ares->type == ACPI_RESOURCE_TYPE_GPIO &&
+		     ares->data.gpio.connection_type == ACPI_RESOURCE_GPIO_TYPE_INT &&
+		     ares->data.gpio.pin_table_length))
+			info->has_interrupt = true;
+		info->other_resources++;
+		return AE_OK;
+	}
+
+	sb = &ares->data.i2c_serial_bus;
+	if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C) {
+		info->other_resources++;
+		return AE_OK;
+	}
+
+	info->i2c_resources++;
+	if (info->found)
+		return AE_OK;
+
+	if (ACPI_FAILURE(acpi_get_handle(info->device,
+					 sb->resource_source.string_ptr,
+					 &info->controller)))
+		return AE_OK;
+
+	info->address = sb->slave_address;
+	info->connection_speed = sb->connection_speed;
+	info->slave_mode = sb->slave_mode;
+	info->connection_sharing = sb->connection_sharing;
+	info->access_mode = sb->access_mode;
+	info->found = true;
+
+	return AE_OK;
+}
+
+static bool acpi_get_i2c_resource_info(acpi_handle handle,
+				       struct acpi_i2c_resource_info *info)
+{
+	acpi_status status;
+
+	memset(info, 0, sizeof(*info));
+	info->device = handle;
+	status = acpi_walk_resources(handle, METHOD_NAME__CRS,
+				     acpi_get_i2c_resource, info);
+
+	return ACPI_SUCCESS(status) && info->found;
+}
+
+struct acpi_ite_ucsi_duplicate_context {
+	const struct acpi_i2c_resource_info *rhproxy;
+	bool found;
+};
+
+static acpi_status acpi_match_ite_ucsi_duplicate(acpi_handle handle,
+						 u32 level, void *context,
+						 void **return_value)
+{
+	struct acpi_ite_ucsi_duplicate_context *match = context;
+	struct acpi_i2c_resource_info info;
+
+	if (!acpi_get_i2c_resource_info(handle, &info))
+		return AE_OK;
+	if (info.i2c_resources != 1 || !info.has_interrupt)
+		return AE_OK;
+
+	if (info.controller != match->rhproxy->controller ||
+	    info.address != match->rhproxy->address ||
+	    info.connection_speed != match->rhproxy->connection_speed ||
+	    info.slave_mode != match->rhproxy->slave_mode ||
+	    info.connection_sharing != match->rhproxy->connection_sharing ||
+	    info.access_mode != match->rhproxy->access_mode)
+		return AE_OK;
+
+	match->found = true;
+	return AE_CTRL_TERMINATE;
+}
+
+static bool acpi_has_ite_ucsi_duplicate(struct acpi_device *adev)
+{
+	static const char * const ite_ucsi_ids[] = {
+		"ITE8853",
+		"ITE8800",
+		"ITE8801",
+		"ITE8802",
+		"ITE8803",
+		"ITE8804",
+		"ITE8805",
+	};
+	struct acpi_ite_ucsi_duplicate_context match;
+	struct acpi_i2c_resource_info rhproxy;
+	unsigned int i;
+
+	if (!acpi_get_i2c_resource_info(adev->handle, &rhproxy))
+		return false;
+	if (rhproxy.i2c_resources != 1 || rhproxy.other_resources)
+		return false;
+
+	match.rhproxy = &rhproxy;
+	match.found = false;
+
+	for (i = 0; i < ARRAY_SIZE(ite_ucsi_ids) && !match.found; i++)
+		acpi_get_devices(ite_ucsi_ids[i], acpi_match_ite_ucsi_duplicate,
+				 &match, NULL);
+
+	return match.found;
+}
+
+static const struct acpi_device_id acpi_rhproxy_ids[] = {
+	{ "MSFT8000" },
+	{ }
+};
+
 static const struct override_status_id override_status_ids[] = {
 	/*
 	 * Bay / Cherry Trail PWM directly poked by GPU driver in win10,
@@ -182,6 +320,19 @@ bool acpi_device_override_status(struct acpi_device *adev, unsigned long long *s
 	bool ret = false;
 	unsigned int i;
 
+	/*
+	 * Some firmware describes an ITE UCSI controller twice: once through
+	 * the Windows Resource Hub Proxy and once as a vendor-specific device.
+	 * Both nodes point at the same I2C address, but only the ITE node has
+	 * the interrupt resource required by the Linux driver. Hide only an
+	 * exact duplicate so unrelated Resource Hub Proxy devices keep working.
+	 */
+	if (!acpi_match_device_ids(adev, acpi_rhproxy_ids) &&
+	    acpi_has_ite_ucsi_duplicate(adev)) {
+		*status = 0;
+		return true;
+	}
+
 	for (i = 0; i < ARRAY_SIZE(override_status_ids); i++) {
 		if (!x86_match_cpu(override_status_ids[i].cpu_ids))
 			continue;
-- 
2.55.0


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

* [PATCH v2 2/2] usb: typec: ucsi: add ITE885x I2C transport driver
  2026-08-25 20:14 [PATCH v2 0/2] Add UCSI I2C transport driver for ITE885x USB-C controllers Edward Blair
  2026-08-25 20:14 ` [PATCH v2 1/2] ACPI: x86: hide duplicate RhProxy node for ITE UCSI controllers Edward Blair
@ 2026-08-25 20:14 ` Edward Blair
  2026-08-27 22:30 ` [PATCH v2 0/2] Add UCSI I2C transport driver for ITE885x USB-C controllers Armin Wolf
  2 siblings, 0 replies; 5+ messages in thread
From: Edward Blair @ 2026-08-25 20:14 UTC (permalink / raw)
  To: linux-acpi, linux-usb
  Cc: rafael, lenb, heikki.krogerus, mika.westerberg, gregkh,
	linux-kernel, Edward Blair

Add a UCSI transport driver for ITE8853 and ITE8800 through ITE8805
USB Type-C controllers found on desktop motherboards.

These controllers expose CCI, MESSAGE_IN and CONTROL at ITE-specific I2C
offsets and signal UCSI and vendor events through a shared interrupt
status register. Read and cache each complete UCSI event before
acknowledging it so command data remains coherent between the interrupt
handler and UCSI core.

The interface does not expose a VERSION register and does not accept
PPM_RESET over I2C. Report UCSI 1.0, limit MESSAGE_IN to its 16-byte
window and handle PPM_RESET locally, matching the vendor driver's
behavior.

Signed-off-by: Edward Blair <edward.blair@gmail.com>
---
 drivers/usb/typec/ucsi/Kconfig    |  11 +
 drivers/usb/typec/ucsi/Makefile   |   1 +
 drivers/usb/typec/ucsi/ucsi_ite.c | 395 ++++++++++++++++++++++++++++++
 3 files changed, 407 insertions(+)
 create mode 100644 drivers/usb/typec/ucsi/ucsi_ite.c

diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig
index 87dd992a4..3819c4f73 100644
--- a/drivers/usb/typec/ucsi/Kconfig
+++ b/drivers/usb/typec/ucsi/Kconfig
@@ -104,4 +104,15 @@ config UCSI_HUAWEI_GAOKUN
 	  To compile the driver as a module, choose M here: the module will be
 	  called ucsi_huawei_gaokun.
 
+config UCSI_ITE
+	tristate "UCSI Interface Driver for ITE885x"
+	depends on ACPI && I2C
+	help
+	  This driver enables UCSI support on platforms that expose an ITE8853
+	  or ITE8800-ITE8805 USB Type-C controller over I2C, commonly found
+	  on ASUS Z690/Z790/X670E motherboards.
+
+	  To compile the driver as a module, choose M here: the module will be
+	  called ucsi_ite.
+
 endif
diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile
index c7e38bf01..9bc1d6bbb 100644
--- a/drivers/usb/typec/ucsi/Makefile
+++ b/drivers/usb/typec/ucsi/Makefile
@@ -28,3 +28,4 @@ obj-$(CONFIG_UCSI_PMIC_GLINK)		+= ucsi_glink.o
 obj-$(CONFIG_CROS_EC_UCSI)		+= cros_ec_ucsi.o
 obj-$(CONFIG_UCSI_LENOVO_YOGA_C630)	+= ucsi_yoga_c630.o
 obj-$(CONFIG_UCSI_HUAWEI_GAOKUN)	+= ucsi_huawei_gaokun.o
+obj-$(CONFIG_UCSI_ITE)			+= ucsi_ite.o
diff --git a/drivers/usb/typec/ucsi/ucsi_ite.c b/drivers/usb/typec/ucsi/ucsi_ite.c
new file mode 100644
index 000000000..16b22c77f
--- /dev/null
+++ b/drivers/usb/typec/ucsi/ucsi_ite.c
@@ -0,0 +1,395 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * UCSI I2C transport driver for ITE885x USB-C controllers
+ *
+ * ITE8853/ITE8800-ITE8805 are UCSI-compliant USB-C controllers found on
+ * desktop motherboards. They communicate over I2C using UCSI registers at
+ * ITE-specific offsets and signal events through a vendor interrupt register.
+ */
+
+#include <linux/acpi.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+
+#include "ucsi.h"
+
+#define ITE_REG_CCI		0x84
+#define ITE_REG_MESSAGE_IN	0x88
+#define ITE_REG_CONTROL		0x98
+#define ITE_REG_INT_ACK		0xbc
+#define ITE_REG_INT_STATUS	0xbd
+
+#define ITE_INT_VENDOR_ALERT	BIT(0)
+#define ITE_INT_CCI		BIT(1)
+#define ITE_INT_MASK		(ITE_INT_VENDOR_ALERT | ITE_INT_CCI)
+
+#define ITE_MESSAGE_IN_MAX_LEN	0x10
+
+enum ucsi_ite_event {
+	ITE_EVENT_NONE,
+	ITE_EVENT_CCI,
+	ITE_EVENT_VENDOR,
+};
+
+struct ucsi_ite {
+	struct i2c_client *client;
+	struct ucsi *ucsi;
+	struct mutex event_lock;	/* Serializes IRQ and polling */
+	struct mutex received_lock;	/* Protects CCI and message_in */
+	u8 message_in[ITE_MESSAGE_IN_MAX_LEN];
+	u32 cci;
+	bool registered;
+};
+
+static int ucsi_ite_read(struct ucsi_ite *ite, u8 reg, void *val, size_t len)
+{
+	struct i2c_client *client = ite->client;
+	struct i2c_msg msgs[] = {
+		{
+			.addr = client->addr,
+			.len = 1,
+			.buf = &reg,
+		},
+		{
+			.addr = client->addr,
+			.flags = I2C_M_RD,
+			.len = len,
+			.buf = val,
+		},
+	};
+	int ret;
+
+	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+	if (ret == ARRAY_SIZE(msgs))
+		return 0;
+
+	ret = ret < 0 ? ret : -EIO;
+	dev_err_ratelimited(&client->dev,
+			    "register 0x%02x read failed: %d\n", reg, ret);
+	return ret;
+}
+
+static int ucsi_ite_write(struct ucsi_ite *ite, u8 reg, const void *val,
+			  size_t len)
+{
+	struct i2c_client *client = ite->client;
+	u8 buf[sizeof(u64) + 1];
+	struct i2c_msg msg = {
+		.addr = client->addr,
+		.len = len + 1,
+		.buf = buf,
+	};
+	int ret;
+
+	if (len > sizeof(buf) - 1)
+		return -EINVAL;
+
+	buf[0] = reg;
+	memcpy(&buf[1], val, len);
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret == 1)
+		return 0;
+
+	ret = ret < 0 ? ret : -EIO;
+	dev_err_ratelimited(&client->dev,
+			    "register 0x%02x write failed: %d\n", reg, ret);
+	return ret;
+}
+
+static int ucsi_ite_process_event(struct ucsi_ite *ite, u32 *cci)
+{
+	u8 message_in[ITE_MESSAGE_IN_MAX_LEN] = {};
+	__le32 raw_cci;
+	u8 status;
+	u8 len = 0;
+	int event;
+	int err = 0;
+	int ret;
+
+	mutex_lock(&ite->event_lock);
+
+	ret = ucsi_ite_read(ite, ITE_REG_INT_STATUS, &status, sizeof(status));
+	if (ret)
+		goto out_unlock;
+
+	status &= ITE_INT_MASK;
+	if (!status) {
+		mutex_lock(&ite->received_lock);
+		*cci = ite->cci;
+		mutex_unlock(&ite->received_lock);
+		ret = ITE_EVENT_NONE;
+		goto out_unlock;
+	}
+
+	if (status & ITE_INT_CCI) {
+		err = ucsi_ite_read(ite, ITE_REG_CCI, &raw_cci,
+				    sizeof(raw_cci));
+		if (!err) {
+			*cci = le32_to_cpu(raw_cci);
+			len = UCSI_CCI_LENGTH(*cci);
+
+			if (len > sizeof(message_in)) {
+				len = sizeof(message_in);
+				*cci &= ~GENMASK(15, 8);
+				*cci |= UCSI_SET_CCI_LENGTH(len);
+			}
+			if (len) {
+				err = ucsi_ite_read(ite, ITE_REG_MESSAGE_IN,
+						    message_in, len);
+			}
+		}
+	}
+
+	/* Acknowledge each latched event with the value expected by the PPM. */
+	if (status & ITE_INT_VENDOR_ALERT) {
+		u8 ack = ITE_INT_VENDOR_ALERT;
+
+		ret = ucsi_ite_write(ite, ITE_REG_INT_ACK, &ack, sizeof(ack));
+		if (ret)
+			goto out_unlock;
+	}
+
+	if ((status & ITE_INT_CCI) && !err) {
+		u8 ack = ITE_INT_CCI;
+
+		ret = ucsi_ite_write(ite, ITE_REG_INT_ACK, &ack, sizeof(ack));
+		if (ret)
+			goto out_unlock;
+	}
+
+	if (err) {
+		ret = err;
+		goto out_unlock;
+	}
+
+	if (status & ITE_INT_CCI) {
+		mutex_lock(&ite->received_lock);
+		ite->cci = *cci;
+		memset(ite->message_in, 0, sizeof(ite->message_in));
+		memcpy(ite->message_in, message_in, len);
+		mutex_unlock(&ite->received_lock);
+		event = ITE_EVENT_CCI;
+	} else {
+		mutex_lock(&ite->received_lock);
+		*cci = ite->cci;
+		mutex_unlock(&ite->received_lock);
+		event = ITE_EVENT_VENDOR;
+	}
+
+	ret = event;
+
+out_unlock:
+	mutex_unlock(&ite->event_lock);
+	return ret;
+}
+
+static int ucsi_ite_read_version(struct ucsi *ucsi, u16 *version)
+{
+	/* The ITE interface does not expose a VERSION register. */
+	*version = UCSI_VERSION_1_0;
+	return 0;
+}
+
+static int ucsi_ite_read_cci(struct ucsi *ucsi, u32 *cci)
+{
+	struct ucsi_ite *ite = ucsi_get_drvdata(ucsi);
+
+	mutex_lock(&ite->received_lock);
+	*cci = ite->cci;
+	mutex_unlock(&ite->received_lock);
+
+	return 0;
+}
+
+static int ucsi_ite_poll_cci(struct ucsi *ucsi, u32 *cci)
+{
+	struct ucsi_ite *ite = ucsi_get_drvdata(ucsi);
+	int ret;
+
+	ret = ucsi_ite_process_event(ite, cci);
+	return ret < 0 ? ret : 0;
+}
+
+static int ucsi_ite_read_message_in(struct ucsi *ucsi, void *val, size_t len)
+{
+	struct ucsi_ite *ite = ucsi_get_drvdata(ucsi);
+
+	if (len > sizeof(ite->message_in))
+		return -EINVAL;
+
+	mutex_lock(&ite->received_lock);
+	memcpy(val, ite->message_in, len);
+	mutex_unlock(&ite->received_lock);
+
+	return 0;
+}
+
+static int ucsi_ite_async_control(struct ucsi *ucsi, u64 command)
+{
+	struct ucsi_ite *ite = ucsi_get_drvdata(ucsi);
+	__le64 raw_command = cpu_to_le64(command);
+	int ret;
+
+	if (UCSI_COMMAND(command) == UCSI_PPM_RESET) {
+		/* The PPM handles reset internally; do not write it over I2C. */
+		mutex_lock(&ite->event_lock);
+		mutex_lock(&ite->received_lock);
+		ite->cci = UCSI_CCI_RESET_COMPLETE;
+		memset(ite->message_in, 0, sizeof(ite->message_in));
+		mutex_unlock(&ite->received_lock);
+		mutex_unlock(&ite->event_lock);
+		return 0;
+	}
+
+	mutex_lock(&ite->event_lock);
+	mutex_lock(&ite->received_lock);
+	ite->cci = 0;
+	memset(ite->message_in, 0, sizeof(ite->message_in));
+	mutex_unlock(&ite->received_lock);
+	ret = ucsi_ite_write(ite, ITE_REG_CONTROL, &raw_command,
+			     sizeof(raw_command));
+	mutex_unlock(&ite->event_lock);
+
+	return ret;
+}
+
+static const struct ucsi_operations ucsi_ite_ops = {
+	.read_version = ucsi_ite_read_version,
+	.read_cci = ucsi_ite_read_cci,
+	.poll_cci = ucsi_ite_poll_cci,
+	.read_message_in = ucsi_ite_read_message_in,
+	.sync_control = ucsi_sync_control_common,
+	.async_control = ucsi_ite_async_control,
+};
+
+static irqreturn_t ucsi_ite_irq(int irq, void *data)
+{
+	struct ucsi_ite *ite = data;
+	u32 cci;
+	int ret;
+
+	ret = ucsi_ite_process_event(ite, &cci);
+	if (ret == ITE_EVENT_NONE)
+		return IRQ_NONE;
+	if (ret < 0)
+		return IRQ_HANDLED;
+
+	if (ret == ITE_EVENT_CCI)
+		ucsi_notify_common(ite->ucsi, cci);
+
+	return IRQ_HANDLED;
+}
+
+static void ucsi_ite_destroy(void *data)
+{
+	struct ucsi_ite *ite = data;
+
+	if (ite->registered)
+		ucsi_unregister(ite->ucsi);
+	ucsi_destroy(ite->ucsi);
+}
+
+static int ucsi_ite_probe(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	struct ucsi_ite *ite;
+	u32 cci;
+	int ret;
+
+	if (client->irq <= 0)
+		return dev_err_probe(dev, -ENODEV, "no IRQ provided\n");
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+		return dev_err_probe(dev, -EOPNOTSUPP,
+				     "adapter does not support I2C transfers\n");
+
+	ite = devm_kzalloc(dev, sizeof(*ite), GFP_KERNEL);
+	if (!ite)
+		return -ENOMEM;
+
+	ite->client = client;
+	mutex_init(&ite->event_lock);
+	mutex_init(&ite->received_lock);
+	i2c_set_clientdata(client, ite);
+
+	ite->ucsi = ucsi_create(dev, &ucsi_ite_ops);
+	if (IS_ERR(ite->ucsi))
+		return dev_err_probe(dev, PTR_ERR(ite->ucsi),
+				     "failed to create UCSI interface\n");
+
+	ret = devm_add_action_or_reset(dev, ucsi_ite_destroy, ite);
+	if (ret)
+		return ret;
+
+	ucsi_set_drvdata(ite->ucsi, ite);
+
+	ret = devm_request_threaded_irq(dev, client->irq, NULL,
+					ucsi_ite_irq, IRQF_ONESHOT,
+					dev_name(dev), ite);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to request IRQ\n");
+
+	ret = ucsi_ite_process_event(ite, &cci);
+	if (ret < 0)
+		dev_warn(dev, "initial event processing failed: %d\n", ret);
+
+	ret = ucsi_register(ite->ucsi);
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "failed to register UCSI interface\n");
+
+	ite->registered = true;
+	return 0;
+}
+
+static int ucsi_ite_suspend(struct device *dev)
+{
+	struct ucsi_ite *ite = dev_get_drvdata(dev);
+	int ret;
+
+	disable_irq(ite->client->irq);
+	ret = ucsi_suspend(ite->ucsi);
+	if (ret)
+		enable_irq(ite->client->irq);
+
+	return ret;
+}
+
+static int ucsi_ite_resume(struct device *dev)
+{
+	struct ucsi_ite *ite = dev_get_drvdata(dev);
+
+	enable_irq(ite->client->irq);
+	return ucsi_resume(ite->ucsi);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(ucsi_ite_pm, ucsi_ite_suspend,
+				ucsi_ite_resume);
+
+static const struct acpi_device_id ucsi_ite_acpi_ids[] = {
+	{ "ITE8853" },
+	{ "ITE8800" },
+	{ "ITE8801" },
+	{ "ITE8802" },
+	{ "ITE8803" },
+	{ "ITE8804" },
+	{ "ITE8805" },
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, ucsi_ite_acpi_ids);
+
+static struct i2c_driver ucsi_ite_driver = {
+	.driver = {
+		.name = "ucsi_ite",
+		.acpi_match_table = ucsi_ite_acpi_ids,
+		.pm = pm_sleep_ptr(&ucsi_ite_pm),
+	},
+	.probe = ucsi_ite_probe,
+};
+module_i2c_driver(ucsi_ite_driver);
+
+MODULE_AUTHOR("Edward Blair <edward.blair@gmail.com>");
+MODULE_DESCRIPTION("UCSI I2C transport driver for ITE885x USB-C controllers");
+MODULE_LICENSE("GPL");
-- 
2.55.0


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

* Re: [PATCH v2 0/2] Add UCSI I2C transport driver for ITE885x USB-C controllers
  2026-08-25 20:14 [PATCH v2 0/2] Add UCSI I2C transport driver for ITE885x USB-C controllers Edward Blair
  2026-08-25 20:14 ` [PATCH v2 1/2] ACPI: x86: hide duplicate RhProxy node for ITE UCSI controllers Edward Blair
  2026-08-25 20:14 ` [PATCH v2 2/2] usb: typec: ucsi: add ITE885x I2C transport driver Edward Blair
@ 2026-08-27 22:30 ` Armin Wolf
       [not found]   ` <CAJDQNggKFhJ=YKanKWmG_hRi98nRi-8Wa19D7mqU1MuM2dF=vQ@mail.gmail.com>
  2 siblings, 1 reply; 5+ messages in thread
From: Armin Wolf @ 2026-08-27 22:30 UTC (permalink / raw)
  To: Edward Blair, linux-acpi, linux-usb
  Cc: rafael, lenb, heikki.krogerus, mika.westerberg, gregkh,
	linux-kernel

Am 25.08.26 um 22:14 schrieb Edward Blair:

> Some ASUS desktop boards describe the same ITE USB-C controller twice
> in ACPI: as an MSFT8000 Resource Hub Proxy and as an ITE885x device at
> the same I2C address. If the RhProxy node claims the address first, the
> ITE device cannot be registered. Only the ITE node provides the
> interrupt required by this transport.
>
> Patch 1 marks the RhProxy node absent only when its I2C resource exactly
> matches a supported ITE node which also has an interrupt.

AFAIK the MSFT8000 device is used to grant userspace applications under Windows
access to I2C, SPI, UART and GPIO resources. Since we already provide those functions
natively i suggest that we generally ignore MSFT8000 devices when checking for resource
conflicts.

Thanks,
Armin Wolf

> Patch 2 adds the ITE885x UCSI transport. These controllers use
> ITE-specific offsets for CCI, MESSAGE_IN, CONTROL and interrupt
> handling. They do not expose a VERSION register or accept PPM_RESET
> over I2C.
>
> The register layout and reset behaviour were checked against the ITE
> Windows driver supplied for this board.
>
> Tested on an ASUS ROG Strix Z790-E Gaming WiFi with ITE8853:
>
> - Built and booted on x86_64.
> - ucsi_ite bound to ITE8853:00 without the previous -EBUSY failure.
> - /sys/class/typec/port0 was registered.
> - USB-C attachment detection worked.
> - No UCSI, ITE or Type-C initialization errors were logged.
>
> Suspend/resume and the ITE8800 through ITE8805 IDs have not been tested.
>
> Changes since v1:
>
> - Move the duplicate-device quirk from the I2C core to the ACPI x86
>    status-override code, as suggested by Mika and Heikki.
> - Correctly identify MSFT8000 as a Resource Hub Proxy.
> - Restrict the quirk to exact matching I2C resources.
> - Use devm_request_threaded_irq(), as suggested by Heikki.
> - Rebase onto Linux 7.2 and update for the current UCSI API.
> - Read and cache complete UCSI events before acknowledging them.
> - Match the vendor transport's MESSAGE_IN and PPM_RESET behaviour.
>
> Link: https://lore.kernel.org/all/20260314013157.7181-1-edward.blair@gmail.com/
>
> Edward Blair (2):
>    ACPI: x86: hide duplicate RhProxy node for ITE UCSI controllers
>    usb: typec: ucsi: add ITE885x I2C transport driver
>
>   drivers/acpi/x86/utils.c          | 151 ++++++++++++
>   drivers/usb/typec/ucsi/Kconfig    |  11 +
>   drivers/usb/typec/ucsi/Makefile   |   1 +
>   drivers/usb/typec/ucsi/ucsi_ite.c | 395 ++++++++++++++++++++++++++++++
>   4 files changed, 558 insertions(+)
>   create mode 100644 drivers/usb/typec/ucsi/ucsi_ite.c
>
>
> base-commit: 66498c75b4f8017f62d720d9b59675bdf3abce91

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

* Re: [PATCH v2 0/2] Add UCSI I2C transport driver for ITE885x USB-C controllers
       [not found]   ` <CAJDQNggKFhJ=YKanKWmG_hRi98nRi-8Wa19D7mqU1MuM2dF=vQ@mail.gmail.com>
@ 2026-08-28  1:04     ` Edward Blair
  0 siblings, 0 replies; 5+ messages in thread
From: Edward Blair @ 2026-08-28  1:04 UTC (permalink / raw)
  To: Armin Wolf
  Cc: linux-acpi, linux-usb, rafael, lenb, heikki.krogerus,
	mika.westerberg, gregkh, linux-kernel

Let's try that again.. without gmail's html...

Thanks, that makes sense. I can rework patch 1 to add MSFT8000 to
ignore_serial_bus_ids[] in acpi_device_enumeration_by_parent(). This
keeps the ACPI device present while preventing its SerialBus resources
from being instantiated as bus clients.

If that sounds reasonable, it seems like a good middle ground between
v1 and v2: it preserves v1's enumeration behaviour while handling the
device generically in the ACPI core, without the ITE-specific x86
status override added in v2.

Thanks,
Edward


On Fri, 28 Aug 2026 at 01:58, Edward Blair <edward.blair@gmail.com> wrote:
>
> Thanks, that makes sense. I can rework patch 1 to add MSFT8000 to ignore_serial_bus_ids[] in acpi_device_enumeration_by_parent(). This keeps the ACPI device present while preventing its SerialBus resources from being instantiated as bus clients.
>
> If that sounds reasonable, it seems like a good middle ground between v1 and v2: it preserves v1's enumeration behaviour while handling the device generically in the ACPI core, without the ITE-specific x86 status override added in v2.
>
> Thanks,
> Edward
>
> On Thu, 27 Aug 2026 at 23:30, Armin Wolf <W_Armin@gmx.de> wrote:
>>
>> Am 25.08.26 um 22:14 schrieb Edward Blair:
>>
>> > Some ASUS desktop boards describe the same ITE USB-C controller twice
>> > in ACPI: as an MSFT8000 Resource Hub Proxy and as an ITE885x device at
>> > the same I2C address. If the RhProxy node claims the address first, the
>> > ITE device cannot be registered. Only the ITE node provides the
>> > interrupt required by this transport.
>> >
>> > Patch 1 marks the RhProxy node absent only when its I2C resource exactly
>> > matches a supported ITE node which also has an interrupt.
>>
>> AFAIK the MSFT8000 device is used to grant userspace applications under Windows
>> access to I2C, SPI, UART and GPIO resources. Since we already provide those functions
>> natively i suggest that we generally ignore MSFT8000 devices when checking for resource
>> conflicts.
>>
>> Thanks,
>> Armin Wolf
>>
>> > Patch 2 adds the ITE885x UCSI transport. These controllers use
>> > ITE-specific offsets for CCI, MESSAGE_IN, CONTROL and interrupt
>> > handling. They do not expose a VERSION register or accept PPM_RESET
>> > over I2C.
>> >
>> > The register layout and reset behaviour were checked against the ITE
>> > Windows driver supplied for this board.
>> >
>> > Tested on an ASUS ROG Strix Z790-E Gaming WiFi with ITE8853:
>> >
>> > - Built and booted on x86_64.
>> > - ucsi_ite bound to ITE8853:00 without the previous -EBUSY failure.
>> > - /sys/class/typec/port0 was registered.
>> > - USB-C attachment detection worked.
>> > - No UCSI, ITE or Type-C initialization errors were logged.
>> >
>> > Suspend/resume and the ITE8800 through ITE8805 IDs have not been tested.
>> >
>> > Changes since v1:
>> >
>> > - Move the duplicate-device quirk from the I2C core to the ACPI x86
>> >    status-override code, as suggested by Mika and Heikki.
>> > - Correctly identify MSFT8000 as a Resource Hub Proxy.
>> > - Restrict the quirk to exact matching I2C resources.
>> > - Use devm_request_threaded_irq(), as suggested by Heikki.
>> > - Rebase onto Linux 7.2 and update for the current UCSI API.
>> > - Read and cache complete UCSI events before acknowledging them.
>> > - Match the vendor transport's MESSAGE_IN and PPM_RESET behaviour.
>> >
>> > Link: https://lore.kernel.org/all/20260314013157.7181-1-edward.blair@gmail.com/
>> >
>> > Edward Blair (2):
>> >    ACPI: x86: hide duplicate RhProxy node for ITE UCSI controllers
>> >    usb: typec: ucsi: add ITE885x I2C transport driver
>> >
>> >   drivers/acpi/x86/utils.c          | 151 ++++++++++++
>> >   drivers/usb/typec/ucsi/Kconfig    |  11 +
>> >   drivers/usb/typec/ucsi/Makefile   |   1 +
>> >   drivers/usb/typec/ucsi/ucsi_ite.c | 395 ++++++++++++++++++++++++++++++
>> >   4 files changed, 558 insertions(+)
>> >   create mode 100644 drivers/usb/typec/ucsi/ucsi_ite.c
>> >
>> >
>> > base-commit: 66498c75b4f8017f62d720d9b59675bdf3abce91

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

end of thread, other threads:[~2026-08-28  1:04 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-25 20:14 [PATCH v2 0/2] Add UCSI I2C transport driver for ITE885x USB-C controllers Edward Blair
2026-08-25 20:14 ` [PATCH v2 1/2] ACPI: x86: hide duplicate RhProxy node for ITE UCSI controllers Edward Blair
2026-08-25 20:14 ` [PATCH v2 2/2] usb: typec: ucsi: add ITE885x I2C transport driver Edward Blair
2026-08-27 22:30 ` [PATCH v2 0/2] Add UCSI I2C transport driver for ITE885x USB-C controllers Armin Wolf
     [not found]   ` <CAJDQNggKFhJ=YKanKWmG_hRi98nRi-8Wa19D7mqU1MuM2dF=vQ@mail.gmail.com>
2026-08-28  1:04     ` Edward Blair

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