Merge "phy: qusb: Add support for DP DM pulsing for HS PHY"

This commit is contained in:
Linux Build Service Account 2019-06-10 00:58:14 -07:00 committed by Gerrit - the friendly Code Review server
commit c9c1e20784
4 changed files with 370 additions and 5 deletions

View file

@ -66,6 +66,8 @@ Optional properties :
which is used as a vote by driver to get max performance in perf mode. 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 - 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. 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 nodes:
- Sub node for "DWC3- USB3 controller". - Sub node for "DWC3- USB3 controller".

View file

@ -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 * 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 * it under the terms of the GNU General Public License version 2 and
@ -66,6 +66,9 @@
/* AHB2PHY read/write waite value */ /* AHB2PHY read/write waite value */
#define ONE_READ_WRITE_WAIT 0x11 #define ONE_READ_WRITE_WAIT 0x11
/* DP_DM linestate float */
#define DP_DM_STATE_FLOAT 0x02
/* cpu to fix usb interrupt */ /* cpu to fix usb interrupt */
static int cpu_to_affin; static int cpu_to_affin;
module_param(cpu_to_affin, int, S_IRUGO|S_IWUSR); module_param(cpu_to_affin, int, S_IRUGO|S_IWUSR);
@ -220,6 +223,8 @@ struct dwc3_msm {
bool hc_died; bool hc_died;
bool xhci_ss_compliance_enable; bool xhci_ss_compliance_enable;
bool no_wakeup_src_in_hostmode; bool no_wakeup_src_in_hostmode;
bool check_for_float;
bool float_detected;
struct extcon_dev *extcon_vbus; struct extcon_dev *extcon_vbus;
struct extcon_dev *extcon_id; struct extcon_dev *extcon_id;
@ -2724,6 +2729,7 @@ static int dwc3_msm_vbus_notifier(struct notifier_block *nb,
if (mdwc->vbus_active == event) if (mdwc->vbus_active == event)
return NOTIFY_DONE; return NOTIFY_DONE;
mdwc->float_detected = false;
cc_state = extcon_get_cable_state_(edev, EXTCON_USB_CC); cc_state = extcon_get_cable_state_(edev, EXTCON_USB_CC);
if (cc_state < 0) if (cc_state < 0)
mdwc->typec_orientation = ORIENTATION_NONE; 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")) if (of_property_read_bool(node, "qcom,disable-dev-mode-pm"))
pm_runtime_get_noresume(mdwc->dev); 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); ret = dwc3_msm_extcon_register(mdwc);
if (ret) if (ret)
goto put_dwc3; goto put_dwc3;
@ -3820,7 +3828,8 @@ static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned mA)
int ret, psy_type; int ret, psy_type;
psy_type = get_psy_type(mdwc); 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) if (!mA)
pval.intval = -ETIMEDOUT; pval.intval = -ETIMEDOUT;
else else
@ -3906,6 +3915,7 @@ static void dwc3_otg_sm_work(struct work_struct *w)
work = 1; work = 1;
} else if (test_bit(B_SESS_VLD, &mdwc->inputs)) { } else if (test_bit(B_SESS_VLD, &mdwc->inputs)) {
dev_dbg(mdwc->dev, "b_sess_vld\n"); dev_dbg(mdwc->dev, "b_sess_vld\n");
mdwc->float_detected = false;
if (get_psy_type(mdwc) == POWER_SUPPLY_TYPE_USB_FLOAT) if (get_psy_type(mdwc) == POWER_SUPPLY_TYPE_USB_FLOAT)
queue_delayed_work(mdwc->dwc3_wq, queue_delayed_work(mdwc->dwc3_wq,
&mdwc->sdp_check, &mdwc->sdp_check,
@ -3918,6 +3928,21 @@ static void dwc3_otg_sm_work(struct work_struct *w)
pm_runtime_get_sync(mdwc->dev); pm_runtime_get_sync(mdwc->dev);
dbg_event(0xFF, "BIDLE gsync", dbg_event(0xFF, "BIDLE gsync",
atomic_read(&mdwc->dev->power.usage_count)); 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); dwc3_otg_start_peripheral(mdwc, 1);
mdwc->otg_state = OTG_STATE_B_PERIPHERAL; mdwc->otg_state = OTG_STATE_B_PERIPHERAL;
work = 1; work = 1;

View file

@ -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 * 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 * it under the terms of the GNU General Public License version 2 and
@ -29,11 +29,23 @@
#include <linux/usb/msm_hsusb.h> #include <linux/usb/msm_hsusb.h>
#include <linux/reset.h> #include <linux/reset.h>
#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_STATUS 0x38
#define QUSB2PHY_PLL_LOCK BIT(5) #define QUSB2PHY_PLL_LOCK BIT(5)
#define QUSB2PHY_PORT_QC1 0x70 #define QUSB2PHY_PORT_QC1 0x70
#define VDM_SRC_EN BIT(4) #define VDM_SRC_EN BIT(4)
#define IDP_SRC_EN BIT(3)
#define VDP_SRC_EN BIT(2) #define VDP_SRC_EN BIT(2)
#define QUSB2PHY_PORT_QC2 0x74 #define QUSB2PHY_PORT_QC2 0x74
@ -57,6 +69,7 @@
#define CORE_READY_STATUS BIT(0) #define CORE_READY_STATUS BIT(0)
#define QUSB2PHY_PORT_UTMI_CTRL1 0xC0 #define QUSB2PHY_PORT_UTMI_CTRL1 0xC0
#define SUSPEND_N BIT(5)
#define TERM_SELECT BIT(4) #define TERM_SELECT BIT(4)
#define XCVR_SELECT_FS BIT(2) #define XCVR_SELECT_FS BIT(2)
#define OP_MODE_NON_DRIVE BIT(0) #define OP_MODE_NON_DRIVE BIT(0)
@ -84,6 +97,7 @@
#define DPSE_INTR_HIGH_SEL BIT(1) #define DPSE_INTR_HIGH_SEL BIT(1)
#define DPSE_INTR_EN BIT(0) #define DPSE_INTR_EN BIT(0)
#define QUSB2PHY_PORT_INT_STATUS 0xF0
#define QUSB2PHY_PORT_UTMI_STATUS 0xF4 #define QUSB2PHY_PORT_UTMI_STATUS 0xF4
#define LINESTATE_DP BIT(0) #define LINESTATE_DP BIT(0)
#define LINESTATE_DM BIT(1) #define LINESTATE_DM BIT(1)
@ -156,6 +170,10 @@ struct qusb_phy {
struct regulator_desc dpdm_rdesc; struct regulator_desc dpdm_rdesc;
struct regulator_dev *dpdm_rdev; struct regulator_dev *dpdm_rdev;
bool dpdm_pulsing_enabled;
struct power_supply *dpdm_psy;
struct power_supply_desc dpdm_psy_desc;
/* emulation targets specific */ /* emulation targets specific */
void __iomem *emu_phy_base; void __iomem *emu_phy_base;
bool emulation; bool emulation;
@ -167,6 +185,11 @@ struct qusb_phy {
int emu_dcm_reset_seq_len; int emu_dcm_reset_seq_len;
bool put_into_high_z_state; bool put_into_high_z_state;
struct mutex phy_lock; 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) static void qusb_phy_enable_clocks(struct qusb_phy *qphy, bool on)
@ -331,10 +354,13 @@ err_vdd:
return ret; return ret;
} }
#define PHY_PULSE_TIME_USEC 250
static int qusb_phy_update_dpdm(struct usb_phy *phy, int value) static int qusb_phy_update_dpdm(struct usb_phy *phy, int value)
{ {
struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy); struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy);
int ret = 0; int ret = 0;
unsigned long flags;
u32 reg;
dev_dbg(phy->dev, "%s value:%d rm_pulldown:%d\n", dev_dbg(phy->dev, "%s value:%d rm_pulldown:%d\n",
__func__, value, qphy->rm_pulldown); __func__, value, qphy->rm_pulldown);
@ -393,6 +419,21 @@ static int qusb_phy_update_dpdm(struct usb_phy *phy, int value)
qusb_phy_enable_clocks(qphy, false); 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); mutex_unlock(&qphy->phy_lock);
break; break;
@ -401,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"); dev_dbg(phy->dev, "POWER_SUPPLY_DP_DM_DPR_DMR\n");
mutex_lock(&qphy->phy_lock); mutex_lock(&qphy->phy_lock);
if (qphy->rm_pulldown) { 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->cable_connected) {
if (qphy->tcsr_clamp_dig_n) if (qphy->tcsr_clamp_dig_n)
writel_relaxed(0x0, writel_relaxed(0x0,
@ -417,15 +474,172 @@ static int qusb_phy_update_dpdm(struct usb_phy *phy, int value)
mutex_unlock(&qphy->phy_lock); mutex_unlock(&qphy->phy_lock);
break; 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: default:
ret = -EINVAL; ret = -EINVAL;
dev_err(phy->dev, "Invalid power supply property(%d)\n", value); dev_err(phy->dev, "Invalid power supply property(%d)\n", value);
break; break;
} }
clk_error:
return ret; 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) static void qusb_phy_get_tune2_param(struct qusb_phy *qphy)
{ {
u8 num_of_bits; u8 num_of_bits;
@ -692,6 +906,83 @@ static void qusb_phy_shutdown(struct usb_phy *phy)
qusb_phy_enable_clocks(qphy, false); 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. * Performs QUSB2 PHY suspend/resume functionality.
* *
@ -902,6 +1193,7 @@ static int qusb_phy_probe(struct platform_device *pdev)
int ret = 0, size = 0; int ret = 0, size = 0;
const char *phy_type; const char *phy_type;
bool hold_phy_reset; bool hold_phy_reset;
struct power_supply_config dpdm_cfg = {};
qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL); qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
if (!qphy) if (!qphy)
@ -1141,6 +1433,7 @@ static int qusb_phy_probe(struct platform_device *pdev)
} }
mutex_init(&qphy->phy_lock); mutex_init(&qphy->phy_lock);
spin_lock_init(&qphy->pulse_lock);
platform_set_drvdata(pdev, qphy); platform_set_drvdata(pdev, qphy);
qphy->phy.label = "msm-qusb-phy"; qphy->phy.label = "msm-qusb-phy";
@ -1150,6 +1443,7 @@ static int qusb_phy_probe(struct platform_device *pdev)
qphy->phy.type = USB_PHY_TYPE_USB2; qphy->phy.type = USB_PHY_TYPE_USB2;
qphy->phy.notify_connect = qusb_phy_notify_connect; qphy->phy.notify_connect = qusb_phy_notify_connect;
qphy->phy.notify_disconnect = qusb_phy_notify_disconnect; 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 * On some platforms multiple QUSB PHYs are available. If QUSB PHY is
@ -1162,25 +1456,57 @@ static int qusb_phy_probe(struct platform_device *pdev)
dev_err(dev, "%s:phy_reset assert failed\n", __func__); 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); ret = usb_add_phy_dev(&qphy->phy);
if (ret) if (ret)
return ret; goto unregister_psy;
ret = qusb_phy_regulator_init(qphy); ret = qusb_phy_regulator_init(qphy);
if (ret) if (ret)
usb_remove_phy(&qphy->phy); goto remove_phy;
/* de-assert clamp dig n to reduce leakage on 1p8 upon boot up */ /* de-assert clamp dig n to reduce leakage on 1p8 upon boot up */
if (qphy->tcsr_clamp_dig_n) if (qphy->tcsr_clamp_dig_n)
writel_relaxed(0x0, qphy->tcsr_clamp_dig_n); writel_relaxed(0x0, qphy->tcsr_clamp_dig_n);
return ret; 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) static int qusb_phy_remove(struct platform_device *pdev)
{ {
struct qusb_phy *qphy = platform_get_drvdata(pdev); struct qusb_phy *qphy = platform_get_drvdata(pdev);
if (qphy->dpdm_psy)
power_supply_unregister(qphy->dpdm_psy);
usb_remove_phy(&qphy->phy); usb_remove_phy(&qphy->phy);
if (qphy->clocks_enabled) { if (qphy->clocks_enabled) {

View file

@ -126,6 +126,9 @@ struct usb_phy {
/* reset the PHY clocks */ /* reset the PHY clocks */
int (*reset)(struct usb_phy *x); 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; 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 */ /* for usb host and peripheral controller drivers */
#if IS_ENABLED(CONFIG_USB_PHY) #if IS_ENABLED(CONFIG_USB_PHY)
extern struct usb_phy *usb_get_phy(enum usb_phy_type type); extern struct usb_phy *usb_get_phy(enum usb_phy_type type);