Exemple #1
0
static void ucsi_connector_change(struct work_struct *work)
{
	struct ucsi_connector *con = container_of(work, struct ucsi_connector,
						  work);
	struct ucsi_connector_status constat;
	struct ucsi *ucsi = con->ucsi;
	struct ucsi_control ctrl;
	int ret;

	mutex_lock(&ucsi->ppm_lock);

	ctrl.cmd.cmd = UCSI_GET_CONNECTOR_STATUS;
	ctrl.cmd.length = 0;
	ctrl.cmd.data = con->num;
	ret = ucsi_run_cmd(con->ucsi, &ctrl, &constat, sizeof(constat));
	if (ret) {
		dev_err(ucsi->dev, "%s: failed to read connector status (%d)\n",
			__func__, ret);
		goto out_ack_event;
	}

	/* Ignoring disconnections and Alternate Modes */
	if (!constat.connected || !(constat.change &
	    (UCSI_CONSTAT_PARTNER_CHANGE | UCSI_CONSTAT_CONNECT_CHANGE)) ||
	    constat.partner_flags & UCSI_CONSTAT_PARTNER_FLAG_ALT_MODE)
		goto out_ack_event;

	/* If the partner got USB Host role, attempting swap */
	if (constat.partner_type & UCSI_CONSTAT_PARTNER_TYPE_DFP) {
		ctrl.uor.cmd = UCSI_SET_UOR;
		ctrl.uor.con_num = con->num;
		ctrl.uor.role = UCSI_UOR_ROLE_DFP;

		ret = ucsi_run_cmd(con->ucsi, &ctrl, NULL, 0);
		if (ret)
			dev_err(ucsi->dev, "%s: failed to swap role (%d)\n",
				__func__, ret);
	}
out_ack_event:
	ucsi_ack(ucsi, UCSI_ACK_EVENT);
	clear_bit(EVENT_PENDING, &ucsi->flags);
	mutex_unlock(&ucsi->ppm_lock);
}
Exemple #2
0
static void ucsi_connector_change(struct work_struct *work)
{
	struct ucsi_connector *con = container_of(work, struct ucsi_connector,
						  work);
	struct ucsi *ucsi = con->ucsi;
	struct ucsi_control ctrl;
	int ret;

	mutex_lock(&ucsi->ppm_lock);

	UCSI_CMD_GET_CONNECTOR_STATUS(ctrl, con->num);
	ret = ucsi_run_command(ucsi, &ctrl, &con->status, sizeof(con->status));
	if (ret < 0) {
		dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n",
			__func__, ret);
		goto out_unlock;
	}

	if (con->status.change & UCSI_CONSTAT_POWER_OPMODE_CHANGE)
		ucsi_pwr_opmode_change(con);

	if (con->status.change & UCSI_CONSTAT_POWER_DIR_CHANGE) {
		typec_set_pwr_role(con->port, con->status.pwr_dir);

		/* Complete pending power role swap */
		if (!completion_done(&con->complete))
			complete(&con->complete);
	}

	if (con->status.change & UCSI_CONSTAT_PARTNER_CHANGE) {
		switch (con->status.partner_type) {
		case UCSI_CONSTAT_PARTNER_TYPE_UFP:
			typec_set_data_role(con->port, TYPEC_HOST);
			break;
		case UCSI_CONSTAT_PARTNER_TYPE_DFP:
			typec_set_data_role(con->port, TYPEC_DEVICE);
			break;
		default:
			break;
		}

		/* Complete pending data role swap */
		if (!completion_done(&con->complete))
			complete(&con->complete);
	}

	if (con->status.change & UCSI_CONSTAT_CONNECT_CHANGE) {
		if (con->status.connected)
			ucsi_register_partner(con);
		else
			ucsi_unregister_partner(con);
	}

	ret = ucsi_ack(ucsi, UCSI_ACK_EVENT);
	if (ret)
		dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret);

	trace_ucsi_connector_change(con->num, &con->status);

out_unlock:
	clear_bit(EVENT_PENDING, &ucsi->flags);
	mutex_unlock(&ucsi->ppm_lock);
}
Exemple #3
0
static int ucsi_run_cmd(struct ucsi *ucsi, struct ucsi_control *ctrl,
			void *data, size_t size)
{
	u16 err_value = 0;
	int ret;

	set_bit(COMMAND_PENDING, &ucsi->flags);

	ret = ucsi_acpi_cmd(ucsi, ctrl);
	if (ret)
		goto err_clear_flag;

	ret = wait_for_completion_timeout(&ucsi->complete,
					  msecs_to_jiffies(UCSI_TIMEOUT_MS));
	if (!ret) {
		ret = -ETIMEDOUT;
		goto err_clear_flag;
	}

	switch (ucsi->status) {
	case UCSI_IDLE:
		if (data)
			memcpy(data, ucsi->data->message_in, size);

		ret = ucsi_ack(ucsi, UCSI_ACK_CMD);
		break;
	case UCSI_BUSY:
		/* The caller decides whether to cancel or not */
		ret = -EBUSY;
		goto err_clear_flag;
	case UCSI_ERROR:
		ret = ucsi_ack(ucsi, UCSI_ACK_CMD);
		if (ret)
			goto err_clear_flag;

		ctrl->cmd.cmd = UCSI_GET_ERROR_STATUS;
		ctrl->cmd.length = 0;
		ctrl->cmd.data = 0;
		ret = ucsi_acpi_cmd(ucsi, ctrl);
		if (ret)
			goto err_clear_flag;

		ret = wait_for_completion_timeout(&ucsi->complete,
					msecs_to_jiffies(UCSI_TIMEOUT_MS));
		if (!ret) {
			ret = -ETIMEDOUT;
			goto err_clear_flag;
		}

		memcpy(&err_value, ucsi->data->message_in, sizeof(err_value));

		/* Something has really gone wrong */
		if (WARN_ON(ucsi->status == UCSI_ERROR)) {
			ret = -ENODEV;
			goto err_clear_flag;
		}

		ret = ucsi_ack(ucsi, UCSI_ACK_CMD);
		if (ret)
			goto err_clear_flag;

		switch (err_value) {
		case UCSI_ERROR_INCOMPATIBLE_PARTNER:
			ret = -EOPNOTSUPP;
			break;
		case UCSI_ERROR_CC_COMMUNICATION_ERR:
			ret = -ECOMM;
			break;
		case UCSI_ERROR_CONTRACT_NEGOTIATION_FAIL:
			ret = -EIO;
			break;
		case UCSI_ERROR_DEAD_BATTERY:
			dev_warn(ucsi->dev, "Dead battery condition!\n");
			ret = -EPERM;
			break;
		/* The following mean a bug in this driver */
		case UCSI_ERROR_INVALID_CON_NUM:
		case UCSI_ERROR_UNREGONIZED_CMD:
		case UCSI_ERROR_INVALID_CMD_ARGUMENT:
		default:
			dev_warn(ucsi->dev,
				 "%s: possible UCSI driver bug - error %hu\n",
				 __func__, err_value);
			ret = -EINVAL;
			break;
		}
		break;
	}
	ctrl->raw_cmd = 0;
err_clear_flag:
	clear_bit(COMMAND_PENDING, &ucsi->flags);
	return ret;
}
Exemple #4
0
static int ucsi_run_command(struct ucsi *ucsi, struct ucsi_control *ctrl,
			    void *data, size_t size)
{
	struct ucsi_control _ctrl;
	u8 data_length;
	u16 error;
	int ret;

	ret = ucsi_command(ucsi, ctrl);
	if (ret)
		goto err;

	switch (ucsi->status) {
	case UCSI_IDLE:
		ret = ucsi_sync(ucsi);
		if (ret)
			dev_warn(ucsi->dev, "%s: sync failed\n", __func__);

		if (data)
			memcpy(data, ucsi->ppm->data->message_in, size);

		data_length = ucsi->ppm->data->cci.data_length;

		ret = ucsi_ack(ucsi, UCSI_ACK_CMD);
		if (!ret)
			ret = data_length;
		break;
	case UCSI_BUSY:
		/* The caller decides whether to cancel or not */
		ret = -EBUSY;
		break;
	case UCSI_ERROR:
		ret = ucsi_ack(ucsi, UCSI_ACK_CMD);
		if (ret)
			break;

		_ctrl.raw_cmd = 0;
		_ctrl.cmd.cmd = UCSI_GET_ERROR_STATUS;
		ret = ucsi_command(ucsi, &_ctrl);
		if (ret) {
			dev_err(ucsi->dev, "reading error failed!\n");
			break;
		}

		memcpy(&error, ucsi->ppm->data->message_in, sizeof(error));

		/* Something has really gone wrong */
		if (WARN_ON(ucsi->status == UCSI_ERROR)) {
			ret = -ENODEV;
			break;
		}

		ret = ucsi_ack(ucsi, UCSI_ACK_CMD);
		if (ret)
			break;

		switch (error) {
		case UCSI_ERROR_INCOMPATIBLE_PARTNER:
			ret = -EOPNOTSUPP;
			break;
		case UCSI_ERROR_CC_COMMUNICATION_ERR:
			ret = -ECOMM;
			break;
		case UCSI_ERROR_CONTRACT_NEGOTIATION_FAIL:
			ret = -EPROTO;
			break;
		case UCSI_ERROR_DEAD_BATTERY:
			dev_warn(ucsi->dev, "Dead battery condition!\n");
			ret = -EPERM;
			break;
		/* The following mean a bug in this driver */
		case UCSI_ERROR_INVALID_CON_NUM:
		case UCSI_ERROR_UNREGONIZED_CMD:
		case UCSI_ERROR_INVALID_CMD_ARGUMENT:
			dev_warn(ucsi->dev,
				 "%s: possible UCSI driver bug - error 0x%x\n",
				 __func__, error);
			ret = -EINVAL;
			break;
		default:
			dev_warn(ucsi->dev,
				 "%s: error without status\n", __func__);
			ret = -EIO;
			break;
		}
		break;
	}

err:
	trace_ucsi_run_command(ctrl, ret);

	return ret;
}