diff options
Diffstat (limited to 'drivers/usb/chipidea/udc.c')
-rw-r--r-- | drivers/usb/chipidea/udc.c | 78 |
1 files changed, 49 insertions, 29 deletions
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index e475fcda1d68..6b4c2f2eb946 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include "udc.h" | 27 | #include "udc.h" |
28 | #include "bits.h" | 28 | #include "bits.h" |
29 | #include "debug.h" | 29 | #include "debug.h" |
30 | #include "otg.h" | ||
30 | 31 | ||
31 | /* control endpoint description */ | 32 | /* control endpoint description */ |
32 | static const struct usb_endpoint_descriptor | 33 | static const struct usb_endpoint_descriptor |
@@ -84,8 +85,10 @@ static int hw_device_state(struct ci_hdrc *ci, u32 dma) | |||
84 | /* interrupt, error, port change, reset, sleep/suspend */ | 85 | /* interrupt, error, port change, reset, sleep/suspend */ |
85 | hw_write(ci, OP_USBINTR, ~0, | 86 | hw_write(ci, OP_USBINTR, ~0, |
86 | USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI); | 87 | USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI); |
88 | hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS); | ||
87 | } else { | 89 | } else { |
88 | hw_write(ci, OP_USBINTR, ~0, 0); | 90 | hw_write(ci, OP_USBINTR, ~0, 0); |
91 | hw_write(ci, OP_USBCMD, USBCMD_RS, 0); | ||
89 | } | 92 | } |
90 | return 0; | 93 | return 0; |
91 | } | 94 | } |
@@ -1445,9 +1448,6 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) | |||
1445 | unsigned long flags; | 1448 | unsigned long flags; |
1446 | int gadget_ready = 0; | 1449 | int gadget_ready = 0; |
1447 | 1450 | ||
1448 | if (!(ci->platdata->flags & CI_HDRC_PULLUP_ON_VBUS)) | ||
1449 | return -EOPNOTSUPP; | ||
1450 | |||
1451 | spin_lock_irqsave(&ci->lock, flags); | 1451 | spin_lock_irqsave(&ci->lock, flags); |
1452 | ci->vbus_active = is_active; | 1452 | ci->vbus_active = is_active; |
1453 | if (ci->driver) | 1453 | if (ci->driver) |
@@ -1459,6 +1459,7 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) | |||
1459 | pm_runtime_get_sync(&_gadget->dev); | 1459 | pm_runtime_get_sync(&_gadget->dev); |
1460 | hw_device_reset(ci, USBMODE_CM_DC); | 1460 | hw_device_reset(ci, USBMODE_CM_DC); |
1461 | hw_device_state(ci, ci->ep0out->qh.dma); | 1461 | hw_device_state(ci, ci->ep0out->qh.dma); |
1462 | dev_dbg(ci->dev, "Connected to host\n"); | ||
1462 | } else { | 1463 | } else { |
1463 | hw_device_state(ci, 0); | 1464 | hw_device_state(ci, 0); |
1464 | if (ci->platdata->notify_event) | 1465 | if (ci->platdata->notify_event) |
@@ -1466,6 +1467,7 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) | |||
1466 | CI_HDRC_CONTROLLER_STOPPED_EVENT); | 1467 | CI_HDRC_CONTROLLER_STOPPED_EVENT); |
1467 | _gadget_stop_activity(&ci->gadget); | 1468 | _gadget_stop_activity(&ci->gadget); |
1468 | pm_runtime_put_sync(&_gadget->dev); | 1469 | pm_runtime_put_sync(&_gadget->dev); |
1470 | dev_dbg(ci->dev, "Disconnected from host\n"); | ||
1469 | } | 1471 | } |
1470 | } | 1472 | } |
1471 | 1473 | ||
@@ -1509,6 +1511,9 @@ static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on) | |||
1509 | { | 1511 | { |
1510 | struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); | 1512 | struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); |
1511 | 1513 | ||
1514 | if (!ci->vbus_active) | ||
1515 | return -EOPNOTSUPP; | ||
1516 | |||
1512 | if (is_on) | 1517 | if (is_on) |
1513 | hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS); | 1518 | hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS); |
1514 | else | 1519 | else |
@@ -1630,14 +1635,11 @@ static int ci_udc_start(struct usb_gadget *gadget, | |||
1630 | 1635 | ||
1631 | ci->driver = driver; | 1636 | ci->driver = driver; |
1632 | pm_runtime_get_sync(&ci->gadget.dev); | 1637 | pm_runtime_get_sync(&ci->gadget.dev); |
1633 | if (ci->platdata->flags & CI_HDRC_PULLUP_ON_VBUS) { | 1638 | if (ci->vbus_active) { |
1634 | if (ci->vbus_active) { | 1639 | hw_device_reset(ci, USBMODE_CM_DC); |
1635 | if (ci->platdata->flags & CI_HDRC_REGS_SHARED) | 1640 | } else { |
1636 | hw_device_reset(ci, USBMODE_CM_DC); | 1641 | pm_runtime_put_sync(&ci->gadget.dev); |
1637 | } else { | 1642 | goto done; |
1638 | pm_runtime_put_sync(&ci->gadget.dev); | ||
1639 | goto done; | ||
1640 | } | ||
1641 | } | 1643 | } |
1642 | 1644 | ||
1643 | retval = hw_device_state(ci, ci->ep0out->qh.dma); | 1645 | retval = hw_device_state(ci, ci->ep0out->qh.dma); |
@@ -1660,8 +1662,7 @@ static int ci_udc_stop(struct usb_gadget *gadget, | |||
1660 | 1662 | ||
1661 | spin_lock_irqsave(&ci->lock, flags); | 1663 | spin_lock_irqsave(&ci->lock, flags); |
1662 | 1664 | ||
1663 | if (!(ci->platdata->flags & CI_HDRC_PULLUP_ON_VBUS) || | 1665 | if (ci->vbus_active) { |
1664 | ci->vbus_active) { | ||
1665 | hw_device_state(ci, 0); | 1666 | hw_device_state(ci, 0); |
1666 | if (ci->platdata->notify_event) | 1667 | if (ci->platdata->notify_event) |
1667 | ci->platdata->notify_event(ci, | 1668 | ci->platdata->notify_event(ci, |
@@ -1796,16 +1797,15 @@ static int udc_start(struct ci_hdrc *ci) | |||
1796 | } | 1797 | } |
1797 | } | 1798 | } |
1798 | 1799 | ||
1799 | if (!(ci->platdata->flags & CI_HDRC_REGS_SHARED)) { | ||
1800 | retval = hw_device_reset(ci, USBMODE_CM_DC); | ||
1801 | if (retval) | ||
1802 | goto put_transceiver; | ||
1803 | } | ||
1804 | |||
1805 | if (ci->transceiver) { | 1800 | if (ci->transceiver) { |
1806 | retval = otg_set_peripheral(ci->transceiver->otg, | 1801 | retval = otg_set_peripheral(ci->transceiver->otg, |
1807 | &ci->gadget); | 1802 | &ci->gadget); |
1808 | if (retval) | 1803 | /* |
1804 | * If we implement all USB functions using chipidea drivers, | ||
1805 | * it doesn't need to call above API, meanwhile, if we only | ||
1806 | * use gadget function, calling above API is useless. | ||
1807 | */ | ||
1808 | if (retval && retval != -ENOTSUPP) | ||
1809 | goto put_transceiver; | 1809 | goto put_transceiver; |
1810 | } | 1810 | } |
1811 | 1811 | ||
@@ -1816,6 +1816,9 @@ static int udc_start(struct ci_hdrc *ci) | |||
1816 | pm_runtime_no_callbacks(&ci->gadget.dev); | 1816 | pm_runtime_no_callbacks(&ci->gadget.dev); |
1817 | pm_runtime_enable(&ci->gadget.dev); | 1817 | pm_runtime_enable(&ci->gadget.dev); |
1818 | 1818 | ||
1819 | /* Update ci->vbus_active */ | ||
1820 | ci_handle_vbus_change(ci); | ||
1821 | |||
1819 | return retval; | 1822 | return retval; |
1820 | 1823 | ||
1821 | remove_trans: | 1824 | remove_trans: |
@@ -1839,13 +1842,13 @@ free_qh_pool: | |||
1839 | } | 1842 | } |
1840 | 1843 | ||
1841 | /** | 1844 | /** |
1842 | * udc_remove: parent remove must call this to remove UDC | 1845 | * ci_hdrc_gadget_destroy: parent remove must call this to remove UDC |
1843 | * | 1846 | * |
1844 | * No interrupts active, the IRQ has been released | 1847 | * No interrupts active, the IRQ has been released |
1845 | */ | 1848 | */ |
1846 | static void udc_stop(struct ci_hdrc *ci) | 1849 | void ci_hdrc_gadget_destroy(struct ci_hdrc *ci) |
1847 | { | 1850 | { |
1848 | if (ci == NULL) | 1851 | if (!ci->roles[CI_ROLE_GADGET]) |
1849 | return; | 1852 | return; |
1850 | 1853 | ||
1851 | usb_del_gadget_udc(&ci->gadget); | 1854 | usb_del_gadget_udc(&ci->gadget); |
@@ -1860,15 +1863,32 @@ static void udc_stop(struct ci_hdrc *ci) | |||
1860 | if (ci->global_phy) | 1863 | if (ci->global_phy) |
1861 | usb_put_phy(ci->transceiver); | 1864 | usb_put_phy(ci->transceiver); |
1862 | } | 1865 | } |
1863 | /* my kobject is dynamic, I swear! */ | 1866 | } |
1864 | memset(&ci->gadget, 0, sizeof(ci->gadget)); | 1867 | |
1868 | static int udc_id_switch_for_device(struct ci_hdrc *ci) | ||
1869 | { | ||
1870 | if (ci->is_otg) { | ||
1871 | ci_clear_otg_interrupt(ci, OTGSC_BSVIS); | ||
1872 | ci_enable_otg_interrupt(ci, OTGSC_BSVIE); | ||
1873 | } | ||
1874 | |||
1875 | return 0; | ||
1876 | } | ||
1877 | |||
1878 | static void udc_id_switch_for_host(struct ci_hdrc *ci) | ||
1879 | { | ||
1880 | if (ci->is_otg) { | ||
1881 | /* host doesn't care B_SESSION_VALID event */ | ||
1882 | ci_clear_otg_interrupt(ci, OTGSC_BSVIS); | ||
1883 | ci_disable_otg_interrupt(ci, OTGSC_BSVIE); | ||
1884 | } | ||
1865 | } | 1885 | } |
1866 | 1886 | ||
1867 | /** | 1887 | /** |
1868 | * ci_hdrc_gadget_init - initialize device related bits | 1888 | * ci_hdrc_gadget_init - initialize device related bits |
1869 | * ci: the controller | 1889 | * ci: the controller |
1870 | * | 1890 | * |
1871 | * This function enables the gadget role, if the device is "device capable". | 1891 | * This function initializes the gadget, if the device is "device capable". |
1872 | */ | 1892 | */ |
1873 | int ci_hdrc_gadget_init(struct ci_hdrc *ci) | 1893 | int ci_hdrc_gadget_init(struct ci_hdrc *ci) |
1874 | { | 1894 | { |
@@ -1881,11 +1901,11 @@ int ci_hdrc_gadget_init(struct ci_hdrc *ci) | |||
1881 | if (!rdrv) | 1901 | if (!rdrv) |
1882 | return -ENOMEM; | 1902 | return -ENOMEM; |
1883 | 1903 | ||
1884 | rdrv->start = udc_start; | 1904 | rdrv->start = udc_id_switch_for_device; |
1885 | rdrv->stop = udc_stop; | 1905 | rdrv->stop = udc_id_switch_for_host; |
1886 | rdrv->irq = udc_irq; | 1906 | rdrv->irq = udc_irq; |
1887 | rdrv->name = "gadget"; | 1907 | rdrv->name = "gadget"; |
1888 | ci->roles[CI_ROLE_GADGET] = rdrv; | 1908 | ci->roles[CI_ROLE_GADGET] = rdrv; |
1889 | 1909 | ||
1890 | return 0; | 1910 | return udc_start(ci); |
1891 | } | 1911 | } |