]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
acpi: typec: ucsi: Introduce a ->poll_cci method
authorChristian A. Ehrhardt <lk@c--e.de>
Mon, 17 Feb 2025 10:54:39 +0000 (13:54 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 13 Mar 2025 12:02:13 +0000 (13:02 +0100)
commit 976e7e9bdc7719a023a4ecccd2e3daec9ab20a40 upstream.

For the ACPI backend of UCSI the UCSI "registers" are just a memory copy
of the register values in an opregion. The ACPI implementation in the
BIOS ensures that the opregion contents are synced to the embedded
controller and it ensures that the registers (in particular CCI) are
synced back to the opregion on notifications. While there is an ACPI call
that syncs the actual registers to the opregion there is rarely a need to
do this and on some ACPI implementations it actually breaks in various
interesting ways.

The only reason to force a sync from the embedded controller is to poll
CCI while notifications are disabled. Only the ucsi core knows if this
is the case and guessing based on the current command is suboptimal, i.e.
leading to the following spurious assertion splat:

WARNING: CPU: 3 PID: 76 at drivers/usb/typec/ucsi/ucsi.c:1388 ucsi_reset_ppm+0x1b4/0x1c0 [typec_ucsi]
CPU: 3 UID: 0 PID: 76 Comm: kworker/3:0 Not tainted 6.12.11-200.fc41.x86_64 #1
Hardware name: LENOVO 21D0/LNVNB161216, BIOS J6CN45WW 03/17/2023
Workqueue: events_long ucsi_init_work [typec_ucsi]
RIP: 0010:ucsi_reset_ppm+0x1b4/0x1c0 [typec_ucsi]
Call Trace:
 <TASK>
 ucsi_init_work+0x3c/0xac0 [typec_ucsi]
 process_one_work+0x179/0x330
 worker_thread+0x252/0x390
 kthread+0xd2/0x100
 ret_from_fork+0x34/0x50
 ret_from_fork_asm+0x1a/0x30
 </TASK>

Thus introduce a ->poll_cci() method that works like ->read_cci() with an
additional forced sync and document that this should be used when polling
with notifications disabled. For all other backends that presumably don't
have this issue use the same implementation for both methods.

Fixes: fa48d7e81624 ("usb: typec: ucsi: Do not call ACPI _DSM method for UCSI read operations")
Cc: stable <stable@kernel.org>
Signed-off-by: Christian A. Ehrhardt <lk@c--e.de>
Tested-by: Fedor Pchelkin <boddah8794@gmail.com>
Signed-off-by: Fedor Pchelkin <boddah8794@gmail.com>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Link: https://lore.kernel.org/r/20250217105442.113486-2-boddah8794@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/typec/ucsi/ucsi.c
drivers/usb/typec/ucsi/ucsi.h
drivers/usb/typec/ucsi/ucsi_acpi.c
drivers/usb/typec/ucsi/ucsi_ccg.c
drivers/usb/typec/ucsi/ucsi_glink.c
drivers/usb/typec/ucsi/ucsi_stm32g0.c
drivers/usb/typec/ucsi/ucsi_yoga_c630.c

index 619bda11f2972a426e43c043f1c0024adcd31984..3f2bc13efa4865939ef4327e28787e230a7e2a52 100644 (file)
@@ -1330,7 +1330,7 @@ static int ucsi_reset_ppm(struct ucsi *ucsi)
 
        mutex_lock(&ucsi->ppm_lock);
 
-       ret = ucsi->ops->read_cci(ucsi, &cci);
+       ret = ucsi->ops->poll_cci(ucsi, &cci);
        if (ret < 0)
                goto out;
 
@@ -1348,7 +1348,7 @@ static int ucsi_reset_ppm(struct ucsi *ucsi)
 
                tmo = jiffies + msecs_to_jiffies(UCSI_TIMEOUT_MS);
                do {
-                       ret = ucsi->ops->read_cci(ucsi, &cci);
+                       ret = ucsi->ops->poll_cci(ucsi, &cci);
                        if (ret < 0)
                                goto out;
                        if (cci & UCSI_CCI_COMMAND_COMPLETE)
@@ -1377,7 +1377,7 @@ static int ucsi_reset_ppm(struct ucsi *ucsi)
                /* Give the PPM time to process a reset before reading CCI */
                msleep(20);
 
-               ret = ucsi->ops->read_cci(ucsi, &cci);
+               ret = ucsi->ops->poll_cci(ucsi, &cci);
                if (ret)
                        goto out;
 
@@ -1913,8 +1913,8 @@ struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops)
        struct ucsi *ucsi;
 
        if (!ops ||
-           !ops->read_version || !ops->read_cci || !ops->read_message_in ||
-           !ops->sync_control || !ops->async_control)
+           !ops->read_version || !ops->read_cci || !ops->poll_cci ||
+           !ops->read_message_in || !ops->sync_control || !ops->async_control)
                return ERR_PTR(-EINVAL);
 
        ucsi = kzalloc(sizeof(*ucsi), GFP_KERNEL);
index 1cf5aad4c23a9e70230449774a89573636fed164..a333006d3496a144781b9452930c27c7995cb783 100644 (file)
@@ -60,6 +60,7 @@ struct dentry;
  * struct ucsi_operations - UCSI I/O operations
  * @read_version: Read implemented UCSI version
  * @read_cci: Read CCI register
+ * @poll_cci: Read CCI register while polling with notifications disabled
  * @read_message_in: Read message data from UCSI
  * @sync_control: Blocking control operation
  * @async_control: Non-blocking control operation
@@ -74,6 +75,7 @@ struct dentry;
 struct ucsi_operations {
        int (*read_version)(struct ucsi *ucsi, u16 *version);
        int (*read_cci)(struct ucsi *ucsi, u32 *cci);
+       int (*poll_cci)(struct ucsi *ucsi, u32 *cci);
        int (*read_message_in)(struct ucsi *ucsi, void *val, size_t val_len);
        int (*sync_control)(struct ucsi *ucsi, u64 command);
        int (*async_control)(struct ucsi *ucsi, u64 command);
index accf15ff1306a28d12800d3235da53519710e507..8de2961718cdd2189e1fd2b6a6cdc104f90e792f 100644 (file)
@@ -59,19 +59,24 @@ static int ucsi_acpi_read_version(struct ucsi *ucsi, u16 *version)
 static int ucsi_acpi_read_cci(struct ucsi *ucsi, u32 *cci)
 {
        struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
-       int ret;
-
-       if (UCSI_COMMAND(ua->cmd) == UCSI_PPM_RESET) {
-               ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ);
-               if (ret)
-                       return ret;
-       }
 
        memcpy(cci, ua->base + UCSI_CCI, sizeof(*cci));
 
        return 0;
 }
 
+static int ucsi_acpi_poll_cci(struct ucsi *ucsi, u32 *cci)
+{
+       struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
+       int ret;
+
+       ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ);
+       if (ret)
+               return ret;
+
+       return ucsi_acpi_read_cci(ucsi, cci);
+}
+
 static int ucsi_acpi_read_message_in(struct ucsi *ucsi, void *val, size_t val_len)
 {
        struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
@@ -94,6 +99,7 @@ static int ucsi_acpi_async_control(struct ucsi *ucsi, u64 command)
 static const struct ucsi_operations ucsi_acpi_ops = {
        .read_version = ucsi_acpi_read_version,
        .read_cci = ucsi_acpi_read_cci,
+       .poll_cci = ucsi_acpi_poll_cci,
        .read_message_in = ucsi_acpi_read_message_in,
        .sync_control = ucsi_sync_control_common,
        .async_control = ucsi_acpi_async_control
@@ -145,6 +151,7 @@ static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command)
 static const struct ucsi_operations ucsi_gram_ops = {
        .read_version = ucsi_acpi_read_version,
        .read_cci = ucsi_acpi_read_cci,
+       .poll_cci = ucsi_acpi_poll_cci,
        .read_message_in = ucsi_gram_read_message_in,
        .sync_control = ucsi_gram_sync_control,
        .async_control = ucsi_acpi_async_control
index 740171f24ef9fae9abdb52d7995abe692e0a7623..4b1668733a4bec4efad1b8aebca83e15a8c54fe3 100644 (file)
@@ -664,6 +664,7 @@ err_put:
 static const struct ucsi_operations ucsi_ccg_ops = {
        .read_version = ucsi_ccg_read_version,
        .read_cci = ucsi_ccg_read_cci,
+       .poll_cci = ucsi_ccg_read_cci,
        .read_message_in = ucsi_ccg_read_message_in,
        .sync_control = ucsi_ccg_sync_control,
        .async_control = ucsi_ccg_async_control,
index 9b6cb76e632807f64f4a1fef5beac4eb3b6aba04..75c0e54c37fa0a9cd7893e7ea1809e611aab9d8f 100644 (file)
@@ -201,6 +201,7 @@ static void pmic_glink_ucsi_connector_status(struct ucsi_connector *con)
 static const struct ucsi_operations pmic_glink_ucsi_ops = {
        .read_version = pmic_glink_ucsi_read_version,
        .read_cci = pmic_glink_ucsi_read_cci,
+       .poll_cci = pmic_glink_ucsi_read_cci,
        .read_message_in = pmic_glink_ucsi_read_message_in,
        .sync_control = ucsi_sync_control_common,
        .async_control = pmic_glink_ucsi_async_control,
index 6923fad31d795194a22db7633e1f8a56475142a0..57ef7d83a412113ce760ad056c48f8620e896b45 100644 (file)
@@ -424,6 +424,7 @@ static irqreturn_t ucsi_stm32g0_irq_handler(int irq, void *data)
 static const struct ucsi_operations ucsi_stm32g0_ops = {
        .read_version = ucsi_stm32g0_read_version,
        .read_cci = ucsi_stm32g0_read_cci,
+       .poll_cci = ucsi_stm32g0_read_cci,
        .read_message_in = ucsi_stm32g0_read_message_in,
        .sync_control = ucsi_sync_control_common,
        .async_control = ucsi_stm32g0_async_control,
index f3a5e24ea84d514889d47472f053c4b38d55da9e..40e5da4fd2a45499f610c88b1bb66b39831bfead 100644 (file)
@@ -74,6 +74,7 @@ static int yoga_c630_ucsi_async_control(struct ucsi *ucsi, u64 command)
 const struct ucsi_operations yoga_c630_ucsi_ops = {
        .read_version = yoga_c630_ucsi_read_version,
        .read_cci = yoga_c630_ucsi_read_cci,
+       .poll_cci = yoga_c630_ucsi_read_cci,
        .read_message_in = yoga_c630_ucsi_read_message_in,
        .sync_control = ucsi_sync_control_common,
        .async_control = yoga_c630_ucsi_async_control,