lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Fri, 18 Sep 2020 15:36:39 +0300
From:   Heikki Krogerus <heikki.krogerus@...ux.intel.com>
To:     Badhri Jagan Sridharan <badhri@...gle.com>
Cc:     Guenter Roeck <linux@...ck-us.net>,
        Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
        linux-usb@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: Re: [PATCH v7 06/11] usb: typec: tcpm: Add support for Sink Fast
 Role SWAP(FRS)

On Thu, Sep 17, 2020 at 03:18:51AM -0700, Badhri Jagan Sridharan wrote:
> PD 3.0 spec defines a new mechanism for power role swap called
> Fast role swap. This change enables TCPM to support FRS when
> acting as sink.
> 
> Once the explicit contract is negotiated, sink port is
> expected to query the source port for sink caps to
> determine whether the source is FRS capable.
> Bits 23 & 24 of fixed pdo of the sink caps from the source, when
> set, indicates the current needed by the source when fast role
> swap is in progress(Implicit contract phasae). 0 indicates that
> the source does not support Fast Role Swap.
> 
> Upon receiving the FRS signal from the source,
> TCPC(TCPM_FRS_EVENT) informs TCPM to start the Fast role swap sequence.
> 
> 1. TCPM sends FRS PD message: FR_SWAP_SEND
> 2. If response is not received within the expiry of
>    SenderResponseTimer, Error recovery is triggered.:
>    FR_SWAP_SEND_TIMEOUT
> 3. Upon receipt of the accept message, TCPM waits for
>    PSSourceOffTimer for PS_READY message from the partner:
>    FR_SWAP_SNK_SRC_NEW_SINK_READY.
> 
> TCPC is expected to autonomously turn on vbus once the FRS
> signal is received and vbus voltage falls below vsafe5v within
> tSrcFrSwap. This is different from traditional power role swap
> where the vbus sourcing is turned on by TCPM.
> 
> 4. By this time, TCPC most likely would have started to
>    source vbus, TCPM waits for tSrcFrSwap to see  if the
>    lower level TCPC driver signals TCPM_SOURCING_VBUS event:
>    FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED.
> 5. When TCPC signals sourcing vbus, TCPM sends PS_READY msg and
>    changes the CC pin from Rd to Rp. This is the end of fast
>    role swap sequence and TCPM initiates the sequnce to negotiate
>    explicit contract by transitioning into SRC_STARTUP after
>    SwapSrcStart.
> 
> The code is written based on the sequence described in "Figure 8-107:
> Dual-role Port in Sink to Source Fast Role Swap State Diagram" of
> USB Power Delivery Specification Revision 3.0, Version 1.2.
> 
> Signed-off-by: Badhri Jagan Sridharan <badhri@...gle.com>

Reviewed-by: Heikki Krogerus <heikki.krogerus@...ux.intel.com>

> ---
> Changes since v1:
> - Changing patch version to v6 to fix version number confusion.
> - Rebased on top of usb-next and resolved conflicts due to the below
>   changes:
>   3ed8e1c2ac99 usb: typec: tcpm: Migrate workqueue to RT priority for processing events
>   6bbe2a90a0bb usb: typec: tcpm: During PR_SWAP, source caps should be sent only after tSwapSourceStart
> - enable_frs sequence is now run as part of the same kthread that runs
>   the state machines.
> - Fixed the implicit fallthrough warning in the switch case for
>   FR_SWAP_CANCEL case.
> 
> Changes since v6:
> - Moved frs_current from caps to tcpm_port as Heikki suggested.
> ---
> 
>  drivers/usb/typec/tcpm/tcpm.c | 229 +++++++++++++++++++++++++++++++++-
>  include/linux/usb/pd.h        |  19 +--
>  include/linux/usb/tcpm.h      |   8 +-
>  3 files changed, 244 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
> index 92806547f485..55535c4f66bf 100644
> --- a/drivers/usb/typec/tcpm/tcpm.c
> +++ b/drivers/usb/typec/tcpm/tcpm.c
> @@ -106,6 +106,13 @@
>  	S(VCONN_SWAP_TURN_ON_VCONN),		\
>  	S(VCONN_SWAP_TURN_OFF_VCONN),		\
>  						\
> +	S(FR_SWAP_SEND),			\
> +	S(FR_SWAP_SEND_TIMEOUT),		\
> +	S(FR_SWAP_SNK_SRC_TRANSITION_TO_OFF),			\
> +	S(FR_SWAP_SNK_SRC_NEW_SINK_READY),		\
> +	S(FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED),	\
> +	S(FR_SWAP_CANCEL),			\
> +						\
>  	S(SNK_TRY),				\
>  	S(SNK_TRY_WAIT),			\
>  	S(SNK_TRY_WAIT_DEBOUNCE),               \
> @@ -127,6 +134,9 @@
>  	S(GET_PPS_STATUS_SEND),			\
>  	S(GET_PPS_STATUS_SEND_TIMEOUT),		\
>  						\
> +	S(GET_SINK_CAP),			\
> +	S(GET_SINK_CAP_TIMEOUT),		\
> +						\
>  	S(ERROR_RECOVERY),			\
>  	S(PORT_RESET),				\
>  	S(PORT_RESET_WAIT_OFF)
> @@ -170,11 +180,25 @@ enum adev_actions {
>  	ADEV_ATTENTION,
>  };
>  
> +/*
> + * Initial current capability of the new source when vSafe5V is applied during PD3.0 Fast Role Swap.
> + * Based on "Table 6-14 Fixed Supply PDO - Sink" of "USB Power Delivery Specification Revision 3.0,
> + * Version 1.2"
> + */
> +enum frs_typec_current {
> +	FRS_NOT_SUPPORTED,
> +	FRS_DEFAULT_POWER,
> +	FRS_5V_1P5A,
> +	FRS_5V_3A,
> +};
> +
>  /* Events from low level driver */
>  
>  #define TCPM_CC_EVENT		BIT(0)
>  #define TCPM_VBUS_EVENT		BIT(1)
>  #define TCPM_RESET_EVENT	BIT(2)
> +#define TCPM_FRS_EVENT		BIT(3)
> +#define TCPM_SOURCING_VBUS	BIT(4)
>  
>  #define LOG_BUFFER_ENTRIES	1024
>  #define LOG_BUFFER_ENTRY_SIZE	128
> @@ -184,6 +208,8 @@ enum adev_actions {
>  #define SVID_DISCOVERY_MAX	16
>  #define ALTMODE_DISCOVERY_MAX	(SVID_DISCOVERY_MAX * MODE_DISCOVERY_MAX)
>  
> +#define GET_SINK_CAP_RETRY_MS	100
> +
>  struct pd_mode_data {
>  	int svid_index;		/* current SVID index		*/
>  	int nsvids;
> @@ -261,6 +287,8 @@ struct tcpm_port {
>  	struct kthread_work state_machine;
>  	struct hrtimer vdm_state_machine_timer;
>  	struct kthread_work vdm_state_machine;
> +	struct hrtimer enable_frs_timer;
> +	struct kthread_work enable_frs;
>  	bool state_machine_running;
>  
>  	struct completion tx_complete;
> @@ -335,6 +363,12 @@ struct tcpm_port {
>  	/* port belongs to a self powered device */
>  	bool self_powered;
>  
> +	/* FRS */
> +	enum frs_typec_current frs_current;
> +
> +	/* Sink caps have been queried */
> +	bool sink_cap_done;
> +
>  #ifdef CONFIG_DEBUG_FS
>  	struct dentry *dentry;
>  	struct mutex logbuffer_lock;	/* log buffer access lock */
> @@ -940,6 +974,16 @@ static void mod_vdm_delayed_work(struct tcpm_port *port, unsigned int delay_ms)
>  	}
>  }
>  
> +static void mod_enable_frs_delayed_work(struct tcpm_port *port, unsigned int delay_ms)
> +{
> +	if (delay_ms) {
> +		hrtimer_start(&port->enable_frs_timer, ms_to_ktime(delay_ms), HRTIMER_MODE_REL);
> +	} else {
> +		hrtimer_cancel(&port->enable_frs_timer);
> +		kthread_queue_work(port->wq, &port->enable_frs);
> +	}
> +}
> +
>  static void tcpm_set_state(struct tcpm_port *port, enum tcpm_state state,
>  			   unsigned int delay_ms)
>  {
> @@ -1669,6 +1713,9 @@ static void tcpm_pd_data_request(struct tcpm_port *port,
>  	unsigned int cnt = pd_header_cnt_le(msg->header);
>  	unsigned int rev = pd_header_rev_le(msg->header);
>  	unsigned int i;
> +	enum frs_typec_current frs_current;
> +	bool frs_enable;
> +	int ret;
>  
>  	switch (type) {
>  	case PD_DATA_SOURCE_CAP:
> @@ -1738,7 +1785,21 @@ static void tcpm_pd_data_request(struct tcpm_port *port,
>  		/* We don't do anything with this at the moment... */
>  		for (i = 0; i < cnt; i++)
>  			port->sink_caps[i] = le32_to_cpu(msg->payload[i]);
> +
> +		frs_current = (port->sink_caps[0] & PDO_FIXED_FRS_CURR_MASK) >>
> +			PDO_FIXED_FRS_CURR_SHIFT;
> +		frs_enable = frs_current && (frs_current <= port->frs_current);
> +		tcpm_log(port,
> +			 "Port partner FRS capable partner_frs_current:%u port_frs_current:%u enable:%c",
> +			 frs_current, port->frs_current, frs_enable ? 'y' : 'n');
> +		if (frs_enable) {
> +			ret  = port->tcpc->enable_frs(port->tcpc, true);
> +			tcpm_log(port, "Enable FRS %s, ret:%d\n", ret ? "fail" : "success", ret);
> +		}
> +
>  		port->nr_sink_caps = cnt;
> +		port->sink_cap_done = true;
> +		tcpm_set_state(port, SNK_READY, 0);
>  		break;
>  	case PD_DATA_VENDOR_DEF:
>  		tcpm_handle_vdm_request(port, msg->payload, cnt);
> @@ -1833,6 +1894,9 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
>  		case VCONN_SWAP_WAIT_FOR_VCONN:
>  			tcpm_set_state(port, VCONN_SWAP_TURN_OFF_VCONN, 0);
>  			break;
> +		case FR_SWAP_SNK_SRC_TRANSITION_TO_OFF:
> +			tcpm_set_state(port, FR_SWAP_SNK_SRC_NEW_SINK_READY, 0);
> +			break;
>  		default:
>  			break;
>  		}
> @@ -1872,6 +1936,13 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
>  					     -EAGAIN : -EOPNOTSUPP);
>  			tcpm_set_state(port, VCONN_SWAP_CANCEL, 0);
>  			break;
> +		case FR_SWAP_SEND:
> +			tcpm_set_state(port, FR_SWAP_CANCEL, 0);
> +			break;
> +		case GET_SINK_CAP:
> +			port->sink_cap_done = true;
> +			tcpm_set_state(port, ready_state(port), 0);
> +			break;
>  		default:
>  			break;
>  		}
> @@ -1906,6 +1977,9 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
>  		case VCONN_SWAP_SEND:
>  			tcpm_set_state(port, VCONN_SWAP_START, 0);
>  			break;
> +		case FR_SWAP_SEND:
> +			tcpm_set_state(port, FR_SWAP_SNK_SRC_TRANSITION_TO_OFF, 0);
> +			break;
>  		default:
>  			break;
>  		}
> @@ -2806,6 +2880,10 @@ static void tcpm_reset_port(struct tcpm_port *port)
>  	port->try_src_count = 0;
>  	port->try_snk_count = 0;
>  	port->usb_type = POWER_SUPPLY_USB_TYPE_C;
> +	port->nr_sink_caps = 0;
> +	port->sink_cap_done = false;
> +	if (port->tcpc->enable_frs)
> +		port->tcpc->enable_frs(port->tcpc, false);
>  
>  	power_supply_changed(port->psy);
>  }
> @@ -3356,10 +3434,9 @@ static void run_state_machine(struct tcpm_port *port)
>  		tcpm_swap_complete(port, 0);
>  		tcpm_typec_connect(port);
>  		tcpm_check_send_discover(port);
> +		mod_enable_frs_delayed_work(port, 0);
>  		tcpm_pps_complete(port, port->pps_status);
> -
>  		power_supply_changed(port->psy);
> -
>  		break;
>  
>  	/* Accessory states */
> @@ -3383,9 +3460,13 @@ static void run_state_machine(struct tcpm_port *port)
>  		tcpm_set_state(port, HARD_RESET_START, 0);
>  		break;
>  	case HARD_RESET_START:
> +		port->sink_cap_done = false;
> +		if (port->tcpc->enable_frs)
> +			port->tcpc->enable_frs(port->tcpc, false);
>  		port->hard_reset_count++;
>  		port->tcpc->set_pd_rx(port->tcpc, false);
>  		tcpm_unregister_altmodes(port);
> +		port->nr_sink_caps = 0;
>  		port->send_discover = true;
>  		if (port->pwr_role == TYPEC_SOURCE)
>  			tcpm_set_state(port, SRC_HARD_RESET_VBUS_OFF,
> @@ -3517,6 +3598,35 @@ static void run_state_machine(struct tcpm_port *port)
>  		tcpm_set_state(port, ready_state(port), 0);
>  		break;
>  
> +	case FR_SWAP_SEND:
> +		if (tcpm_pd_send_control(port, PD_CTRL_FR_SWAP)) {
> +			tcpm_set_state(port, ERROR_RECOVERY, 0);
> +			break;
> +		}
> +		tcpm_set_state_cond(port, FR_SWAP_SEND_TIMEOUT, PD_T_SENDER_RESPONSE);
> +		break;
> +	case FR_SWAP_SEND_TIMEOUT:
> +		tcpm_set_state(port, ERROR_RECOVERY, 0);
> +		break;
> +	case FR_SWAP_SNK_SRC_TRANSITION_TO_OFF:
> +		tcpm_set_state(port, ERROR_RECOVERY, PD_T_PS_SOURCE_OFF);
> +		break;
> +	case FR_SWAP_SNK_SRC_NEW_SINK_READY:
> +		if (port->vbus_source)
> +			tcpm_set_state(port, FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED, 0);
> +		else
> +			tcpm_set_state(port, ERROR_RECOVERY, PD_T_RECEIVER_RESPONSE);
> +		break;
> +	case FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED:
> +		tcpm_set_pwr_role(port, TYPEC_SOURCE);
> +		if (tcpm_pd_send_control(port, PD_CTRL_PS_RDY)) {
> +			tcpm_set_state(port, ERROR_RECOVERY, 0);
> +			break;
> +		}
> +		tcpm_set_cc(port, tcpm_rp_cc(port));
> +		tcpm_set_state(port, SRC_STARTUP, PD_T_SWAP_SRC_START);
> +		break;
> +
>  	/* PR_Swap states */
>  	case PR_SWAP_ACCEPT:
>  		tcpm_pd_send_control(port, PD_CTRL_ACCEPT);
> @@ -3640,6 +3750,12 @@ static void run_state_machine(struct tcpm_port *port)
>  		else
>  			tcpm_set_state(port, SNK_READY, 0);
>  		break;
> +	case FR_SWAP_CANCEL:
> +		if (port->pwr_role == TYPEC_SOURCE)
> +			tcpm_set_state(port, SRC_READY, 0);
> +		else
> +			tcpm_set_state(port, SNK_READY, 0);
> +		break;
>  
>  	case BIST_RX:
>  		switch (BDO_MODE_MASK(port->bist_request)) {
> @@ -3674,6 +3790,14 @@ static void run_state_machine(struct tcpm_port *port)
>  	case GET_PPS_STATUS_SEND_TIMEOUT:
>  		tcpm_set_state(port, ready_state(port), 0);
>  		break;
> +	case GET_SINK_CAP:
> +		tcpm_pd_send_control(port, PD_CTRL_GET_SINK_CAP);
> +		tcpm_set_state(port, GET_SINK_CAP_TIMEOUT, PD_T_SENDER_RESPONSE);
> +		break;
> +	case GET_SINK_CAP_TIMEOUT:
> +		port->sink_cap_done = true;
> +		tcpm_set_state(port, ready_state(port), 0);
> +		break;
>  	case ERROR_RECOVERY:
>  		tcpm_swap_complete(port, -EPROTO);
>  		tcpm_pps_complete(port, -EPROTO);
> @@ -3889,6 +4013,13 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1,
>  		 * Ignore it.
>  		 */
>  		break;
> +	case FR_SWAP_SEND:
> +	case FR_SWAP_SEND_TIMEOUT:
> +	case FR_SWAP_SNK_SRC_TRANSITION_TO_OFF:
> +	case FR_SWAP_SNK_SRC_NEW_SINK_READY:
> +	case FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED:
> +		/* Do nothing, CC change expected */
> +		break;
>  
>  	case PORT_RESET:
>  	case PORT_RESET_WAIT_OFF:
> @@ -3959,6 +4090,9 @@ static void _tcpm_pd_vbus_on(struct tcpm_port *port)
>  	case SRC_TRY_DEBOUNCE:
>  		/* Do nothing, waiting for sink detection */
>  		break;
> +	case FR_SWAP_SNK_SRC_NEW_SINK_READY:
> +		tcpm_set_state(port, FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED, 0);
> +		break;
>  
>  	case PORT_RESET:
>  	case PORT_RESET_WAIT_OFF:
> @@ -4038,6 +4172,14 @@ static void _tcpm_pd_vbus_off(struct tcpm_port *port)
>  		 */
>  		break;
>  
> +	case FR_SWAP_SEND:
> +	case FR_SWAP_SEND_TIMEOUT:
> +	case FR_SWAP_SNK_SRC_TRANSITION_TO_OFF:
> +	case FR_SWAP_SNK_SRC_NEW_SINK_READY:
> +	case FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED:
> +		/* Do nothing, vbus drop expected */
> +		break;
> +
>  	default:
>  		if (port->pwr_role == TYPEC_SINK &&
>  		    port->attached)
> @@ -4092,6 +4234,25 @@ static void tcpm_pd_event_handler(struct kthread_work *work)
>  			if (port->tcpc->get_cc(port->tcpc, &cc1, &cc2) == 0)
>  				_tcpm_cc_change(port, cc1, cc2);
>  		}
> +		if (events & TCPM_FRS_EVENT) {
> +			if (port->state == SNK_READY)
> +				tcpm_set_state(port, FR_SWAP_SEND, 0);
> +			else
> +				tcpm_log(port, "Discarding FRS_SIGNAL! Not in sink ready");
> +		}
> +		if (events & TCPM_SOURCING_VBUS) {
> +			tcpm_log(port, "sourcing vbus");
> +			/*
> +			 * In fast role swap case TCPC autonomously sources vbus. Set vbus_source
> +			 * true as TCPM wouldn't have called tcpm_set_vbus.
> +			 *
> +			 * When vbus is sourced on the command on TCPM i.e. TCPM called
> +			 * tcpm_set_vbus to source vbus, vbus_source would already be true.
> +			 */
> +			port->vbus_source = true;
> +			_tcpm_pd_vbus_on(port);
> +		}
> +
>  		spin_lock(&port->pd_event_lock);
>  	}
>  	spin_unlock(&port->pd_event_lock);
> @@ -4125,6 +4286,50 @@ void tcpm_pd_hard_reset(struct tcpm_port *port)
>  }
>  EXPORT_SYMBOL_GPL(tcpm_pd_hard_reset);
>  
> +void tcpm_sink_frs(struct tcpm_port *port)
> +{
> +	spin_lock(&port->pd_event_lock);
> +	port->pd_events = TCPM_FRS_EVENT;
> +	spin_unlock(&port->pd_event_lock);
> +	kthread_queue_work(port->wq, &port->event_work);
> +}
> +EXPORT_SYMBOL_GPL(tcpm_sink_frs);
> +
> +void tcpm_sourcing_vbus(struct tcpm_port *port)
> +{
> +	spin_lock(&port->pd_event_lock);
> +	port->pd_events = TCPM_SOURCING_VBUS;
> +	spin_unlock(&port->pd_event_lock);
> +	kthread_queue_work(port->wq, &port->event_work);
> +}
> +EXPORT_SYMBOL_GPL(tcpm_sourcing_vbus);
> +
> +static void tcpm_enable_frs_work(struct kthread_work *work)
> +{
> +	struct tcpm_port *port = container_of(work, struct tcpm_port, enable_frs);
> +
> +	mutex_lock(&port->lock);
> +	/* Not FRS capable */
> +	if (!port->connected || port->port_type != TYPEC_PORT_DRP ||
> +	    port->pwr_opmode != TYPEC_PWR_MODE_PD ||
> +	    !port->tcpc->enable_frs ||
> +	    /* Sink caps queried */
> +	    port->sink_cap_done || port->negotiated_rev < PD_REV30)
> +		goto unlock;
> +
> +	/* Send when the state machine is idle */
> +	if (port->state != SNK_READY || port->vdm_state != VDM_STATE_DONE || port->send_discover)
> +		goto resched;
> +
> +	tcpm_set_state(port, GET_SINK_CAP, 0);
> +	port->sink_cap_done = true;
> +
> +resched:
> +	mod_enable_frs_delayed_work(port, GET_SINK_CAP_RETRY_MS);
> +unlock:
> +	mutex_unlock(&port->lock);
> +}
> +
>  static int tcpm_dr_set(struct typec_port *p, enum typec_data_role data)
>  {
>  	struct tcpm_port *port = typec_get_drvdata(p);
> @@ -4532,7 +4737,7 @@ static int tcpm_fw_get_caps(struct tcpm_port *port,
>  {
>  	const char *cap_str;
>  	int ret;
> -	u32 mw;
> +	u32 mw, frs_current;
>  
>  	if (!fwnode)
>  		return -EINVAL;
> @@ -4601,6 +4806,13 @@ static int tcpm_fw_get_caps(struct tcpm_port *port,
>  
>  	port->self_powered = fwnode_property_read_bool(fwnode, "self-powered");
>  
> +	/* FRS can only be supported byb DRP ports */
> +	if (port->port_type == TYPEC_PORT_DRP) {
> +		ret = fwnode_property_read_u32(fwnode, "frs-typec-current", &frs_current);
> +		if (ret >= 0 && frs_current <= FRS_5V_3A)
> +			port->frs_current = frs_current;
> +	}
> +
>  	return 0;
>  }
>  
> @@ -4845,6 +5057,14 @@ static enum hrtimer_restart vdm_state_machine_timer_handler(struct hrtimer *time
>  	return HRTIMER_NORESTART;
>  }
>  
> +static enum hrtimer_restart enable_frs_timer_handler(struct hrtimer *timer)
> +{
> +	struct tcpm_port *port = container_of(timer, struct tcpm_port, enable_frs_timer);
> +
> +	kthread_queue_work(port->wq, &port->enable_frs);
> +	return HRTIMER_NORESTART;
> +}
> +
>  struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
>  {
>  	struct tcpm_port *port;
> @@ -4874,10 +5094,13 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
>  	kthread_init_work(&port->state_machine, tcpm_state_machine_work);
>  	kthread_init_work(&port->vdm_state_machine, vdm_state_machine_work);
>  	kthread_init_work(&port->event_work, tcpm_pd_event_handler);
> +	kthread_init_work(&port->enable_frs, tcpm_enable_frs_work);
>  	hrtimer_init(&port->state_machine_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
>  	port->state_machine_timer.function = state_machine_timer_handler;
>  	hrtimer_init(&port->vdm_state_machine_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
>  	port->vdm_state_machine_timer.function = vdm_state_machine_timer_handler;
> +	hrtimer_init(&port->enable_frs_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
> +	port->enable_frs_timer.function = enable_frs_timer_handler;
>  
>  	spin_lock_init(&port->pd_event_lock);
>  
> diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h
> index f842e4589bd2..3a805e2ecbc9 100644
> --- a/include/linux/usb/pd.h
> +++ b/include/linux/usb/pd.h
> @@ -219,14 +219,16 @@ enum pd_pdo_type {
>  #define PDO_CURR_MASK		0x3ff
>  #define PDO_PWR_MASK		0x3ff
>  
> -#define PDO_FIXED_DUAL_ROLE	BIT(29)	/* Power role swap supported */
> -#define PDO_FIXED_SUSPEND	BIT(28) /* USB Suspend supported (Source) */
> -#define PDO_FIXED_HIGHER_CAP	BIT(28) /* Requires more than vSafe5V (Sink) */
> -#define PDO_FIXED_EXTPOWER	BIT(27) /* Externally powered */
> -#define PDO_FIXED_USB_COMM	BIT(26) /* USB communications capable */
> -#define PDO_FIXED_DATA_SWAP	BIT(25) /* Data role swap supported */
> -#define PDO_FIXED_VOLT_SHIFT	10	/* 50mV units */
> -#define PDO_FIXED_CURR_SHIFT	0	/* 10mA units */
> +#define PDO_FIXED_DUAL_ROLE		BIT(29)	/* Power role swap supported */
> +#define PDO_FIXED_SUSPEND		BIT(28) /* USB Suspend supported (Source) */
> +#define PDO_FIXED_HIGHER_CAP		BIT(28) /* Requires more than vSafe5V (Sink) */
> +#define PDO_FIXED_EXTPOWER		BIT(27) /* Externally powered */
> +#define PDO_FIXED_USB_COMM		BIT(26) /* USB communications capable */
> +#define PDO_FIXED_DATA_SWAP		BIT(25) /* Data role swap supported */
> +#define PDO_FIXED_FRS_CURR_MASK		(BIT(24) | BIT(23)) /* FR_Swap Current (Sink) */
> +#define PDO_FIXED_FRS_CURR_SHIFT	23
> +#define PDO_FIXED_VOLT_SHIFT		10	/* 50mV units */
> +#define PDO_FIXED_CURR_SHIFT		0	/* 10mA units */
>  
>  #define PDO_FIXED_VOLT(mv)	((((mv) / 50) & PDO_VOLT_MASK) << PDO_FIXED_VOLT_SHIFT)
>  #define PDO_FIXED_CURR(ma)	((((ma) / 10) & PDO_CURR_MASK) << PDO_FIXED_CURR_SHIFT)
> @@ -454,6 +456,7 @@ static inline unsigned int rdo_max_power(u32 rdo)
>  #define PD_T_DB_DETECT		10000	/* 10 - 15 seconds */
>  #define PD_T_SEND_SOURCE_CAP	150	/* 100 - 200 ms */
>  #define PD_T_SENDER_RESPONSE	60	/* 24 - 30 ms, relaxed */
> +#define PD_T_RECEIVER_RESPONSE	15	/* 15ms max */
>  #define PD_T_SOURCE_ACTIVITY	45
>  #define PD_T_SINK_ACTIVITY	135
>  #define PD_T_SINK_WAIT_CAP	240
> diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h
> index 89f58760cf48..09762d26fa0c 100644
> --- a/include/linux/usb/tcpm.h
> +++ b/include/linux/usb/tcpm.h
> @@ -78,8 +78,11 @@ enum tcpm_transmit_type {
>   *		automatically if a connection is established.
>   * @try_role:	Optional; called to set a preferred role
>   * @pd_transmit:Called to transmit PD message
> - * @mux:	Pointer to multiplexer data
>   * @set_bist_data: Turn on/off bist data mode for compliance testing
> + * @enable_frs:
> + *		Optional; Called to enable/disable PD 3.0 fast role swap.
> + *		Enabling frs is accessory dependent as not all PD3.0
> + *		accessories support fast role swap.
>   */
>  struct tcpc_dev {
>  	struct fwnode_handle *fwnode;
> @@ -105,6 +108,7 @@ struct tcpc_dev {
>  	int (*pd_transmit)(struct tcpc_dev *dev, enum tcpm_transmit_type type,
>  			   const struct pd_message *msg);
>  	int (*set_bist_data)(struct tcpc_dev *dev, bool on);
> +	int (*enable_frs)(struct tcpc_dev *dev, bool enable);
>  };
>  
>  struct tcpm_port;
> @@ -114,6 +118,8 @@ void tcpm_unregister_port(struct tcpm_port *port);
>  
>  void tcpm_vbus_change(struct tcpm_port *port);
>  void tcpm_cc_change(struct tcpm_port *port);
> +void tcpm_sink_frs(struct tcpm_port *port);
> +void tcpm_sourcing_vbus(struct tcpm_port *port);
>  void tcpm_pd_receive(struct tcpm_port *port,
>  		     const struct pd_message *msg);
>  void tcpm_pd_transmit_complete(struct tcpm_port *port,
> -- 
> 2.28.0.618.gf4bc123cb7-goog

-- 
heikki

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ