Commit e2b61c1d authored by Pavankumar Kondeti's avatar Pavankumar Kondeti Committed by Greg Kroah-Hartman

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: default avatarPavankumar Kondeti <pkondeti@codeaurora.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 0e6ca199
...@@ -1608,12 +1608,19 @@ static int _gadget_stop_activity(struct usb_gadget *gadget) ...@@ -1608,12 +1608,19 @@ static int _gadget_stop_activity(struct usb_gadget *gadget)
{ {
struct usb_ep *ep; struct usb_ep *ep;
struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget); struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget);
unsigned long flags;
trace("%p", gadget); trace("%p", gadget);
if (gadget == NULL) if (gadget == NULL)
return -EINVAL; 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 */ /* flush all endpoints */
gadget_for_each_ep(ep, gadget) { gadget_for_each_ep(ep, gadget) {
usb_ep_fifo_flush(ep); usb_ep_fifo_flush(ep);
...@@ -1747,7 +1754,8 @@ __acquires(mEp->lock) ...@@ -1747,7 +1754,8 @@ __acquires(mEp->lock)
} }
if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) { 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; retval = 0;
} else if ((setup->bRequestType & USB_RECIP_MASK) \ } else if ((setup->bRequestType & USB_RECIP_MASK) \
== USB_RECIP_ENDPOINT) { == USB_RECIP_ENDPOINT) {
...@@ -1913,9 +1921,9 @@ __acquires(udc->lock) ...@@ -1913,9 +1921,9 @@ __acquires(udc->lock)
switch (req.bRequest) { switch (req.bRequest) {
case USB_REQ_CLEAR_FEATURE: case USB_REQ_CLEAR_FEATURE:
if (type != (USB_DIR_OUT|USB_RECIP_ENDPOINT) && if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
le16_to_cpu(req.wValue) != USB_ENDPOINT_HALT) le16_to_cpu(req.wValue) ==
goto delegate; USB_ENDPOINT_HALT) {
if (req.wLength != 0) if (req.wLength != 0)
break; break;
num = le16_to_cpu(req.wIndex); num = le16_to_cpu(req.wIndex);
...@@ -1929,6 +1937,16 @@ __acquires(udc->lock) ...@@ -1929,6 +1937,16 @@ __acquires(udc->lock)
break; break;
} }
err = isr_setup_status_phase(udc); 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;
}
break; break;
case USB_REQ_GET_STATUS: case USB_REQ_GET_STATUS:
if (type != (USB_DIR_IN|USB_RECIP_DEVICE) && if (type != (USB_DIR_IN|USB_RECIP_DEVICE) &&
...@@ -1952,9 +1970,9 @@ __acquires(udc->lock) ...@@ -1952,9 +1970,9 @@ __acquires(udc->lock)
err = isr_setup_status_phase(udc); err = isr_setup_status_phase(udc);
break; break;
case USB_REQ_SET_FEATURE: case USB_REQ_SET_FEATURE:
if (type != (USB_DIR_OUT|USB_RECIP_ENDPOINT) && if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
le16_to_cpu(req.wValue) != USB_ENDPOINT_HALT) le16_to_cpu(req.wValue) ==
goto delegate; USB_ENDPOINT_HALT) {
if (req.wLength != 0) if (req.wLength != 0)
break; break;
num = le16_to_cpu(req.wIndex); num = le16_to_cpu(req.wIndex);
...@@ -1963,9 +1981,18 @@ __acquires(udc->lock) ...@@ -1963,9 +1981,18 @@ __acquires(udc->lock)
spin_unlock(udc->lock); spin_unlock(udc->lock);
err = usb_ep_set_halt(&udc->ci13xxx_ep[num].ep); err = usb_ep_set_halt(&udc->ci13xxx_ep[num].ep);
spin_lock(udc->lock); spin_lock(udc->lock);
if (err) 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; break;
udc->remote_wakeup = 1;
err = isr_setup_status_phase(udc); err = isr_setup_status_phase(udc);
} else {
goto delegate;
}
break; break;
default: default:
delegate: delegate:
...@@ -2401,6 +2428,31 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active) ...@@ -2401,6 +2428,31 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
return 0; 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, * Device operations part of the API to the USB controller hardware,
* which don't involve endpoints (or i/o) * 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,6 +2460,7 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
*/ */
static const struct usb_gadget_ops usb_gadget_ops = { static const struct usb_gadget_ops usb_gadget_ops = {
.vbus_session = ci13xxx_vbus_session, .vbus_session = ci13xxx_vbus_session,
.wakeup = ci13xxx_wakeup,
}; };
/** /**
...@@ -2650,6 +2703,12 @@ static irqreturn_t udc_irq(void) ...@@ -2650,6 +2703,12 @@ static irqreturn_t udc_irq(void)
isr_statistics.pci++; isr_statistics.pci++;
udc->gadget.speed = hw_port_is_high_speed() ? udc->gadget.speed = hw_port_is_high_speed() ?
USB_SPEED_HIGH : USB_SPEED_FULL; 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) if (USBi_UEI & intr)
isr_statistics.uei++; isr_statistics.uei++;
...@@ -2657,8 +2716,15 @@ static irqreturn_t udc_irq(void) ...@@ -2657,8 +2716,15 @@ static irqreturn_t udc_irq(void)
isr_statistics.ui++; isr_statistics.ui++;
isr_tr_complete_handler(udc); 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++; isr_statistics.sli++;
}
retval = IRQ_HANDLED; retval = IRQ_HANDLED;
} else { } else {
isr_statistics.none++; isr_statistics.none++;
......
...@@ -128,6 +128,9 @@ struct ci13xxx { ...@@ -128,6 +128,9 @@ struct ci13xxx {
u32 ep0_dir; /* ep0 direction */ u32 ep0_dir; /* ep0 direction */
#define ep0out ci13xxx_ep[0] #define ep0out ci13xxx_ep[0]
#define ep0in ci13xxx_ep[16] #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 usb_gadget_driver *driver; /* 3rd party gadget driver */
struct ci13xxx_udc_driver *udc_driver; /* device controller driver */ struct ci13xxx_udc_driver *udc_driver; /* device controller driver */
...@@ -169,6 +172,7 @@ struct ci13xxx { ...@@ -169,6 +172,7 @@ struct ci13xxx {
#define DEVICEADDR_USBADR (0x7FUL << 25) #define DEVICEADDR_USBADR (0x7FUL << 25)
/* PORTSC */ /* PORTSC */
#define PORTSC_FPR BIT(6)
#define PORTSC_SUSP BIT(7) #define PORTSC_SUSP BIT(7)
#define PORTSC_HSP BIT(9) #define PORTSC_HSP BIT(9)
#define PORTSC_PTC (0x0FUL << 16) #define PORTSC_PTC (0x0FUL << 16)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment