diff options
author | Linux Build Service Account <lnxbuild@localhost> | 2017-02-15 06:11:04 -0800 |
---|---|---|
committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2017-02-15 06:11:04 -0800 |
commit | 4ddd471716e8d3e50f4f09bb02b41c4b6a5d0901 (patch) | |
tree | cd904ac9256b2a8cac02ee25c32096591e40031c | |
parent | d98999459a263e29542726fabaeb02f9c8fd1e8e (diff) | |
parent | 47fdec0cf105986642b30e3e901db4930860996f (diff) |
Merge "usb: pd: Make select_pdo_store() wait until request goes through"
-rw-r--r-- | drivers/usb/pd/policy_engine.c | 73 |
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); |