summaryrefslogtreecommitdiffstats
path: root/drivers/usb/gadget
diff options
context:
space:
mode:
authorPavankumar Kondeti <pkondeti@codeaurora.org>2011-02-18 17:43:17 +0530
committerGreg Kroah-Hartman <gregkh@suse.de>2011-02-25 11:33:57 -0800
commite2b61c1df650595d0216c6d086024b5a98d949c7 (patch)
tree4f64e77a3a8aa9532f018169df2293b7d586438a /drivers/usb/gadget
parent0e6ca1998e4c803b0be98f97a1d1e1ea562b8964 (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.c124
-rw-r--r--drivers/usb/gadget/ci13xxx_udc.h4
2 files changed, 99 insertions, 29 deletions
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index da01f333a51..17526759c9c 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)
{
struct usb_ep *ep;
struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget);
+ unsigned long flags;
trace("%p", gadget);
if (gadget == NULL)
return -EINVAL;
+ spin_lock_irqsave(udc->lock, flags);
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ udc->remote_wakeup = 0;
+ udc->suspended = 0;
+ spin_unlock_irqrestore(udc->lock, flags);
+
/* flush all endpoints */
gadget_for_each_ep(ep, gadget) {
usb_ep_fifo_flush(ep);
@@ -1747,7 +1754,8 @@ __acquires(mEp->lock)
}
if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
- /* TODO: D1 - Remote Wakeup; D0 - Self Powered */
+ /* Assume that device is bus powered for now. */
+ *((u16 *)req->buf) = _udc->remote_wakeup << 1;
retval = 0;
} else if ((setup->bRequestType & USB_RECIP_MASK) \
== USB_RECIP_ENDPOINT) {
@@ -1913,22 +1921,32 @@ __acquires(udc->lock)
switch (req.bRequest) {
case USB_REQ_CLEAR_FEATURE:
- if (type != (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
- le16_to_cpu(req.wValue) != USB_ENDPOINT_HALT)
- goto delegate;
- if (req.wLength != 0)
- break;
- num = le16_to_cpu(req.wIndex);
- num &= USB_ENDPOINT_NUMBER_MASK;
- if (!udc->ci13xxx_ep[num].wedge) {
- spin_unlock(udc->lock);
- err = usb_ep_clear_halt(
- &udc->ci13xxx_ep[num].ep);
- spin_lock(udc->lock);
- if (err)
+ if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
+ le16_to_cpu(req.wValue) ==
+ USB_ENDPOINT_HALT) {
+ if (req.wLength != 0)
+ break;
+ num = le16_to_cpu(req.wIndex);
+ num &= USB_ENDPOINT_NUMBER_MASK;
+ if (!udc->ci13xxx_ep[num].wedge) {
+ spin_unlock(udc->lock);
+ err = usb_ep_clear_halt(
+ &udc->ci13xxx_ep[num].ep);
+ spin_lock(udc->lock);
+ if (err)
+ break;
+ }
+ err = isr_setup_status_phase(udc);
+ } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) &&
+ le16_to_cpu(req.wValue) ==
+ USB_DEVICE_REMOTE_WAKEUP) {
+ if (req.wLength != 0)
break;
+ udc->remote_wakeup = 0;
+ err = isr_setup_status_phase(udc);
+ } else {
+ goto delegate;
}
- err = isr_setup_status_phase(udc);
break;
case USB_REQ_GET_STATUS:
if (type != (USB_DIR_IN|USB_RECIP_DEVICE) &&
@@ -1952,20 +1970,29 @@ __acquires(udc->lock)
err = isr_setup_status_phase(udc);
break;
case USB_REQ_SET_FEATURE:
- if (type != (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
- le16_to_cpu(req.wValue) != USB_ENDPOINT_HALT)
- goto delegate;
- if (req.wLength != 0)
- break;
- num = le16_to_cpu(req.wIndex);
- num &= USB_ENDPOINT_NUMBER_MASK;
+ if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
+ le16_to_cpu(req.wValue) ==
+ USB_ENDPOINT_HALT) {
+ if (req.wLength != 0)
+ break;
+ num = le16_to_cpu(req.wIndex);
+ num &= USB_ENDPOINT_NUMBER_MASK;
- spin_unlock(udc->lock);
- err = usb_ep_set_halt(&udc->ci13xxx_ep[num].ep);
- spin_lock(udc->lock);
- if (err)
- break;
- err = isr_setup_status_phase(udc);
+ spin_unlock(udc->lock);
+ err = usb_ep_set_halt(&udc->ci13xxx_ep[num].ep);
+ spin_lock(udc->lock);
+ if (!err)
+ err = isr_setup_status_phase(udc);
+ } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) &&
+ le16_to_cpu(req.wValue) ==
+ USB_DEVICE_REMOTE_WAKEUP) {
+ if (req.wLength != 0)
+ break;
+ udc->remote_wakeup = 1;
+ err = isr_setup_status_phase(udc);
+ } else {
+ goto delegate;
+ }
break;
default:
delegate:
@@ -2401,6 +2428,31 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
return 0;
}
+static int ci13xxx_wakeup(struct usb_gadget *_gadget)
+{
+ struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget);
+ unsigned long flags;
+ int ret = 0;
+
+ trace();
+
+ spin_lock_irqsave(udc->lock, flags);
+ if (!udc->remote_wakeup) {
+ ret = -EOPNOTSUPP;
+ dbg_trace("remote wakeup feature is not enabled\n");
+ goto out;
+ }
+ if (!hw_cread(CAP_PORTSC, PORTSC_SUSP)) {
+ ret = -EINVAL;
+ dbg_trace("port is not suspended\n");
+ goto out;
+ }
+ hw_cwrite(CAP_PORTSC, PORTSC_FPR, PORTSC_FPR);
+out:
+ spin_unlock_irqrestore(udc->lock, flags);
+ return ret;
+}
+
/**
* Device operations part of the API to the USB controller hardware,
* which don't involve endpoints (or i/o)
@@ -2408,6 +2460,7 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
*/
static const struct usb_gadget_ops usb_gadget_ops = {
.vbus_session = ci13xxx_vbus_session,
+ .wakeup = ci13xxx_wakeup,
};
/**
@@ -2650,6 +2703,12 @@ static irqreturn_t udc_irq(void)
isr_statistics.pci++;
udc->gadget.speed = hw_port_is_high_speed() ?
USB_SPEED_HIGH : USB_SPEED_FULL;
+ if (udc->suspended) {
+ spin_unlock(udc->lock);
+ udc->driver->resume(&udc->gadget);
+ spin_lock(udc->lock);
+ udc->suspended = 0;
+ }
}
if (USBi_UEI & intr)
isr_statistics.uei++;
@@ -2657,8 +2716,15 @@ static irqreturn_t udc_irq(void)
isr_statistics.ui++;
isr_tr_complete_handler(udc);
}
- if (USBi_SLI & intr)
+ if (USBi_SLI & intr) {
+ if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
+ udc->suspended = 1;
+ spin_unlock(udc->lock);
+ udc->driver->suspend(&udc->gadget);
+ spin_lock(udc->lock);
+ }
isr_statistics.sli++;
+ }
retval = IRQ_HANDLED;
} else {
isr_statistics.none++;
diff --git a/drivers/usb/gadget/ci13xxx_udc.h b/drivers/usb/gadget/ci13xxx_udc.h
index 3fad3adeacc..6cfab20db6b 100644
--- a/drivers/usb/gadget/ci13xxx_udc.h
+++ b/drivers/usb/gadget/ci13xxx_udc.h
@@ -128,6 +128,9 @@ struct ci13xxx {
u32 ep0_dir; /* ep0 direction */
#define ep0out ci13xxx_ep[0]
#define ep0in ci13xxx_ep[16]
+ u8 remote_wakeup; /* Is remote wakeup feature
+ enabled by the host? */
+ u8 suspended; /* suspended by the host */
struct usb_gadget_driver *driver; /* 3rd party gadget driver */
struct ci13xxx_udc_driver *udc_driver; /* device controller driver */
@@ -169,6 +172,7 @@ struct ci13xxx {
#define DEVICEADDR_USBADR (0x7FUL << 25)
/* PORTSC */
+#define PORTSC_FPR BIT(6)
#define PORTSC_SUSP BIT(7)
#define PORTSC_HSP BIT(9)
#define PORTSC_PTC (0x0FUL << 16)