summaryrefslogtreecommitdiffstats
path: root/drivers/usb/phy/phy-msm-usb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/phy/phy-msm-usb.c')
-rw-r--r--drivers/usb/phy/phy-msm-usb.c110
1 files changed, 110 insertions, 0 deletions
diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c
index c9156beeadef..00c49bb1bd29 100644
--- a/drivers/usb/phy/phy-msm-usb.c
+++ b/drivers/usb/phy/phy-msm-usb.c
@@ -240,8 +240,14 @@ static void ulpi_init(struct msm_otg *motg)
static int msm_phy_notify_disconnect(struct usb_phy *phy,
enum usb_device_speed speed)
{
+ struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
int val;
+ if (motg->manual_pullup) {
+ val = ULPI_MISC_A_VBUSVLDEXT | ULPI_MISC_A_VBUSVLDEXTSEL;
+ usb_phy_io_write(phy, val, ULPI_CLR(ULPI_MISC_A));
+ }
+
/*
* Put the transceiver in non-driving mode. Otherwise host
* may not detect soft-disconnection.
@@ -422,6 +428,24 @@ static int msm_phy_init(struct usb_phy *phy)
ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_FALL);
}
+ if (motg->manual_pullup) {
+ val = ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT;
+ ulpi_write(phy, val, ULPI_SET(ULPI_MISC_A));
+
+ val = readl(USB_GENCONFIG_2);
+ val |= GENCONFIG_2_SESS_VLD_CTRL_EN;
+ writel(val, USB_GENCONFIG_2);
+
+ val = readl(USB_USBCMD);
+ val |= USBCMD_SESS_VLD_CTRL;
+ writel(val, USB_USBCMD);
+
+ val = ulpi_read(phy, ULPI_FUNC_CTRL);
+ val &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
+ val |= ULPI_FUNC_CTRL_OPMODE_NORMAL;
+ ulpi_write(phy, val, ULPI_FUNC_CTRL);
+ }
+
if (motg->phy_number)
writel(readl(USB_PHY_CTRL2) | BIT(16), USB_PHY_CTRL2);
@@ -1436,9 +1460,42 @@ static const struct of_device_id msm_otg_dt_match[] = {
};
MODULE_DEVICE_TABLE(of, msm_otg_dt_match);
+static int msm_otg_vbus_notifier(struct notifier_block *nb, unsigned long event,
+ void *ptr)
+{
+ struct msm_usb_cable *vbus = container_of(nb, struct msm_usb_cable, nb);
+ struct msm_otg *motg = container_of(vbus, struct msm_otg, vbus);
+
+ if (event)
+ set_bit(B_SESS_VLD, &motg->inputs);
+ else
+ clear_bit(B_SESS_VLD, &motg->inputs);
+
+ schedule_work(&motg->sm_work);
+
+ return NOTIFY_DONE;
+}
+
+static int msm_otg_id_notifier(struct notifier_block *nb, unsigned long event,
+ void *ptr)
+{
+ struct msm_usb_cable *id = container_of(nb, struct msm_usb_cable, nb);
+ struct msm_otg *motg = container_of(id, struct msm_otg, id);
+
+ if (event)
+ clear_bit(ID, &motg->inputs);
+ else
+ set_bit(ID, &motg->inputs);
+
+ schedule_work(&motg->sm_work);
+
+ return NOTIFY_DONE;
+}
+
static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg)
{
struct msm_otg_platform_data *pdata;
+ struct extcon_dev *ext_id, *ext_vbus;
const struct of_device_id *id;
struct device_node *node = pdev->dev.of_node;
struct property *prop;
@@ -1487,6 +1544,54 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg)
motg->vdd_levels[VDD_LEVEL_MAX] = tmp[VDD_LEVEL_MAX];
}
+ motg->manual_pullup = of_property_read_bool(node, "qcom,manual-pullup");
+
+ ext_id = ERR_PTR(-ENODEV);
+ ext_vbus = ERR_PTR(-ENODEV);
+ if (of_property_read_bool(node, "extcon")) {
+
+ /* Each one of them is not mandatory */
+ ext_vbus = extcon_get_edev_by_phandle(&pdev->dev, 0);
+ if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV)
+ return PTR_ERR(ext_vbus);
+
+ ext_id = extcon_get_edev_by_phandle(&pdev->dev, 1);
+ if (IS_ERR(ext_id) && PTR_ERR(ext_id) != -ENODEV)
+ return PTR_ERR(ext_id);
+ }
+
+ if (!IS_ERR(ext_vbus)) {
+ motg->vbus.nb.notifier_call = msm_otg_vbus_notifier;
+ ret = extcon_register_interest(&motg->vbus.conn, ext_vbus->name,
+ "USB", &motg->vbus.nb);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "register VBUS notifier failed\n");
+ return ret;
+ }
+
+ ret = extcon_get_cable_state(ext_vbus, "USB");
+ if (ret)
+ set_bit(B_SESS_VLD, &motg->inputs);
+ else
+ clear_bit(B_SESS_VLD, &motg->inputs);
+ }
+
+ if (!IS_ERR(ext_id)) {
+ motg->id.nb.notifier_call = msm_otg_id_notifier;
+ ret = extcon_register_interest(&motg->id.conn, ext_id->name,
+ "USB-HOST", &motg->id.nb);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "register ID notifier failed\n");
+ return ret;
+ }
+
+ ret = extcon_get_cable_state(ext_id, "USB-HOST");
+ if (ret)
+ clear_bit(ID, &motg->inputs);
+ else
+ set_bit(ID, &motg->inputs);
+ }
+
prop = of_find_property(node, "qcom,phy-init-sequence", &len);
if (!prop || !len)
return 0;
@@ -1700,6 +1805,11 @@ static int msm_otg_remove(struct platform_device *pdev)
if (phy->otg->host || phy->otg->gadget)
return -EBUSY;
+ if (motg->id.conn.edev)
+ extcon_unregister_interest(&motg->id.conn);
+ if (motg->vbus.conn.edev)
+ extcon_unregister_interest(&motg->vbus.conn);
+
msm_otg_debugfs_cleanup();
cancel_delayed_work_sync(&motg->chg_work);
cancel_work_sync(&motg->sm_work);