From 7805ee656a5629230b513f3a648090735de95d1e Mon Sep 17 00:00:00 2001 From: Jack Pham Date: Thu, 25 May 2017 11:53:36 -0700 Subject: [PATCH 1/6] usb: pd: Notify charger when PR Swap is in progress Charger driver needs to know when PR Swap is happening in order to avoid resetting state when the disconnect (which is expected) occurs. Utilize the new power supply PR_SWAP property to notify whenever PD is in PR Swap. Change-Id: I13fdbd4a3cf3e0716d48d1268795189f6b064598 Signed-off-by: Jack Pham --- drivers/usb/pd/policy_engine.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index 7d893c5815e2..2258e9f1e44a 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -571,6 +571,8 @@ static int pd_eval_src_caps(struct usbpd *pd) static void pd_send_hard_reset(struct usbpd *pd) { + union power_supply_propval val = {0}; + usbpd_dbg(&pd->dev, "send hard reset"); /* Force CC logic to source/sink to keep Rp/Rd unchanged */ @@ -578,6 +580,7 @@ static void pd_send_hard_reset(struct usbpd *pd) pd->hard_reset_count++; pd_phy_signal(HARD_RESET_SIG, 5); /* tHardResetComplete */ pd->in_pr_swap = false; + power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PR_SWAP, &val); } static void kick_sm(struct usbpd *pd, int ms) @@ -716,6 +719,9 @@ 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; + val.intval = 0; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PR_SWAP, &val); pd->current_pr = PR_NONE; set_power_role(pd, PR_NONE); pd->typec_mode = POWER_SUPPLY_TYPEC_NONE; @@ -771,6 +777,9 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) if (pd->in_pr_swap) { kick_sm(pd, SWAP_SOURCE_START_TIME); pd->in_pr_swap = false; + val.intval = 0; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PR_SWAP, &val); break; } @@ -1564,6 +1573,9 @@ static void usbpd_sm(struct work_struct *w) rx_msg_cleanup(pd); val.intval = 0; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PR_SWAP, &val); + power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); @@ -1622,6 +1634,10 @@ static void usbpd_sm(struct work_struct *w) POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); pd->in_pr_swap = false; + val.intval = 0; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PR_SWAP, &val); + pd->in_explicit_contract = false; pd->selected_pdo = pd->requested_pdo = 0; pd->rdo = 0; @@ -1888,6 +1904,9 @@ static void usbpd_sm(struct work_struct *w) case PE_SNK_WAIT_FOR_CAPABILITIES: pd->in_pr_swap = false; + val.intval = 0; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PR_SWAP, &val); if (IS_DATA(rx_msg, MSG_SOURCE_CAPABILITIES)) { val.intval = 0; @@ -2066,6 +2085,9 @@ static void usbpd_sm(struct work_struct *w) } pd->in_pr_swap = true; + val.intval = 1; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PR_SWAP, &val); usbpd_set_state(pd, PE_PRS_SNK_SRC_TRANSITION_TO_OFF); break; } else if (IS_CTRL(rx_msg, MSG_VCONN_SWAP)) { @@ -2209,6 +2231,9 @@ static void usbpd_sm(struct work_struct *w) case PE_PRS_SRC_SNK_TRANSITION_TO_OFF: pd->in_pr_swap = true; + val.intval = 1; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PR_SWAP, &val); pd->in_explicit_contract = false; if (pd->vbus_enabled) { @@ -2249,6 +2274,9 @@ static void usbpd_sm(struct work_struct *w) } pd->in_pr_swap = true; + val.intval = 1; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PR_SWAP, &val); usbpd_set_state(pd, PE_PRS_SNK_SRC_TRANSITION_TO_OFF); break; From 95d46e9eb081048feab618a08e261379e2b9bf8b Mon Sep 17 00:00:00 2001 From: Jack Pham Date: Wed, 24 May 2017 13:44:56 -0700 Subject: [PATCH 2/6] usb: pd: Don't draw current during sink hard reset During hard reset inform charger driver to suspend current draw as the sink should not draw more than 1mA while VBUS is driven to VSafe0V. Once VBUS returns to VSafe5V the current draw can be restored when contract is re-established. Change-Id: I3f529c861706d4544f3c8b65d6fc6be47a598309 Signed-off-by: Jack Pham --- drivers/usb/pd/policy_engine.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index 2258e9f1e44a..90e9f14d550c 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -1633,6 +1633,17 @@ static void usbpd_sm(struct work_struct *w) power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); + if (pd->requested_current) { + val.intval = pd->requested_current = 0; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val); + } + + pd->requested_voltage = 5000000; + val.intval = pd->requested_voltage; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_MIN, &val); + pd->in_pr_swap = false; val.intval = 0; power_supply_set_property(pd->usb_psy, From ed97270b784c0ecaa3809fe8474aa2609ecd3492 Mon Sep 17 00:00:00 2001 From: Jack Pham Date: Tue, 6 Jun 2017 11:10:03 -0700 Subject: [PATCH 3/6] usb: pd: Set PD_IN_HARD_RESET as soon as hard reset received Move setting of PD_IN_HARD_RESET to the phy_sig_received() callback in order to inform the charger to ignore the upcoming VBUS toggle as soon as possible. This will help decrease the possibility of racing to set the appropriate HW bits while VBUS turns off and leads to the charger indicating disconnect. Change-Id: I42cc2969defa48c9987fa4bea64780866861d868 Signed-off-by: Jack Pham --- drivers/usb/pd/policy_engine.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index 90e9f14d550c..bf3fe5b7579e 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -596,6 +596,8 @@ static void kick_sm(struct usbpd *pd, int ms) static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type) { + union power_supply_propval val = {1}; + if (type != HARD_RESET_SIG) { usbpd_err(&pd->dev, "invalid signal (%d) received\n", type); return; @@ -606,6 +608,9 @@ static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type) /* Force CC logic to source/sink to keep Rp/Rd unchanged */ set_power_role(pd, pd->current_pr); pd->hard_reset_recvd = true; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); + kick_sm(pd, 0); } @@ -1629,10 +1634,6 @@ static void usbpd_sm(struct work_struct *w) if (pd->hard_reset_recvd) { pd->hard_reset_recvd = false; - val.intval = 1; - power_supply_set_property(pd->usb_psy, - POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); - if (pd->requested_current) { val.intval = pd->requested_current = 0; power_supply_set_property(pd->usb_psy, From 00adcfcbad43e8f68bed8c3f3df63b58f978137b Mon Sep 17 00:00:00 2001 From: Jack Pham Date: Tue, 6 Jun 2017 22:22:18 -0700 Subject: [PATCH 4/6] usb: pd: Keep PROP_PR_SWAP set during ERROR_RECOVERY Since ErrorRecovery handling requires a spoof disconnect, the charger driver needs to avoid immediately reapplying the terminations. Re-use the PROP_PR_SWAP property which ignores the disconnect. After the 25ms delay, policy engine will explicitly reset back to DRP. Also remove the transition to ErrorRecovery from PE_SNK_DISCOVERY as it is removed in V1.3 of the PD 2.0 specification. Change-Id: I41f1da293208b6813dfb540a123ee84ac35b4a68 Signed-off-by: Jack Pham --- drivers/usb/pd/policy_engine.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index bf3fe5b7579e..0df527a0d131 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -724,9 +724,6 @@ 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; - val.intval = 0; - power_supply_set_property(pd->usb_psy, - POWER_SUPPLY_PROP_PR_SWAP, &val); pd->current_pr = PR_NONE; set_power_role(pd, PR_NONE); pd->typec_mode = POWER_SUPPLY_TYPEC_NONE; @@ -1577,10 +1574,6 @@ static void usbpd_sm(struct work_struct *w) memset(&pd->received_pdos, 0, sizeof(pd->received_pdos)); rx_msg_cleanup(pd); - val.intval = 0; - power_supply_set_property(pd->usb_psy, - POWER_SUPPLY_PROP_PR_SWAP, &val); - power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); @@ -1611,6 +1604,10 @@ static void usbpd_sm(struct work_struct *w) usleep_range(ERROR_RECOVERY_TIME * USEC_PER_MSEC, (ERROR_RECOVERY_TIME + 5) * USEC_PER_MSEC); + val.intval = 0; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PR_SWAP, &val); + /* set due to dual_role class "mode" change */ if (pd->forced_pr != POWER_SUPPLY_TYPEC_PR_NONE) val.intval = pd->forced_pr; @@ -1938,15 +1935,6 @@ static void usbpd_sm(struct work_struct *w) 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) { - usbpd_info(&pd->dev, "Sink hard reset count exceeded, forcing reconnect\n"); - - val.intval = 0; - power_supply_set_property(pd->usb_psy, - POWER_SUPPLY_PROP_PD_IN_HARD_RESET, - &val); - - usbpd_set_state(pd, PE_ERROR_RECOVERY); } else { usbpd_dbg(&pd->dev, "Sink hard reset count exceeded, disabling PD\n"); From 71c2e3fc4ffa5aa629f3afd9d971b2e1f521255f Mon Sep 17 00:00:00 2001 From: Jack Pham Date: Tue, 6 Jun 2017 22:30:47 -0700 Subject: [PATCH 5/6] usb: pdphy: Prevent sending when message is just received Prevent pd_phy_write() from sending a TX message if an RX message IRQ is being handled, or if the RX_TOKEN is set (indicating a message has just arrived) by returning -EBUSY to let the upper layer gracefully abort. This helps in cases (such as compliance testing) in which VDM messages are received very quickly after one another and the protocol layer needs to first handle the incoming message. Change-Id: I3e26d7ff062ff7f51b6c66ab8d078b05749f808a Signed-off-by: Jack Pham --- drivers/usb/pd/qpnp-pdphy.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/usb/pd/qpnp-pdphy.c b/drivers/usb/pd/qpnp-pdphy.c index 588af94db6cd..22c03ace78e7 100644 --- a/drivers/usb/pd/qpnp-pdphy.c +++ b/drivers/usb/pd/qpnp-pdphy.c @@ -108,6 +108,7 @@ struct usb_pdphy { int tx_status; u8 frame_filter_val; bool in_test_data_mode; + bool rx_busy; enum data_role data_role; enum power_role power_role; @@ -492,6 +493,12 @@ int pd_phy_write(u16 hdr, const u8 *data, size_t data_len, return -EINVAL; } + ret = pdphy_reg_read(pdphy, &val, USB_PDPHY_RX_ACKNOWLEDGE, 1); + if (ret || val || pdphy->rx_busy) { + dev_err(pdphy->dev, "%s: RX message pending\n", __func__); + return -EBUSY; + } + pdphy->tx_status = -EINPROGRESS; /* write 2 byte SOP message header */ @@ -664,6 +671,15 @@ static int pd_phy_bist_mode(u8 bist_mode) BIST_MODE_MASK | BIST_ENABLE, bist_mode | BIST_ENABLE); } +static irqreturn_t pdphy_msg_rx_irq(int irq, void *data) +{ + struct usb_pdphy *pdphy = data; + + pdphy->rx_busy = true; + + return IRQ_WAKE_THREAD; +} + static irqreturn_t pdphy_msg_rx_irq_thread(int irq, void *data) { u8 size, rx_status, frame_type; @@ -720,6 +736,7 @@ static irqreturn_t pdphy_msg_rx_irq_thread(int irq, void *data) false); pdphy->rx_bytes += size + 1; done: + pdphy->rx_busy = false; return IRQ_HANDLED; } @@ -805,7 +822,7 @@ static int pdphy_probe(struct platform_device *pdev) return ret; ret = pdphy_request_irq(pdphy, pdev->dev.of_node, - &pdphy->msg_rx_irq, "msg-rx", NULL, + &pdphy->msg_rx_irq, "msg-rx", pdphy_msg_rx_irq, pdphy_msg_rx_irq_thread, (IRQF_TRIGGER_RISING | IRQF_ONESHOT)); if (ret < 0) return ret; From c8735c75303bdff8ba4e8270932222d386e8cf40 Mon Sep 17 00:00:00 2001 From: Jack Pham Date: Wed, 7 Jun 2017 15:35:57 -0700 Subject: [PATCH 6/6] usb: pd: Increment MessageID only on successful TX According to the PD 2.0 Spec, Section 6.2.1.3, the MessageID field shall only be incremented upon successful transmission (meaning GoodCRC was received). Thus, increment the counter only when pd_phy_write() succeeds. Change-Id: I73dce5736c473a1f79db9be868442c7941d13c44 Signed-off-by: Jack Pham --- drivers/usb/pd/policy_engine.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index 0df527a0d131..f93ab9158772 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -481,13 +481,12 @@ static int pd_send_msg(struct usbpd *pd, u8 hdr_type, const u32 *data, ret = pd_phy_write(hdr, (u8 *)data, num_data * sizeof(u32), type, 15); /* TODO figure out timeout. based on tReceive=1.1ms x nRetryCount? */ - /* MessageID incremented regardless of Tx error */ - pd->tx_msgid = (pd->tx_msgid + 1) & PD_MAX_MSG_ID; - if (ret < 0) return ret; else if (ret != num_data * sizeof(u32)) return -EIO; + + pd->tx_msgid = (pd->tx_msgid + 1) & PD_MAX_MSG_ID; return 0; }