}
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.
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;
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;
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;
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;
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;
}
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;
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);
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 {
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;
}
* @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
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,
};
#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)
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);
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);
.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;
}
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;
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:
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;
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;