]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
usb: typec: ucsi: Add support for message_out data structure
authorPooja Katiyar <pooja.katiyar@intel.com>
Tue, 19 May 2026 18:45:12 +0000 (11:45 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 22 May 2026 09:35:50 +0000 (11:35 +0200)
Add support for UCSI message_out data structure. The UCSI
interface defines separate message_in and message_out data
structure for bidirectional communication, where commands
like Set PDOs and LPM Firmware Update require writing data
to message_out before command execution.

Add write_message_out operation to ucsi_operations structure
to allow platform drivers to implement message_out data writing
capability.

Update ucsi_sync_control_common to accept message_out parameters
and call write_message_out followed by command execution to
maintain proper sequencing as per the UCSI specification.

Introduce ucsi_write_message_out_command for commands that need
to send message_out data, while maintaining ucsi_send_command
for commands that only require message_in response data.

Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Pooja Katiyar <pooja.katiyar@intel.com>
Link: https://patch.msgid.link/6d4e1ba7f92e713638f66925ae6389528597df6e.1778798352.git.pooja.katiyar@intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/typec/ucsi/cros_ec_ucsi.c
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_yoga_c630.c

index 251aa7251ce6ad00f758023fe71bb73f6934c143..c192d42d449ee1b8950bdb14d95a6fecf35e9281 100644 (file)
@@ -114,12 +114,14 @@ static int cros_ucsi_async_control(struct ucsi *ucsi, u64 cmd)
 }
 
 static int cros_ucsi_sync_control(struct ucsi *ucsi, u64 cmd, u32 *cci,
-                                 void *data, size_t size)
+                                 void *data, size_t size, void *msg_out,
+                                 size_t msg_out_size)
 {
        struct cros_ucsi_data *udata = ucsi_get_drvdata(ucsi);
        int ret;
 
-       ret = ucsi_sync_control_common(ucsi, cmd, cci, data, size);
+       ret = ucsi_sync_control_common(ucsi, cmd, cci, data, size, msg_out,
+                                      msg_out_size);
        switch (ret) {
        case -EBUSY:
                /* EC may return -EBUSY if CCI.busy is set.
index 43da7512dea0608d96db8d930b71b975f8588006..32f6d6b4919d84af7a6fafedf0ddefa1b35d70b8 100644 (file)
@@ -63,7 +63,8 @@ void ucsi_notify_common(struct ucsi *ucsi, u32 cci)
 EXPORT_SYMBOL_GPL(ucsi_notify_common);
 
 int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci,
-                            void *data, size_t size)
+                            void *data, size_t size, void *msg_out,
+                            size_t msg_out_size)
 {
        bool ack = UCSI_COMMAND(command) == UCSI_ACK_CC_CI;
        int ret;
@@ -75,6 +76,17 @@ int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci,
 
        reinit_completion(&ucsi->complete);
 
+       if (msg_out && msg_out_size) {
+               if (!ucsi->ops->write_message_out) {
+                       ret = -EOPNOTSUPP;
+                       goto out_clear_bit;
+               }
+
+               ret = ucsi->ops->write_message_out(ucsi, msg_out, msg_out_size);
+               if (ret)
+                       goto out_clear_bit;
+       }
+
        ret = ucsi->ops->async_control(ucsi, command);
        if (ret)
                goto out_clear_bit;
@@ -110,11 +122,12 @@ static int ucsi_acknowledge(struct ucsi *ucsi, bool conn_ack)
                ctrl |= UCSI_ACK_CONNECTOR_CHANGE;
        }
 
-       return ucsi->ops->sync_control(ucsi, ctrl, NULL, NULL, 0);
+       return ucsi->ops->sync_control(ucsi, ctrl, NULL, NULL, 0, NULL, 0);
 }
 
 static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci,
-                           void *data, size_t size, bool conn_ack)
+                           void *data, size_t size, void *msg_out,
+                           size_t msg_out_size, bool conn_ack)
 {
        int ret, err;
 
@@ -123,10 +136,12 @@ static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci,
        if (size > UCSI_MAX_DATA_LENGTH(ucsi))
                return -EINVAL;
 
-       ret = ucsi->ops->sync_control(ucsi, command, cci, data, size);
+       ret = ucsi->ops->sync_control(ucsi, command, cci, data, size,
+                                     msg_out, msg_out_size);
 
        if (*cci & UCSI_CCI_BUSY)
-               return ucsi_run_command(ucsi, UCSI_CANCEL, cci, NULL, 0, false) ?: -EBUSY;
+               return ucsi_run_command(ucsi, UCSI_CANCEL, cci,
+                                       NULL, 0, NULL, 0, false) ?: -EBUSY;
        if (ret)
                return ret;
 
@@ -158,7 +173,8 @@ static int ucsi_read_error(struct ucsi *ucsi, u8 connector_num)
        int ret;
 
        command = UCSI_GET_ERROR_STATUS | UCSI_CONNECTOR_NUMBER(connector_num);
-       ret = ucsi_run_command(ucsi, command, &cci, &error, sizeof(error), false);
+       ret = ucsi_run_command(ucsi, command, &cci, &error,
+                              sizeof(error), NULL, 0, false);
        if (ret < 0)
                return ret;
 
@@ -208,7 +224,8 @@ static int ucsi_read_error(struct ucsi *ucsi, u8 connector_num)
 }
 
 static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd,
-                                   void *data, size_t size, bool conn_ack)
+                                   void *data, size_t size, void *msg_out,
+                                   size_t msg_out_size, bool conn_ack)
 {
        u8 connector_num;
        u32 cci;
@@ -236,7 +253,8 @@ static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd,
 
        mutex_lock(&ucsi->ppm_lock);
 
-       ret = ucsi_run_command(ucsi, cmd, &cci, data, size, conn_ack);
+       ret = ucsi_run_command(ucsi, cmd, &cci, data, size,
+                              msg_out, msg_out_size, conn_ack);
 
        if (cci & UCSI_CCI_ERROR)
                ret = ucsi_read_error(ucsi, connector_num);
@@ -250,10 +268,23 @@ static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd,
 int ucsi_send_command(struct ucsi *ucsi, u64 command,
                      void *data, size_t size)
 {
-       return ucsi_send_command_common(ucsi, command, data, size, false);
+       return ucsi_send_command_common(ucsi, command, data,
+                                       size, NULL, 0, false);
 }
 EXPORT_SYMBOL_GPL(ucsi_send_command);
 
+int ucsi_write_message_out_command(struct ucsi *ucsi, u64 command,
+                                  void *data, size_t size, void *msg_out,
+                                  size_t msg_out_size)
+{
+       if (msg_out_size > UCSI_MAX_MSG_OUT_DATA_LEN(ucsi))
+               return -EINVAL;
+
+       return ucsi_send_command_common(ucsi, command, data,
+                                       size, msg_out, msg_out_size, false);
+}
+EXPORT_SYMBOL_GPL(ucsi_write_message_out_command);
+
 /* -------------------------------------------------------------------------- */
 
 struct ucsi_work {
@@ -681,7 +712,8 @@ static int ucsi_get_connector_status(struct ucsi_connector *con, bool conn_ack)
                          UCSI_MAX_DATA_LENGTH(con->ucsi));
        int ret;
 
-       ret = ucsi_send_command_common(con->ucsi, command, &con->status, size, conn_ack);
+       ret = ucsi_send_command_common(con->ucsi, command, &con->status, size,
+                                      NULL, 0, conn_ack);
 
        return ret < 0 ? ret : 0;
 }
index 51f6c3c0d36537bb83831908a779ff6fd4cd9420..402f45494dfa893627361652c8d0d58f514ae80a 100644 (file)
@@ -65,6 +65,7 @@ struct dentry;
  * @read_cci: Read CCI register
  * @poll_cci: Read CCI register while polling with notifications disabled
  * @read_message_in: Read message data from UCSI
+ * @write_message_out: Write message data to UCSI
  * @sync_control: Blocking control operation
  * @async_control: Non-blocking control operation
  * @update_altmodes: Squashes duplicate DP altmodes
@@ -82,8 +83,9 @@ struct ucsi_operations {
        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 (*write_message_out)(struct ucsi *ucsi, void *data, size_t data_len);
        int (*sync_control)(struct ucsi *ucsi, u64 command, u32 *cci,
-                           void *data, size_t size);
+                           void *data, size_t size, void *msg_out, size_t msg_out_size);
        int (*async_control)(struct ucsi *ucsi, u64 command);
        bool (*update_altmodes)(struct ucsi *ucsi, u8 recipient,
                                struct ucsi_altmode *orig,
@@ -503,6 +505,9 @@ struct ucsi {
 };
 
 #define UCSI_MAX_DATA_LENGTH(u) (((u)->version < UCSI_VERSION_2_0) ? 0x10 : 0xff)
+#define UCSI_MAX_MSG_OUT_DATA_LEN(u) \
+       (((u)->version >= UCSI_VERSION_3_0) ? 255 : \
+        (((u)->version >= UCSI_VERSION_2_0) ? 256 : 16))
 
 #define UCSI_MAX_SVID          5
 #define UCSI_MAX_ALTMODES      (UCSI_MAX_SVID * 6)
@@ -565,13 +570,17 @@ struct ucsi_connector {
 
 int ucsi_send_command(struct ucsi *ucsi, u64 command,
                      void *retval, size_t size);
+int ucsi_write_message_out_command(struct ucsi *ucsi, u64 command,
+                                  void *retval, size_t size,
+                                  void *msg_out, size_t msg_out_size);
 
 void ucsi_altmode_update_active(struct ucsi_connector *con);
 int ucsi_resume(struct ucsi *ucsi);
 
 void ucsi_notify_common(struct ucsi *ucsi, u32 cci);
 int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci,
-                            void *data, size_t size);
+                            void *data, size_t size, void *msg_out,
+                            size_t msg_out_size);
 
 #if IS_ENABLED(CONFIG_POWER_SUPPLY)
 int ucsi_register_port_psy(struct ucsi_connector *con);
index 6b92f296e98509a146e847f9a4000a2f6bbe175b..60b12961e1a47b71e094847920a5e97b583e3ac0 100644 (file)
@@ -86,6 +86,21 @@ static int ucsi_acpi_read_message_in(struct ucsi *ucsi, void *val, size_t val_le
        return 0;
 }
 
+static int ucsi_acpi_write_message_out(struct ucsi *ucsi, void *data, size_t data_len)
+{
+       struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
+
+       if (!data || !data_len)
+               return -EINVAL;
+
+       if (ucsi->version <= UCSI_VERSION_1_2)
+               memcpy(ua->base + UCSI_MESSAGE_OUT, data, data_len);
+       else
+               memcpy(ua->base + UCSIv2_MESSAGE_OUT, data, data_len);
+
+       return 0;
+}
+
 static int ucsi_acpi_async_control(struct ucsi *ucsi, u64 command)
 {
        struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
@@ -101,19 +116,22 @@ static const struct ucsi_operations ucsi_acpi_ops = {
        .read_cci = ucsi_acpi_read_cci,
        .poll_cci = ucsi_acpi_poll_cci,
        .read_message_in = ucsi_acpi_read_message_in,
+       .write_message_out = ucsi_acpi_write_message_out,
        .sync_control = ucsi_sync_control_common,
        .async_control = ucsi_acpi_async_control
 };
 
 static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command, u32 *cci,
-                                 void *val, size_t len)
+                                 void *val, size_t len, void *msg_out,
+                                 size_t msg_out_size)
 {
        u16 bogus_change = UCSI_CONSTAT_POWER_LEVEL_CHANGE |
                           UCSI_CONSTAT_PDOS_CHANGE;
        struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
        int ret;
 
-       ret = ucsi_sync_control_common(ucsi, command, cci, val, len);
+       ret = ucsi_sync_control_common(ucsi, command, cci, val, len,
+                                      msg_out, msg_out_size);
        if (ret < 0)
                return ret;
 
index ddde0a7702f079e1510b7b991e646bf57698a138..57d8e49ef8ae0da704e0364c3a49d6d6d617f6ae 100644 (file)
@@ -608,7 +608,8 @@ static int ucsi_ccg_async_control(struct ucsi *ucsi, u64 command)
 }
 
 static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command, u32 *cci,
-                                void *data, size_t size)
+                                void *data, size_t size, void *msg_out,
+                                size_t msg_out_size)
 {
        struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
        struct ucsi_connector *con;
@@ -630,7 +631,8 @@ static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command, u32 *cci,
                ucsi_ccg_update_set_new_cam_cmd(uc, con, &command);
        }
 
-       ret = ucsi_sync_control_common(ucsi, command, cci, data, size);
+       ret = ucsi_sync_control_common(ucsi, command, cci, data, size,
+                                      msg_out, msg_out_size);
 
        switch (UCSI_COMMAND(command)) {
        case UCSI_GET_CURRENT_CAM:
index 0187c1c4b21abc7b5429526ebb4538c28b2e2e77..1be18d10184264d0a273f65388180ede5fffe302 100644 (file)
@@ -89,7 +89,8 @@ static int yoga_c630_ucsi_async_control(struct ucsi *ucsi, u64 command)
 static int yoga_c630_ucsi_sync_control(struct ucsi *ucsi,
                                       u64 command,
                                       u32 *cci,
-                                      void *data, size_t size)
+                                      void *data, size_t size,
+                                      void *msg_out, size_t msg_out_size)
 {
        int ret;
 
@@ -126,7 +127,8 @@ static int yoga_c630_ucsi_sync_control(struct ucsi *ucsi,
                return 0;
        }
 
-       ret = ucsi_sync_control_common(ucsi, command, cci, data, size);
+       ret = ucsi_sync_control_common(ucsi, command, cci,
+                                      data, size, msg_out, msg_out_size);
        if (ret < 0)
                return ret;