Commit 063933f4 authored by Kyle Tso's avatar Kyle Tso Committed by Greg Kroah-Hartman
Browse files

usb: typec: tcpm: Properly handle Alert and Status Messages



When receiving Alert Message, if it is not unexpected but is
unsupported for some reason, the port should return Not_Supported
Message response.

Also, according to PD3.0 Spec 6.5.2.1.4 Event Flags Field, the
OTP/OVP/OCP flags in the Event Flags field in Status Message no longer
require Get_PPS_Status Message to clear them. Thus remove it when
receiving Status Message with those flags being set.

In addition, add the missing AMS operations for Status Message.

Fixes: 64f7c494 ("typec: tcpm: Add support for sink PPS related messages")
Fixes: 0908c5ac ("usb: typec: tcpm: AMS and Collision Avoidance")
Signed-off-by: default avatarKyle Tso <kyletso@google.com>
Link: https://lore.kernel.org/r/20210531164928.2368606-1-kyletso@google.com


Cc: stable <stable@vger.kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 4d2aa178
Loading
Loading
Loading
Loading
+27 −25
Original line number Diff line number Diff line
@@ -2188,20 +2188,25 @@ static void tcpm_handle_alert(struct tcpm_port *port, const __le32 *payload,

	if (!type) {
		tcpm_log(port, "Alert message received with no type");
		tcpm_queue_message(port, PD_MSG_CTRL_NOT_SUPP);
		return;
	}

	/* Just handling non-battery alerts for now */
	if (!(type & USB_PD_ADO_TYPE_BATT_STATUS_CHANGE)) {
		switch (port->state) {
		case SRC_READY:
		case SNK_READY:
		if (port->pwr_role == TYPEC_SOURCE) {
			port->upcoming_state = GET_STATUS_SEND;
			tcpm_ams_start(port, GETTING_SOURCE_SINK_STATUS);
		} else {
			/*
			 * Do not check SinkTxOk here in case the Source doesn't set its Rp to
			 * SinkTxOk in time.
			 */
			port->ams = GETTING_SOURCE_SINK_STATUS;
			tcpm_set_state(port, GET_STATUS_SEND, 0);
			break;
		default:
			tcpm_queue_message(port, PD_MSG_CTRL_WAIT);
			break;
		}
	} else {
		tcpm_queue_message(port, PD_MSG_CTRL_NOT_SUPP);
	}
}

@@ -2445,6 +2450,11 @@ static void tcpm_pd_data_request(struct tcpm_port *port,
		tcpm_pd_handle_state(port, BIST_RX, BIST, 0);
		break;
	case PD_DATA_ALERT:
		if (port->state != SRC_READY && port->state != SNK_READY)
			tcpm_pd_handle_state(port, port->pwr_role == TYPEC_SOURCE ?
					     SRC_SOFT_RESET_WAIT_SNK_TX : SNK_SOFT_RESET,
					     NONE_AMS, 0);
		else
			tcpm_handle_alert(port, msg->payload, cnt);
		break;
	case PD_DATA_BATT_STATUS:
@@ -2769,24 +2779,16 @@ static void tcpm_pd_ext_msg_request(struct tcpm_port *port,

	switch (type) {
	case PD_EXT_STATUS:
		/*
		 * If PPS related events raised then get PPS status to clear
		 * (see USB PD 3.0 Spec, 6.5.2.4)
		 */
		if (msg->ext_msg.data[USB_PD_EXT_SDB_EVENT_FLAGS] &
		    USB_PD_EXT_SDB_PPS_EVENTS)
			tcpm_pd_handle_state(port, GET_PPS_STATUS_SEND,
					     GETTING_SOURCE_SINK_STATUS, 0);

		else
			tcpm_pd_handle_state(port, ready_state(port), NONE_AMS, 0);
		break;
	case PD_EXT_PPS_STATUS:
		/*
		 * For now the PPS status message is used to clear events
		 * and nothing more.
		 */
		tcpm_pd_handle_state(port, ready_state(port), NONE_AMS, 0);
		if (port->ams == GETTING_SOURCE_SINK_STATUS) {
			tcpm_ams_finish(port);
			tcpm_set_state(port, ready_state(port), 0);
		} else {
			/* unexpected Status or PPS_Status Message */
			tcpm_pd_handle_state(port, port->pwr_role == TYPEC_SOURCE ?
					     SRC_SOFT_RESET_WAIT_SNK_TX : SNK_SOFT_RESET,
					     NONE_AMS, 0);
		}
		break;
	case PD_EXT_SOURCE_CAP_EXT:
	case PD_EXT_GET_BATT_CAP:
+0 −4
Original line number Diff line number Diff line
@@ -24,8 +24,4 @@ enum usb_pd_ext_sdb_fields {
#define USB_PD_EXT_SDB_EVENT_OVP		BIT(3)
#define USB_PD_EXT_SDB_EVENT_CF_CV_MODE		BIT(4)

#define USB_PD_EXT_SDB_PPS_EVENTS	(USB_PD_EXT_SDB_EVENT_OCP |	\
					 USB_PD_EXT_SDB_EVENT_OTP |	\
					 USB_PD_EXT_SDB_EVENT_OVP)

#endif /* __LINUX_USB_PD_EXT_SDB_H */