Commit 5e9c1662 authored by Dmitry Baryshkov's avatar Dmitry Baryshkov Committed by Greg Kroah-Hartman
Browse files

usb: typec: ucsi: rework command execution functions



Rework command execution code to remove recursive calls of
ucsi_exec_command. This also streamlines the sync_control / read(CCI)
read (MESSAGE_IN) sequence, allowing further rework of the command code.

Tested-by: default avatarHeikki Krogerus <heikki.krogerus@linux.intel.com>
Reviewed-by: default avatarHeikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: default avatarDmitry Baryshkov <dmitry.baryshkov@linaro.org>
Link: https://lore.kernel.org/r/20240627-ucsi-rework-interface-v4-4-289ddc6874c7@linaro.org


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 467399d9
Loading
Loading
Loading
Loading
+66 −72
Original line number Diff line number Diff line
@@ -63,27 +63,76 @@ static int ucsi_acknowledge(struct ucsi *ucsi, bool conn_ack)
	return ucsi->ops->sync_control(ucsi, ctrl);
}

static int ucsi_exec_command(struct ucsi *ucsi, u64 command);

static int ucsi_read_error(struct ucsi *ucsi, u8 connector_num)
static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci,
			    void *data, size_t size, bool conn_ack)
{
	u64 command;
	u16 error;
	int ret;

	command = UCSI_GET_ERROR_STATUS | UCSI_CONNECTOR_NUMBER(connector_num);
	ret = ucsi_exec_command(ucsi, command);
	if (ret < 0)
	*cci = 0;

	ret = ucsi->ops->sync_control(ucsi, command);
	if (ret)
		return ret;

	ret = ucsi_read_message_in(ucsi, &error, sizeof(error));
	ret = ucsi->ops->read_cci(ucsi, cci);
	if (ret)
		return ret;

	if (*cci & UCSI_CCI_BUSY)
		return -EBUSY;

	if (!(*cci & UCSI_CCI_COMMAND_COMPLETE))
		return -EIO;

	if (*cci & UCSI_CCI_NOT_SUPPORTED) {
		if (ucsi_acknowledge(ucsi, false) < 0)
			dev_err(ucsi->dev,
				"ACK of unsupported command failed\n");
		return -EOPNOTSUPP;
	}

	if (*cci & UCSI_CCI_ERROR) {
		/* Acknowledge the command that failed */
		ret = ucsi_acknowledge(ucsi, false);
		return ret ? ret : -EIO;
	}

	if (data) {
		ret = ucsi_read_message_in(ucsi, data, size);
		if (ret)
			return ret;
	}

	ret = ucsi_acknowledge(ucsi, conn_ack);
	if (ret)
		return ret;

	return 0;
}

static int ucsi_read_error(struct ucsi *ucsi, u8 connector_num)
{
	u64 command;
	u16 error;
	u32 cci;
	int ret;

	command = UCSI_GET_ERROR_STATUS | UCSI_CONNECTOR_NUMBER(connector_num);
	ret = ucsi_run_command(ucsi, command, &cci,
			       &error, sizeof(error), false);

	if (cci & UCSI_CCI_BUSY) {
		ret = ucsi_run_command(ucsi, UCSI_CANCEL, &cci, NULL, 0, false);

		return ret ? ret : -EBUSY;
	}

	if (ret < 0)
		return ret;

	if (cci & UCSI_CCI_ERROR)
		return -EIO;

	switch (error) {
	case UCSI_ERROR_INCOMPATIBLE_PARTNER:
		return -EOPNOTSUPP;
@@ -129,7 +178,8 @@ static int ucsi_read_error(struct ucsi *ucsi, u8 connector_num)
	return -EIO;
}

static int ucsi_exec_command(struct ucsi *ucsi, u64 cmd)
static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd,
				    void *data, size_t size, bool conn_ack)
{
	u8 connector_num;
	u32 cci;
@@ -155,73 +205,17 @@ static int ucsi_exec_command(struct ucsi *ucsi, u64 cmd)
		connector_num = 0;
	}

	ret = ucsi->ops->sync_control(ucsi, cmd);
	if (ret)
		return ret;

	ret = ucsi->ops->read_cci(ucsi, &cci);
	if (ret)
		return ret;

	if (cmd != UCSI_CANCEL && cci & UCSI_CCI_BUSY)
		return ucsi_exec_command(ucsi, UCSI_CANCEL);

	if (!(cci & UCSI_CCI_COMMAND_COMPLETE))
		return -EIO;

	if (cci & UCSI_CCI_NOT_SUPPORTED) {
		if (ucsi_acknowledge(ucsi, false) < 0)
			dev_err(ucsi->dev,
				"ACK of unsupported command failed\n");
		return -EOPNOTSUPP;
	}

	if (cci & UCSI_CCI_ERROR) {
		/* Acknowledge the command that failed */
		ret = ucsi_acknowledge(ucsi, false);
		if (ret)
			return ret;

		if (cmd == UCSI_GET_ERROR_STATUS)
			return -EIO;

		return ucsi_read_error(ucsi, connector_num);
	}

	if (cmd == UCSI_CANCEL && cci & UCSI_CCI_CANCEL_COMPLETE) {
		ret = ucsi_acknowledge(ucsi, false);
		return ret ? ret : -EBUSY;
	}

	return UCSI_CCI_LENGTH(cci);
}

static int ucsi_send_command_common(struct ucsi *ucsi, u64 command,
				    void *data, size_t size, bool conn_ack)
{
	u8 length;
	int ret;

	mutex_lock(&ucsi->ppm_lock);

	ret = ucsi_exec_command(ucsi, command);
	if (ret < 0)
		goto out;

	length = ret;

	if (data) {
		ret = ucsi_read_message_in(ucsi, data, size);
		if (ret)
			goto out;
	ret = ucsi_run_command(ucsi, cmd, &cci, data, size, conn_ack);
	if (cci & UCSI_CCI_BUSY) {
		ret = ucsi_run_command(ucsi, UCSI_CANCEL, &cci, NULL, 0, false);
		return ret ? ret : -EBUSY;
	}

	ret = ucsi_acknowledge(ucsi, conn_ack);
	if (ret)
		goto out;
	if (cci & UCSI_CCI_ERROR)
		return ucsi_read_error(ucsi, connector_num);

	ret = length;
out:
	mutex_unlock(&ucsi->ppm_lock);
	return ret;
}