summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2017-02-15 06:11:04 -0800
committerGerrit - the friendly Code Review server <code-review@localhost>2017-02-15 06:11:04 -0800
commit4ddd471716e8d3e50f4f09bb02b41c4b6a5d0901 (patch)
treecd904ac9256b2a8cac02ee25c32096591e40031c /drivers/usb
parentd98999459a263e29542726fabaeb02f9c8fd1e8e (diff)
parent47fdec0cf105986642b30e3e901db4930860996f (diff)
Merge "usb: pd: Make select_pdo_store() wait until request goes through"
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/pd/policy_engine.c73
1 files changed, 58 insertions, 15 deletions
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index 17b8a3d43424..3064a5815edd 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -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);