From 669f50368505b3a67fb81a5ffec4914c5baa8b7f Mon Sep 17 00:00:00 2001 From: Sriharsha Allenki Date: Tue, 14 May 2019 18:28:23 +0530 Subject: [PATCH 1/2] usb: dwc3: Support float charger detection Chargers on some platforms do not support FLOAT charger detection. On these platforms, the float charger can be detected depending on the state of the DP and DM lines as detected by the PHY. So, on these platforms check for the DP DM line state and if found floating, do not start the peripheral mode and notify PMIC -ETIMEDOUT implying the connected charger is of type float. Change-Id: I6bf54b0d5c143a849ce9ea7bc515d62204ed2f33 Signed-off-by: Sriharsha Allenki --- .../devicetree/bindings/usb/msm-ssusb.txt | 2 + drivers/usb/dwc3/dwc3-msm.c | 29 +++++- drivers/usb/phy/phy-msm-qusb.c | 94 ++++++++++++++++++- include/linux/usb/phy.h | 12 +++ 4 files changed, 134 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/msm-ssusb.txt b/Documentation/devicetree/bindings/usb/msm-ssusb.txt index 54792335e67e..a8229051cbd8 100644 --- a/Documentation/devicetree/bindings/usb/msm-ssusb.txt +++ b/Documentation/devicetree/bindings/usb/msm-ssusb.txt @@ -66,6 +66,8 @@ Optional properties : which is used as a vote by driver to get max performance in perf mode. - qcom,no-wakeup-src-in-hostmode: If present then driver doesn't use wakeup_source APIs in host mode. This allows PM suspend to happen irrespective of runtimePM state of host. +- qcom,check-for-float: If present, the driver will always check for possible + float connection irrespective of the charger type. Sub nodes: - Sub node for "DWC3- USB3 controller". diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index 1c7d7af6787a..a2a692dc9195 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -66,6 +66,9 @@ /* AHB2PHY read/write waite value */ #define ONE_READ_WRITE_WAIT 0x11 +/* DP_DM linestate float */ +#define DP_DM_STATE_FLOAT 0x02 + /* cpu to fix usb interrupt */ static int cpu_to_affin; module_param(cpu_to_affin, int, S_IRUGO|S_IWUSR); @@ -220,6 +223,8 @@ struct dwc3_msm { bool hc_died; bool xhci_ss_compliance_enable; bool no_wakeup_src_in_hostmode; + bool check_for_float; + bool float_detected; struct extcon_dev *extcon_vbus; struct extcon_dev *extcon_id; @@ -2724,6 +2729,7 @@ static int dwc3_msm_vbus_notifier(struct notifier_block *nb, if (mdwc->vbus_active == event) return NOTIFY_DONE; + mdwc->float_detected = false; cc_state = extcon_get_cable_state_(edev, EXTCON_USB_CC); if (cc_state < 0) mdwc->typec_orientation = ORIENTATION_NONE; @@ -3272,6 +3278,8 @@ static int dwc3_msm_probe(struct platform_device *pdev) if (of_property_read_bool(node, "qcom,disable-dev-mode-pm")) pm_runtime_get_noresume(mdwc->dev); + mdwc->check_for_float = of_property_read_bool(node, + "qcom,check-for-float"); ret = dwc3_msm_extcon_register(mdwc); if (ret) goto put_dwc3; @@ -3820,7 +3828,8 @@ static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned mA) int ret, psy_type; psy_type = get_psy_type(mdwc); - if (psy_type == POWER_SUPPLY_TYPE_USB_FLOAT) { + if (psy_type == POWER_SUPPLY_TYPE_USB_FLOAT + || (mdwc->check_for_float && mdwc->float_detected)) { if (!mA) pval.intval = -ETIMEDOUT; else @@ -3906,6 +3915,7 @@ static void dwc3_otg_sm_work(struct work_struct *w) work = 1; } else if (test_bit(B_SESS_VLD, &mdwc->inputs)) { dev_dbg(mdwc->dev, "b_sess_vld\n"); + mdwc->float_detected = false; if (get_psy_type(mdwc) == POWER_SUPPLY_TYPE_USB_FLOAT) queue_delayed_work(mdwc->dwc3_wq, &mdwc->sdp_check, @@ -3918,6 +3928,21 @@ static void dwc3_otg_sm_work(struct work_struct *w) pm_runtime_get_sync(mdwc->dev); dbg_event(0xFF, "BIDLE gsync", atomic_read(&mdwc->dev->power.usage_count)); + if (mdwc->check_for_float) { + /* + * If DP_DM are found to be floating, do not + * start the peripheral mode. + */ + if (usb_phy_dpdm_with_idp_src(mdwc->hs_phy) == + DP_DM_STATE_FLOAT) { + mdwc->float_detected = true; + dwc3_msm_gadget_vbus_draw(mdwc, 0); + pm_runtime_put_sync(mdwc->dev); + dbg_event(0xFF, "FLT sync", atomic_read( + &mdwc->dev->power.usage_count)); + break; + } + } dwc3_otg_start_peripheral(mdwc, 1); mdwc->otg_state = OTG_STATE_B_PERIPHERAL; work = 1; diff --git a/drivers/usb/phy/phy-msm-qusb.c b/drivers/usb/phy/phy-msm-qusb.c index be63c6c0a86a..f97d550e0381 100644 --- a/drivers/usb/phy/phy-msm-qusb.c +++ b/drivers/usb/phy/phy-msm-qusb.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2017,2019 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -29,11 +29,23 @@ #include #include +#define QUSB2PHY_PLL_PWR_CTL 0x18 +#define REF_BUF_EN BIT(0) +#define REXT_EN BIT(1) +#define PLL_BYPASSNL BIT(2) +#define REXT_TRIM_0 BIT(4) + +#define QUSB2PHY_PLL_AUTOPGM_CTL1 0x1C +#define PLL_RESET_N_CNT_5 0x5 +#define PLL_RESET_N BIT(4) +#define PLL_AUTOPGM_EN BIT(7) + #define QUSB2PHY_PLL_STATUS 0x38 #define QUSB2PHY_PLL_LOCK BIT(5) #define QUSB2PHY_PORT_QC1 0x70 #define VDM_SRC_EN BIT(4) +#define IDP_SRC_EN BIT(3) #define VDP_SRC_EN BIT(2) #define QUSB2PHY_PORT_QC2 0x74 @@ -57,6 +69,7 @@ #define CORE_READY_STATUS BIT(0) #define QUSB2PHY_PORT_UTMI_CTRL1 0xC0 +#define SUSPEND_N BIT(5) #define TERM_SELECT BIT(4) #define XCVR_SELECT_FS BIT(2) #define OP_MODE_NON_DRIVE BIT(0) @@ -84,6 +97,7 @@ #define DPSE_INTR_HIGH_SEL BIT(1) #define DPSE_INTR_EN BIT(0) +#define QUSB2PHY_PORT_INT_STATUS 0xF0 #define QUSB2PHY_PORT_UTMI_STATUS 0xF4 #define LINESTATE_DP BIT(0) #define LINESTATE_DM BIT(1) @@ -692,6 +706,83 @@ static void qusb_phy_shutdown(struct usb_phy *phy) qusb_phy_enable_clocks(qphy, false); } + +/** + * Returns DP/DM linestate with Idp_src enabled to detect if lines are floating + * + * @uphy - usb phy pointer. + * + */ +static int qusb_phy_linestate_with_idp_src(struct usb_phy *phy) +{ + struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy); + u8 int_status, ret; + + /* Disable/powerdown the PHY */ + writel_relaxed(CLAMP_N_EN | FREEZIO_N | POWER_DOWN, + qphy->base + QUSB2PHY_PORT_POWERDOWN); + + /* Put PHY in non-driving mode */ + writel_relaxed(TERM_SELECT | XCVR_SELECT_FS | OP_MODE_NON_DRIVE | + SUSPEND_N, qphy->base + QUSB2PHY_PORT_UTMI_CTRL1); + + /* Switch PHY to utmi register mode */ + writel_relaxed(UTMI_ULPI_SEL | UTMI_TEST_MUX_SEL, + qphy->base + QUSB2PHY_PORT_UTMI_CTRL2); + + writel_relaxed(PLL_RESET_N_CNT_5, + qphy->base + QUSB2PHY_PLL_AUTOPGM_CTL1); + + /* Enable PHY */ + writel_relaxed(CLAMP_N_EN | FREEZIO_N, + qphy->base + QUSB2PHY_PORT_POWERDOWN); + + writel_relaxed(REF_BUF_EN | REXT_EN | PLL_BYPASSNL | REXT_TRIM_0, + qphy->base + QUSB2PHY_PLL_PWR_CTL); + + usleep_range(5, 1000); + + writel_relaxed(PLL_RESET_N | PLL_RESET_N_CNT_5, + qphy->base + QUSB2PHY_PLL_AUTOPGM_CTL1); + usleep_range(50, 1000); + + writel_relaxed(0x00, qphy->base + QUSB2PHY_PORT_QC1); + writel_relaxed(0x00, qphy->base + QUSB2PHY_PORT_QC2); + + /* Enable all chg_det events from PHY */ + writel_relaxed(0x1F, qphy->base + QUSB2PHY_PORT_INTR_CTRL); + /* Enable Idp_src */ + writel_relaxed(IDP_SRC_EN, qphy->base + QUSB2PHY_PORT_QC1); + + usleep_range(1000, 2000); + int_status = readl_relaxed(qphy->base + QUSB2PHY_PORT_INT_STATUS); + + /* Exit chg_det mode, set PHY regs to default values */ + writel_relaxed(CLAMP_N_EN | FREEZIO_N | POWER_DOWN, + qphy->base + QUSB2PHY_PORT_POWERDOWN); /* 23 */ + + writel_relaxed(PLL_AUTOPGM_EN | PLL_RESET_N | PLL_RESET_N_CNT_5, + qphy->base + QUSB2PHY_PLL_AUTOPGM_CTL1); + + writel_relaxed(UTMI_ULPI_SEL, qphy->base + QUSB2PHY_PORT_UTMI_CTRL2); + + writel_relaxed(TERM_SELECT, qphy->base + QUSB2PHY_PORT_UTMI_CTRL1); + + writel_relaxed(CLAMP_N_EN | FREEZIO_N, + qphy->base + QUSB2PHY_PORT_POWERDOWN); + + int_status = int_status & 0x5; + + /* + * int_status's Bit(0) is DP and Bit(2) is DM. + * Caller expects bit(1) as DP and bit(0) DM i.e. usual linestate format + */ + ret = (int_status >> 2) | ((int_status & 0x1) << 1); + pr_debug("%s: int_status:%x, dpdm:%x\n", __func__, int_status, ret); + + return ret; +} + /** * Performs QUSB2 PHY suspend/resume functionality. * @@ -1150,6 +1241,7 @@ static int qusb_phy_probe(struct platform_device *pdev) qphy->phy.type = USB_PHY_TYPE_USB2; qphy->phy.notify_connect = qusb_phy_notify_connect; qphy->phy.notify_disconnect = qusb_phy_notify_disconnect; + qphy->phy.dpdm_with_idp_src = qusb_phy_linestate_with_idp_src; /* * On some platforms multiple QUSB PHYs are available. If QUSB PHY is diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h index d25125402fda..f066d65ac3dd 100644 --- a/include/linux/usb/phy.h +++ b/include/linux/usb/phy.h @@ -126,6 +126,9 @@ struct usb_phy { /* reset the PHY clocks */ int (*reset)(struct usb_phy *x); + + /* return linestate with Idp_src (used for DCD with USB2 PHY) */ + int (*dpdm_with_idp_src)(struct usb_phy *x); }; /** @@ -209,6 +212,15 @@ usb_phy_reset(struct usb_phy *x) return 0; } +static inline int +usb_phy_dpdm_with_idp_src(struct usb_phy *x) +{ + if (x && x->dpdm_with_idp_src) + return x->dpdm_with_idp_src(x); + + return 0; +} + /* for usb host and peripheral controller drivers */ #if IS_ENABLED(CONFIG_USB_PHY) extern struct usb_phy *usb_get_phy(enum usb_phy_type type); From 0e71405de372e24638e0d6bfd46e112d59e6ad4a Mon Sep 17 00:00:00 2001 From: Sriharsha Allenki Date: Thu, 23 May 2019 19:29:33 +0530 Subject: [PATCH 2/2] phy: qusb: Add support for DP DM pulsing for HS PHY PMIC on certain targets does not support DP DM pulsing. This is needed to support detection of HVDCP chargers. Add support for DP DM pulsing in the HS PHY driver that PMIC can rely on, to dectect these chargers. Change-Id: Ib889f57c88658e9d232872ca17270fe8cac2648d Signed-off-by: Sriharsha Allenki --- drivers/usb/phy/phy-msm-qusb.c | 238 ++++++++++++++++++++++++++++++++- 1 file changed, 236 insertions(+), 2 deletions(-) diff --git a/drivers/usb/phy/phy-msm-qusb.c b/drivers/usb/phy/phy-msm-qusb.c index f97d550e0381..ae72ec6b3d19 100644 --- a/drivers/usb/phy/phy-msm-qusb.c +++ b/drivers/usb/phy/phy-msm-qusb.c @@ -170,6 +170,10 @@ struct qusb_phy { struct regulator_desc dpdm_rdesc; struct regulator_dev *dpdm_rdev; + bool dpdm_pulsing_enabled; + struct power_supply *dpdm_psy; + struct power_supply_desc dpdm_psy_desc; + /* emulation targets specific */ void __iomem *emu_phy_base; bool emulation; @@ -181,6 +185,11 @@ struct qusb_phy { int emu_dcm_reset_seq_len; bool put_into_high_z_state; struct mutex phy_lock; + spinlock_t pulse_lock; +}; + +static enum power_supply_property dpdm_props[] = { + POWER_SUPPLY_PROP_DP_DM, }; static void qusb_phy_enable_clocks(struct qusb_phy *qphy, bool on) @@ -345,10 +354,13 @@ err_vdd: return ret; } +#define PHY_PULSE_TIME_USEC 250 static int qusb_phy_update_dpdm(struct usb_phy *phy, int value) { struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy); int ret = 0; + unsigned long flags; + u32 reg; dev_dbg(phy->dev, "%s value:%d rm_pulldown:%d\n", __func__, value, qphy->rm_pulldown); @@ -407,6 +419,21 @@ static int qusb_phy_update_dpdm(struct usb_phy *phy, int value) qusb_phy_enable_clocks(qphy, false); } } + + /* Clear QC1 and QC2 registers when rm_pulldown = 1 */ + if (qphy->dpdm_pulsing_enabled && qphy->rm_pulldown) { + dev_dbg(phy->dev, "clearing qc1 and qc2 registers.\n"); + ret = clk_prepare_enable(qphy->cfg_ahb_clk); + if (ret) + goto clk_error; + + /* Clear qc1 and qc2 registers */ + writel_relaxed(0x00, qphy->base + QUSB2PHY_PORT_QC1); + writel_relaxed(0x00, qphy->base + QUSB2PHY_PORT_QC2); + /* to make sure above write goes through */ + mb(); + clk_disable_unprepare(qphy->cfg_ahb_clk); + } mutex_unlock(&qphy->phy_lock); break; @@ -415,6 +442,22 @@ static int qusb_phy_update_dpdm(struct usb_phy *phy, int value) dev_dbg(phy->dev, "POWER_SUPPLY_DP_DM_DPR_DMR\n"); mutex_lock(&qphy->phy_lock); if (qphy->rm_pulldown) { + dev_dbg(phy->dev, "clearing qc1 and qc2 registers.\n"); + if (qphy->dpdm_pulsing_enabled) { + ret = clk_prepare_enable(qphy->cfg_ahb_clk); + if (ret) + goto clk_error; + + /* Clear qc1 and qc2 registers */ + writel_relaxed(0x00, + qphy->base + QUSB2PHY_PORT_QC1); + writel_relaxed(0x00, + qphy->base + QUSB2PHY_PORT_QC2); + /* to make sure above write goes through */ + mb(); + clk_disable_unprepare(qphy->cfg_ahb_clk); + } + if (!qphy->cable_connected) { if (qphy->tcsr_clamp_dig_n) writel_relaxed(0x0, @@ -431,15 +474,172 @@ static int qusb_phy_update_dpdm(struct usb_phy *phy, int value) mutex_unlock(&qphy->phy_lock); break; + case POWER_SUPPLY_DP_DM_DP0P6_DMF: + if (!qphy->dpdm_pulsing_enabled) + break; + + dev_dbg(phy->dev, "POWER_SUPPLY_DP_DM_DP0P6_DMF\n"); + ret = clk_prepare_enable(qphy->cfg_ahb_clk); + if (ret) + goto clk_error; + + /* Set DP to 0.6v and DM to High Z state */ + writel_relaxed(VDP_SRC_EN, qphy->base + QUSB2PHY_PORT_QC1); + /* complete above write */ + mb(); + clk_disable_unprepare(qphy->cfg_ahb_clk); + break; + + case POWER_SUPPLY_DP_DM_DP0P6_DM3P3: + if (!qphy->dpdm_pulsing_enabled) + break; + + dev_dbg(phy->dev, "POWER_SUPPLY_DP_DM_DP0PHVDCP_36_DM3P3\n"); + ret = clk_prepare_enable(qphy->cfg_ahb_clk); + if (ret) + goto clk_error; + + /* Set DP to 0.6v */ + writel_relaxed(VDP_SRC_EN, qphy->base + QUSB2PHY_PORT_QC1); + /* Set DM to 3.075v */ + writel_relaxed(RPUM_LOW_EN | RDM_UP_EN, + qphy->base + QUSB2PHY_PORT_QC2); + /* complete above write */ + mb(); + clk_disable_unprepare(qphy->cfg_ahb_clk); + break; + + case POWER_SUPPLY_DP_DM_DP_PULSE: + if (!qphy->dpdm_pulsing_enabled) + break; + + dev_dbg(phy->dev, "POWER_SUPPLY_DP_DM_DP_PULSE\n"); + ret = clk_prepare_enable(qphy->cfg_ahb_clk); + if (ret) + goto clk_error; + + spin_lock_irqsave(&qphy->pulse_lock, flags); + /*Set DP to 3.075v, sleep for .25 ms */ + reg = readl_relaxed(qphy->base + QUSB2PHY_PORT_QC2); + reg |= (RDP_UP_EN | RPUP_LOW_EN); + writel_relaxed(reg, qphy->base + QUSB2PHY_PORT_QC2); + + /* complete above write */ + mb(); + + /* + * It is recommended to wait here to get voltage change on + * DP/DM line. + */ + udelay(PHY_PULSE_TIME_USEC); + + /* Set DP to 0.6v, sleep 2-3ms */ + reg = readl_relaxed(qphy->base + QUSB2PHY_PORT_QC1); + reg |= VDP_SRC_EN; + writel_relaxed(reg, qphy->base + QUSB2PHY_PORT_QC1); + + reg = readl_relaxed(qphy->base + QUSB2PHY_PORT_QC2); + reg &= ~(RDP_UP_EN | RPUP_LOW_EN); + writel_relaxed(reg, qphy->base + QUSB2PHY_PORT_QC2); + /* complete above write */ + mb(); + spin_unlock_irqrestore(&qphy->pulse_lock, flags); + /* + * It is recommended to wait here to get voltage change on + * DP/DM line. + */ + usleep_range(2000, 3000); + clk_disable_unprepare(qphy->cfg_ahb_clk); + break; + + case POWER_SUPPLY_DP_DM_DM_PULSE: + if (!qphy->dpdm_pulsing_enabled) + break; + + dev_dbg(phy->dev, "POWER_SUPPLY_DP_DM_DM_PULSE\n"); + ret = clk_prepare_enable(qphy->cfg_ahb_clk); + if (ret) + goto clk_error; + + spin_lock_irqsave(&qphy->pulse_lock, flags); + /* Set DM to 0.6v, sleep .25 ms */ + reg = readl_relaxed(qphy->base + QUSB2PHY_PORT_QC1); + reg |= VDM_SRC_EN; + writel_relaxed(reg, qphy->base + QUSB2PHY_PORT_QC1); + + reg = readl_relaxed(qphy->base + QUSB2PHY_PORT_QC2); + reg &= ~(RDM_UP_EN | RPUM_LOW_EN); + writel_relaxed(reg, qphy->base + QUSB2PHY_PORT_QC2); + + /* complete above write */ + mb(); + + /* + * It is recommended to wait here to get voltage change on + * DP/DM line. + */ + udelay(PHY_PULSE_TIME_USEC); + + /* DM to 3.075v, sleep 2-3ms */ + reg = readl_relaxed(qphy->base + QUSB2PHY_PORT_QC2); + reg |= (RPUM_LOW_EN | RDM_UP_EN); + writel_relaxed(reg, qphy->base + QUSB2PHY_PORT_QC2); + + reg = readl_relaxed(qphy->base + QUSB2PHY_PORT_QC1); + reg &= ~VDM_SRC_EN; + writel_relaxed(reg, qphy->base + QUSB2PHY_PORT_QC1); + + /* complete above write */ + mb(); + spin_unlock_irqrestore(&qphy->pulse_lock, flags); + + /* + * It is recommended to wait here to get voltage change on + * DP/DM line. + */ + usleep_range(2000, 3000); + clk_disable_unprepare(qphy->cfg_ahb_clk); + break; default: ret = -EINVAL; dev_err(phy->dev, "Invalid power supply property(%d)\n", value); break; } +clk_error: return ret; } +static int qusb_phy_get_property_usb(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + return -EINVAL; +} + +static int qusb_phy_set_property_usb(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct qusb_phy *qphy = power_supply_get_drvdata(psy); + int ret = 0; + + switch (prop) { + case POWER_SUPPLY_PROP_DP_DM: + ret = qusb_phy_update_dpdm(&qphy->phy, val->intval); + if (ret) { + dev_dbg(qphy->phy.dev, "error in dpdm update: %d\n", + ret); + return ret; + } + break; + default: + return -EINVAL; + } + + return 0; +} + static void qusb_phy_get_tune2_param(struct qusb_phy *qphy) { u8 num_of_bits; @@ -993,6 +1193,7 @@ static int qusb_phy_probe(struct platform_device *pdev) int ret = 0, size = 0; const char *phy_type; bool hold_phy_reset; + struct power_supply_config dpdm_cfg = {}; qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL); if (!qphy) @@ -1232,6 +1433,7 @@ static int qusb_phy_probe(struct platform_device *pdev) } mutex_init(&qphy->phy_lock); + spin_lock_init(&qphy->pulse_lock); platform_set_drvdata(pdev, qphy); qphy->phy.label = "msm-qusb-phy"; @@ -1254,25 +1456,57 @@ static int qusb_phy_probe(struct platform_device *pdev) dev_err(dev, "%s:phy_reset assert failed\n", __func__); } + qphy->dpdm_pulsing_enabled = of_property_read_bool(dev->of_node, + "qcom,enable-dpdm-pulsing"); + + if (qphy->dpdm_pulsing_enabled) { + qphy->dpdm_psy_desc.name = "dpdm"; + qphy->dpdm_psy_desc.type = POWER_SUPPLY_TYPE_USB; + qphy->dpdm_psy_desc.properties = dpdm_props; + qphy->dpdm_psy_desc.num_properties = ARRAY_SIZE(dpdm_props); + qphy->dpdm_psy_desc.set_property = qusb_phy_set_property_usb; + qphy->dpdm_psy_desc.get_property = qusb_phy_get_property_usb; + + dpdm_cfg.drv_data = qphy; + dpdm_cfg.of_node = dev->of_node; + qphy->dpdm_psy = power_supply_register(&pdev->dev, + &qphy->dpdm_psy_desc, &dpdm_cfg); + if (IS_ERR(qphy->dpdm_psy)) { + dev_err(&pdev->dev, "%s:dpdm power_supply_register failed\n", + __func__); + return PTR_ERR(qphy->dpdm_psy); + } + } + ret = usb_add_phy_dev(&qphy->phy); if (ret) - return ret; + goto unregister_psy; ret = qusb_phy_regulator_init(qphy); if (ret) - usb_remove_phy(&qphy->phy); + goto remove_phy; /* de-assert clamp dig n to reduce leakage on 1p8 upon boot up */ if (qphy->tcsr_clamp_dig_n) writel_relaxed(0x0, qphy->tcsr_clamp_dig_n); return ret; + +remove_phy: + usb_remove_phy(&qphy->phy); +unregister_psy: + if (qphy->dpdm_psy) + power_supply_unregister(qphy->dpdm_psy); + + return ret; } static int qusb_phy_remove(struct platform_device *pdev) { struct qusb_phy *qphy = platform_get_drvdata(pdev); + if (qphy->dpdm_psy) + power_supply_unregister(qphy->dpdm_psy); usb_remove_phy(&qphy->phy); if (qphy->clocks_enabled) {