public inbox for linux-i2c@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] Add UCSI I2C transport driver for ITE885x USB-C controllers
@ 2026-03-14  1:31 Edward Blair
  2026-03-14  1:31 ` [PATCH 1/2] i2c: acpi: skip generic I2C device when vendor-specific sibling exists Edward Blair
  2026-03-14  1:31 ` [PATCH 2/2] usb: typec: ucsi: add ITE885x I2C transport driver Edward Blair
  0 siblings, 2 replies; 9+ messages in thread
From: Edward Blair @ 2026-03-14  1:31 UTC (permalink / raw)
  To: linux-i2c, linux-usb
  Cc: heikki.krogerus, gregkh, wsa+renesas, westeri, linux-acpi,
	linux-kernel, Edward Blair

ASUS Z690/Z790/X670E motherboards use ITE8853 (and related ITE8800-ITE8805)
USB Type-C controllers that implement UCSI over I2C. These are currently
unsupported in Linux, causing a boot error:

  i2c i2c-1: Failed to register i2c client ITE8853:00 at 0x40 (-16)

The BIOS on these boards declares two ACPI devices for the same physical
hardware at the same I2C address: a generic UCSI device (MSFT8000) and a
vendor-specific device (ITE8853). The generic device registers first during
ACPI enumeration, blocking the vendor-specific one with -EBUSY. The ITE8853
ACPI device carries the GPIO interrupt resource needed for proper operation,
while MSFT8000 does not.

Patch 1 fixes the i2c ACPI enumeration to skip known generic devices when a
vendor-specific sibling exists at the same address. This is a general fix
that could benefit other platforms with the same BIOS pattern.

Patch 2 adds the ITE885x UCSI transport driver, modeled after ucsi_stm32g0.c.
The only device-specific quirk is that PPM_RESET is handled internally by the
ITE8853 and must not be sent over I2C.

Tested on ASUS ROG Strix Z790-E Gaming WiFi:
  - Boot error eliminated
  - /sys/class/typec/port0 registered successfully
  - UCSI commands complete within one polling cycle (~50ms)
  - USB-C device attach/detach detected with partner enumeration
  - USB Power Delivery negotiation functional
  - SuperSpeed Plus Gen 2x1 devices operational through UCSI-managed port

No MAINTAINERS entry is added as the driver falls under the existing
USB TYPEC UCSI section. Happy to add one if preferred.

Edward Blair (2):
  i2c: acpi: skip generic I2C device when vendor-specific sibling exists
  usb: typec: ucsi: add ITE885x I2C transport driver

 drivers/i2c/i2c-core-acpi.c       |  88 +++++++++
 drivers/usb/typec/ucsi/Kconfig    |  11 ++
 drivers/usb/typec/ucsi/Makefile   |   1 +
 drivers/usb/typec/ucsi/ucsi_ite.c | 285 ++++++++++++++++++++++++++++++
 4 files changed, 385 insertions(+)
 create mode 100644 drivers/usb/typec/ucsi/ucsi_ite.c

-- 
2.53.0


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

* [PATCH 1/2] i2c: acpi: skip generic I2C device when vendor-specific sibling exists
  2026-03-14  1:31 [PATCH 0/2] Add UCSI I2C transport driver for ITE885x USB-C controllers Edward Blair
@ 2026-03-14  1:31 ` Edward Blair
  2026-03-16 13:12   ` Mika Westerberg
  2026-03-16 14:07   ` Heikki Krogerus
  2026-03-14  1:31 ` [PATCH 2/2] usb: typec: ucsi: add ITE885x I2C transport driver Edward Blair
  1 sibling, 2 replies; 9+ messages in thread
From: Edward Blair @ 2026-03-14  1:31 UTC (permalink / raw)
  To: linux-i2c, linux-usb
  Cc: heikki.krogerus, gregkh, wsa+renesas, westeri, linux-acpi,
	linux-kernel, Edward Blair

Some BIOS implementations (notably ASUS Z690/Z790/X670E motherboards)
declare both a generic UCSI device (MSFT8000) and a vendor-specific
device (e.g., ITE8853) as ACPI children of the same I2C controller,
both referencing the same I2C slave address.

During ACPI I2C enumeration, whichever device is walked first claims
the address, causing the second to fail with -EBUSY. When the generic
MSFT8000 device registers first, the vendor-specific driver cannot
bind, losing access to device-specific features like GPIO interrupt
resources that are only declared on the vendor-specific ACPI device.

Fix this by checking, before registering a known generic I2C device,
whether a sibling ACPI device exists at the same address on the same
adapter. If so, skip the generic device to let the vendor-specific
one register instead.

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

diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c
index 2cbd31f77..87582eac7 100644
--- a/drivers/i2c/i2c-core-acpi.c
+++ b/drivers/i2c/i2c-core-acpi.c
@@ -137,6 +137,17 @@ static const struct acpi_device_id i2c_acpi_ignored_device_ids[] = {
 	{}
 };
 
+/*
+ * Generic I2C device IDs that may be duplicated by vendor-specific devices.
+ * When a vendor-specific sibling exists at the same address, the generic
+ * device is skipped to avoid -EBUSY address conflicts.
+ */
+static const struct acpi_device_id i2c_acpi_generic_device_ids[] = {
+	/* Microsoft UCSI - often paired with vendor-specific UCSI device */
+	{ "MSFT8000" },
+	{}
+};
+
 struct i2c_acpi_irq_context {
 	int irq;
 	bool wake_capable;
@@ -274,6 +285,76 @@ static int i2c_acpi_get_info(struct acpi_device *adev,
 	return 0;
 }
 
+struct i2c_acpi_sibling_check {
+	struct acpi_device *self;
+	struct i2c_adapter *adapter;
+	unsigned short addr;
+	bool found;
+};
+
+static int i2c_acpi_check_sibling_addr(struct acpi_device *adev, void *data)
+{
+	struct i2c_acpi_sibling_check *check = data;
+	struct i2c_acpi_lookup lookup;
+	struct i2c_board_info info;
+
+	if (adev == check->self)
+		return 0;
+
+	/* Only yield to vendor-specific devices, not other generic ones */
+	if (!acpi_match_device_ids(adev, i2c_acpi_generic_device_ids))
+		return 0;
+
+	memset(&lookup, 0, sizeof(lookup));
+	lookup.info = &info;
+	lookup.index = -1;
+
+	if (i2c_acpi_do_lookup(adev, &lookup))
+		return 0;
+
+	if (!device_match_acpi_handle(&check->adapter->dev,
+				      lookup.adapter_handle))
+		return 0;
+
+	if (info.addr == check->addr) {
+		check->found = true;
+		return 1;
+	}
+
+	return 0;
+}
+
+/*
+ * Check whether this generic ACPI device has a vendor-specific sibling at the
+ * same I2C address. Some BIOS implementations (e.g., ASUS Z690/Z790/X670E)
+ * declare both a generic UCSI device (MSFT8000) and a vendor-specific device
+ * (e.g., ITE8853) at the same address. Skip the generic one so the vendor
+ * driver can bind with proper interrupt and device-specific resources.
+ */
+static bool i2c_acpi_has_vendor_sibling(struct acpi_device *adev,
+					struct i2c_adapter *adapter,
+					struct i2c_board_info *info)
+{
+	struct acpi_device *parent;
+	struct i2c_acpi_sibling_check check;
+
+	if (acpi_match_device_ids(adev, i2c_acpi_generic_device_ids))
+		return false;
+
+	parent = acpi_dev_parent(adev);
+	if (!parent)
+		return false;
+
+	check.self = adev;
+	check.adapter = adapter;
+	check.addr = info->addr;
+	check.found = false;
+
+	acpi_dev_for_each_child(parent, i2c_acpi_check_sibling_addr, &check);
+
+	return check.found;
+}
+
 static void i2c_acpi_register_device(struct i2c_adapter *adapter,
 				     struct acpi_device *adev,
 				     struct i2c_board_info *info)
@@ -302,6 +383,13 @@ static acpi_status i2c_acpi_add_device(acpi_handle handle, u32 level,
 	if (!adev || i2c_acpi_get_info(adev, &info, adapter, NULL))
 		return AE_OK;
 
+	if (i2c_acpi_has_vendor_sibling(adev, adapter, &info)) {
+		dev_info(&adapter->dev,
+			 "skipping %s in favor of vendor-specific device at 0x%02x\n",
+			 dev_name(&adev->dev), info.addr);
+		return AE_OK;
+	}
+
 	i2c_acpi_register_device(adapter, adev, &info);
 
 	return AE_OK;
-- 
2.53.0


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

* [PATCH 2/2] usb: typec: ucsi: add ITE885x I2C transport driver
  2026-03-14  1:31 [PATCH 0/2] Add UCSI I2C transport driver for ITE885x USB-C controllers Edward Blair
  2026-03-14  1:31 ` [PATCH 1/2] i2c: acpi: skip generic I2C device when vendor-specific sibling exists Edward Blair
@ 2026-03-14  1:31 ` Edward Blair
  2026-03-16 14:57   ` Heikki Krogerus
  1 sibling, 1 reply; 9+ messages in thread
From: Edward Blair @ 2026-03-14  1:31 UTC (permalink / raw)
  To: linux-i2c, linux-usb
  Cc: heikki.krogerus, gregkh, wsa+renesas, westeri, linux-acpi,
	linux-kernel, Edward Blair

Add a UCSI transport driver for ITE8853/ITE8800-ITE8805 USB Type-C
controllers, commonly found on ASUS Z690/Z790/X670E motherboards.

These controllers implement the UCSI protocol over I2C with
ITE-proprietary register offsets and dedicated interrupt registers:

  - CCI at 0x84, MESSAGE_IN at 0x88, CONTROL at 0x98
  - INT_STATUS at 0xBD, INT_ACK at 0xBC
  - GPIO interrupt (level-triggered, active-low)

The ITE8853 does not accept PPM_RESET over I2C (the Windows driver
handles it internally), so the driver intercepts this command and
returns a synthetic reset-complete response.

Tested on ASUS ROG Strix Z790-E Gaming WiFi with ITE8853 at I2C
address 0x40 on the DesignWare I2C controller.

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 | 285 ++++++++++++++++++++++++++++++
 3 files changed, 297 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..455272b5a 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 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..400b844a1
--- /dev/null
+++ b/drivers/usb/typec/ucsi/ucsi_ite.c
@@ -0,0 +1,285 @@
+// 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
+ * ASUS Z690/Z790/X670E motherboards. They communicate via I2C with
+ * ITE-proprietary register offsets and interrupt registers.
+ *
+ * Note: Some BIOS implementations declare both MSFT8000 (generic UCSI) and
+ * ITE8853 (vendor-specific) ACPI devices at the same I2C address. The i2c
+ * core skips the generic device when a vendor-specific sibling exists,
+ * allowing this driver to bind to the ITE8853 client with proper IRQ.
+ */
+
+#include <linux/acpi.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+
+#include "ucsi.h"
+
+/* ITE-specific I2C register offsets */
+#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
+
+/* INT_STATUS register bits */
+#define ITE_INT_VENDOR_ALERT	BIT(0)
+#define ITE_INT_CCI		BIT(1)
+
+/* INT_ACK register values */
+#define ITE_ACK_VENDOR		0x01
+#define ITE_ACK_CCI		0x02
+
+struct ucsi_ite {
+	struct device *dev;
+	struct i2c_client *client;
+	struct ucsi *ucsi;
+};
+
+static int ucsi_ite_read(struct ucsi_ite *ite, u8 reg, void *val, size_t len)
+{
+	struct i2c_msg msgs[] = {
+		{
+			.addr = ite->client->addr,
+			.flags = 0,
+			.len = 1,
+			.buf = &reg,
+		},
+		{
+			.addr = ite->client->addr,
+			.flags = I2C_M_RD,
+			.len = len,
+			.buf = val,
+		},
+	};
+	int ret;
+
+	ret = i2c_transfer(ite->client->adapter, msgs, ARRAY_SIZE(msgs));
+	if (ret != ARRAY_SIZE(msgs)) {
+		dev_err(ite->dev, "i2c read 0x%02x failed: %d\n", reg, ret);
+		return ret < 0 ? ret : -EIO;
+	}
+
+	return 0;
+}
+
+static int ucsi_ite_write(struct ucsi_ite *ite, u8 reg, const void *val,
+			  size_t len)
+{
+	struct i2c_msg msg = {
+		.addr = ite->client->addr,
+		.flags = 0,
+	};
+	u8 *buf;
+	int ret;
+
+	buf = kmalloc(len + 1, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	buf[0] = reg;
+	memcpy(&buf[1], val, len);
+	msg.len = len + 1;
+	msg.buf = buf;
+
+	ret = i2c_transfer(ite->client->adapter, &msg, 1);
+	kfree(buf);
+	if (ret != 1) {
+		dev_err(ite->dev, "i2c write 0x%02x failed: %d\n", reg, ret);
+		return ret < 0 ? ret : -EIO;
+	}
+
+	return 0;
+}
+
+static int ucsi_ite_read_version(struct ucsi *ucsi, u16 *version)
+{
+	/*
+	 * The ITE8853 does not expose a UCSI VERSION register over I2C.
+	 * The Windows driver never reads it. Report UCSI 1.0.
+	 */
+	*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);
+
+	return ucsi_ite_read(ite, ITE_REG_CCI, cci, sizeof(*cci));
+}
+
+static int ucsi_ite_read_message_in(struct ucsi *ucsi, void *val, size_t len)
+{
+	struct ucsi_ite *ite = ucsi_get_drvdata(ucsi);
+
+	return ucsi_ite_read(ite, ITE_REG_MESSAGE_IN, val, len);
+}
+
+static int ucsi_ite_async_control(struct ucsi *ucsi, u64 command)
+{
+	struct ucsi_ite *ite = ucsi_get_drvdata(ucsi);
+
+	return ucsi_ite_write(ite, ITE_REG_CONTROL, &command, sizeof(command));
+}
+
+static int ucsi_ite_sync_control(struct ucsi *ucsi, u64 command, u32 *cci,
+				 void *data, size_t size)
+{
+	/*
+	 * The ITE8853 handles PPM_RESET internally and does not accept it
+	 * over I2C — the Windows driver never sends it. Fake a successful
+	 * reset so the UCSI core can proceed with initialization.
+	 */
+	if ((command & 0xff) == UCSI_PPM_RESET) {
+		if (cci)
+			*cci = UCSI_CCI_RESET_COMPLETE;
+		return 0;
+	}
+
+	return ucsi_sync_control_common(ucsi, command, cci, data, size);
+}
+
+static const struct ucsi_operations ucsi_ite_ops = {
+	.read_version = ucsi_ite_read_version,
+	.read_cci = ucsi_ite_read_cci,
+	.poll_cci = ucsi_ite_read_cci,
+	.read_message_in = ucsi_ite_read_message_in,
+	.sync_control = ucsi_ite_sync_control,
+	.async_control = ucsi_ite_async_control,
+};
+
+static irqreturn_t ucsi_ite_irq(int irq, void *data)
+{
+	struct ucsi_ite *ite = data;
+	u8 status;
+	u32 cci;
+	u8 ack;
+	int ret;
+
+	ret = ucsi_ite_read(ite, ITE_REG_INT_STATUS, &status, sizeof(status));
+	if (ret)
+		return IRQ_NONE;
+
+	if (!(status & (ITE_INT_CCI | ITE_INT_VENDOR_ALERT)))
+		return IRQ_NONE;
+
+	if (status & ITE_INT_CCI) {
+		ack = ITE_ACK_CCI;
+		ucsi_ite_write(ite, ITE_REG_INT_ACK, &ack, sizeof(ack));
+
+		ret = ucsi_ite_read(ite, ITE_REG_CCI, &cci, sizeof(cci));
+		if (!ret)
+			ucsi_notify_common(ite->ucsi, cci);
+	}
+
+	if (status & ITE_INT_VENDOR_ALERT) {
+		ack = ITE_ACK_VENDOR;
+		ucsi_ite_write(ite, ITE_REG_INT_ACK, &ack, sizeof(ack));
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int ucsi_ite_probe(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	struct ucsi_ite *ite;
+	int ret;
+
+	ite = devm_kzalloc(dev, sizeof(*ite), GFP_KERNEL);
+	if (!ite)
+		return -ENOMEM;
+
+	ite->dev = dev;
+	ite->client = client;
+	i2c_set_clientdata(client, ite);
+
+	ite->ucsi = ucsi_create(dev, &ucsi_ite_ops);
+	if (IS_ERR(ite->ucsi))
+		return PTR_ERR(ite->ucsi);
+
+	ucsi_set_drvdata(ite->ucsi, ite);
+
+	ret = request_threaded_irq(client->irq, NULL, ucsi_ite_irq,
+				   IRQF_ONESHOT, dev_name(dev), ite);
+	if (ret) {
+		dev_err(dev, "failed to request IRQ: %d\n", ret);
+		goto err_destroy;
+	}
+
+	ret = ucsi_register(ite->ucsi);
+	if (ret) {
+		dev_err(dev, "failed to register UCSI: %d\n", ret);
+		goto err_free_irq;
+	}
+
+	return 0;
+
+err_free_irq:
+	free_irq(client->irq, ite);
+err_destroy:
+	ucsi_destroy(ite->ucsi);
+	return ret;
+}
+
+static void ucsi_ite_remove(struct i2c_client *client)
+{
+	struct ucsi_ite *ite = i2c_get_clientdata(client);
+
+	ucsi_unregister(ite->ucsi);
+	free_irq(client->irq, ite);
+	ucsi_destroy(ite->ucsi);
+}
+
+static int ucsi_ite_suspend(struct device *dev)
+{
+	struct ucsi_ite *ite = dev_get_drvdata(dev);
+
+	disable_irq(ite->client->irq);
+
+	return 0;
+}
+
+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,
+	.remove = ucsi_ite_remove,
+};
+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.53.0


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

* Re: [PATCH 1/2] i2c: acpi: skip generic I2C device when vendor-specific sibling exists
  2026-03-14  1:31 ` [PATCH 1/2] i2c: acpi: skip generic I2C device when vendor-specific sibling exists Edward Blair
@ 2026-03-16 13:12   ` Mika Westerberg
  2026-03-16 14:32     ` Edward Blair
  2026-03-16 14:07   ` Heikki Krogerus
  1 sibling, 1 reply; 9+ messages in thread
From: Mika Westerberg @ 2026-03-16 13:12 UTC (permalink / raw)
  To: Edward Blair
  Cc: linux-i2c, linux-usb, heikki.krogerus, gregkh, wsa+renesas,
	westeri, linux-acpi, linux-kernel

On Sat, Mar 14, 2026 at 01:31:55AM +0000, Edward Blair wrote:
> Some BIOS implementations (notably ASUS Z690/Z790/X670E motherboards)
> declare both a generic UCSI device (MSFT8000) and a vendor-specific
> device (e.g., ITE8853) as ACPI children of the same I2C controller,
> both referencing the same I2C slave address.
> 
> During ACPI I2C enumeration, whichever device is walked first claims
> the address, causing the second to fail with -EBUSY. When the generic
> MSFT8000 device registers first, the vendor-specific driver cannot
> bind, losing access to device-specific features like GPIO interrupt
> resources that are only declared on the vendor-specific ACPI device.
> 
> Fix this by checking, before registering a known generic I2C device,
> whether a sibling ACPI device exists at the same address on the same
> adapter. If so, skip the generic device to let the vendor-specific
> one register instead.
> 
> Signed-off-by: Edward Blair <edward.blair@gmail.com>
> ---
>  drivers/i2c/i2c-core-acpi.c | 88 +++++++++++++++++++++++++++++++++++++
>  1 file changed, 88 insertions(+)
> 
> diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c
> index 2cbd31f77..87582eac7 100644
> --- a/drivers/i2c/i2c-core-acpi.c
> +++ b/drivers/i2c/i2c-core-acpi.c
> @@ -137,6 +137,17 @@ static const struct acpi_device_id i2c_acpi_ignored_device_ids[] = {
>  	{}
>  };
>  
> +/*
> + * Generic I2C device IDs that may be duplicated by vendor-specific devices.
> + * When a vendor-specific sibling exists at the same address, the generic
> + * device is skipped to avoid -EBUSY address conflicts.
> + */
> +static const struct acpi_device_id i2c_acpi_generic_device_ids[] = {
> +	/* Microsoft UCSI - often paired with vendor-specific UCSI device */
> +	{ "MSFT8000" },
> +	{}
> +};

Are they both "present"? I mean their _STA() returns 0xF for both?

We have a quirk table already in drivers/acpi/x86/utils.c that I think could
be used to mark the other one being not present.

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

* Re: [PATCH 1/2] i2c: acpi: skip generic I2C device when vendor-specific sibling exists
  2026-03-14  1:31 ` [PATCH 1/2] i2c: acpi: skip generic I2C device when vendor-specific sibling exists Edward Blair
  2026-03-16 13:12   ` Mika Westerberg
@ 2026-03-16 14:07   ` Heikki Krogerus
  1 sibling, 0 replies; 9+ messages in thread
From: Heikki Krogerus @ 2026-03-16 14:07 UTC (permalink / raw)
  To: Edward Blair
  Cc: linux-i2c, linux-usb, gregkh, wsa+renesas, westeri, linux-acpi,
	linux-kernel

Sat, Mar 14, 2026 at 01:31:55AM +0000, Edward Blair wrote:
> Some BIOS implementations (notably ASUS Z690/Z790/X670E motherboards)
> declare both a generic UCSI device (MSFT8000) and a vendor-specific
> device (e.g., ITE8853) as ACPI children of the same I2C controller,
> both referencing the same I2C slave address.
> 
> During ACPI I2C enumeration, whichever device is walked first claims
> the address, causing the second to fail with -EBUSY. When the generic
> MSFT8000 device registers first, the vendor-specific driver cannot
> bind, losing access to device-specific features like GPIO interrupt
> resources that are only declared on the vendor-specific ACPI device.
> 
> Fix this by checking, before registering a known generic I2C device,
> whether a sibling ACPI device exists at the same address on the same
> adapter. If so, skip the generic device to let the vendor-specific
> one register instead.
> 
> Signed-off-by: Edward Blair <edward.blair@gmail.com>
> ---
>  drivers/i2c/i2c-core-acpi.c | 88 +++++++++++++++++++++++++++++++++++++
>  1 file changed, 88 insertions(+)
> 
> diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c
> index 2cbd31f77..87582eac7 100644
> --- a/drivers/i2c/i2c-core-acpi.c
> +++ b/drivers/i2c/i2c-core-acpi.c
> @@ -137,6 +137,17 @@ static const struct acpi_device_id i2c_acpi_ignored_device_ids[] = {
>  	{}
>  };
>  
> +/*
> + * Generic I2C device IDs that may be duplicated by vendor-specific devices.
> + * When a vendor-specific sibling exists at the same address, the generic
> + * device is skipped to avoid -EBUSY address conflicts.
> + */
> +static const struct acpi_device_id i2c_acpi_generic_device_ids[] = {
> +	/* Microsoft UCSI - often paired with vendor-specific UCSI device */
> +	{ "MSFT8000" },
> +	{}
> +};

That's not a generic UCSI device. That's the "RhProxy" device unless
I'm completely mistaken, and it's meant to be used in Windows to give
the user mode access to GPIOs, SPI, I2C and UART:
https://learn.microsoft.com/en-us/windows/uwp/devices-sensors/enable-usermode-access

I think we need to handle that one in drivers/acpi/x86/utils.c like
Mika said.

thanks,

-- 
heikki

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

* Re: [PATCH 1/2] i2c: acpi: skip generic I2C device when vendor-specific sibling exists
  2026-03-16 13:12   ` Mika Westerberg
@ 2026-03-16 14:32     ` Edward Blair
  2026-03-16 14:45       ` Mika Westerberg
  0 siblings, 1 reply; 9+ messages in thread
From: Edward Blair @ 2026-03-16 14:32 UTC (permalink / raw)
  To: mika.westerberg; +Cc: heikki.krogerus, linux-usb, linux-i2c, linux-acpi

On Sun, 16 Mar 2026 at 13:12, Mika Westerberg wrote:
> Are they both 'present'? I mean their _STA() returns 0xF for both?

MSFT8000:00 has no _STA method at all. The sysfs status attribute is
absent, which only happens when acpi_has_method(handle, "_STA")
returns false (device_sysfs.c line 591). So it defaults to present per
the ACPI spec. ITE8853:00 has _STA returning 0xF.

As Heikki pointed out, MSFT8000 is the RhProxy device, not UCSI. My
mistake in the commit message.

> We have a quirk table already in drivers/acpi/x86/utils.c that I
> think could be used to mark the other one being not present.

That would work. acpi_device_override_status() runs before _STA
evaluation so it can force status=0 even without a _STA method.

My concern is scope. MSFT8000 is a Windows-only Resource Hub Proxy
(RhProxy) device with no Linux driver, no module binding, and no
in-kernel consumer. It's a static ACPI node with no _STA, so the
BIOS exports it unconditionally. Skipping it during I2C client
enumeration would have zero functional impact on Linux while avoiding
a quirk table entry that needs duplicating per board.

Thanks,
Edward

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

* Re: [PATCH 1/2] i2c: acpi: skip generic I2C device when vendor-specific sibling exists
  2026-03-16 14:32     ` Edward Blair
@ 2026-03-16 14:45       ` Mika Westerberg
  2026-03-16 15:04         ` Edward Blair
  0 siblings, 1 reply; 9+ messages in thread
From: Mika Westerberg @ 2026-03-16 14:45 UTC (permalink / raw)
  To: Edward Blair; +Cc: heikki.krogerus, linux-usb, linux-i2c, linux-acpi

On Mon, Mar 16, 2026 at 02:32:42PM +0000, Edward Blair wrote:
> On Sun, 16 Mar 2026 at 13:12, Mika Westerberg wrote:
> > Are they both 'present'? I mean their _STA() returns 0xF for both?
> 
> MSFT8000:00 has no _STA method at all. The sysfs status attribute is
> absent, which only happens when acpi_has_method(handle, "_STA")
> returns false (device_sysfs.c line 591). So it defaults to present per
> the ACPI spec. ITE8853:00 has _STA returning 0xF.

Okay.

> As Heikki pointed out, MSFT8000 is the RhProxy device, not UCSI. My
> mistake in the commit message.
> 
> > We have a quirk table already in drivers/acpi/x86/utils.c that I
> > think could be used to mark the other one being not present.
> 
> That would work. acpi_device_override_status() runs before _STA
> evaluation so it can force status=0 even without a _STA method.
> 
> My concern is scope. MSFT8000 is a Windows-only Resource Hub Proxy
> (RhProxy) device with no Linux driver, no module binding, and no
> in-kernel consumer. It's a static ACPI node with no _STA, so the
> BIOS exports it unconditionally. Skipping it during I2C client
> enumeration would have zero functional impact on Linux while avoiding
> a quirk table entry that needs duplicating per board.

Well it needs to be somewhere unfortunately :( Gathering these into one
file at least makes it sligthly more maintainable IMHO. I think the
override_status_ids could be made to use only the HID so no need to add DMI
strings. Of course if we ever want to bind a driver to it then it needs to
be exposed again (but that sounds unlikely since we already have a real
device that a driver could bind to).

Just my 2 cents.

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

* Re: [PATCH 2/2] usb: typec: ucsi: add ITE885x I2C transport driver
  2026-03-14  1:31 ` [PATCH 2/2] usb: typec: ucsi: add ITE885x I2C transport driver Edward Blair
@ 2026-03-16 14:57   ` Heikki Krogerus
  0 siblings, 0 replies; 9+ messages in thread
From: Heikki Krogerus @ 2026-03-16 14:57 UTC (permalink / raw)
  To: Edward Blair
  Cc: linux-i2c, linux-usb, gregkh, wsa+renesas, westeri, linux-acpi,
	linux-kernel

Hi,

> +// 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
> + * ASUS Z690/Z790/X670E motherboards. They communicate via I2C with
> + * ITE-proprietary register offsets and interrupt registers.
> + *
> + * Note: Some BIOS implementations declare both MSFT8000 (generic UCSI) and
> + * ITE8853 (vendor-specific) ACPI devices at the same I2C address. The i2c
> + * core skips the generic device when a vendor-specific sibling exists,
> + * allowing this driver to bind to the ITE8853 client with proper IRQ.
> + */

Besides the above comment, this looks okay to me.

But couldn't you use the resource managed version to request the irq:
devm_request_threaded_irq() ?

thanks,

-- 
heikki

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

* Re: [PATCH 1/2] i2c: acpi: skip generic I2C device when vendor-specific sibling exists
  2026-03-16 14:45       ` Mika Westerberg
@ 2026-03-16 15:04         ` Edward Blair
  0 siblings, 0 replies; 9+ messages in thread
From: Edward Blair @ 2026-03-16 15:04 UTC (permalink / raw)
  To: mika.westerberg; +Cc: heikki.krogerus, linux-usb, linux-i2c, linux-acpi

On Sun, 16 Mar 2026 at 14:45, Mika Westerberg wrote:
> Well it needs to be somewhere unfortunately :( Gathering these into
> one file at least makes it sligthly more maintainable IMHO.

Fair enough, I'll move it to override_status_ids[] for v2.

One thing I noticed: the CPU match in acpi_device_override_status()
is mandatory, so each CPU family that ships boards with RhProxy will
need its own entry. The DMI check is already optional (skipped when
slot is unset), but there's no equivalent path for cpu_ids.

For now I'll add an entry for INTEL_RAPTORLAKE_S without DMI, which
covers all Raptor Lake desktop boards. Other platforms can add
entries as needed. If the per-CPU duplication becomes a problem later,
making cpu_ids optional would be a small change to the loop.

Thanks,
Edward

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

end of thread, other threads:[~2026-03-16 15:04 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-14  1:31 [PATCH 0/2] Add UCSI I2C transport driver for ITE885x USB-C controllers Edward Blair
2026-03-14  1:31 ` [PATCH 1/2] i2c: acpi: skip generic I2C device when vendor-specific sibling exists Edward Blair
2026-03-16 13:12   ` Mika Westerberg
2026-03-16 14:32     ` Edward Blair
2026-03-16 14:45       ` Mika Westerberg
2026-03-16 15:04         ` Edward Blair
2026-03-16 14:07   ` Heikki Krogerus
2026-03-14  1:31 ` [PATCH 2/2] usb: typec: ucsi: add ITE885x I2C transport driver Edward Blair
2026-03-16 14:57   ` Heikki Krogerus

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