aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/dwc3/dwc3-msm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/dwc3/dwc3-msm.c')
-rw-r--r--drivers/usb/dwc3/dwc3-msm.c226
1 files changed, 206 insertions, 20 deletions
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 32b0e900..bc32c75c 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -10,7 +10,6 @@
* GNU General Public License for more details.
*
*/
-
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
@@ -97,6 +96,8 @@ MODULE_PARM_DESC(dcp_max_current, "max current drawn for DCP charger");
#define CGCTL_REG (QSCRATCH_REG_OFFSET + 0x28)
#define PWR_EVNT_IRQ_STAT_REG (QSCRATCH_REG_OFFSET + 0x58)
#define PWR_EVNT_IRQ_MASK_REG (QSCRATCH_REG_OFFSET + 0x5C)
+#define QSCRATCH_USB30_STS_REG (QSCRATCH_REG_OFFSET + 0xF8)
+
#define PWR_EVNT_POWERDOWN_IN_P3_MASK BIT(2)
#define PWR_EVNT_POWERDOWN_OUT_P3_MASK BIT(3)
@@ -209,6 +210,9 @@ struct dwc3_msm {
enum usb_otg_state otg_state;
enum usb_chg_state chg_state;
int pmic_id_irq;
+//yexh1, add for usb otg id pin interrupt
+ int usb_id_gpio;
+//yexh1, end
struct work_struct bus_vote_w;
unsigned int bus_vote;
u32 bus_perf_client;
@@ -242,6 +246,12 @@ struct dwc3_msm {
bool init;
};
+//yexh1, add for usb id detection tcmd
+static bool host_mode_disable;
+static bool host_id_state;
+static struct dwc3_msm *the_msm_dwc3;
+//yexh1, add end
+
#define USB_HSPHY_3P3_VOL_MIN 3050000 /* uV */
#define USB_HSPHY_3P3_VOL_MAX 3300000 /* uV */
#define USB_HSPHY_3P3_HPM_LOAD 16000 /* uA */
@@ -744,7 +754,6 @@ static int dwc3_msm_ep_queue(struct usb_ep *ep,
dwc3_msm_read_reg(mdwc->base, DWC3_GEVNTADRLO(0)),
dwc3_msm_read_reg(mdwc->base, DWC3_GEVNTADRHI(0)),
DWC3_GEVNTSIZ_SIZE(size));
-
ret = __dwc3_msm_ep_queue(dep, req);
if (ret < 0) {
dev_err(mdwc->dev,
@@ -1797,7 +1806,7 @@ static void dwc3_msm_notify_event(struct dwc3 *dwc, unsigned event,
break;
case DWC3_CONTROLLER_RESTART_USB_SESSION:
dev_dbg(mdwc->dev, "DWC3_CONTROLLER_RESTART_USB_SESSION received\n");
- dwc3_restart_usb_work(&mdwc->restart_usb_work);
+ schedule_work(&mdwc->restart_usb_work);
break;
case DWC3_CONTROLLER_NOTIFY_DISABLE_UPDXFER:
dwc3_msm_dbm_disable_updxfer(dwc, value);
@@ -1879,10 +1888,11 @@ static int dwc3_msm_prepare_suspend(struct dwc3_msm *mdwc)
{
unsigned long timeout;
u32 reg = 0;
+ struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
if ((mdwc->in_host_mode || (mdwc->vbus_active
&& mdwc->otg_state == OTG_STATE_B_SUSPEND))
- && dwc3_msm_is_superspeed(mdwc)) {
+ && dwc3_msm_is_superspeed(mdwc) && !mdwc->in_restart) {
if (!atomic_read(&mdwc->in_p3)) {
dev_err(mdwc->dev, "Not in P3,aborting LPM sequence\n");
return -EBUSY;
@@ -1906,11 +1916,20 @@ static int dwc3_msm_prepare_suspend(struct dwc3_msm *mdwc)
break;
}
- if (!(reg & PWR_EVNT_LPM_IN_L2_MASK)) {
+ if (!(reg & PWR_EVNT_LPM_IN_L2_MASK))
dev_err(mdwc->dev, "could not transition HS PHY to L2\n");
- queue_delayed_work(mdwc->dwc3_wq, &mdwc->resume_work, 0);
- return -EBUSY;
- }
+
+ dbg_event(0xFF, "PWR_EVNT_LPM",
+ dwc3_msm_read_reg(mdwc->base, PWR_EVNT_IRQ_STAT_REG));
+ dbg_event(0xFF, "QUSB_STS",
+ dwc3_msm_read_reg(mdwc->base, QSCRATCH_USB30_STS_REG));
+ /* Mark fatal error for host mode or USB bus suspend case */
+ if (mdwc->in_host_mode || (mdwc->vbus_active
+ && mdwc->otg_state == OTG_STATE_B_SUSPEND)) {
+ queue_delayed_work(mdwc->dwc3_wq,
+ &mdwc->resume_work, 0);
+ return -EBUSY;
+ }
/* Clear L2 event bit */
dwc3_msm_write_reg(mdwc->base, PWR_EVNT_IRQ_STAT_REG,
@@ -2075,10 +2094,13 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc)
|| mdwc->in_host_mode) {
enable_irq_wake(mdwc->hs_phy_irq);
enable_irq(mdwc->hs_phy_irq);
- if (mdwc->ss_phy_irq) {
+//lenovo sw yexh1, mask the ss phy irq trigger the system wakeup, which is not used
+/* if (mdwc->ss_phy_irq) {
enable_irq_wake(mdwc->ss_phy_irq);
enable_irq(mdwc->ss_phy_irq);
- }
+ }*/
+//lenovo sw yexh1, end
+
mdwc->lpm_flags |= MDWC3_ASYNC_IRQ_WAKE_CAPABILITY;
}
@@ -2563,6 +2585,9 @@ dwc3_msm_property_is_writeable(struct power_supply *psy,
static char *dwc3_msm_pm_power_supplied_to[] = {
"battery",
"bms",
+/*lenovo-sw weiweij added for ext charger psy*/
+ "ext-charger",
+/*lenovo-sw weiweij added for ext charger psy end*/
};
static enum power_supply_property dwc3_msm_pm_power_props_usb[] = {
@@ -2576,6 +2601,121 @@ static enum power_supply_property dwc3_msm_pm_power_props_usb[] = {
POWER_SUPPLY_PROP_USB_OTG,
};
+//yexh1, add for usb id detection tcmd
+static int msm_otg_enable_gpio_pull(struct dwc3_msm *dwc3, int enable)
+{
+ int err = 0;
+ /* pin ctl */
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *gpio_state;
+
+ if (!dwc3 || !dwc3->usb_id_gpio)
+ return 0;
+
+ pinctrl = devm_pinctrl_get(dwc3->dev);
+ if (IS_ERR_OR_NULL(pinctrl)) {
+ pr_err("%s:Getting pinctrl handle failed \r\n", __func__);
+ return -EINVAL;
+ }
+
+ if (enable)
+ gpio_state = pinctrl_lookup_state(pinctrl, "usbid_default");
+ else
+ gpio_state = pinctrl_lookup_state(pinctrl, "usbid_deactive");
+
+ if (pinctrl && gpio_state) {
+ err = pinctrl_select_state(pinctrl, gpio_state);
+ if (err) {
+ pr_err("%s:pinctrl usb id state, err = %d\r\n", __func__, err);
+ return -EINVAL;
+ }
+ }
+ return 1;
+}
+static int msm_otg_enable_ext_id(struct dwc3_msm *dwc3, int enable)
+{
+ int res;
+ int irq;
+ if (!dwc3->usb_id_gpio)
+ return -ENODEV;
+ /* usb_id_gpio to irq */
+ irq = gpio_to_irq(dwc3->usb_id_gpio);
+
+ if (enable) {
+ res = msm_otg_enable_gpio_pull(dwc3, 1);
+ msleep(100);
+ enable_irq(irq);
+ } else {
+ disable_irq(irq);
+ msleep(50);
+ res = msm_otg_enable_gpio_pull(dwc3, 0);
+ }
+ return res;
+}
+
+static int set_host_mode_disable(const char *val, const struct kernel_param *kp)
+{
+ int res;
+ int rv = param_set_bool(val, kp);
+ struct dwc3_msm *dwc3 = the_msm_dwc3;
+
+ if (!dwc3 || !dwc3->usb_id_gpio)
+ return 0;
+
+ if (host_mode_disable)
+ res = msm_otg_enable_ext_id(dwc3, 0);
+ else
+ res = msm_otg_enable_ext_id(dwc3, 1);
+
+ if (res) {
+ pr_err("%s:host_mode_disable:%d fail \n", __func__, host_mode_disable);
+ }
+
+ return rv;
+}
+
+static struct kernel_param_ops host_mode_disable_param_ops = {
+ .set = set_host_mode_disable,
+ .get = param_get_bool,
+};
+
+module_param_cb(host_mode_disable, &host_mode_disable_param_ops,
+ &host_mode_disable, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(host_mode_disable, "Whether to disable Host Mode");
+
+static int get_host_id_state(char *val, const struct kernel_param *kp)
+{
+ struct dwc3_msm *dwc3 = the_msm_dwc3;
+ enum dwc3_id_state id;
+
+ if (!dwc3 || !dwc3->usb_id_gpio)
+ return 0;
+
+ if (gpio_is_valid(dwc3->usb_id_gpio)) {
+ id = gpio_get_value(dwc3->usb_id_gpio);
+ }
+
+ if (id == DWC3_ID_FLOAT) {
+ host_id_state = true;
+ } else {
+ host_id_state = false;
+ }
+
+ return param_get_int(val, kp);
+}
+
+static struct kernel_param_ops host_id_state_param_ops = {
+ .set = param_set_int,
+ .get = get_host_id_state,
+};
+
+module_param_cb(host_id_state, &host_id_state_param_ops,
+ &host_id_state, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(host_id_state, "Get the USB ID pin state");
+
+//yexh1, end
+
+
static irqreturn_t dwc3_pmic_id_irq(int irq, void *data)
{
struct dwc3_msm *mdwc = data;
@@ -2583,6 +2723,12 @@ static irqreturn_t dwc3_pmic_id_irq(int irq, void *data)
/* If we can't read ID line state for some reason, treat it as float */
id = !!irq_read_line(irq);
+//yexh1, add for usb otg id pin interrupt
+ if (gpio_is_valid(mdwc->usb_id_gpio)) {
+ id = gpio_get_value(mdwc->usb_id_gpio);
+ pr_err("%s: USB ID %d\n", __func__, id);
+ }
+//yexh1, end
if (mdwc->id_state != id) {
mdwc->id_state = id;
schedule_work(&mdwc->resume_work.work);
@@ -2713,10 +2859,23 @@ static int dwc3_msm_probe(struct platform_device *pdev)
int ext_hub_reset_gpio;
u32 val;
+//lenovo sw, yexh1, fix usb cannot connect after boot with usb cable
+ msleep(600);
+//lenovo sw, yexh1, end
+
mdwc = devm_kzalloc(&pdev->dev, sizeof(*mdwc), GFP_KERNEL);
if (!mdwc)
return -ENOMEM;
+ if (of_get_property(pdev->dev.of_node, "qcom,usb-dbm", NULL)) {
+ mdwc->dbm = usb_get_dbm_by_phandle(&pdev->dev, "qcom,usb-dbm");
+ if (IS_ERR(mdwc->dbm)) {
+ dev_err(&pdev->dev, "unable to get dbm device\n");
+ ret = -EPROBE_DEFER;
+ goto err_dbm;
+ }
+ }
+
if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))) {
dev_err(&pdev->dev, "setting DMA mask to 64 failed.\n");
if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32))) {
@@ -2759,6 +2918,17 @@ static int dwc3_msm_probe(struct platform_device *pdev)
dev_dbg(&pdev->dev, "setting lpm_to_suspend_delay to zero.\n");
mdwc->lpm_to_suspend_delay = 0;
}
+//yexh1, add for usb otg id pin interrupt
+ mdwc->usb_id_gpio =
+ of_get_named_gpio(node, "qcom,usbid-gpio", 0);
+ if (mdwc->usb_id_gpio < 0)
+ pr_err("usb_id_gpio is not available\n");
+//yexh1, end
+
+//yexh1, add for usb id detection tcmd
+ the_msm_dwc3 = mdwc;
+ msm_otg_enable_gpio_pull(mdwc, 1);
+//yexh1, add end
/*
* DWC3 has separate IRQ line for OTG events (ID/BSV) and for
@@ -2825,8 +2995,21 @@ static int dwc3_msm_probe(struct platform_device *pdev)
goto err;
}
}
-
- mdwc->pmic_id_irq = platform_get_irq_byname(pdev, "pmic_id_irq");
+//yexh1, add for usb otg id pin interrupt
+ if (gpio_is_valid(mdwc->usb_id_gpio)) {
+ /* usb_id_gpio request */
+ ret = gpio_request(mdwc->usb_id_gpio, "USB_ID_GPIO");
+ pr_err("%s: USB ID pin %d with ret %d \n", __func__, mdwc->usb_id_gpio,ret);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "gpio req failed for id\n");
+ mdwc->usb_id_gpio = 0;
+ goto err;
+ }
+ /* usb_id_gpio to irq */
+ mdwc->pmic_id_irq = gpio_to_irq(mdwc->usb_id_gpio);
+ }else
+//yexh1, end
+ mdwc->pmic_id_irq = platform_get_irq_byname(pdev, "pmic_id_irq");
if (mdwc->pmic_id_irq > 0) {
irq_set_status_flags(mdwc->pmic_id_irq, IRQ_NOAUTOEN);
ret = devm_request_irq(&pdev->dev,
@@ -2909,13 +3092,7 @@ static int dwc3_msm_probe(struct platform_device *pdev)
}
}
- if (of_get_property(pdev->dev.of_node, "qcom,usb-dbm", NULL)) {
- mdwc->dbm = usb_get_dbm_by_phandle(&pdev->dev, "qcom,usb-dbm");
- if (IS_ERR(mdwc->dbm)) {
- dev_err(&pdev->dev, "unable to get dbm device\n");
- ret = -EPROBE_DEFER;
- goto err;
- }
+ if (mdwc->dbm) {
/*
* Add power event if the dbm indicates coming out of L1
* by interrupt
@@ -3057,6 +3234,12 @@ static int dwc3_msm_probe(struct platform_device *pdev)
enable_irq(mdwc->pmic_id_irq);
local_irq_save(flags);
mdwc->id_state = !!irq_read_line(mdwc->pmic_id_irq);
+//yexh1, add for usb otg id pin interrupt
+ if (gpio_is_valid(mdwc->usb_id_gpio)) {
+ mdwc->id_state = gpio_get_value(mdwc->usb_id_gpio);;
+ pr_err("%s: USB ID pin init state %d \n", __func__ ,mdwc->id_state);
+ }
+//yexh1, end
if (mdwc->id_state == DWC3_ID_GROUND)
dwc3_ext_event_notify(mdwc);
local_irq_restore(flags);
@@ -3080,6 +3263,9 @@ put_psupply:
power_supply_unregister(&mdwc->usb_psy);
err:
return ret;
+err_dbm:
+ devm_kfree(&pdev->dev, mdwc);
+ return ret;
}
static int dwc3_msm_remove_children(struct device *dev, void *data)
@@ -3510,7 +3696,7 @@ static void dwc3_otg_sm_work(struct work_struct *w)
}
state = usb_otg_state_string(mdwc->otg_state);
- dev_dbg(mdwc->dev, "%s state\n", state);
+ dev_info(mdwc->dev, "%s state\n", state);
dbg_event(0xFF, state, 0);
/* Check OTG state */