diff options
Diffstat (limited to 'drivers/usb/phy/phy-msm-usb.c')
-rw-r--r-- | drivers/usb/phy/phy-msm-usb.c | 110 |
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) | |||
240 | static int msm_phy_notify_disconnect(struct usb_phy *phy, | 240 | static int msm_phy_notify_disconnect(struct usb_phy *phy, |
241 | enum usb_device_speed speed) | 241 | enum usb_device_speed speed) |
242 | { | 242 | { |
243 | struct msm_otg *motg = container_of(phy, struct msm_otg, phy); | ||
243 | int val; | 244 | int val; |
244 | 245 | ||
246 | if (motg->manual_pullup) { | ||
247 | val = ULPI_MISC_A_VBUSVLDEXT | ULPI_MISC_A_VBUSVLDEXTSEL; | ||
248 | usb_phy_io_write(phy, val, ULPI_CLR(ULPI_MISC_A)); | ||
249 | } | ||
250 | |||
245 | /* | 251 | /* |
246 | * Put the transceiver in non-driving mode. Otherwise host | 252 | * Put the transceiver in non-driving mode. Otherwise host |
247 | * may not detect soft-disconnection. | 253 | * may not detect soft-disconnection. |
@@ -422,6 +428,24 @@ static int msm_phy_init(struct usb_phy *phy) | |||
422 | ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_FALL); | 428 | ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_FALL); |
423 | } | 429 | } |
424 | 430 | ||
431 | if (motg->manual_pullup) { | ||
432 | val = ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT; | ||
433 | ulpi_write(phy, val, ULPI_SET(ULPI_MISC_A)); | ||
434 | |||
435 | val = readl(USB_GENCONFIG_2); | ||
436 | val |= GENCONFIG_2_SESS_VLD_CTRL_EN; | ||
437 | writel(val, USB_GENCONFIG_2); | ||
438 | |||
439 | val = readl(USB_USBCMD); | ||
440 | val |= USBCMD_SESS_VLD_CTRL; | ||
441 | writel(val, USB_USBCMD); | ||
442 | |||
443 | val = ulpi_read(phy, ULPI_FUNC_CTRL); | ||
444 | val &= ~ULPI_FUNC_CTRL_OPMODE_MASK; | ||
445 | val |= ULPI_FUNC_CTRL_OPMODE_NORMAL; | ||
446 | ulpi_write(phy, val, ULPI_FUNC_CTRL); | ||
447 | } | ||
448 | |||
425 | if (motg->phy_number) | 449 | if (motg->phy_number) |
426 | writel(readl(USB_PHY_CTRL2) | BIT(16), USB_PHY_CTRL2); | 450 | writel(readl(USB_PHY_CTRL2) | BIT(16), USB_PHY_CTRL2); |
427 | 451 | ||
@@ -1436,9 +1460,42 @@ static const struct of_device_id msm_otg_dt_match[] = { | |||
1436 | }; | 1460 | }; |
1437 | MODULE_DEVICE_TABLE(of, msm_otg_dt_match); | 1461 | MODULE_DEVICE_TABLE(of, msm_otg_dt_match); |
1438 | 1462 | ||
1463 | static int msm_otg_vbus_notifier(struct notifier_block *nb, unsigned long event, | ||
1464 | void *ptr) | ||
1465 | { | ||
1466 | struct msm_usb_cable *vbus = container_of(nb, struct msm_usb_cable, nb); | ||
1467 | struct msm_otg *motg = container_of(vbus, struct msm_otg, vbus); | ||
1468 | |||
1469 | if (event) | ||
1470 | set_bit(B_SESS_VLD, &motg->inputs); | ||
1471 | else | ||
1472 | clear_bit(B_SESS_VLD, &motg->inputs); | ||
1473 | |||
1474 | schedule_work(&motg->sm_work); | ||
1475 | |||
1476 | return NOTIFY_DONE; | ||
1477 | } | ||
1478 | |||
1479 | static int msm_otg_id_notifier(struct notifier_block *nb, unsigned long event, | ||
1480 | void *ptr) | ||
1481 | { | ||
1482 | struct msm_usb_cable *id = container_of(nb, struct msm_usb_cable, nb); | ||
1483 | struct msm_otg *motg = container_of(id, struct msm_otg, id); | ||
1484 | |||
1485 | if (event) | ||
1486 | clear_bit(ID, &motg->inputs); | ||
1487 | else | ||
1488 | set_bit(ID, &motg->inputs); | ||
1489 | |||
1490 | schedule_work(&motg->sm_work); | ||
1491 | |||
1492 | return NOTIFY_DONE; | ||
1493 | } | ||
1494 | |||
1439 | static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg) | 1495 | static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg) |
1440 | { | 1496 | { |
1441 | struct msm_otg_platform_data *pdata; | 1497 | struct msm_otg_platform_data *pdata; |
1498 | struct extcon_dev *ext_id, *ext_vbus; | ||
1442 | const struct of_device_id *id; | 1499 | const struct of_device_id *id; |
1443 | struct device_node *node = pdev->dev.of_node; | 1500 | struct device_node *node = pdev->dev.of_node; |
1444 | struct property *prop; | 1501 | struct property *prop; |
@@ -1487,6 +1544,54 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg) | |||
1487 | motg->vdd_levels[VDD_LEVEL_MAX] = tmp[VDD_LEVEL_MAX]; | 1544 | motg->vdd_levels[VDD_LEVEL_MAX] = tmp[VDD_LEVEL_MAX]; |
1488 | } | 1545 | } |
1489 | 1546 | ||
1547 | motg->manual_pullup = of_property_read_bool(node, "qcom,manual-pullup"); | ||
1548 | |||
1549 | ext_id = ERR_PTR(-ENODEV); | ||
1550 | ext_vbus = ERR_PTR(-ENODEV); | ||
1551 | if (of_property_read_bool(node, "extcon")) { | ||
1552 | |||
1553 | /* Each one of them is not mandatory */ | ||
1554 | ext_vbus = extcon_get_edev_by_phandle(&pdev->dev, 0); | ||
1555 | if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV) | ||
1556 | return PTR_ERR(ext_vbus); | ||
1557 | |||
1558 | ext_id = extcon_get_edev_by_phandle(&pdev->dev, 1); | ||
1559 | if (IS_ERR(ext_id) && PTR_ERR(ext_id) != -ENODEV) | ||
1560 | return PTR_ERR(ext_id); | ||
1561 | } | ||
1562 | |||
1563 | if (!IS_ERR(ext_vbus)) { | ||
1564 | motg->vbus.nb.notifier_call = msm_otg_vbus_notifier; | ||
1565 | ret = extcon_register_interest(&motg->vbus.conn, ext_vbus->name, | ||
1566 | "USB", &motg->vbus.nb); | ||
1567 | if (ret < 0) { | ||
1568 | dev_err(&pdev->dev, "register VBUS notifier failed\n"); | ||
1569 | return ret; | ||
1570 | } | ||
1571 | |||
1572 | ret = extcon_get_cable_state(ext_vbus, "USB"); | ||
1573 | if (ret) | ||
1574 | set_bit(B_SESS_VLD, &motg->inputs); | ||
1575 | else | ||
1576 | clear_bit(B_SESS_VLD, &motg->inputs); | ||
1577 | } | ||
1578 | |||
1579 | if (!IS_ERR(ext_id)) { | ||
1580 | motg->id.nb.notifier_call = msm_otg_id_notifier; | ||
1581 | ret = extcon_register_interest(&motg->id.conn, ext_id->name, | ||
1582 | "USB-HOST", &motg->id.nb); | ||
1583 | if (ret < 0) { | ||
1584 | dev_err(&pdev->dev, "register ID notifier failed\n"); | ||
1585 | return ret; | ||
1586 | } | ||
1587 | |||
1588 | ret = extcon_get_cable_state(ext_id, "USB-HOST"); | ||
1589 | if (ret) | ||
1590 | clear_bit(ID, &motg->inputs); | ||
1591 | else | ||
1592 | set_bit(ID, &motg->inputs); | ||
1593 | } | ||
1594 | |||
1490 | prop = of_find_property(node, "qcom,phy-init-sequence", &len); | 1595 | prop = of_find_property(node, "qcom,phy-init-sequence", &len); |
1491 | if (!prop || !len) | 1596 | if (!prop || !len) |
1492 | return 0; | 1597 | return 0; |
@@ -1700,6 +1805,11 @@ static int msm_otg_remove(struct platform_device *pdev) | |||
1700 | if (phy->otg->host || phy->otg->gadget) | 1805 | if (phy->otg->host || phy->otg->gadget) |
1701 | return -EBUSY; | 1806 | return -EBUSY; |
1702 | 1807 | ||
1808 | if (motg->id.conn.edev) | ||
1809 | extcon_unregister_interest(&motg->id.conn); | ||
1810 | if (motg->vbus.conn.edev) | ||
1811 | extcon_unregister_interest(&motg->vbus.conn); | ||
1812 | |||
1703 | msm_otg_debugfs_cleanup(); | 1813 | msm_otg_debugfs_cleanup(); |
1704 | cancel_delayed_work_sync(&motg->chg_work); | 1814 | cancel_delayed_work_sync(&motg->chg_work); |
1705 | cancel_work_sync(&motg->sm_work); | 1815 | cancel_work_sync(&motg->sm_work); |