From 7ae4a1711ecb0fbd69578348a63e9592c611dda3 Mon Sep 17 00:00:00 2001 From: Jack Pham Date: Wed, 19 Oct 2016 12:54:58 -0700 Subject: [PATCH 1/5] usb: pd: Start state machine when PE_START is true Support the new POWER_SUPPLY_PROP_PE_START property which indicates when the policy engine state machine can begin. This helps to simplify the psy_changed() routine as we can now rely on this property to indicate that PROP_TYPEC_MODE and PROP_TYPE are already settled. The state machine work can now simply begin when seeing a change in TYPEC_MODE. This replaces the previous use of PROP_PD_ALLOWED which prior to commit 18da08334eb3 ("usb: pd: Handle PD_ALLOWED within state machine") was intended to be a marker to start up the policy engine but now simply indicates whether or not to start PD comms. We can now move reading of this property to usbpd_set_state() as it is now only needed locally in the SNK_STARTUP handling. Change-Id: Ia0b9e5b011ae72e1afcaf5109b8253d124afc021 Signed-off-by: Jack Pham --- drivers/usb/pd/policy_engine.c | 203 +++++++++++++++++---------------- 1 file changed, 103 insertions(+), 100 deletions(-) diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index 26c91e0ce163..c81a56c7be49 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -299,7 +299,6 @@ struct usbpd { enum power_supply_typec_mode typec_mode; enum power_supply_type psy_type; bool vbus_present; - bool pd_allowed; enum data_role current_dr; enum power_role current_pr; @@ -828,7 +827,15 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) } } - if (!pd->pd_allowed) + ret = power_supply_get_property(pd->usb_psy, + POWER_SUPPLY_PROP_PD_ALLOWED, &val); + if (ret) { + usbpd_err(&pd->dev, "Unable to read USB PROP_PD_ALLOWED: %d\n", + ret); + break; + } + + if (!val.intval) break; /* Reset protocol layer */ @@ -2036,33 +2043,11 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) struct usbpd *pd = container_of(nb, struct usbpd, psy_nb); union power_supply_propval val; enum power_supply_typec_mode typec_mode; - bool do_work = false; int ret; if (ptr != pd->usb_psy || evt != PSY_EVENT_PROP_CHANGED) return 0; - ret = power_supply_get_property(pd->usb_psy, - POWER_SUPPLY_PROP_PD_ALLOWED, &val); - if (ret) { - usbpd_err(&pd->dev, "Unable to read USB PROP_PD_ALLOWED: %d\n", - ret); - return ret; - } - - if (pd->pd_allowed != val.intval) - do_work = true; - pd->pd_allowed = val.intval; - - ret = power_supply_get_property(pd->usb_psy, - POWER_SUPPLY_PROP_PRESENT, &val); - if (ret) { - usbpd_err(&pd->dev, "Unable to read USB PRESENT: %d\n", ret); - return ret; - } - - pd->vbus_present = val.intval; - ret = power_supply_get_property(pd->usb_psy, POWER_SUPPLY_PROP_TYPEC_MODE, &val); if (ret) { @@ -2072,6 +2057,28 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) typec_mode = val.intval; + ret = power_supply_get_property(pd->usb_psy, + POWER_SUPPLY_PROP_PE_START, &val); + if (ret) { + usbpd_err(&pd->dev, "Unable to read USB PROP_PE_START: %d\n", + ret); + return ret; + } + + /* Don't proceed if PE_START=0 as other props may still change */ + if (!val.intval && !pd->pd_connected && + typec_mode != POWER_SUPPLY_TYPEC_NONE) + return 0; + + ret = power_supply_get_property(pd->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &val); + if (ret) { + usbpd_err(&pd->dev, "Unable to read USB PRESENT: %d\n", ret); + return ret; + } + + pd->vbus_present = val.intval; + ret = power_supply_get_property(pd->usb_psy, POWER_SUPPLY_PROP_TYPE, &val); if (ret) { @@ -2079,91 +2086,87 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) return ret; } - if (pd->psy_type != val.intval) - do_work = true; pd->psy_type = val.intval; + if (pd->typec_mode == typec_mode) + return 0; + + pd->typec_mode = typec_mode; + usbpd_dbg(&pd->dev, "typec mode:%d present:%d type:%d orientation:%d\n", typec_mode, pd->vbus_present, pd->psy_type, usbpd_get_plug_orientation(pd)); - if (pd->typec_mode != typec_mode) { - pd->typec_mode = typec_mode; - do_work = true; - - switch (typec_mode) { - /* Disconnect */ - case POWER_SUPPLY_TYPEC_NONE: - if (pd->in_pr_swap) { - usbpd_dbg(&pd->dev, "Ignoring disconnect due to PR swap\n"); - do_work = false; - } - - /* - * 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 - * pd->hard_reset can be momentarily true even when a - * non-PD capable source is attached, and can't be - * distinguished from a physical disconnect. 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"); - do_work = false; - } - - break; - - /* Sink states */ - case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT: - case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM: - case POWER_SUPPLY_TYPEC_SOURCE_HIGH: - usbpd_info(&pd->dev, "Type-C Source (%s) connected\n", - src_current(typec_mode)); - pd->current_pr = PR_SINK; - pd->in_pr_swap = false; - break; - - /* Source states */ - case POWER_SUPPLY_TYPEC_SINK_POWERED_CABLE: - case POWER_SUPPLY_TYPEC_SINK: - usbpd_info(&pd->dev, "Type-C Sink%s connected\n", - typec_mode == POWER_SUPPLY_TYPEC_SINK ? - "" : " (powered)"); - pd->current_pr = PR_SRC; - pd->in_pr_swap = false; - break; - - case POWER_SUPPLY_TYPEC_SINK_DEBUG_ACCESSORY: - usbpd_info(&pd->dev, "Type-C Debug Accessory connected\n"); - break; - case POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER: - usbpd_info(&pd->dev, "Type-C Analog Audio Adapter connected\n"); - break; - default: - usbpd_warn(&pd->dev, "Unsupported typec mode:%d\n", - typec_mode); - break; + switch (typec_mode) { + /* Disconnect */ + case POWER_SUPPLY_TYPEC_NONE: + if (pd->in_pr_swap) { + usbpd_dbg(&pd->dev, "Ignoring disconnect due to PR swap\n"); + 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 + * pd->hard_reset can be momentarily true even when a + * non-PD capable source is attached, and can't be + * distinguished from a physical disconnect. 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; + } + + break; + + /* Sink states */ + case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT: + case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM: + case POWER_SUPPLY_TYPEC_SOURCE_HIGH: + usbpd_info(&pd->dev, "Type-C Source (%s) connected\n", + src_current(typec_mode)); + pd->current_pr = PR_SINK; + pd->in_pr_swap = false; + break; + + /* Source states */ + case POWER_SUPPLY_TYPEC_SINK_POWERED_CABLE: + case POWER_SUPPLY_TYPEC_SINK: + usbpd_info(&pd->dev, "Type-C Sink%s connected\n", + typec_mode == POWER_SUPPLY_TYPEC_SINK ? + "" : " (powered)"); + pd->current_pr = PR_SRC; + pd->in_pr_swap = false; + break; + + case POWER_SUPPLY_TYPEC_SINK_DEBUG_ACCESSORY: + usbpd_info(&pd->dev, "Type-C Debug Accessory connected\n"); + break; + case POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER: + usbpd_info(&pd->dev, "Type-C Analog Audio Adapter connected\n"); + break; + default: + usbpd_warn(&pd->dev, "Unsupported typec mode:%d\n", + typec_mode); + break; } - /* only queue state machine if CC state or PD_ALLOWED changes */ - if (do_work) - kick_sm(pd, 0); - + /* queue state machine due to CC state change */ + kick_sm(pd, 0); return 0; } From f2449fb61a2505b60f8e19093daca9a6f999c974 Mon Sep 17 00:00:00 2001 From: Jack Pham Date: Wed, 19 Oct 2016 12:49:20 -0700 Subject: [PATCH 2/5] usb: pd: Clear PD_IN_HARD_RESET in PE_SNK_TRANSITION_TO_DEFAULT Make sure to properly clear the PD_IN_HARD_RESET property upon reaching the SNK_Transition_to_default state to ensure that the charger driver is notified that hard reset has completed. Move the clearing of pd->hard_reset flag here as well for clarity. Also clear the pd->in_pr_swap flag when initiating or receiving hard reset signal as that should promptly abort any PR swap operation in progress. Change-Id: I967e3841af614ecd2129bf60dc08a1b19731c4e3 Signed-off-by: Jack Pham --- drivers/usb/pd/policy_engine.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index c81a56c7be49..a72c874f19a5 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -497,6 +497,7 @@ static void pd_send_hard_reset(struct usbpd *pd) ret = pd_phy_signal(HARD_RESET_SIG, 5); /* tHardResetComplete */ if (!ret) pd->hard_reset = true; + pd->in_pr_swap = false; } static void kick_sm(struct usbpd *pd, int ms) @@ -913,8 +914,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) break; case PE_SNK_TRANSITION_TO_DEFAULT: - pd->hard_reset = false; - if (pd->current_dr != DR_UFP) { extcon_set_cable_state_(pd->extcon, EXTCON_USB_HOST, 0); @@ -1462,6 +1461,7 @@ static void usbpd_sm(struct work_struct *w) power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); + pd->in_pr_swap = false; reset_vdm_state(pd); if (pd->current_pr == PR_SINK) @@ -1829,6 +1829,12 @@ static void usbpd_sm(struct work_struct *w) break; case PE_SNK_TRANSITION_TO_DEFAULT: + pd->hard_reset = false; + + 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 { From 2c903a2fbb7f6a02eee6a292ea66c2120db6c877 Mon Sep 17 00:00:00 2001 From: Abhijeet Dharmapurikar Date: Mon, 17 Oct 2016 16:58:58 -0700 Subject: [PATCH 3/5] qpnp-smb2: implement PE_START property The policy engine needs to be informed that its time to start its activities when APSD results are available and/or PD_ALLOWED is decided. USB type property shouldn't change after that. Since HVDCP_TIMEOUT_VOTER is the last one to cast its allow vote in the sequence, use it to reflect the PE_START property. While at it since PE_START property is returned assuming an atomic context, the read of PD_ALLOWED could be moved to its sleepable variants. This aids in keeping the policy engine code simple and also assures race free code. Change-Id: Ib98ac10d87200a2fd5492e27399f696f2468eba6 Signed-off-by: Abhijeet Dharmapurikar --- drivers/power/qcom-charger/qpnp-smb2.c | 4 ++++ drivers/power/qcom-charger/smb-lib.c | 27 ++++++++++++++++---------- drivers/power/qcom-charger/smb-lib.h | 2 ++ 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/drivers/power/qcom-charger/qpnp-smb2.c b/drivers/power/qcom-charger/qpnp-smb2.c index d5dbd6374ace..4cb4c2bd665c 100644 --- a/drivers/power/qcom-charger/qpnp-smb2.c +++ b/drivers/power/qcom-charger/qpnp-smb2.c @@ -351,6 +351,7 @@ static enum power_supply_property smb2_usb_props[] = { POWER_SUPPLY_PROP_PD_ACTIVE, POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, POWER_SUPPLY_PROP_INPUT_CURRENT_NOW, + POWER_SUPPLY_PROP_PE_START, }; static int smb2_usb_get_prop(struct power_supply *psy, @@ -422,6 +423,9 @@ static int smb2_usb_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_PD_USB_SUSPEND_SUPPORTED: val->intval = chg->system_suspend_supported; break; + case POWER_SUPPLY_PROP_PE_START: + rc = smblib_get_pe_start(chg, val); + break; default: pr_err("get prop %d is not supported\n", psp); rc = -EINVAL; diff --git a/drivers/power/qcom-charger/smb-lib.c b/drivers/power/qcom-charger/smb-lib.c index 5f9775cc0fee..4b57c1a002c2 100644 --- a/drivers/power/qcom-charger/smb-lib.c +++ b/drivers/power/qcom-charger/smb-lib.c @@ -471,9 +471,8 @@ static int try_rerun_apsd_for_hvdcp(struct smb_charger *chg) return 0; } -static int smblib_update_usb_type(struct smb_charger *chg) +static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg) { - int rc = 0; const struct apsd_result *apsd_result; /* if PD is active, APSD is disabled so won't have a valid result */ @@ -484,7 +483,7 @@ static int smblib_update_usb_type(struct smb_charger *chg) apsd_result = smblib_get_apsd_result(chg); chg->usb_psy_desc.type = apsd_result->pst; - return rc; + return apsd_result; } static int smblib_notifier_call(struct notifier_block *nb, @@ -1767,7 +1766,7 @@ int smblib_get_prop_typec_power_role(struct smb_charger *chg, int smblib_get_prop_pd_allowed(struct smb_charger *chg, union power_supply_propval *val) { - val->intval = get_effective_result_locked(chg->pd_allowed_votable); + val->intval = get_effective_result(chg->pd_allowed_votable); return 0; } @@ -1793,6 +1792,19 @@ int smblib_get_prop_pd_in_hard_reset(struct smb_charger *chg, return 0; } +int smblib_get_pe_start(struct smb_charger *chg, + union power_supply_propval *val) +{ + /* + * hvdcp timeout voter is the last one to allow pd. Use its vote + * to indicate start of pe engine + */ + val->intval + = !get_client_vote_locked(chg->pd_disallowed_votable_indirect, + HVDCP_TIMEOUT_VOTER); + return 0; +} + /******************* * USB PSY SETTERS * * *****************/ @@ -2298,13 +2310,12 @@ static void smblib_handle_hvdcp_detect_done(struct smb_charger *chg, #define HVDCP_DET_MS 2500 static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) { - int rc; const struct apsd_result *apsd_result; if (!rising) return; - apsd_result = smblib_get_apsd_result(chg); + apsd_result = smblib_update_usb_type(chg); switch (apsd_result->bit) { case SDP_CHARGER_BIT: case CDP_CHARGER_BIT: @@ -2323,10 +2334,6 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) break; } - rc = smblib_update_usb_type(chg); - if (rc < 0) - smblib_err(chg, "Couldn't update usb type rc=%d\n", rc); - smblib_dbg(chg, PR_INTERRUPT, "IRQ: apsd-done rising; %s detected\n", apsd_result->name); } diff --git a/drivers/power/qcom-charger/smb-lib.h b/drivers/power/qcom-charger/smb-lib.h index 77badf6c7ec1..fb8356d3bdba 100644 --- a/drivers/power/qcom-charger/smb-lib.h +++ b/drivers/power/qcom-charger/smb-lib.h @@ -309,6 +309,8 @@ int smblib_get_prop_input_current_settled(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_pd_in_hard_reset(struct smb_charger *chg, union power_supply_propval *val); +int smblib_get_pe_start(struct smb_charger *chg, + union power_supply_propval *val); int smblib_get_prop_charger_temp(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_charger_temp_max(struct smb_charger *chg, From 757155c6d6067e8980ca290a934859de50787fad Mon Sep 17 00:00:00 2001 From: Harry Yang Date: Fri, 14 Oct 2016 13:03:46 -0700 Subject: [PATCH 4/5] qcom-charger: smb2: disable EN_TRY_SINK_MODE for PD Try.SNK is not permitted in PD per spec. Disable it while pd_active is true. Change-Id: I90891232d37b95f011b3f2d5278f0fd0f4c9eb71 Signed-off-by: Harry Yang Signed-off-by: Abhijeet Dharmapurikar --- drivers/power/qcom-charger/smb-lib.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/power/qcom-charger/smb-lib.c b/drivers/power/qcom-charger/smb-lib.c index 4b57c1a002c2..af63362e31ec 100644 --- a/drivers/power/qcom-charger/smb-lib.c +++ b/drivers/power/qcom-charger/smb-lib.c @@ -1975,6 +1975,13 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, smblib_update_usb_type(chg); power_supply_changed(chg->usb_psy); + rc = smblib_masked_write(chg, TYPE_C_CFG_3_REG, EN_TRYSINK_MODE_BIT, + chg->pd_active ? 0 : EN_TRYSINK_MODE_BIT); + if (rc < 0) { + dev_err(chg->dev, "Couldn't set TRYSINK_MODE rc=%d\n", rc); + return rc; + } + return rc; } From 8a09bace078ef3a454fd5f1d09b269ac978714af Mon Sep 17 00:00:00 2001 From: Harry Yang Date: Fri, 14 Oct 2016 09:31:15 -0700 Subject: [PATCH 5/5] qpnp-smb2: support setting FCC and float voltage in battery power supply Fast charge current (FCC) and float voltage are parameters that are battery specific and needs to be set based on the profile detected by Fuel Gauge driver. Expose the following properties from battery power supply so that FG can set them. - POWER_SUPPLY_PROP_VOLTAGE_MAX - POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX Change-Id: I72465484b154b1a758285d58906ce7661a246767 Signed-off-by: Harry Yang Signed-off-by: Subbaraman Narayanamurthy --- drivers/power/qcom-charger/qpnp-smb2.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/power/qcom-charger/qpnp-smb2.c b/drivers/power/qcom-charger/qpnp-smb2.c index 4cb4c2bd665c..a7f907dbcbea 100644 --- a/drivers/power/qcom-charger/qpnp-smb2.c +++ b/drivers/power/qcom-charger/qpnp-smb2.c @@ -637,7 +637,9 @@ static enum power_supply_property smb2_batt_props[] = { POWER_SUPPLY_PROP_CHARGER_TEMP_MAX, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED, POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_MAX, POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED, @@ -694,9 +696,16 @@ static int smb2_batt_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_NOW: rc = smblib_get_prop_batt_voltage_now(chg, val); break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + val->intval = get_client_vote(chg->fv_votable, DEFAULT_VOTER); + break; case POWER_SUPPLY_PROP_CURRENT_NOW: rc = smblib_get_prop_batt_current_now(chg, val); break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + val->intval = get_client_vote(chg->fcc_max_votable, + DEFAULT_VOTER); + break; case POWER_SUPPLY_PROP_TEMP: rc = smblib_get_prop_batt_temp(chg, val); break; @@ -752,6 +761,12 @@ static int smb2_batt_set_prop(struct power_supply *psy, chg->pl.slave_pct = val->intval; rerun_election(chg->fcc_votable); break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + vote(chg->fv_votable, DEFAULT_VOTER, true, val->intval); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + vote(chg->fcc_max_votable, DEFAULT_VOTER, true, val->intval); + break; default: rc = -EINVAL; }