diff options
author | Pavankumar Kondeti <pkondeti@codeaurora.org> | 2011-02-18 07:13:17 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-02-25 14:33:57 -0500 |
commit | e2b61c1df650595d0216c6d086024b5a98d949c7 (patch) | |
tree | 4f64e77a3a8aa9532f018169df2293b7d586438a /drivers/usb/gadget | |
parent | 0e6ca1998e4c803b0be98f97a1d1e1ea562b8964 (diff) |
USB: gadget: Implement remote wakeup in ci13xxx_udc
This patch adds support for remote wakeup. The following things
are handled:
- Process SET_FEATURE/CLEAR_FEATURE control requests sent by host
for enabling/disabling remote wakeup feature.
- Report remote wakeup enable status in response to GET_STATUS
control request.
- Implement wakeup method defined in usb_gadget_ops for initiating
remote wakeup.
- Notify gadget driver about suspend and resume.
Signed-off-by: Pavankumar Kondeti <pkondeti@codeaurora.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/gadget')
-rw-r--r-- | drivers/usb/gadget/ci13xxx_udc.c | 124 | ||||
-rw-r--r-- | drivers/usb/gadget/ci13xxx_udc.h | 4 |
2 files changed, 99 insertions, 29 deletions
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c index da01f333a51c..17526759c9ce 100644 --- a/drivers/usb/gadget/ci13xxx_udc.c +++ b/drivers/usb/gadget/ci13xxx_udc.c | |||
@@ -1608,12 +1608,19 @@ static int _gadget_stop_activity(struct usb_gadget *gadget) | |||
1608 | { | 1608 | { |
1609 | struct usb_ep *ep; | 1609 | struct usb_ep *ep; |
1610 | struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget); | 1610 | struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget); |
1611 | unsigned long flags; | ||
1611 | 1612 | ||
1612 | trace("%p", gadget); | 1613 | trace("%p", gadget); |
1613 | 1614 | ||
1614 | if (gadget == NULL) | 1615 | if (gadget == NULL) |
1615 | return -EINVAL; | 1616 | return -EINVAL; |
1616 | 1617 | ||
1618 | spin_lock_irqsave(udc->lock, flags); | ||
1619 | udc->gadget.speed = USB_SPEED_UNKNOWN; | ||
1620 | udc->remote_wakeup = 0; | ||
1621 | udc->suspended = 0; | ||
1622 | spin_unlock_irqrestore(udc->lock, flags); | ||
1623 | |||
1617 | /* flush all endpoints */ | 1624 | /* flush all endpoints */ |
1618 | gadget_for_each_ep(ep, gadget) { | 1625 | gadget_for_each_ep(ep, gadget) { |
1619 | usb_ep_fifo_flush(ep); | 1626 | usb_ep_fifo_flush(ep); |
@@ -1747,7 +1754,8 @@ __acquires(mEp->lock) | |||
1747 | } | 1754 | } |
1748 | 1755 | ||
1749 | if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) { | 1756 | if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) { |
1750 | /* TODO: D1 - Remote Wakeup; D0 - Self Powered */ | 1757 | /* Assume that device is bus powered for now. */ |
1758 | *((u16 *)req->buf) = _udc->remote_wakeup << 1; | ||
1751 | retval = 0; | 1759 | retval = 0; |
1752 | } else if ((setup->bRequestType & USB_RECIP_MASK) \ | 1760 | } else if ((setup->bRequestType & USB_RECIP_MASK) \ |
1753 | == USB_RECIP_ENDPOINT) { | 1761 | == USB_RECIP_ENDPOINT) { |
@@ -1913,22 +1921,32 @@ __acquires(udc->lock) | |||
1913 | 1921 | ||
1914 | switch (req.bRequest) { | 1922 | switch (req.bRequest) { |
1915 | case USB_REQ_CLEAR_FEATURE: | 1923 | case USB_REQ_CLEAR_FEATURE: |
1916 | if (type != (USB_DIR_OUT|USB_RECIP_ENDPOINT) && | 1924 | if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) && |
1917 | le16_to_cpu(req.wValue) != USB_ENDPOINT_HALT) | 1925 | le16_to_cpu(req.wValue) == |
1918 | goto delegate; | 1926 | USB_ENDPOINT_HALT) { |
1919 | if (req.wLength != 0) | 1927 | if (req.wLength != 0) |
1920 | break; | 1928 | break; |
1921 | num = le16_to_cpu(req.wIndex); | 1929 | num = le16_to_cpu(req.wIndex); |
1922 | num &= USB_ENDPOINT_NUMBER_MASK; | 1930 | num &= USB_ENDPOINT_NUMBER_MASK; |
1923 | if (!udc->ci13xxx_ep[num].wedge) { | 1931 | if (!udc->ci13xxx_ep[num].wedge) { |
1924 | spin_unlock(udc->lock); | 1932 | spin_unlock(udc->lock); |
1925 | err = usb_ep_clear_halt( | 1933 | err = usb_ep_clear_halt( |
1926 | &udc->ci13xxx_ep[num].ep); | 1934 | &udc->ci13xxx_ep[num].ep); |
1927 | spin_lock(udc->lock); | 1935 | spin_lock(udc->lock); |
1928 | if (err) | 1936 | if (err) |
1937 | break; | ||
1938 | } | ||
1939 | err = isr_setup_status_phase(udc); | ||
1940 | } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) && | ||
1941 | le16_to_cpu(req.wValue) == | ||
1942 | USB_DEVICE_REMOTE_WAKEUP) { | ||
1943 | if (req.wLength != 0) | ||
1929 | break; | 1944 | break; |
1945 | udc->remote_wakeup = 0; | ||
1946 | err = isr_setup_status_phase(udc); | ||
1947 | } else { | ||
1948 | goto delegate; | ||
1930 | } | 1949 | } |
1931 | err = isr_setup_status_phase(udc); | ||
1932 | break; | 1950 | break; |
1933 | case USB_REQ_GET_STATUS: | 1951 | case USB_REQ_GET_STATUS: |
1934 | if (type != (USB_DIR_IN|USB_RECIP_DEVICE) && | 1952 | if (type != (USB_DIR_IN|USB_RECIP_DEVICE) && |
@@ -1952,20 +1970,29 @@ __acquires(udc->lock) | |||
1952 | err = isr_setup_status_phase(udc); | 1970 | err = isr_setup_status_phase(udc); |
1953 | break; | 1971 | break; |
1954 | case USB_REQ_SET_FEATURE: | 1972 | case USB_REQ_SET_FEATURE: |
1955 | if (type != (USB_DIR_OUT|USB_RECIP_ENDPOINT) && | 1973 | if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) && |
1956 | le16_to_cpu(req.wValue) != USB_ENDPOINT_HALT) | 1974 | le16_to_cpu(req.wValue) == |
1957 | goto delegate; | 1975 | USB_ENDPOINT_HALT) { |
1958 | if (req.wLength != 0) | 1976 | if (req.wLength != 0) |
1959 | break; | 1977 | break; |
1960 | num = le16_to_cpu(req.wIndex); | 1978 | num = le16_to_cpu(req.wIndex); |
1961 | num &= USB_ENDPOINT_NUMBER_MASK; | 1979 | num &= USB_ENDPOINT_NUMBER_MASK; |
1962 | 1980 | ||
1963 | spin_unlock(udc->lock); | 1981 | spin_unlock(udc->lock); |
1964 | err = usb_ep_set_halt(&udc->ci13xxx_ep[num].ep); | 1982 | err = usb_ep_set_halt(&udc->ci13xxx_ep[num].ep); |
1965 | spin_lock(udc->lock); | 1983 | spin_lock(udc->lock); |
1966 | if (err) | 1984 | if (!err) |
1967 | break; | 1985 | err = isr_setup_status_phase(udc); |
1968 | err = isr_setup_status_phase(udc); | 1986 | } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) && |
1987 | le16_to_cpu(req.wValue) == | ||
1988 | USB_DEVICE_REMOTE_WAKEUP) { | ||
1989 | if (req.wLength != 0) | ||
1990 | break; | ||
1991 | udc->remote_wakeup = 1; | ||
1992 | err = isr_setup_status_phase(udc); | ||
1993 | } else { | ||
1994 | goto delegate; | ||
1995 | } | ||
1969 | break; | 1996 | break; |
1970 | default: | 1997 | default: |
1971 | delegate: | 1998 | delegate: |
@@ -2401,6 +2428,31 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active) | |||
2401 | return 0; | 2428 | return 0; |
2402 | } | 2429 | } |
2403 | 2430 | ||
2431 | static int ci13xxx_wakeup(struct usb_gadget *_gadget) | ||
2432 | { | ||
2433 | struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget); | ||
2434 | unsigned long flags; | ||
2435 | int ret = 0; | ||
2436 | |||
2437 | trace(); | ||
2438 | |||
2439 | spin_lock_irqsave(udc->lock, flags); | ||
2440 | if (!udc->remote_wakeup) { | ||
2441 | ret = -EOPNOTSUPP; | ||
2442 | dbg_trace("remote wakeup feature is not enabled\n"); | ||
2443 | goto out; | ||
2444 | } | ||
2445 | if (!hw_cread(CAP_PORTSC, PORTSC_SUSP)) { | ||
2446 | ret = -EINVAL; | ||
2447 | dbg_trace("port is not suspended\n"); | ||
2448 | goto out; | ||
2449 | } | ||
2450 | hw_cwrite(CAP_PORTSC, PORTSC_FPR, PORTSC_FPR); | ||
2451 | out: | ||
2452 | spin_unlock_irqrestore(udc->lock, flags); | ||
2453 | return ret; | ||
2454 | } | ||
2455 | |||
2404 | /** | 2456 | /** |
2405 | * Device operations part of the API to the USB controller hardware, | 2457 | * Device operations part of the API to the USB controller hardware, |
2406 | * which don't involve endpoints (or i/o) | 2458 | * which don't involve endpoints (or i/o) |
@@ -2408,6 +2460,7 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active) | |||
2408 | */ | 2460 | */ |
2409 | static const struct usb_gadget_ops usb_gadget_ops = { | 2461 | static const struct usb_gadget_ops usb_gadget_ops = { |
2410 | .vbus_session = ci13xxx_vbus_session, | 2462 | .vbus_session = ci13xxx_vbus_session, |
2463 | .wakeup = ci13xxx_wakeup, | ||
2411 | }; | 2464 | }; |
2412 | 2465 | ||
2413 | /** | 2466 | /** |
@@ -2650,6 +2703,12 @@ static irqreturn_t udc_irq(void) | |||
2650 | isr_statistics.pci++; | 2703 | isr_statistics.pci++; |
2651 | udc->gadget.speed = hw_port_is_high_speed() ? | 2704 | udc->gadget.speed = hw_port_is_high_speed() ? |
2652 | USB_SPEED_HIGH : USB_SPEED_FULL; | 2705 | USB_SPEED_HIGH : USB_SPEED_FULL; |
2706 | if (udc->suspended) { | ||
2707 | spin_unlock(udc->lock); | ||
2708 | udc->driver->resume(&udc->gadget); | ||
2709 | spin_lock(udc->lock); | ||
2710 | udc->suspended = 0; | ||
2711 | } | ||
2653 | } | 2712 | } |
2654 | if (USBi_UEI & intr) | 2713 | if (USBi_UEI & intr) |
2655 | isr_statistics.uei++; | 2714 | isr_statistics.uei++; |
@@ -2657,8 +2716,15 @@ static irqreturn_t udc_irq(void) | |||
2657 | isr_statistics.ui++; | 2716 | isr_statistics.ui++; |
2658 | isr_tr_complete_handler(udc); | 2717 | isr_tr_complete_handler(udc); |
2659 | } | 2718 | } |
2660 | if (USBi_SLI & intr) | 2719 | if (USBi_SLI & intr) { |
2720 | if (udc->gadget.speed != USB_SPEED_UNKNOWN) { | ||
2721 | udc->suspended = 1; | ||
2722 | spin_unlock(udc->lock); | ||
2723 | udc->driver->suspend(&udc->gadget); | ||
2724 | spin_lock(udc->lock); | ||
2725 | } | ||
2661 | isr_statistics.sli++; | 2726 | isr_statistics.sli++; |
2727 | } | ||
2662 | retval = IRQ_HANDLED; | 2728 | retval = IRQ_HANDLED; |
2663 | } else { | 2729 | } else { |
2664 | isr_statistics.none++; | 2730 | isr_statistics.none++; |
diff --git a/drivers/usb/gadget/ci13xxx_udc.h b/drivers/usb/gadget/ci13xxx_udc.h index 3fad3adeacc8..6cfab20db6bd 100644 --- a/drivers/usb/gadget/ci13xxx_udc.h +++ b/drivers/usb/gadget/ci13xxx_udc.h | |||
@@ -128,6 +128,9 @@ struct ci13xxx { | |||
128 | u32 ep0_dir; /* ep0 direction */ | 128 | u32 ep0_dir; /* ep0 direction */ |
129 | #define ep0out ci13xxx_ep[0] | 129 | #define ep0out ci13xxx_ep[0] |
130 | #define ep0in ci13xxx_ep[16] | 130 | #define ep0in ci13xxx_ep[16] |
131 | u8 remote_wakeup; /* Is remote wakeup feature | ||
132 | enabled by the host? */ | ||
133 | u8 suspended; /* suspended by the host */ | ||
131 | 134 | ||
132 | struct usb_gadget_driver *driver; /* 3rd party gadget driver */ | 135 | struct usb_gadget_driver *driver; /* 3rd party gadget driver */ |
133 | struct ci13xxx_udc_driver *udc_driver; /* device controller driver */ | 136 | struct ci13xxx_udc_driver *udc_driver; /* device controller driver */ |
@@ -169,6 +172,7 @@ struct ci13xxx { | |||
169 | #define DEVICEADDR_USBADR (0x7FUL << 25) | 172 | #define DEVICEADDR_USBADR (0x7FUL << 25) |
170 | 173 | ||
171 | /* PORTSC */ | 174 | /* PORTSC */ |
175 | #define PORTSC_FPR BIT(6) | ||
172 | #define PORTSC_SUSP BIT(7) | 176 | #define PORTSC_SUSP BIT(7) |
173 | #define PORTSC_HSP BIT(9) | 177 | #define PORTSC_HSP BIT(9) |
174 | #define PORTSC_PTC (0x0FUL << 16) | 178 | #define PORTSC_PTC (0x0FUL << 16) |