Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b4f023e5 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "usb: pd: Fix VDM and misc timing issues"

parents b23cd82d dd1a207e
Loading
Loading
Loading
Loading
+140 −84
Original line number Diff line number Diff line
@@ -163,11 +163,11 @@ static void *usbpd_ipc_log;
/* Timeouts (in ms) */
#define ERROR_RECOVERY_TIME	25
#define SENDER_RESPONSE_TIME	26
#define SINK_WAIT_CAP_TIME	620
#define SINK_WAIT_CAP_TIME	500
#define PS_TRANSITION_TIME	450
#define SRC_CAP_TIME		120
#define SRC_TRANSITION_TIME	25
#define SRC_RECOVER_TIME	660
#define SRC_RECOVER_TIME	750
#define PS_HARD_RESET_TIME	25
#define PS_SOURCE_ON		400
#define PS_SOURCE_OFF		750
@@ -175,8 +175,11 @@ static void *usbpd_ipc_log;
#define VDM_BUSY_TIME		50
#define VCONN_ON_TIME		100

/* tPSHardReset + tSafe0V + tSrcRecover + tSrcTurnOn */
#define SNK_HARD_RESET_RECOVER_TIME	(35 + 650 + 1000 + 275)
/* tPSHardReset + tSafe0V */
#define SNK_HARD_RESET_VBUS_OFF_TIME	(35 + 650)

/* tSrcRecover + tSrcTurnOn */
#define SNK_HARD_RESET_VBUS_ON_TIME	(1000 + 275)

#define PD_CAPS_COUNT		50

@@ -317,6 +320,7 @@ struct usbpd {

	struct regulator	*vbus;
	struct regulator	*vconn;
	bool			vbus_enabled;
	bool			vconn_enabled;
	bool			vconn_is_external;

@@ -409,6 +413,17 @@ static struct usbpd_svid_handler *find_svid_handler(struct usbpd *pd, u16 svid)
	return NULL;
}

/* Reset protocol layer */
static inline void pd_reset_protocol(struct usbpd *pd)
{
	/*
	 * first Rx ID should be 0; set this to a sentinel of -1 so that in
	 * phy_msg_received() we can check if we had seen it before.
	 */
	pd->rx_msgid = -1;
	pd->tx_msgid = 0;
}

static int pd_send_msg(struct usbpd *pd, u8 hdr_type, const u32 *data,
		size_t num_data, enum pd_msg_type type)
{
@@ -423,7 +438,9 @@ static int pd_send_msg(struct usbpd *pd, u8 hdr_type, const u32 *data,
	/* MessageID incremented regardless of Tx error */
	pd->tx_msgid = (pd->tx_msgid + 1) & PD_MAX_MSG_ID;

	if (ret != num_data * sizeof(u32))
	if (ret < 0)
		return ret;
	else if (ret != num_data * sizeof(u32))
		return -EIO;
	return 0;
}
@@ -631,6 +648,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
	switch (next_state) {
	case PE_ERROR_RECOVERY: /* perform hard disconnect/reconnect */
		pd->in_pr_swap = false;
		pd->current_pr = PR_NONE;
		set_power_role(pd, PR_NONE);
		pd->typec_mode = POWER_SUPPLY_TYPEC_NONE;
		kick_sm(pd, 0);
@@ -651,7 +669,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
		power_supply_set_property(pd->usb_psy,
				POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &val);

		pd->rx_msgid = -1;
		pd_reset_protocol(pd);

		if (!pd->in_pr_swap) {
			if (pd->pd_phy_opened) {
@@ -677,6 +695,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
		pd->current_state = PE_SRC_SEND_CAPABILITIES;
		if (pd->in_pr_swap) {
			kick_sm(pd, SWAP_SOURCE_START_TIME);
			pd->in_pr_swap = false;
			break;
		}

@@ -768,9 +787,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)

	case PE_SRC_SEND_SOFT_RESET:
	case PE_SNK_SEND_SOFT_RESET:
		/* Reset protocol layer */
		pd->tx_msgid = 0;
		pd->rx_msgid = -1;
		pd_reset_protocol(pd);

		ret = pd_send_msg(pd, MSG_SOFT_RESET, NULL, 0, SOP_MSG);
		if (ret) {
@@ -812,9 +829,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
		if (!val.intval)
			break;

		/* Reset protocol layer */
		pd->tx_msgid = 0;
		pd->rx_msgid = -1;
		pd_reset_protocol(pd);

		if (!pd->in_pr_swap) {
			if (pd->pd_phy_opened) {
@@ -837,7 +852,17 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
			pd->pd_phy_opened = true;
		}

		pd->current_voltage = 5000000;
		pd->current_voltage = pd->requested_voltage = 5000000;
		val.intval = pd->requested_voltage; /* set max range to 5V */
		power_supply_set_property(pd->usb_psy,
				POWER_SUPPLY_PROP_VOLTAGE_MAX, &val);

		if (!pd->vbus_present) {
			pd->current_state = PE_SNK_DISCOVERY;
			/* max time for hard reset to turn vbus back on */
			kick_sm(pd, SNK_HARD_RESET_VBUS_ON_TIME);
			break;
		}

		pd->current_state = PE_SNK_WAIT_FOR_CAPABILITIES;
		/* fall-through */
@@ -901,13 +926,8 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
			pd->vconn_enabled = false;
		}

		val.intval = pd->requested_voltage; /* set range back to 5V */
		power_supply_set_property(pd->usb_psy,
				POWER_SUPPLY_PROP_VOLTAGE_MAX, &val);
		pd->current_voltage = pd->requested_voltage;

		/* max time for hard reset to toggle vbus off/on */
		kick_sm(pd, SNK_HARD_RESET_RECOVER_TIME);
		/* max time for hard reset to turn vbus off */
		kick_sm(pd, SNK_HARD_RESET_VBUS_OFF_TIME);
		break;

	case PE_PRS_SNK_SRC_TRANSITION_TO_OFF:
@@ -996,7 +1016,7 @@ int usbpd_send_vdm(struct usbpd *pd, u32 vdm_hdr, const u32 *vdos, int num_vdos)
	pd->vdm_tx = vdm_tx;

	/* slight delay before queuing to prioritize handling of incoming VDM */
	kick_sm(pd, 5);
	kick_sm(pd, 2);

	return 0;
}
@@ -1214,21 +1234,32 @@ static void handle_vdm_rx(struct usbpd *pd, struct rx_msg *rx_msg)
static void handle_vdm_tx(struct usbpd *pd)
{
	int ret;
	unsigned long flags;

	/* only send one VDM at a time */
	if (pd->vdm_tx) {
		u32 vdm_hdr = pd->vdm_tx->data[0];

		/* bail out and try again later if a message just arrived */
		spin_lock_irqsave(&pd->rx_lock, flags);
		if (!list_empty(&pd->rx_q)) {
			spin_unlock_irqrestore(&pd->rx_lock, flags);
			return;
		}
		spin_unlock_irqrestore(&pd->rx_lock, flags);

		ret = pd_send_msg(pd, MSG_VDM, pd->vdm_tx->data,
				pd->vdm_tx->size, SOP_MSG);
		if (ret) {
			usbpd_err(&pd->dev, "Error sending VDM command %d\n",
					SVDM_HDR_CMD(pd->vdm_tx->data[0]));
			usbpd_err(&pd->dev, "Error (%d) sending VDM command %d\n",
					ret, SVDM_HDR_CMD(pd->vdm_tx->data[0]));

			/* retry when hitting PE_SRC/SNK_Ready again */
			if (ret != -EBUSY)
				usbpd_set_state(pd, pd->current_pr == PR_SRC ?
					PE_SRC_SEND_SOFT_RESET :
					PE_SNK_SEND_SOFT_RESET);

			/* retry when hitting PE_SRC/SNK_Ready again */
			return;
		}

@@ -1240,7 +1271,7 @@ static void handle_vdm_tx(struct usbpd *pd)
			SVDM_HDR_CMD_TYPE(vdm_hdr) == SVDM_CMD_TYPE_INITIATOR &&
			SVDM_HDR_CMD(vdm_hdr) <= USBPD_SVDM_DISCOVER_SVIDS) {
			if (pd->vdm_tx_retry) {
				usbpd_err(&pd->dev, "Previous Discover VDM command %d not ACKed/NAKed\n",
				usbpd_dbg(&pd->dev, "Previous Discover VDM command %d not ACKed/NAKed\n",
					SVDM_HDR_CMD(
						pd->vdm_tx_retry->data[0]));
				kfree(pd->vdm_tx_retry);
@@ -1319,6 +1350,13 @@ static void vconn_swap(struct usbpd *pd)

		pd->vconn_enabled = true;

		/*
		 * Small delay to ensure Vconn has ramped up. This is well
		 * below tVCONNSourceOn (100ms) so we still send PS_RDY within
		 * the allowed time.
		 */
		usleep_range(5000, 10000);

		ret = pd_send_msg(pd, MSG_PS_RDY, NULL, 0, SOP_MSG);
		if (ret) {
			usbpd_err(&pd->dev, "Error sending PS_RDY\n");
@@ -1366,7 +1404,7 @@ static void usbpd_sm(struct work_struct *w)
	spin_unlock_irqrestore(&pd->rx_lock, flags);

	/* Disconnect? */
	if (pd->typec_mode == POWER_SUPPLY_TYPEC_NONE && !pd->in_pr_swap) {
	if (pd->current_pr == PR_NONE) {
		if (pd->current_state == PE_UNKNOWN)
			goto sm_done;

@@ -1400,8 +1438,10 @@ static void usbpd_sm(struct work_struct *w)
		power_supply_set_property(pd->usb_psy,
				POWER_SUPPLY_PROP_PD_ACTIVE, &val);

		if (pd->current_pr == PR_SRC)
		if (pd->vbus_enabled) {
			regulator_disable(pd->vbus);
			pd->vbus_enabled = false;
		}

		if (pd->vconn_enabled) {
			regulator_disable(pd->vconn);
@@ -1473,6 +1513,8 @@ static void usbpd_sm(struct work_struct *w)
			ret = regulator_enable(pd->vbus);
			if (ret)
				usbpd_err(&pd->dev, "Unable to enable vbus\n");
			else
				pd->vbus_enabled = true;

			if (!pd->vconn_enabled &&
					pd->typec_mode ==
@@ -1521,10 +1563,6 @@ static void usbpd_sm(struct work_struct *w)
			break;
		}

		val.intval = 1;
		power_supply_set_property(pd->usb_psy,
				POWER_SUPPLY_PROP_PD_ACTIVE, &val);

		/* transmit was successful if GoodCRC was received */
		pd->caps_count = 0;
		pd->hard_reset_count = 0;
@@ -1533,6 +1571,10 @@ static void usbpd_sm(struct work_struct *w)
		/* wait for REQUEST */
		pd->current_state = PE_SRC_SEND_CAPABILITIES_WAIT;
		kick_sm(pd, SENDER_RESPONSE_TIME);

		val.intval = 1;
		power_supply_set_property(pd->usb_psy,
				POWER_SUPPLY_PROP_PD_ACTIVE, &val);
		break;

	case PE_SRC_SEND_CAPABILITIES_WAIT:
@@ -1618,6 +1660,7 @@ static void usbpd_sm(struct work_struct *w)
	case PE_SRC_TRANSITION_TO_DEFAULT:
		if (pd->vconn_enabled)
			regulator_disable(pd->vconn);
		if (pd->vbus_enabled)
			regulator_disable(pd->vbus);

		if (pd->current_dr != DR_DFP) {
@@ -1628,9 +1671,12 @@ static void usbpd_sm(struct work_struct *w)

		msleep(SRC_RECOVER_TIME);

		pd->vbus_enabled = false;
		ret = regulator_enable(pd->vbus);
		if (ret)
			usbpd_err(&pd->dev, "Unable to enable vbus\n");
		else
			pd->vbus_enabled = true;

		if (pd->vconn_enabled) {
			ret = regulator_enable(pd->vconn);
@@ -1665,23 +1711,48 @@ static void usbpd_sm(struct work_struct *w)
		usbpd_set_state(pd, PE_SNK_STARTUP);
		break;

	case PE_SNK_DISCOVERY:
		if (!rx_msg) {
			if (pd->vbus_present)
				usbpd_set_state(pd,
						PE_SNK_WAIT_FOR_CAPABILITIES);

			/*
			 * Handle disconnection in the middle of PR_Swap.
			 * Since in psy_changed() if pd->in_pr_swap is true
			 * we ignore the typec_mode==NONE change since that is
			 * expected to happen. However if the cable really did
			 * get disconnected we need to check for it here after
			 * waiting for VBUS presence times out.
			 */
			if (!pd->typec_mode) {
				pd->current_pr = PR_NONE;
				kick_sm(pd, 0);
			}

			break;
		}
		/* else fall-through */

	case PE_SNK_WAIT_FOR_CAPABILITIES:
		pd->in_pr_swap = false;

		if (IS_DATA(rx_msg, MSG_SOURCE_CAPABILITIES)) {
			val.intval = 0;
			power_supply_set_property(pd->usb_psy,
					POWER_SUPPLY_PROP_PD_IN_HARD_RESET,
					&val);

			val.intval = 1;
			power_supply_set_property(pd->usb_psy,
					POWER_SUPPLY_PROP_PD_ACTIVE, &val);

			/* save the PDOs so userspace can further evaluate */
			memcpy(&pd->received_pdos, rx_msg->payload,
					sizeof(pd->received_pdos));
			pd->src_cap_id++;

			usbpd_set_state(pd, PE_SNK_EVALUATE_CAPABILITY);

			val.intval = 1;
			power_supply_set_property(pd->usb_psy,
					POWER_SUPPLY_PROP_PD_ACTIVE, &val);
		} else if (pd->hard_reset_count < 3) {
			usbpd_set_state(pd, PE_SNK_HARD_RESET);
		} else if (pd->pd_connected) {
@@ -1872,28 +1943,12 @@ static void usbpd_sm(struct work_struct *w)
		break;

	case PE_SNK_TRANSITION_TO_DEFAULT:
		val.intval = 0;
		power_supply_set_property(pd->usb_psy,
				POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);

		if (pd->vbus_present) {
		usbpd_set_state(pd, PE_SNK_STARTUP);
		} else {
			/* Hard reset and VBUS didn't come back? */
			power_supply_get_property(pd->usb_psy,
					POWER_SUPPLY_PROP_TYPEC_MODE, &val);
			if (val.intval == POWER_SUPPLY_TYPEC_NONE) {
				pd->typec_mode = POWER_SUPPLY_TYPEC_NONE;
				kick_sm(pd, 0);
			}
		}
		break;

	case PE_SRC_SOFT_RESET:
	case PE_SNK_SOFT_RESET:
		/* Reset protocol layer */
		pd->tx_msgid = 0;
		pd->rx_msgid = -1;
		pd_reset_protocol(pd);

		ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG);
		if (ret) {
@@ -1969,7 +2024,10 @@ static void usbpd_sm(struct work_struct *w)
		pd->in_pr_swap = true;
		pd->in_explicit_contract = false;

		if (pd->vbus_enabled) {
			regulator_disable(pd->vbus);
			pd->vbus_enabled = false;
		}

		/* PE_PRS_SRC_SNK_Assert_Rd */
		pd->current_pr = PR_SINK;
@@ -2024,6 +2082,8 @@ static void usbpd_sm(struct work_struct *w)
		ret = regulator_enable(pd->vbus);
		if (ret)
			usbpd_err(&pd->dev, "Unable to enable vbus\n");
		else
			pd->vbus_enabled = true;

		msleep(200); /* allow time VBUS ramp-up, must be < tNewSrc */

@@ -2134,6 +2194,21 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr)

	pd->psy_type = val.intval;

	/*
	 * For sink hard reset, state machine needs to know when VBUS changes
	 *   - when in PE_SNK_TRANSITION_TO_DEFAULT, notify when VBUS falls
	 *   - when in PE_SNK_DISCOVERY, notify when VBUS rises
	 */
	if (typec_mode && ((!pd->vbus_present &&
			pd->current_state == PE_SNK_TRANSITION_TO_DEFAULT) ||
		(pd->vbus_present && pd->current_state == PE_SNK_DISCOVERY))) {
		usbpd_dbg(&pd->dev, "hard reset: typec mode:%d present:%d\n",
			typec_mode, pd->vbus_present);
		pd->typec_mode = typec_mode;
		kick_sm(pd, 0);
		return 0;
	}

	if (pd->typec_mode == typec_mode)
		return 0;

@@ -2151,32 +2226,7 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr)
			return 0;
		}

		/*
		 * Workaround for PMIC HW bug.
		 *
		 * During hard reset when VBUS goes to 0 the CC logic
		 * will report this as a disconnection. In those cases
		 * it can be ignored, however the downside is that
		 * we can also happen to be in the SNK_Transition_to_default
		 * state due to a hard reset attempt even with a non-PD
		 * capable source, in which a physical disconnect may get
		 * masked. In that case, allow for the common case of
		 * disconnecting from an SDP.
		 *
		 * The less common case is a PD-capable SDP which will
		 * result in a hard reset getting treated like a
		 * disconnect. We can live with this until the HW bug
		 * is fixed: in which disconnection won't be reported
		 * on VBUS loss alone unless pullup is also removed
		 * from CC.
		 */
		if (pd->psy_type != POWER_SUPPLY_TYPE_USB &&
			  pd->current_state ==
				  PE_SNK_TRANSITION_TO_DEFAULT) {
			usbpd_dbg(&pd->dev, "Ignoring disconnect due to hard reset\n");
			return 0;
		}

		pd->current_pr = PR_NONE;
		break;

	/* Sink states */
@@ -2185,8 +2235,11 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr)
	case POWER_SUPPLY_TYPEC_SOURCE_HIGH:
		usbpd_info(&pd->dev, "Type-C Source (%s) connected\n",
				src_current(typec_mode));

		if (pd->current_pr == PR_SINK)
			return 0;

		pd->current_pr = PR_SINK;
		pd->in_pr_swap = false;
		break;

	/* Source states */
@@ -2195,8 +2248,11 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr)
		usbpd_info(&pd->dev, "Type-C Sink%s connected\n",
				typec_mode == POWER_SUPPLY_TYPEC_SINK ?
					"" : " (powered)");

		if (pd->current_pr == PR_SRC)
			return 0;

		pd->current_pr = PR_SRC;
		pd->in_pr_swap = false;
		break;

	case POWER_SUPPLY_TYPEC_SINK_DEBUG_ACCESSORY: