Merge "usb: dwc3: Drive a pulse on DP on CDP detection"

This commit is contained in:
qctecmdr 2020-09-21 06:03:02 -07:00 • committed by Gerrit - the friendly Code Review server
commit cbd5b7fafc
3 changed files with 126 additions and 3 deletions

View file

@ -11,6 +11,7 @@
#include <linux/dmapool.h>
#include <linux/pm_runtime.h>
#include <linux/ratelimit.h>
#include <linux/iio/consumer.h>
#include <linux/interrupt.h>
#include <linux/iommu.h>
#include <linux/ioport.h>
@ -33,6 +34,7 @@
#include <linux/pm_wakeup.h>
#include <linux/pm_qos.h>
#include <linux/power_supply.h>
#include <linux/qti_power_supply.h>
#include <linux/cdev.h>
#include <linux/completion.h>
#include <linux/interconnect.h>
@ -441,6 +443,7 @@ struct dwc3_msm {
enum bus_vote override_bus_vote;
struct icc_path *icc_paths[3];
struct power_supply *usb_psy;
struct iio_channel *chg_type;
struct work_struct vbus_draw_work;
bool in_host_mode;
bool in_device_mode;
@ -3396,6 +3399,7 @@ static irqreturn_t msm_dwc3_pwr_irq(int irq, void *data)
}
static void dwc3_otg_sm_work(struct work_struct *w);
static int get_chg_type(struct dwc3_msm *mdwc);
static int dwc3_msm_get_clk_gdsc(struct dwc3_msm *mdwc)
{
@ -3532,6 +3536,7 @@ static int dwc3_msm_id_notifier(struct notifier_block *nb,
return NOTIFY_DONE;
}
#define DP_PULSE_WIDTH_MSEC 200
static int dwc3_msm_vbus_notifier(struct notifier_block *nb,
unsigned long event, void *ptr)
@ -3566,6 +3571,16 @@ static int dwc3_msm_vbus_notifier(struct notifier_block *nb,
mdwc->vbus_active = event;
}
/*
* Drive a pulse on DP to ensure proper CDP detection
* and only when the vbus connect event is a valid one.
*/
if (get_chg_type(mdwc) == POWER_SUPPLY_TYPE_USB_CDP &&
mdwc->vbus_active && !mdwc->check_eud_state) {
dev_dbg(mdwc->dev, "Connected to CDP, pull DP up\n");
usb_phy_drive_dp_pulse(mdwc->hs_phy, DP_PULSE_WIDTH_MSEC);
}
mdwc->ext_idx = enb->idx;
if (dwc->dr_mode == USB_DR_MODE_OTG && !mdwc->in_restart)
queue_work(mdwc->dwc3_wq, &mdwc->resume_work);
@ -4839,10 +4854,32 @@ static int dwc3_otg_start_peripheral(struct dwc3_msm *mdwc, int on)
return 0;
}
static int get_chg_type(struct dwc3_msm *mdwc)
{
int ret, value;
if (!mdwc->chg_type) {
mdwc->chg_type = devm_iio_channel_get(mdwc->dev, "chg_type");
if (IS_ERR_OR_NULL(mdwc->chg_type)) {
dev_dbg(mdwc->dev, "unable to get iio channel\n");
mdwc->chg_type = NULL;
return -ENODEV;
}
}
ret = iio_read_channel_processed(mdwc->chg_type, &value);
if (ret < 0) {
dev_err(mdwc->dev, "failed to get charger type\n");
return ret;
}
return value;
}
static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned int mA)
{
union power_supply_propval pval = {0};
int ret;
int ret, chg_type;
if (!mdwc->usb_psy && of_property_read_bool(mdwc->dev->of_node,
"qcom,usb-charger")) {
@ -4856,13 +4893,20 @@ static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned int mA)
if (!mdwc->usb_psy)
return 0;
if (mdwc->max_power == mA)
/*
* Set the valid current only when the device
* is connected to a Standard Downstream Port.
*/
chg_type = get_chg_type(mdwc);
if (mdwc->max_power == mA || (chg_type != -ENODEV
&& chg_type != POWER_SUPPLY_TYPE_USB))
return 0;
dev_info(mdwc->dev, "Avail curr from USB = %u\n", mA);
/* Set max current limit in uA */
pval.intval = 1000 * mA;
ret = power_supply_set_property(mdwc->usb_psy,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &pval);
if (ret) {
@ -4874,7 +4918,6 @@ static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned int mA)
return 0;
}
/**
* dwc3_otg_sm_work - workqueue function.
*

View file

@ -461,6 +461,25 @@ static void qusb_phy_write_seq(void __iomem *base, u32 *seq, int cnt,
}
}
static void msm_usb_write_readback(void __iomem *base, u32 offset,
const u32 mask, u32 val)
{
u32 write_val, tmp = readl_relaxed(base + offset);
tmp &= ~mask; /* retain other bits */
write_val = tmp | val;
writel_relaxed(write_val, base + offset);
/* Read back to see if val was written */
tmp = readl_relaxed(base + offset);
tmp &= mask; /* clear other bits */
if (tmp != val)
pr_err("%s: write: %x to QSCRATCH: %x FAILED\n",
__func__, val, offset);
}
static void qusb_phy_reset(struct qusb_phy *qphy)
{
int ret;
@ -815,6 +834,59 @@ static int qusb_phy_notify_disconnect(struct usb_phy *phy,
return 0;
}
void usb_phy_drive_dp_pulse(void *phy, unsigned int interval_ms)
{
struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy);
int ret;
ret = qusb_phy_enable_power(qphy);
if (ret < 0) {
dev_dbg(qphy->phy.dev,
"dpdm regulator enable failed:%d\n", ret);
return;
}
qusb_phy_enable_clocks(qphy, true);
msm_usb_write_readback(qphy->base, qphy->phy_reg[PWR_CTRL1],
PWR_CTRL1_POWR_DOWN, 0x00);
msm_usb_write_readback(qphy->base, qphy->phy_reg[DEBUG_CTRL4],
FORCED_UTMI_DPPULLDOWN, 0x00);
msm_usb_write_readback(qphy->base, qphy->phy_reg[DEBUG_CTRL4],
FORCED_UTMI_DMPULLDOWN,
FORCED_UTMI_DMPULLDOWN);
msm_usb_write_readback(qphy->base, qphy->phy_reg[DEBUG_CTRL3],
0xd1, 0xd1);
msm_usb_write_readback(qphy->base, qphy->phy_reg[PWR_CTRL1],
CLAMP_N_EN, CLAMP_N_EN);
msm_usb_write_readback(qphy->base, qphy->phy_reg[INTR_CTRL],
DPSE_INTR_HIGH_SEL, 0x00);
msm_usb_write_readback(qphy->base, qphy->phy_reg[INTR_CTRL],
DPSE_INTR_EN, DPSE_INTR_EN);
msleep(interval_ms);
msm_usb_write_readback(qphy->base, qphy->phy_reg[INTR_CTRL],
DPSE_INTR_HIGH_SEL |
DPSE_INTR_EN, 0x00);
msm_usb_write_readback(qphy->base, qphy->phy_reg[DEBUG_CTRL3],
0xd1, 0x00);
msm_usb_write_readback(qphy->base, qphy->phy_reg[DEBUG_CTRL4],
FORCED_UTMI_DPPULLDOWN |
FORCED_UTMI_DMPULLDOWN, 0x00);
msm_usb_write_readback(qphy->base, qphy->phy_reg[PWR_CTRL1],
PWR_CTRL1_POWR_DOWN |
CLAMP_N_EN, 0x00);
msleep(20);
qusb_phy_enable_clocks(qphy, false);
ret = qusb_phy_disable_power(qphy);
if (ret < 0) {
dev_dbg(qphy->phy.dev,
"dpdm regulator disable failed:%d\n", ret);
}
}
EXPORT_SYMBOL(usb_phy_drive_dp_pulse);
static int qusb_phy_dpdm_regulator_enable(struct regulator_dev *rdev)
{
int ret = 0;

View file

@ -111,6 +111,14 @@ struct gsi_channel_info {
struct usb_gsi_request *ch_req;
};
#if IS_ENABLED(CONFIG_MSM_QUSB_PHY)
extern void usb_phy_drive_dp_pulse(void *phy,
unsigned int interval_ms);
#else
static inline void usb_phy_drive_dp_pulse(void *phy, unsigned int interval_ms)
{ }
#endif
#if IS_ENABLED(CONFIG_USB_DWC3_MSM)
struct usb_ep *usb_ep_autoconfig_by_name(struct usb_gadget *gadget,
struct usb_endpoint_descriptor *desc, const char *ep_name);