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 <sallenki@codeaurora.org>
This commit is contained in:
Sriharsha Allenki 2019-05-23 19:29:33 +05:30 committed by Gerrit - the friendly Code Review server
parent 669f503685
commit 0e71405de3

View file

@ -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) {