From 0d8df81b814d21c3a309ca8dca1b2031d9e722e8 Mon Sep 17 00:00:00 2001 From: Vijayavardhan Vennapusa Date: Thu, 1 Apr 2021 12:44:54 +0530 Subject: [PATCH 1/3] USB: phy: msm: Apply DP pulse for CDP during bootup from sm_work Currently driver is calling API to apply pulse from vbus notifier, but it might lead to race condition during bootup and result in unclocked access. Hence avoid applying DP pulse during bootup from vbus notifier callback. Also enable LDOs and clocks only if usb is in lpm, else apply DP pulse without enabling LDOs and clocks again. Change-Id: I987d93de5261147481f3856254941091dedba108 Signed-off-by: Vijayavardhan Vennapusa --- drivers/usb/phy/phy-msm-usb.c | 61 +++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index bb243d61f07a..6ecfa5426775 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -2862,6 +2862,10 @@ static void check_for_sdp_connection(struct work_struct *w) msm_otg_set_vbus_state(motg->vbus_state); } +#define DP_PULSE_WIDTH_MSEC 200 +static int +msm_otg_phy_drive_dp_pulse(struct msm_otg *motg, unsigned int pulse_width); + static void msm_otg_sm_work(struct work_struct *w) { struct msm_otg *motg = container_of(w, struct msm_otg, sm_work); @@ -2905,6 +2909,9 @@ static void msm_otg_sm_work(struct work_struct *w) get_pm_runtime_counter(dev), 0); pm_runtime_put_sync(dev); break; + } else if (get_psy_type(motg) == POWER_SUPPLY_TYPE_USB_CDP) { + pr_debug("Connected to CDP, pull DP up from sm_work\n"); + msm_otg_phy_drive_dp_pulse(motg, DP_PULSE_WIDTH_MSEC); } pm_runtime_put(dev); /* FALL THROUGH */ @@ -3073,19 +3080,25 @@ msm_otg_phy_drive_dp_pulse(struct msm_otg *motg, unsigned int pulse_width) { int ret = 0; u32 val; + bool in_lpm = false; msm_otg_dbg_log_event(&motg->phy, "DRIVE DP PULSE", motg->inputs, 0); - ret = msm_hsusb_ldo_enable(motg, USB_PHY_REG_ON); - if (ret) - return ret; - msm_hsusb_config_vddcx(1); - ret = regulator_enable(hsusb_vdd); - WARN(ret, "hsusb_vdd LDO enable failed for driving pulse\n"); - clk_prepare_enable(motg->xo_clk); - clk_prepare_enable(motg->phy_csr_clk); - clk_prepare_enable(motg->core_clk); - clk_prepare_enable(motg->pclk); + if (atomic_read(&motg->in_lpm)) + in_lpm = true; + + if (in_lpm) { + ret = msm_hsusb_ldo_enable(motg, USB_PHY_REG_ON); + if (ret) + return ret; + msm_hsusb_config_vddcx(1); + ret = regulator_enable(hsusb_vdd); + WARN(ret, "hsusb_vdd LDO enable failed for driving pulse\n"); + clk_prepare_enable(motg->xo_clk); + clk_prepare_enable(motg->phy_csr_clk); + clk_prepare_enable(motg->core_clk); + clk_prepare_enable(motg->pclk); + } msm_otg_exit_phy_retention(motg); @@ -3131,24 +3144,27 @@ msm_otg_phy_drive_dp_pulse(struct msm_otg *motg, unsigned int pulse_width) /* Make sure above writes are completed before clks off */ mb(); - clk_disable_unprepare(motg->pclk); - clk_disable_unprepare(motg->core_clk); - clk_disable_unprepare(motg->phy_csr_clk); - clk_disable_unprepare(motg->xo_clk); - regulator_disable(hsusb_vdd); - msm_hsusb_config_vddcx(0); - msm_hsusb_ldo_enable(motg, USB_PHY_REG_OFF); + if (in_lpm) { + clk_disable_unprepare(motg->pclk); + clk_disable_unprepare(motg->core_clk); + clk_disable_unprepare(motg->phy_csr_clk); + clk_disable_unprepare(motg->xo_clk); + regulator_disable(hsusb_vdd); + msm_hsusb_config_vddcx(0); + msm_hsusb_ldo_enable(motg, USB_PHY_REG_OFF); + } else { + msm_otg_reset(&motg->phy); + } msm_otg_dbg_log_event(&motg->phy, "DP PULSE DRIVEN", motg->inputs, 0); return 0; } -#define DP_PULSE_WIDTH_MSEC 200 - static void msm_otg_set_vbus_state(int online) { struct msm_otg *motg = the_msm_otg; + struct usb_otg *otg = motg->phy.otg; motg->vbus_state = online; @@ -3161,7 +3177,12 @@ static void msm_otg_set_vbus_state(int online) motg->inputs, 0); if (test_and_set_bit(B_SESS_VLD, &motg->inputs)) return; - if (get_psy_type(motg) == POWER_SUPPLY_TYPE_USB_CDP) { + /* + * It might race with block reset happening in sm_work, while + * state machine is in undefined state. Add check to avoid it. + */ + if ((get_psy_type(motg) == POWER_SUPPLY_TYPE_USB_CDP) && + (otg->state != OTG_STATE_UNDEFINED)) { pr_debug("Connected to CDP, pull DP up\n"); msm_otg_phy_drive_dp_pulse(motg, DP_PULSE_WIDTH_MSEC); } From dde84941255c56cbb6657a9e2189dde2f9de7325 Mon Sep 17 00:00:00 2001 From: Vijayavardhan Vennapusa Date: Sat, 5 Jun 2021 16:11:29 +0530 Subject: [PATCH 2/3] phy: msm: usb: Fail suspend if psy_type is USB or USB_CDP Currently driver is checking only charger type from internal variable in msm_otg_suspend(), which is valid for PHY based charger detection. If PMIC charger does charger detection, that charger type will not be updated and result in allowing low power mode sequence even VBUS is active. Hence fix it by checking psy_type as well to avoid allowing low power mode sequence while VBUS is active. Change-Id: I8cb47912867b15ebdc30bc15b58ff99026a1e180 Signed-off-by: Vijayavardhan Vennapusa --- drivers/usb/phy/phy-msm-usb.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index 6ecfa5426775..1b4cd03c0630 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -1420,6 +1420,7 @@ static irqreturn_t msm_otg_phy_irq_handler(int irq, void *data) static void msm_otg_set_vbus_state(int online); static void msm_otg_perf_vote_update(struct msm_otg *motg, bool perf_mode); +static int get_psy_type(struct msm_otg *motg); #ifdef CONFIG_PM_SLEEP static int msm_otg_suspend(struct msm_otg *motg) @@ -1458,7 +1459,11 @@ static int msm_otg_suspend(struct msm_otg *motg) msm_otg_perf_vote_update(motg, false); host_pc_charger = (motg->chg_type == USB_SDP_CHARGER) || - (motg->chg_type == USB_CDP_CHARGER); + (motg->chg_type == USB_CDP_CHARGER) || + (get_psy_type(motg) == POWER_SUPPLY_TYPE_USB) || + (get_psy_type(motg) == POWER_SUPPLY_TYPE_USB_CDP); + msm_otg_dbg_log_event(phy, "CHARGER CONNECTED", + host_pc_charger, motg->inputs); /* !BSV, but its handling is in progress by otg sm_work */ sm_work_busy = !test_bit(B_SESS_VLD, &motg->inputs) && From af15029bda1d7c02992bdb80c23e46524a636227 Mon Sep 17 00:00:00 2001 From: Vijayavardhan Vennapusa Date: Tue, 6 Jul 2021 12:40:10 +0530 Subject: [PATCH 3/3] phy: msm: usb: Check VBUS state before accessing registers in suspend Currently driver is checking for VBUS state after accessing register in msm_otg_suspend() routine. In case of CDP, there will be race between msm_otg_suspend() and driving DP pulse. This could cause accessing register in msm_otg_suspend() while clocks are off as part of sequence to drive pulse on D+ line and later it is checking for VBUS state high to abort suspend sequence. Hence fix it by checking for VBUS state before accessing registers in msm_otg_suspend(). Change-Id: I95ad5339b21647e3971908d15f2eabe0c6311800 Signed-off-by: Vijayavardhan Vennapusa --- drivers/usb/phy/phy-msm-usb.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index 1b4cd03c0630..e925966a45b2 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -1473,17 +1473,6 @@ static int msm_otg_suspend(struct msm_otg *motg) if (motg->err_event_seen) msm_otg_reset(phy); - /* Enable line state difference wakeup fix for only device and host - * bus suspend scenarios. Otherwise PHY can not be suspended when - * a charger that pulls DP/DM high is connected. - */ - config2 = readl_relaxed(USB_GENCONFIG_2); - if (device_bus_suspend) - config2 |= GENCONFIG_2_LINESTATE_DIFF_WAKEUP_EN; - else - config2 &= ~GENCONFIG_2_LINESTATE_DIFF_WAKEUP_EN; - writel_relaxed(config2, USB_GENCONFIG_2); - /* * Abort suspend when, * 1. host mode activation in progress due to Micro-A cable insertion @@ -1501,6 +1490,17 @@ static int msm_otg_suspend(struct msm_otg *motg) return -EBUSY; } + /* Enable line state difference wakeup fix for only device and host + * bus suspend scenarios. Otherwise PHY can not be suspended when + * a charger that pulls DP/DM high is connected. + */ + config2 = readl_relaxed(USB_GENCONFIG_2); + if (device_bus_suspend) + config2 |= GENCONFIG_2_LINESTATE_DIFF_WAKEUP_EN; + else + config2 &= ~GENCONFIG_2_LINESTATE_DIFF_WAKEUP_EN; + writel_relaxed(config2, USB_GENCONFIG_2); + if (motg->caps & ALLOW_VDD_MIN_WITH_RETENTION_DISABLED) { /* put the controller in non-driving mode */ func_ctrl = ulpi_read(phy, ULPI_FUNC_CTRL);