From nobody Mon Sep 28 05:42:44 2026 Received: from mail-wm1-f51.google.com (mail-wm1-f51.google.com [209.85.128.51]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EE5C148F839 for ; Tue, 25 Aug 2026 20:14:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787688884; cv=none; b=jbFI+/J07ZipyiY2fJhVbrDCnqHndkgEiZjx6/P/t2OSQZSUEJgO/2J31+HtUFEVJEATuc5FTtcoUMKK+ASGGGui5KryBGj0dwrlBka2w1b/bZ8h2WVZXzPfv6krDHOE460sgDc2KdhbnDZPn2mtBDyDlc/USwwIdWbgr9LHDNQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787688884; c=relaxed/simple; bh=x7nVjauRI+U0yX7J0JyAbzfVg4TDMgnH3ULu6xPpkL0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=XFHS+vwOguM0+7MUn1vqqxjW2fsFBJ8M2QbGlBIoaPmlkomxfJr1Jvqch3poS3UYIwSNl6dPfqxh9XgtPWb1Ym0JREmK72g+zGck+pSuqownmUM6X/ZbuXtgPn7bMULZxeynjBFrP/fMk3p9J85RbFLK+9wfTAuLR8bTHUw9OKo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=sjdcXKlw; arc=none smtp.client-ip=209.85.128.51 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="sjdcXKlw" Received: by mail-wm1-f51.google.com with SMTP id 5b1f17b1804b1-49554ebb87dso1268975e9.3 for ; Tue, 25 Aug 2026 13:14:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1787688881; x=1788293681; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=SmZxmecvZG15vtYaUoB5D0+zMfbkIPe0/Mtk0u+hNh0=; b=sjdcXKlwbrybOV0F9tXdoDJQq0YMteCQK1XUSuXmboSw2dmirzoUQGHJ+lq1JIwtBh awiplPEetLEchGJi5XLF9zGxJC+T8Ndg2VAFjbprgntuYJzwmiia0jUKD+iAAgwQuRg+ pCjttMHvBEgs+0+t/g52Y6VQpcVCHsi3kULhrrNoaJPad2zejT8yFnoq2cy32AuyUmMU l1LDk/5o3h8bRWeJ+LpmY84XIKfKRZhv5KSpOLfIm2FhZyyTMFeEnivo/iHMAMxyzJdq XuSqjm5ChFJMi+mBftbQ6PqJ+IPwfD7NpfT8v3waFl59cY4m0F8YIen5eoXBwrENFLtF PBoQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1787688881; x=1788293681; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=SmZxmecvZG15vtYaUoB5D0+zMfbkIPe0/Mtk0u+hNh0=; b=IHADPyiKYF68WGEmFwUYpAcd1eDoAL57PVfWJUPLdoDZCiOOdnNdu1PontZg08vDEl lfmBnLox7bSeMEceUUio5Fu/hbXf27QFWCDj8b9qgWj3Akq3sEa71GLC5lJ3++As3acw WXZ2L6wYypD/X99FMvnUQdlYYz2KfoiOmOYxc3WL8jZGk8VuqiX7vGN08hkU/SMicU/b kr+zcBirzbYBsJQ7szdYkQykypHlphp849hqLCUnv/DxRSP3jXaS7w5XVXQRA9mrcbTc wRRMkUEzMseyTmypcnEF/v35g6pWjFGjhR9CtblydXEM6iE0gnXwAZuWEilYH4jMZl5R vWfw== X-Forwarded-Encrypted: i=1; AHgh+RqOeARUYA/M2Xzg8NyztZSr/LwjFjRAJPSlyPfQvW5RhKMd4giUUP+1jd7eUPjPa5tHV3E0bVimKucDDBE=@vger.kernel.org X-Gm-Message-State: AFuF++l9BEMn6iSRcepaz+LMF2FspX9pxVqUVJG3edOZfrZE/joiuTnn 7fWUq4756yyO+8zcvZHRituXuk7fRSMNQBI5/qqSaoelNAWe0sGHN9oG X-Gm-Gg: AR+sD10zSnREgjTE3INzNt3lk2sJf2d2u6c726Sq2wmIsVwzlTKJwDBZoRIqmACQoMP NkvQpgay5TGpWhYaOLxtVsPGILrgJiurgPXIJx40gjKgsX0NE/apuHV+6F6LAaCAZxmViRMu1V2 rqjpn8ypDnDj0HwqNyWTNVRmZavrzGdhGnYehIssDem0WGsmwrWOpNWuYiLrdROmhn54/pIM9qd zolb6Vtzdt4En2hZJFraSXMcPEGXaveDLVXgyQtMjOtqHVxkAw+l35CApWW8wXt62OPY4XdwL6r HSul+JH3Yxj7XNlOzedbG8g4dELomFyLvBT8pr39GHrIvUWYkzz3hljxsHMbTxo6zRBTvoAUXNZ 4UQn7rQ0CNvE9uTJU7h0+XEs+8SRBK2O23F0UD/qS2Bf6XNQwyXuAEmefbXqyX3hPUt5zpRtlSz ++Lual/2u/bLfZNBI/IEIEp9mneNtjcc8CtsdFbBW+sen4xZVBi+VAVvAkZoWMqrLFGA8eErdyP GBzVpo73o3QZ6GQcQnYxWPC8vzLKSWIjOiiRUnPM7leHFAYnHvPNKeIxo6KphWL7TEa9AxvvR4u 8iiVzFRxrmUzjphtycQHasZAX0FJuWccqugTmi6xiMwGZAaA//6nFPna/Ik= X-Received: by 2002:a05:600c:6912:b0:499:cd34:100d with SMTP id 5b1f17b1804b1-499dc703bd3mr15456995e9.7.1787688880894; Tue, 25 Aug 2026 13:14:40 -0700 (PDT) Received: from scambox.localdomain (5-198-68-184.static.kc.net.uk. [5.198.68.184]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-499dc960e80sm5050435e9.1.2026.08.25.13.14.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 25 Aug 2026 13:14:40 -0700 (PDT) From: Edward Blair To: linux-acpi@vger.kernel.org, linux-usb@vger.kernel.org Cc: rafael@kernel.org, lenb@kernel.org, heikki.krogerus@linux.intel.com, mika.westerberg@linux.intel.com, gregkh@linuxfoundation.org, linux-kernel@vger.kernel.org, Edward Blair Subject: [PATCH v2 1/2] ACPI: x86: hide duplicate RhProxy node for ITE UCSI controllers Date: Tue, 25 Aug 2026 21:14:25 +0100 Message-ID: <20260825201426.47030-2-edward.blair@gmail.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260825201426.47030-1-edward.blair@gmail.com> References: <20260825201426.47030-1-edward.blair@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 --- 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) =20 +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 =3D context; + struct acpi_resource_i2c_serialbus *sb; + + if (ares->type =3D=3D ACPI_RESOURCE_TYPE_END_TAG) + return AE_OK; + + if (ares->type !=3D ACPI_RESOURCE_TYPE_SERIAL_BUS) { + if ((ares->type =3D=3D ACPI_RESOURCE_TYPE_IRQ && + ares->data.irq.interrupt_count) || + (ares->type =3D=3D ACPI_RESOURCE_TYPE_EXTENDED_IRQ && + ares->data.extended_irq.interrupt_count) || + (ares->type =3D=3D ACPI_RESOURCE_TYPE_GPIO && + ares->data.gpio.connection_type =3D=3D ACPI_RESOURCE_GPIO_TYPE_INT = && + ares->data.gpio.pin_table_length)) + info->has_interrupt =3D true; + info->other_resources++; + return AE_OK; + } + + sb =3D &ares->data.i2c_serial_bus; + if (sb->type !=3D 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 =3D sb->slave_address; + info->connection_speed =3D sb->connection_speed; + info->slave_mode =3D sb->slave_mode; + info->connection_sharing =3D sb->connection_sharing; + info->access_mode =3D sb->access_mode; + info->found =3D 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 =3D handle; + status =3D 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 =3D context; + struct acpi_i2c_resource_info info; + + if (!acpi_get_i2c_resource_info(handle, &info)) + return AE_OK; + if (info.i2c_resources !=3D 1 || !info.has_interrupt) + return AE_OK; + + if (info.controller !=3D match->rhproxy->controller || + info.address !=3D match->rhproxy->address || + info.connection_speed !=3D match->rhproxy->connection_speed || + info.slave_mode !=3D match->rhproxy->slave_mode || + info.connection_sharing !=3D match->rhproxy->connection_sharing || + info.access_mode !=3D match->rhproxy->access_mode) + return AE_OK; + + match->found =3D true; + return AE_CTRL_TERMINATE; +} + +static bool acpi_has_ite_ucsi_duplicate(struct acpi_device *adev) +{ + static const char * const ite_ucsi_ids[] =3D { + "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 !=3D 1 || rhproxy.other_resources) + return false; + + match.rhproxy =3D &rhproxy; + match.found =3D false; + + for (i =3D 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[] =3D { + { "MSFT8000" }, + { } +}; + static const struct override_status_id override_status_ids[] =3D { /* * Bay / Cherry Trail PWM directly poked by GPU driver in win10, @@ -182,6 +320,19 @@ bool acpi_device_override_status(struct acpi_device *a= dev, unsigned long long *s bool ret =3D false; unsigned int i; =20 + /* + * 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 =3D 0; + return true; + } + for (i =3D 0; i < ARRAY_SIZE(override_status_ids); i++) { if (!x86_match_cpu(override_status_ids[i].cpu_ids)) continue; --=20 2.55.0 From nobody Mon Sep 28 05:42:44 2026 Received: from mail-wm1-f48.google.com (mail-wm1-f48.google.com [209.85.128.48]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E342F48EBE8 for ; Tue, 25 Aug 2026 20:14:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.48 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787688887; cv=none; b=TBgB8e+tQnw4aob6aF1+aovfnR29LS69XRVW48CXbXNEMjBopZ8MBGk0A7kT+wPlXyiyHtNeSe2/pLcQwoCicOLoEyYk4f8f1xIjIJ+99RlzvRHzyquRE9qTwh6WLKIw+Ty1/B+q9RUvRK+xponogykB607vFfioayhiQ06B1KQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787688887; c=relaxed/simple; bh=/vTICs4rfZnQqWAY7jp6Xx8e1B3/4HZ5WGwfLBmD4E8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=bzRRrJmGRswDSALe5cSfMGPJsQ/rlQKREXtXaffAl9JxT8jFZJzupKv6rpFWTl6UezxnHFVb7iurgkgn33d1A7OLHEh7oFpAPzJpWqHcZ2Hoohe3M9JKjmPFdsb5871XqZM0imM9uSpqOgxQT+02c8EP3HPlXJjEUrDytJvC048= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=no13wnrI; arc=none smtp.client-ip=209.85.128.48 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="no13wnrI" Received: by mail-wm1-f48.google.com with SMTP id 5b1f17b1804b1-4953e04ef16so1123655e9.2 for ; Tue, 25 Aug 2026 13:14:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1787688884; x=1788293684; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=hWvvM2eJTieYNAJ1IrJEwIoJaD0ytMJ17q0W4sd4AmA=; b=no13wnrIWtL13vXQkmEAVFZmcye2xYOSzJISNBI57Ca0fAuh4FptkhTEq0nMYKx0fF Z6MwIYliX/WttdhybRmVClrS9Z4h+G94ZpgMGul8Hbcy3ZkejzgImBezpdqYP2PoGUb2 pF3csgsWluJ3rxOcegCGElricJwL6t+E5rrGC+Csz9OHotPPJHZjj7nECzDilNVt6SAO C+xD263FqgJfde4TpFcIiujJY/YLLns0pWumU5qfoovrbxt/IfOgozxy8RCAbV/6auwB LIdxSIk4DqZX5FTr2SB7WoKHHqitAMyw8LGhE8ieeaSNCt1eVZXmclI0tODNQ0KSVyeu 8e2A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1787688884; x=1788293684; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=hWvvM2eJTieYNAJ1IrJEwIoJaD0ytMJ17q0W4sd4AmA=; b=sHMCUXvdOOhHYfLRSbNnE617UJbX7S8eigIIeka6fXy0n6/nHpw2lcVtemLkvoP8E8 lYGuP0NI7IwyioDPrfbwZ5LPq4n04JLSBR/mYE0sRsDYHEUVltjuGNGcBBxF6g3V9bzB HWewJWsdTGKaF20W7oAq4iATjN0rJ1A56EwoYAsWf1HkkPQnkTXMl+pFLqB1GQNQ0Amn sxQakdatRG4xhXdWrXpdM4PEeIpBsVvo5/4Zm8FpSTgRxyMjN6r7SHkzS9bxZPTTy1YW LjESMPKb6NGM5up4sMYgUJu3s/BuFmz44TqD33V/9PlLrASWn+RQ1mH5ZBGEYrhVD6XS NjCA== X-Forwarded-Encrypted: i=1; AHgh+Roh/s1teJBW2dLO/xyYpJmH8xg3gHyJBbCDLrZG5KrOBYGw3GVyMScIPQ/5CYl+M3POWS3LX3xGeON3Ssw=@vger.kernel.org X-Gm-Message-State: AFuF++nuXzaJeq0ak9QWgq+/WtVvuKYjdJcfjbtRceC/uT+irZ/rZadV QnrhxjFPe0ntct0QihU+CCCi9jgPL5ZvcM4w0sli+qTmw/WKbXFxXoDW X-Gm-Gg: AR+sD13MxFnSpwt45X8QJ0o41GzBA/bgKQAeCvqXpp7LrF+704PYjfNJXenBYM2NxE9 cQ5a0LdTnZ0oYSfY+ze0fr3CV1bW3M2NbdpxXFZLpj7Szmi1jaS7yGAGMOmqZVaS7DymTlrDB0c RKZ0L/adYZd40f/xVVxmF0kpF70EH04MY/RAz6CuFaO6qOIYJTpH7qmzfua03YMOzM6tNJQxGMJ oGe5EeDBRDQIIe+t4noSiwkyex+/1n/BGEk+LFRl+9c8ld8xUINM2/Z3K6L+8j24HEymbTOhr43 fbXc+XPcz5VW4dXKzDm8pa8SxE+n+FISj3mWRhQCWW9OLNr4IXOH2tsNkm5upqq7ykvXiCXfZFB BXq7eQPx1yz05fIbZqpQiKptpVxWBiFyMA4oEJQL1glbVQ84EUay0am/HfiFAsgiSHtdcJOHy4O XCzl88CyyHBAzlrzwAeDDPExtKnRJHA6N9NdtsqmZWTtDsvoIknRvI4iCAcDmgFSs9GXgxP0CTU E7daY4tKi5/lJMKZk1F/2XzqCd71jHUREftfY9DsaPlMOKSHEYjL8dy3epMYMteA0spWn4oJlpI nsaUkcZLf5+n4AuATCf9sQd/TGUS9wo8OBN/rlDiKGpYF6+n X-Received: by 2002:a05:600c:811a:b0:499:db9b:f253 with SMTP id 5b1f17b1804b1-499dc6ec214mr15659295e9.3.1787688883796; Tue, 25 Aug 2026 13:14:43 -0700 (PDT) Received: from scambox.localdomain (5-198-68-184.static.kc.net.uk. [5.198.68.184]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-499dc960e80sm5050435e9.1.2026.08.25.13.14.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 25 Aug 2026 13:14:43 -0700 (PDT) From: Edward Blair To: linux-acpi@vger.kernel.org, linux-usb@vger.kernel.org Cc: rafael@kernel.org, lenb@kernel.org, heikki.krogerus@linux.intel.com, mika.westerberg@linux.intel.com, gregkh@linuxfoundation.org, linux-kernel@vger.kernel.org, Edward Blair Subject: [PATCH v2 2/2] usb: typec: ucsi: add ITE885x I2C transport driver Date: Tue, 25 Aug 2026 21:14:26 +0100 Message-ID: <20260825201426.47030-3-edward.blair@gmail.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260825201426.47030-1-edward.blair@gmail.com> References: <20260825201426.47030-1-edward.blair@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 --- 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. =20 +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/Makef= ile 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) +=3D ucsi_glink.o obj-$(CONFIG_CROS_EC_UCSI) +=3D cros_ec_ucsi.o obj-$(CONFIG_UCSI_LENOVO_YOGA_C630) +=3D ucsi_yoga_c630.o obj-$(CONFIG_UCSI_HUAWEI_GAOKUN) +=3D ucsi_huawei_gaokun.o +obj-$(CONFIG_UCSI_ITE) +=3D ucsi_ite.o diff --git a/drivers/usb/typec/ucsi/ucsi_ite.c b/drivers/usb/typec/ucsi/ucs= i_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 regis= ter. + */ + +#include +#include +#include +#include +#include + +#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 l= en) +{ + struct i2c_client *client =3D ite->client; + struct i2c_msg msgs[] =3D { + { + .addr =3D client->addr, + .len =3D 1, + .buf =3D ®, + }, + { + .addr =3D client->addr, + .flags =3D I2C_M_RD, + .len =3D len, + .buf =3D val, + }, + }; + int ret; + + ret =3D i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret =3D=3D ARRAY_SIZE(msgs)) + return 0; + + ret =3D 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 =3D ite->client; + u8 buf[sizeof(u64) + 1]; + struct i2c_msg msg =3D { + .addr =3D client->addr, + .len =3D len + 1, + .buf =3D buf, + }; + int ret; + + if (len > sizeof(buf) - 1) + return -EINVAL; + + buf[0] =3D reg; + memcpy(&buf[1], val, len); + + ret =3D i2c_transfer(client->adapter, &msg, 1); + if (ret =3D=3D 1) + return 0; + + ret =3D 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] =3D {}; + __le32 raw_cci; + u8 status; + u8 len =3D 0; + int event; + int err =3D 0; + int ret; + + mutex_lock(&ite->event_lock); + + ret =3D ucsi_ite_read(ite, ITE_REG_INT_STATUS, &status, sizeof(status)); + if (ret) + goto out_unlock; + + status &=3D ITE_INT_MASK; + if (!status) { + mutex_lock(&ite->received_lock); + *cci =3D ite->cci; + mutex_unlock(&ite->received_lock); + ret =3D ITE_EVENT_NONE; + goto out_unlock; + } + + if (status & ITE_INT_CCI) { + err =3D ucsi_ite_read(ite, ITE_REG_CCI, &raw_cci, + sizeof(raw_cci)); + if (!err) { + *cci =3D le32_to_cpu(raw_cci); + len =3D UCSI_CCI_LENGTH(*cci); + + if (len > sizeof(message_in)) { + len =3D sizeof(message_in); + *cci &=3D ~GENMASK(15, 8); + *cci |=3D UCSI_SET_CCI_LENGTH(len); + } + if (len) { + err =3D 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 =3D ITE_INT_VENDOR_ALERT; + + ret =3D ucsi_ite_write(ite, ITE_REG_INT_ACK, &ack, sizeof(ack)); + if (ret) + goto out_unlock; + } + + if ((status & ITE_INT_CCI) && !err) { + u8 ack =3D ITE_INT_CCI; + + ret =3D ucsi_ite_write(ite, ITE_REG_INT_ACK, &ack, sizeof(ack)); + if (ret) + goto out_unlock; + } + + if (err) { + ret =3D err; + goto out_unlock; + } + + if (status & ITE_INT_CCI) { + mutex_lock(&ite->received_lock); + ite->cci =3D *cci; + memset(ite->message_in, 0, sizeof(ite->message_in)); + memcpy(ite->message_in, message_in, len); + mutex_unlock(&ite->received_lock); + event =3D ITE_EVENT_CCI; + } else { + mutex_lock(&ite->received_lock); + *cci =3D ite->cci; + mutex_unlock(&ite->received_lock); + event =3D ITE_EVENT_VENDOR; + } + + ret =3D 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 =3D UCSI_VERSION_1_0; + return 0; +} + +static int ucsi_ite_read_cci(struct ucsi *ucsi, u32 *cci) +{ + struct ucsi_ite *ite =3D ucsi_get_drvdata(ucsi); + + mutex_lock(&ite->received_lock); + *cci =3D ite->cci; + mutex_unlock(&ite->received_lock); + + return 0; +} + +static int ucsi_ite_poll_cci(struct ucsi *ucsi, u32 *cci) +{ + struct ucsi_ite *ite =3D ucsi_get_drvdata(ucsi); + int ret; + + ret =3D 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 l= en) +{ + struct ucsi_ite *ite =3D 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 =3D ucsi_get_drvdata(ucsi); + __le64 raw_command =3D cpu_to_le64(command); + int ret; + + if (UCSI_COMMAND(command) =3D=3D 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 =3D 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 =3D 0; + memset(ite->message_in, 0, sizeof(ite->message_in)); + mutex_unlock(&ite->received_lock); + ret =3D 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 =3D { + .read_version =3D ucsi_ite_read_version, + .read_cci =3D ucsi_ite_read_cci, + .poll_cci =3D ucsi_ite_poll_cci, + .read_message_in =3D ucsi_ite_read_message_in, + .sync_control =3D ucsi_sync_control_common, + .async_control =3D ucsi_ite_async_control, +}; + +static irqreturn_t ucsi_ite_irq(int irq, void *data) +{ + struct ucsi_ite *ite =3D data; + u32 cci; + int ret; + + ret =3D ucsi_ite_process_event(ite, &cci); + if (ret =3D=3D ITE_EVENT_NONE) + return IRQ_NONE; + if (ret < 0) + return IRQ_HANDLED; + + if (ret =3D=3D ITE_EVENT_CCI) + ucsi_notify_common(ite->ucsi, cci); + + return IRQ_HANDLED; +} + +static void ucsi_ite_destroy(void *data) +{ + struct ucsi_ite *ite =3D data; + + if (ite->registered) + ucsi_unregister(ite->ucsi); + ucsi_destroy(ite->ucsi); +} + +static int ucsi_ite_probe(struct i2c_client *client) +{ + struct device *dev =3D &client->dev; + struct ucsi_ite *ite; + u32 cci; + int ret; + + if (client->irq <=3D 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 =3D devm_kzalloc(dev, sizeof(*ite), GFP_KERNEL); + if (!ite) + return -ENOMEM; + + ite->client =3D client; + mutex_init(&ite->event_lock); + mutex_init(&ite->received_lock); + i2c_set_clientdata(client, ite); + + ite->ucsi =3D 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 =3D devm_add_action_or_reset(dev, ucsi_ite_destroy, ite); + if (ret) + return ret; + + ucsi_set_drvdata(ite->ucsi, ite); + + ret =3D 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 =3D ucsi_ite_process_event(ite, &cci); + if (ret < 0) + dev_warn(dev, "initial event processing failed: %d\n", ret); + + ret =3D ucsi_register(ite->ucsi); + if (ret) + return dev_err_probe(dev, ret, + "failed to register UCSI interface\n"); + + ite->registered =3D true; + return 0; +} + +static int ucsi_ite_suspend(struct device *dev) +{ + struct ucsi_ite *ite =3D dev_get_drvdata(dev); + int ret; + + disable_irq(ite->client->irq); + ret =3D 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 =3D 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[] =3D { + { "ITE8853" }, + { "ITE8800" }, + { "ITE8801" }, + { "ITE8802" }, + { "ITE8803" }, + { "ITE8804" }, + { "ITE8805" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, ucsi_ite_acpi_ids); + +static struct i2c_driver ucsi_ite_driver =3D { + .driver =3D { + .name =3D "ucsi_ite", + .acpi_match_table =3D ucsi_ite_acpi_ids, + .pm =3D pm_sleep_ptr(&ucsi_ite_pm), + }, + .probe =3D ucsi_ite_probe, +}; +module_i2c_driver(ucsi_ite_driver); + +MODULE_AUTHOR("Edward Blair "); +MODULE_DESCRIPTION("UCSI I2C transport driver for ITE885x USB-C controller= s"); +MODULE_LICENSE("GPL"); --=20 2.55.0