]> git.ipfire.org Git - thirdparty/linux.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>
Wed, 19 Feb 2025 14:19:52 +0000 (15:19 +0100)
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 fcf499cc9458c0d12015a7e36e5f1ac448c3a431..0fe1476f4c297cbf0e53e7bdc59d288ee06d2752 100644 (file)
@@ -1346,7 +1346,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;
 
@@ -1364,7 +1364,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)
@@ -1393,7 +1393,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;
 
@@ -1929,8 +1929,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 82735eb34f0e335c769799465bb40a8d40a75961..28780acc4af2e7e6b9730e7c0cf37dc72a363170 100644 (file)
@@ -62,6 +62,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
@@ -76,6 +77,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 5c55155519634d95b6e544632f869c2867093617..ac1ebb5d952720d2cd7620751a684c87a63f8fe1 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
@@ -142,6 +148,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 fed39d45809050f1e08dc1d34008b5c561461391..8af79101a2fc75ae17be1d119b25a9c862e010b3 100644 (file)
@@ -206,6 +206,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 4cae85c0dc12afa9e6955f77069b806cf827ee9a..d33e3f2dd1d80f7b49d9c5e7355fb14a97c74dd5 100644 (file)
@@ -74,6 +74,7 @@ static int yoga_c630_ucsi_async_control(struct ucsi *ucsi, u64 command)
 static 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,