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

Commit 4c685b2f authored by Kyle Yan's avatar Kyle Yan Committed by Gerrit - the friendly Code Review server
Browse files

Merge "usb: pd: Make select_pdo_store() wait until request goes through" into msm-4.9

parents ba8b1302 af7d3841
Loading
Loading
Loading
Loading
+58 −15
Original line number Diff line number Diff line
@@ -338,8 +338,10 @@ struct usbpd {
	enum power_role		current_pr;
	bool			in_pr_swap;
	bool			pd_phy_opened;
	struct completion	swap_complete;
	bool			send_request;
	struct completion	is_ready;

	struct mutex		swap_lock;
	struct dual_role_phy_instance	*dual_role;
	struct dual_role_phy_desc	dr_desc;
	bool			send_pr_swap;
@@ -463,6 +465,9 @@ static inline void pd_reset_protocol(struct usbpd *pd)
	 */
	pd->rx_msgid = -1;
	pd->tx_msgid = 0;
	pd->send_request = false;
	pd->send_pr_swap = false;
	pd->send_dr_swap = false;
}

static int pd_send_msg(struct usbpd *pd, u8 hdr_type, const u32 *data,
@@ -842,7 +847,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
		}

		kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE);
		complete(&pd->swap_complete);
		complete(&pd->is_ready);
		dual_role_instance_changed(pd->dual_role);
		break;

@@ -977,7 +982,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
	case PE_SNK_READY:
		pd->in_explicit_contract = true;
		kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE);
		complete(&pd->swap_complete);
		complete(&pd->is_ready);
		dual_role_instance_changed(pd->dual_role);
		break;

@@ -2075,6 +2080,9 @@ static void usbpd_sm(struct work_struct *w)
			vconn_swap(pd);
		} else if (IS_DATA(rx_msg, MSG_VDM)) {
			handle_vdm_rx(pd, rx_msg);
		} else if (pd->send_request) {
			pd->send_request = false;
			usbpd_set_state(pd, PE_SNK_SELECT_CAPABILITY);
		} else if (pd->send_pr_swap && is_sink_tx_ok(pd)) {
			pd->send_pr_swap = false;
			ret = pd_send_msg(pd, MSG_PR_SWAP, NULL, 0, SOP_MSG);
@@ -2540,17 +2548,21 @@ static int usbpd_dr_set_property(struct dual_role_phy_instance *dual_role,
				return -EAGAIN;
			}

			reinit_completion(&pd->swap_complete);
			mutex_lock(&pd->swap_lock);
			reinit_completion(&pd->is_ready);
			pd->send_dr_swap = true;
			kick_sm(pd, 0);

			/* wait for operation to complete */
			if (!wait_for_completion_timeout(&pd->swap_complete,
			if (!wait_for_completion_timeout(&pd->is_ready,
					msecs_to_jiffies(100))) {
				usbpd_err(&pd->dev, "data_role swap timed out\n");
				mutex_unlock(&pd->swap_lock);
				return -ETIMEDOUT;
			}

			mutex_unlock(&pd->swap_lock);

			if ((*val == DUAL_ROLE_PROP_DR_HOST &&
					pd->current_dr != DR_DFP) ||
				(*val == DUAL_ROLE_PROP_DR_DEVICE &&
@@ -2591,17 +2603,21 @@ static int usbpd_dr_set_property(struct dual_role_phy_instance *dual_role,
				return -EAGAIN;
			}

			reinit_completion(&pd->swap_complete);
			mutex_lock(&pd->swap_lock);
			reinit_completion(&pd->is_ready);
			pd->send_pr_swap = true;
			kick_sm(pd, 0);

			/* wait for operation to complete */
			if (!wait_for_completion_timeout(&pd->swap_complete,
			if (!wait_for_completion_timeout(&pd->is_ready,
					msecs_to_jiffies(2000))) {
				usbpd_err(&pd->dev, "power_role swap timed out\n");
				mutex_unlock(&pd->swap_lock);
				return -ETIMEDOUT;
			}

			mutex_unlock(&pd->swap_lock);

			if ((*val == DUAL_ROLE_PROP_PR_SRC &&
					pd->current_pr != PR_SRC) ||
				(*val == DUAL_ROLE_PROP_PR_SNK &&
@@ -2864,36 +2880,62 @@ static ssize_t select_pdo_store(struct device *dev,
	int pdo, uv = 0, ua = 0;
	int ret;

	mutex_lock(&pd->swap_lock);

	/* Only allowed if we are already in explicit sink contract */
	if (pd->current_state != PE_SNK_READY || !is_sink_tx_ok(pd)) {
		usbpd_err(&pd->dev, "select_pdo: Cannot select new PDO yet\n");
		return -EBUSY;
		ret = -EBUSY;
		goto out;
	}

	ret = sscanf(buf, "%d %d %d %d", &src_cap_id, &pdo, &uv, &ua);
	if (ret != 2 && ret != 4) {
		usbpd_err(&pd->dev, "select_pdo: Must specify <src cap id> <PDO> [<uV> <uA>]\n");
		return -EINVAL;
		ret = -EINVAL;
		goto out;
	}

	if (src_cap_id != pd->src_cap_id) {
		usbpd_err(&pd->dev, "select_pdo: src_cap_id mismatch.  Requested:%d, current:%d\n",
				src_cap_id, pd->src_cap_id);
		return -EINVAL;
		ret = -EINVAL;
		goto out;
	}

	if (pdo < 1 || pdo > 7) {
		usbpd_err(&pd->dev, "select_pdo: invalid PDO:%d\n", pdo);
		return -EINVAL;
		ret = -EINVAL;
		goto out;
	}

	ret = pd_select_pdo(pd, pdo, uv, ua);
	if (ret)
		return ret;
		goto out;

	usbpd_set_state(pd, PE_SNK_SELECT_CAPABILITY);
	reinit_completion(&pd->is_ready);
	pd->send_request = true;
	kick_sm(pd, 0);

	return size;
	/* wait for operation to complete */
	if (!wait_for_completion_timeout(&pd->is_ready,
			msecs_to_jiffies(1000))) {
		usbpd_err(&pd->dev, "select_pdo: request timed out\n");
		ret = -ETIMEDOUT;
		goto out;
	}

	/* determine if request was accepted/rejected */
	if (pd->selected_pdo != pd->requested_pdo ||
			pd->current_voltage != pd->requested_voltage) {
		usbpd_err(&pd->dev, "select_pdo: request rejected\n");
		ret = -EINVAL;
	}

out:
	pd->send_request = false;
	mutex_unlock(&pd->swap_lock);
	return ret ? ret : size;
}

static ssize_t select_pdo_show(struct device *dev,
@@ -3123,6 +3165,7 @@ struct usbpd *usbpd_create(struct device *parent)
	INIT_WORK(&pd->sm_work, usbpd_sm);
	hrtimer_init(&pd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
	pd->timer.function = pd_timeout;
	mutex_init(&pd->swap_lock);

	pd->usb_psy = power_supply_get_by_name("usb");
	if (!pd->usb_psy) {
@@ -3233,7 +3276,7 @@ struct usbpd *usbpd_create(struct device *parent)
	spin_lock_init(&pd->rx_lock);
	INIT_LIST_HEAD(&pd->rx_q);
	INIT_LIST_HEAD(&pd->svid_handlers);
	init_completion(&pd->swap_complete);
	init_completion(&pd->is_ready);

	pd->psy_nb.notifier_call = psy_changed;
	ret = power_supply_reg_notifier(&pd->psy_nb);