From: Pooja Katiyar Date: Tue, 19 May 2026 18:45:12 +0000 (-0700) Subject: usb: typec: ucsi: Add support for message_out data structure X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=7c40306927107c8c88030a30875e1c98abb908fd;p=thirdparty%2Flinux.git usb: typec: ucsi: Add support for message_out data structure 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 Signed-off-by: Pooja Katiyar Link: https://patch.msgid.link/6d4e1ba7f92e713638f66925ae6389528597df6e.1778798352.git.pooja.katiyar@intel.com Signed-off-by: Greg Kroah-Hartman --- diff --git a/drivers/usb/typec/ucsi/cros_ec_ucsi.c b/drivers/usb/typec/ucsi/cros_ec_ucsi.c index 251aa7251ce6..c192d42d449e 100644 --- a/drivers/usb/typec/ucsi/cros_ec_ucsi.c +++ b/drivers/usb/typec/ucsi/cros_ec_ucsi.c @@ -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. diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 43da7512dea0..32f6d6b4919d 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -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; } diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 51f6c3c0d365..402f45494dfa 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -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); diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c index 6b92f296e985..60b12961e1a4 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -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; diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index ddde0a7702f0..57d8e49ef8ae 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -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: diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c index 0187c1c4b21a..1be18d101842 100644 --- a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c +++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c @@ -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;