RE: [PATCH 4/5] usb: typec: ucsi: Preliminary support for alternate modes

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Heikki

> -----Original Message-----
> From: linux-usb-owner@xxxxxxxxxxxxxxx <linux-usb-owner@xxxxxxxxxxxxxxx> On
> Behalf Of Heikki Krogerus
> Sent: Friday, February 1, 2019 2:48 AM
> To: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>; Ajay Gupta
> <ajayg@xxxxxxxxxx>; Michael Hsu <mhsu@xxxxxxxxxx>
> Cc: linux-usb@xxxxxxxxxxxxxxx
> Subject: [PATCH 4/5] usb: typec: ucsi: Preliminary support for alternate modes
> 
> With UCSI the alternate modes, just like everything else related to USB Type-C
> connectors, are handled in firmware.
> The operating system can see the status and is allowed to request certain things,
> for example entering and exiting the modes, but the support for alternate
> modes is very limited in UCSI. The feature is also optional, which means that
> even when the platform supports alternate modes, the operating system may
> not be even made aware of them.
> 
> UCSI does not support direct VDM reading or writing.
> Instead, alternate modes can be entered and exited using a single custom
> command which takes also an optional SVID specific configuration value as
> parameter. That means every supported alternate mode has to be handled
> separately in UCSI driver.
> 
> This commit does not include support for any specific alternate mode. The
> discovered alternate modes are now registered, but binding a driver to an
> alternate mode will not be possible until support for that alternate mode is
> added to the UCSI driver.
> 
> Signed-off-by: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx>
> ---
>  drivers/usb/typec/ucsi/trace.c |  12 ++  drivers/usb/typec/ucsi/trace.h |  26 +++
> drivers/usb/typec/ucsi/ucsi.c  | 351 +++++++++++++++++++++++++++------
> drivers/usb/typec/ucsi/ucsi.h  |  72 +++++++
>  4 files changed, 396 insertions(+), 65 deletions(-)
> 
> diff --git a/drivers/usb/typec/ucsi/trace.c b/drivers/usb/typec/ucsi/trace.c index
> ffa3b4c3f338..1dabafb74320 100644
> --- a/drivers/usb/typec/ucsi/trace.c
> +++ b/drivers/usb/typec/ucsi/trace.c
> @@ -60,3 +60,15 @@ const char *ucsi_cci_str(u32 cci)
> 
>  	return "";
>  }
> +
> +static const char * const ucsi_recipient_strs[] = {
> +	[UCSI_RECIPIENT_CON]		= "port",
> +	[UCSI_RECIPIENT_SOP]		= "partner",
> +	[UCSI_RECIPIENT_SOP_P]		= "plug (prime)",
> +	[UCSI_RECIPIENT_SOP_PP]		= "plug (double prime)",
> +};
> +
> +const char *ucsi_recipient_str(u8 recipient) {
> +	return ucsi_recipient_strs[recipient]; }
> diff --git a/drivers/usb/typec/ucsi/trace.h b/drivers/usb/typec/ucsi/trace.h index
> 5e2906df2db7..783ec9c72055 100644
> --- a/drivers/usb/typec/ucsi/trace.h
> +++ b/drivers/usb/typec/ucsi/trace.h
> @@ -7,6 +7,7 @@
>  #define __UCSI_TRACE_H
> 
>  #include <linux/tracepoint.h>
> +#include <linux/usb/typec_altmode.h>
> 
>  const char *ucsi_cmd_str(u64 raw_cmd);
>  const char *ucsi_ack_str(u8 ack);
> @@ -134,6 +135,31 @@ DEFINE_EVENT(ucsi_log_connector_status,
> ucsi_register_port,
>  	TP_ARGS(port, status)
>  );
> 
> +DECLARE_EVENT_CLASS(ucsi_log_register_altmode,
> +	TP_PROTO(u8 recipient, struct typec_altmode *alt),
> +	TP_ARGS(recipient, alt),
> +	TP_STRUCT__entry(
> +		__field(u8, recipient)
> +		__field(u16, svid)
> +		__field(u8, mode)
> +		__field(u32, vdo)
> +	),
> +	TP_fast_assign(
> +		__entry->recipient = recipient;
> +		__entry->svid = alt->svid;
> +		__entry->mode = alt->mode;
> +		__entry->vdo = alt->vdo;
> +	),
> +	TP_printk("%s alt mode: svid %04x, mode %d vdo %x",
> +		  ucsi_recipient_str(__entry->recipient), __entry->svid,
> +		  __entry->mode, __entry->vdo)
> +);
> +
> +DEFINE_EVENT(ucsi_log_register_altmode, ucsi_register_altmode,
> +	TP_PROTO(u8 recipient, struct typec_altmode *alt),
> +	TP_ARGS(recipient, alt)
> +);
> +
>  #endif /* __UCSI_TRACE_H */
> 
>  /* This part must be outside protection */ diff --git
> a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index
> 8d0a6fe748bd..5190f8dd4548 100644
> --- a/drivers/usb/typec/ucsi/ucsi.c
> +++ b/drivers/usb/typec/ucsi/ucsi.c
> @@ -12,7 +12,7 @@
>  #include <linux/module.h>
>  #include <linux/delay.h>
>  #include <linux/slab.h>
> -#include <linux/usb/typec.h>
> +#include <linux/usb/typec_altmode.h>
> 
>  #include "ucsi.h"
>  #include "trace.h"
> @@ -45,22 +45,6 @@ enum ucsi_status {
>  	UCSI_ERROR,
>  };
> 
> -struct ucsi_connector {
> -	int num;
> -
> -	struct ucsi *ucsi;
> -	struct work_struct work;
> -	struct completion complete;
> -
> -	struct typec_port *port;
> -	struct typec_partner *partner;
> -
> -	struct typec_capability typec_cap;
> -
> -	struct ucsi_connector_status status;
> -	struct ucsi_connector_capability cap;
> -};
> -
>  struct ucsi {
>  	struct device *dev;
>  	struct ucsi_ppm *ppm;
> @@ -238,8 +222,201 @@ static int ucsi_run_command(struct ucsi *ucsi, struct
> ucsi_control *ctrl,
>  	return ret;
>  }
> 
> +int ucsi_send_command(struct ucsi *ucsi, struct ucsi_control *ctrl,
> +		      void *retval, size_t size)
> +{
> +	int ret;
> +
> +	mutex_lock(&ucsi->ppm_lock);
> +	ret = ucsi_run_command(ucsi, ctrl, retval, size);
> +	mutex_unlock(&ucsi->ppm_lock);
> +
> +	return ret;
> +}
> +
>  /* -------------------------------------------------------------------------- */
> 
> +void ucsi_altmode_update_active(struct ucsi_connector *con) {
> +	struct typec_altmode *altmode;
> +	struct ucsi_control ctrl;
> +	int ret;
> +	u8 cur;
> +	int i;
> +
> +	UCSI_CMD_GET_CURRENT_CAM(ctrl, con->num);
> +	ret = ucsi_run_command(con->ucsi, &ctrl, &cur, sizeof(cur));
> +	if (ret < 0) {
> +		dev_err(con->ucsi->dev, "GET_CURRENT_CAM command
> failed\n");
> +		return;
> +	}
> +
> +	altmode = typec_match_altmode(con->partner_altmode,
> UCSI_MAX_ALTMODES,
> +				      con->port_altmode[cur]->svid,
> +				      con->port_altmode[cur]->mode);
> +
> +	for (i = 0; con->partner_altmode[i]; i++)
> +		typec_altmode_update_active(con->partner_altmode[i],
> +					    con->partner_altmode[i] ==
> altmode); }
> +
> +static u8 ucsi_altmode_next_mode(struct typec_altmode **alt, u16 svid)
> +{
> +	u8 mode = 1;
> +	int i;
> +
> +	for (i = 0; alt[i]; i++)
> +		if (alt[i]->svid == svid)
> +			mode++;
> +
> +	return mode;
> +}
> +
> +static int ucsi_next_altmode(struct typec_altmode **alt) {
> +	int i = 0;
> +
> +	for (i = 0; i < UCSI_MAX_ALTMODES; i++)
> +		if (!alt[i])
> +			return i;
> +
> +	return -ENOENT;
> +}
> +
> +static int ucsi_register_altmode(struct ucsi_connector *con,
> +				 struct typec_altmode_desc *desc,
> +				 u8 recipient)
> +{
> +	struct typec_altmode *alt;
> +	int ret;
> +	int i;
> +
> +	switch (recipient) {
> +	case UCSI_RECIPIENT_CON:
> +		i = ucsi_next_altmode(con->port_altmode);
> +		if (i < 0) {
> +			ret = i;
> +			goto err;
> +		}
> +
> +		desc->mode = ucsi_altmode_next_mode(con->port_altmode,
> +						    desc->svid);
> +
> +		alt = typec_port_register_altmode(con->port, desc);
> +		if (IS_ERR(alt)) {
> +			ret = PTR_ERR(alt);
> +			goto err;
> +		}
> +
> +		con->port_altmode[i] = alt;
> +		break;
> +	case UCSI_RECIPIENT_SOP:
> +		i = ucsi_next_altmode(con->partner_altmode);
We are seeing duplicate partner altmode devices getting created when we set "active" file from 1->0->1
Please add a check here to see if altmode device already exists. 

[...]
        case UCSI_RECIPIENT_SOP:
                /* check to see if partner altmode already exists */
                if (ucsi_altmode_found(con->partner_altmode, desc))
                        break;

                i = ucsi_next_altmode(con->partner_altmode);
	 if (i < 0) {
[...]


static bool ucsi_altmode_found(struct typec_altmode **alt,
                               struct typec_altmode_desc *desc)
{
        int i;

        for (i = 0; i < UCSI_MAX_ALTMODES; i++) {
                if (!alt[i])
                        return false;
                if (alt[i]->svid == desc->svid && alt[i]->vdo == desc->vdo)
                        return true;
        }

        return false;
}

thanks
> nvpublic
> +		if (i < 0) {
> +			ret = i;
> +			goto err;
> +		}
> +
> +		desc->mode = ucsi_altmode_next_mode(con-
> >partner_altmode,
> +						    desc->svid);
> +
> +		alt = typec_partner_register_altmode(con->partner, desc);
> +		if (IS_ERR(alt)) {
> +			ret = PTR_ERR(alt);
> +			goto err;
> +		}
> +
> +		con->partner_altmode[i] = alt;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	trace_ucsi_register_altmode(recipient, alt);
> +
> +	return 0;
> +
> +err:
> +	dev_err(con->ucsi->dev, "failed to registers svid 0x%04x mode %d\n",
> +		desc->svid, desc->mode);
> +
> +	return ret;
> +}
> +
> +static int ucsi_register_altmodes(struct ucsi_connector *con, u8
> +recipient) {
> +	int max_altmodes = UCSI_MAX_ALTMODES;
> +	struct typec_altmode_desc desc;
> +	struct ucsi_altmode alt[2];
> +	struct ucsi_control ctrl;
> +	int num = 1;
> +	int ret;
> +	int len;
> +	int j;
> +	int i;
> +
> +	if (!(con->ucsi->cap.features & UCSI_CAP_ALT_MODE_DETAILS))
> +		return 0;
> +
> +	if (recipient == UCSI_RECIPIENT_CON)
> +		max_altmodes = con->ucsi->cap.num_alt_modes;
> +
> +	for (i = 0; i < max_altmodes;) {
> +		memset(alt, 0, sizeof(alt));
> +		UCSI_CMD_GET_ALTERNATE_MODES(ctrl, recipient, con->num,
> i, 1);
> +		len = ucsi_run_command(con->ucsi, &ctrl, alt, sizeof(alt));
> +		if (len <= 0)
> +			return len;
> +
> +		/*
> +		 * This code is requesting one alt mode at a time, but some
> PPMs
> +		 * may still return two. If that happens both alt modes need be
> +		 * registered and the offset for the next alt mode has to be
> +		 * incremented.
> +		 */
> +		num = len / sizeof(alt[0]);
> +		i += num;
> +
> +		for (j = 0; j < num; j++) {
> +			if (!alt[j].svid)
> +				return 0;
> +
> +			memset(&desc, 0, sizeof(desc));
> +			desc.vdo = alt[j].mid;
> +			desc.svid = alt[j].svid;
> +			desc.roles = TYPEC_PORT_DRD;
> +
> +			ret = ucsi_register_altmode(con, &desc, recipient);
> +			if (ret)
> +				return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static void ucsi_unregister_altmodes(struct ucsi_connector *con, u8
> +recipient) {
> +	struct typec_altmode **adev;
> +	int i = 0;
> +
> +	switch (recipient) {
> +	case UCSI_RECIPIENT_CON:
> +		adev = con->port_altmode;
> +		break;
> +	case UCSI_RECIPIENT_SOP:
> +		adev = con->partner_altmode;
> +		break;
> +	default:
> +		return;
> +	}
> +
> +	while (adev[i]) {
> +		typec_unregister_altmode(adev[i]);
> +		adev[i++] = NULL;
> +	}
> +}
> +
>  static void ucsi_pwr_opmode_change(struct ucsi_connector *con)  {
>  	switch (con->status.pwr_op_mode) {
> @@ -299,10 +476,43 @@ static void ucsi_unregister_partner(struct
> ucsi_connector *con)
>  	if (!con->partner)
>  		return;
> 
> +	ucsi_unregister_altmodes(con, UCSI_RECIPIENT_SOP);
>  	typec_unregister_partner(con->partner);
>  	con->partner = NULL;
>  }
> 
> +static void ucsi_partner_change(struct ucsi_connector *con) {
> +	int ret;
> +
> +	if (!con->partner)
> +		return;
> +
> +	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);
> +
> +	/* Can't rely on Partner Flags field. Always checking the alt modes. */
> +	ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP);
> +	if (ret)
> +		dev_err(con->ucsi->dev,
> +			"con%d: failed to register partner alternate modes\n",
> +			con->num);
> +	else
> +		ucsi_altmode_update_active(con);
> +}
> +
>  static void ucsi_connector_change(struct work_struct *work)  {
>  	struct ucsi_connector *con = container_of(work, struct ucsi_connector,
> @@ -311,10 +521,10 @@ static void ucsi_connector_change(struct work_struct
> *work)
>  	struct ucsi_control ctrl;
>  	int ret;
> 
> -	mutex_lock(&ucsi->ppm_lock);
> +	mutex_lock(&con->lock);
> 
>  	UCSI_CMD_GET_CONNECTOR_STATUS(ctrl, con->num);
> -	ret = ucsi_run_command(ucsi, &ctrl, &con->status, sizeof(con->status));
> +	ret = ucsi_send_command(ucsi, &ctrl, &con->status,
> +sizeof(con->status));
>  	if (ret < 0) {
>  		dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed
> (%d)\n",
>  			__func__, ret);
> @@ -332,23 +542,6 @@ static void ucsi_connector_change(struct work_struct
> *work)
>  			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) {
>  		typec_set_pwr_role(con->port, con->status.pwr_dir);
> 
> @@ -369,6 +562,19 @@ static void ucsi_connector_change(struct work_struct
> *work)
>  			ucsi_unregister_partner(con);
>  	}
> 
> +	if (con->status.change & UCSI_CONSTAT_CAM_CHANGE) {
> +		/*
> +		 * We don't need to know the currently supported alt modes
> here.
> +		 * Running GET_CAM_SUPPORTED command just to make sure
> the PPM
> +		 * does not get stuck in case it assumes we do so.
> +		 */
> +		UCSI_CMD_GET_CAM_SUPPORTED(ctrl, con->num);
> +		ucsi_run_command(con->ucsi, &ctrl, NULL, 0);
> +	}
> +
> +	if (con->status.change & UCSI_CONSTAT_PARTNER_CHANGE)
> +		ucsi_partner_change(con);
> +
>  	ret = ucsi_ack(ucsi, UCSI_ACK_EVENT);
>  	if (ret)
>  		dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret); @@ -
> 377,7 +583,7 @@ static void ucsi_connector_change(struct work_struct *work)
> 
>  out_unlock:
>  	clear_bit(EVENT_PENDING, &ucsi->flags);
> -	mutex_unlock(&ucsi->ppm_lock);
> +	mutex_unlock(&con->lock);
>  }
> 
>  /**
> @@ -427,7 +633,7 @@ static int ucsi_reset_connector(struct ucsi_connector
> *con, bool hard)
> 
>  	UCSI_CMD_CONNECTOR_RESET(ctrl, con, hard);
> 
> -	return ucsi_run_command(con->ucsi, &ctrl, NULL, 0);
> +	return ucsi_send_command(con->ucsi, &ctrl, NULL, 0);
>  }
> 
>  static int ucsi_reset_ppm(struct ucsi *ucsi) @@ -481,15 +687,17 @@ static int
> ucsi_role_cmd(struct ucsi_connector *con, struct ucsi_control *ctrl)  {
>  	int ret;
> 
> -	ret = ucsi_run_command(con->ucsi, ctrl, NULL, 0);
> +	ret = ucsi_send_command(con->ucsi, ctrl, NULL, 0);
>  	if (ret == -ETIMEDOUT) {
>  		struct ucsi_control c;
> 
>  		/* PPM most likely stopped responding. Resetting everything. */
> +		mutex_lock(&con->ucsi->ppm_lock);
>  		ucsi_reset_ppm(con->ucsi);
> +		mutex_unlock(&con->ucsi->ppm_lock);
> 
>  		UCSI_CMD_SET_NTFY_ENABLE(c, UCSI_ENABLE_NTFY_ALL);
> -		ucsi_run_command(con->ucsi, &c, NULL, 0);
> +		ucsi_send_command(con->ucsi, &c, NULL, 0);
> 
>  		ucsi_reset_connector(con, true);
>  	}
> @@ -504,10 +712,12 @@ ucsi_dr_swap(const struct typec_capability *cap,
> enum typec_data_role role)
>  	struct ucsi_control ctrl;
>  	int ret = 0;
> 
> -	if (!con->partner)
> -		return -ENOTCONN;
> +	mutex_lock(&con->lock);
> 
> -	mutex_lock(&con->ucsi->ppm_lock);
> +	if (!con->partner) {
> +		ret = -ENOTCONN;
> +		goto out_unlock;
> +	}
> 
>  	if ((con->status.partner_type == UCSI_CONSTAT_PARTNER_TYPE_DFP
> &&
>  	     role == TYPEC_DEVICE) ||
> @@ -520,18 +730,14 @@ ucsi_dr_swap(const struct typec_capability *cap,
> enum typec_data_role role)
>  	if (ret < 0)
>  		goto out_unlock;
> 
> -	mutex_unlock(&con->ucsi->ppm_lock);
> -
>  	if (!wait_for_completion_timeout(&con->complete,
> 
> 	msecs_to_jiffies(UCSI_SWAP_TIMEOUT_MS)))
> -		return -ETIMEDOUT;
> -
> -	return 0;
> +		ret = -ETIMEDOUT;
> 
>  out_unlock:
> -	mutex_unlock(&con->ucsi->ppm_lock);
> +	mutex_unlock(&con->lock);
> 
> -	return ret;
> +	return ret < 0 ? ret : 0;
>  }
> 
>  static int
> @@ -541,10 +747,12 @@ ucsi_pr_swap(const struct typec_capability *cap,
> enum typec_role role)
>  	struct ucsi_control ctrl;
>  	int ret = 0;
> 
> -	if (!con->partner)
> -		return -ENOTCONN;
> +	mutex_lock(&con->lock);
> 
> -	mutex_lock(&con->ucsi->ppm_lock);
> +	if (!con->partner) {
> +		ret = -ENOTCONN;
> +		goto out_unlock;
> +	}
> 
>  	if (con->status.pwr_dir == role)
>  		goto out_unlock;
> @@ -554,13 +762,11 @@ ucsi_pr_swap(const struct typec_capability *cap,
> enum typec_role role)
>  	if (ret < 0)
>  		goto out_unlock;
> 
> -	mutex_unlock(&con->ucsi->ppm_lock);
> -
>  	if (!wait_for_completion_timeout(&con->complete,
> -
> 	msecs_to_jiffies(UCSI_SWAP_TIMEOUT_MS)))
> -		return -ETIMEDOUT;
> -
> -	mutex_lock(&con->ucsi->ppm_lock);
> +				msecs_to_jiffies(UCSI_SWAP_TIMEOUT_MS))) {
> +		ret = -ETIMEDOUT;
> +		goto out_unlock;
> +	}
> 
>  	/* Something has gone wrong while swapping the role */
>  	if (con->status.pwr_op_mode != UCSI_CONSTAT_PWR_OPMODE_PD) {
> @@ -569,7 +775,7 @@ ucsi_pr_swap(const struct typec_capability *cap, enum
> typec_role role)
>  	}
> 
>  out_unlock:
> -	mutex_unlock(&con->ucsi->ppm_lock);
> +	mutex_unlock(&con->lock);
> 
>  	return ret;
>  }
> @@ -595,6 +801,7 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
> 
>  	INIT_WORK(&con->work, ucsi_connector_change);
>  	init_completion(&con->complete);
> +	mutex_init(&con->lock);
>  	con->num = index + 1;
>  	con->ucsi = ucsi;
> 
> @@ -636,6 +843,12 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
>  	if (IS_ERR(con->port))
>  		return PTR_ERR(con->port);
> 
> +	/* Alternate modes */
> +	ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_CON);
> +	if (ret)
> +		dev_err(ucsi->dev, "con%d: failed to register alt modes\n",
> +			con->num);
> +
>  	/* Get the status */
>  	UCSI_CMD_GET_CONNECTOR_STATUS(ctrl, con->num);
>  	ret = ucsi_run_command(ucsi, &ctrl, &con->status, sizeof(con->status));
> @@ -662,6 +875,16 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
>  	if (con->status.connected)
>  		ucsi_register_partner(con);
> 
> +	if (con->partner) {
> +		ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP);
> +		if (ret)
> +			dev_err(ucsi->dev,
> +				"con%d: failed to register alternate modes\n",
> +				con->num);
> +		else
> +			ucsi_altmode_update_active(con);
> +	}
> +
>  	trace_ucsi_register_port(con->num, &con->status);
> 
>  	return 0;
> @@ -788,17 +1011,15 @@ void ucsi_unregister_ppm(struct ucsi *ucsi)
>  	/* Make sure that we are not in the middle of driver initialization */
>  	cancel_work_sync(&ucsi->work);
> 
> -	mutex_lock(&ucsi->ppm_lock);
> -
>  	/* Disable everything except command complete notification */
>  	UCSI_CMD_SET_NTFY_ENABLE(ctrl,
> UCSI_ENABLE_NTFY_CMD_COMPLETE)
> -	ucsi_run_command(ucsi, &ctrl, NULL, 0);
> -
> -	mutex_unlock(&ucsi->ppm_lock);
> +	ucsi_send_command(ucsi, &ctrl, NULL, 0);
> 
>  	for (i = 0; i < ucsi->cap.num_connectors; i++) {
>  		cancel_work_sync(&ucsi->connector[i].work);
>  		ucsi_unregister_partner(&ucsi->connector[i]);
> +		ucsi_unregister_altmodes(&ucsi->connector[i],
> +					 UCSI_RECIPIENT_CON);
>  		typec_unregister_port(ucsi->connector[i].port);
>  	}
> 
> diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index
> 53b80f40a908..c416bae4b5ca 100644
> --- a/drivers/usb/typec/ucsi/ucsi.h
> +++ b/drivers/usb/typec/ucsi/ucsi.h
> @@ -6,6 +6,7 @@
>  #include <linux/bitops.h>
>  #include <linux/device.h>
>  #include <linux/types.h>
> +#include <linux/usb/typec.h>
> 
>  /* -------------------------------------------------------------------------- */
> 
> @@ -60,6 +61,20 @@ struct ucsi_uor_cmd {
>  	u16:6; /* reserved */
>  } __packed;
> 
> +/* Get Alternate Modes Command structure */ struct ucsi_altmode_cmd {
> +	u8 cmd;
> +	u8 length;
> +	u8 recipient;
> +#define UCSI_RECIPIENT_CON			0
> +#define UCSI_RECIPIENT_SOP			1
> +#define UCSI_RECIPIENT_SOP_P			2
> +#define UCSI_RECIPIENT_SOP_PP			3
> +	u8 con_num;
> +	u8 offset;
> +	u8 num_altmodes;
> +} __packed;
> +
>  struct ucsi_control {
>  	union {
>  		u64 raw_cmd;
> @@ -67,6 +82,7 @@ struct ucsi_control {
>  		struct ucsi_uor_cmd uor;
>  		struct ucsi_ack_cmd ack;
>  		struct ucsi_con_rst con_rst;
> +		struct ucsi_altmode_cmd alt;
>  	};
>  };
> 
> @@ -112,6 +128,30 @@ struct ucsi_control {
>  	(_ctrl_).cmd.data = _con_;					\
>  }
> 
> +/* Helper for preparing ucsi_control for GET_ALTERNATE_MODES command.
> +*/ #define UCSI_CMD_GET_ALTERNATE_MODES(_ctrl_, _r_, _con_num_, _o_,
> _num_)\
> +{									\
> +	__UCSI_CMD((_ctrl_), UCSI_GET_ALTERNATE_MODES)
> 	\
> +	_ctrl_.alt.recipient = (_r_);					\
> +	_ctrl_.alt.con_num = (_con_num_);				\
> +	_ctrl_.alt.offset = (_o_);					\
> +	_ctrl_.alt.num_altmodes = (_num_) - 1;				\
> +}
> +
> +/* Helper for preparing ucsi_control for GET_CAM_SUPPORTED command. */
> +#define UCSI_CMD_GET_CAM_SUPPORTED(_ctrl_, _con_)
> 	\
> +{									\
> +	__UCSI_CMD((_ctrl_), UCSI_GET_CAM_SUPPORTED)
> 	\
> +	_ctrl_.cmd.data = (_con_);					\
> +}
> +
> +/* Helper for preparing ucsi_control for GET_CAM_SUPPORTED command. */
> +#define UCSI_CMD_GET_CURRENT_CAM(_ctrl_, _con_)			\
> +{									\
> +	__UCSI_CMD((_ctrl_), UCSI_GET_CURRENT_CAM)
> 	\
> +	_ctrl_.cmd.data = (_con_);					\
> +}
> +
>  /* Helper for preparing ucsi_control for GET_CONNECTOR_STATUS command.
> */
>  #define UCSI_CMD_GET_CONNECTOR_STATUS(_ctrl_, _con_)
> 	\
>  {									\
> @@ -334,4 +374,36 @@ struct ucsi *ucsi_register_ppm(struct device *dev,
> struct ucsi_ppm *ppm);  void ucsi_unregister_ppm(struct ucsi *ucsi);  void
> ucsi_notify(struct ucsi *ucsi);
> 
> +/*
> +-----------------------------------------------------------------------
> +--- */
> +
> +struct ucsi;
> +
> +#define UCSI_MAX_SVID		5
> +#define UCSI_MAX_ALTMODES	(UCSI_MAX_SVID * 6)
> +
> +struct ucsi_connector {
> +	int num;
> +
> +	struct ucsi *ucsi;
> +	struct mutex lock; /* port lock */
> +	struct work_struct work;
> +	struct completion complete;
> +
> +	struct typec_port *port;
> +	struct typec_partner *partner;
> +
> +	struct typec_altmode *port_altmode[UCSI_MAX_ALTMODES];
> +	struct typec_altmode *partner_altmode[UCSI_MAX_ALTMODES];
> +
> +	struct typec_capability typec_cap;
> +
> +	struct ucsi_connector_status status;
> +	struct ucsi_connector_capability cap;
> +};
> +
> +int ucsi_send_command(struct ucsi *ucsi, struct ucsi_control *ctrl,
> +		      void *retval, size_t size);
> +
> +void ucsi_altmode_update_active(struct ucsi_connector *con);
> +
>  #endif /* __DRIVER_USB_TYPEC_UCSI_H */
> --
> 2.20.1





[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux