summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHaavard Skinnemoen <hskinnemoen@atmel.com>2007-10-11 13:40:30 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2007-10-12 14:55:35 -0700
commit58ed7b94d98245fbad54a0af7ea3317ab1dd6876 (patch)
treecc5df430c9a4a152d8b264f9f0c613ed40a2c333
parentd466a9190ff1ceddfee50686e61d63590fc820d9 (diff)
atmel_usba_udc: Keep track of the device status
Keep track of the device status (as returned by the GET_STATUS request) and allow it to be manipulated by set_selfpowered() as well as SET_FEATURE/CLEAR_FEATURE (for remote wakeup) Implement the wakeup() op, which refuses to do anything if the DEVICE_REMOTE_WAKEUP feature wasn't set by the host. Now this driver passes USBCV (at least, with gadget zero). Fix one more locking bug; lockdep is every developer's friend. Signed-off-by: Haavard Skinnemoen <hskinnemoen@atmel.com> Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/usb/gadget/atmel_usba_udc.c57
-rw-r--r--drivers/usb/gadget/atmel_usba_udc.h4
2 files changed, 51 insertions, 10 deletions
diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c
index 2bb28a58393..4fb5ff46957 100644
--- a/drivers/usb/gadget/atmel_usba_udc.c
+++ b/drivers/usb/gadget/atmel_usba_udc.c
@@ -989,8 +989,44 @@ static int usba_udc_get_frame(struct usb_gadget *gadget)
return USBA_BFEXT(FRAME_NUMBER, usba_readl(udc, FNUM));
}
+static int usba_udc_wakeup(struct usb_gadget *gadget)
+{
+ struct usba_udc *udc = to_usba_udc(gadget);
+ unsigned long flags;
+ u32 ctrl;
+ int ret = -EINVAL;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ if (udc->devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) {
+ ctrl = usba_readl(udc, CTRL);
+ usba_writel(udc, CTRL, ctrl | USBA_REMOTE_WAKE_UP);
+ ret = 0;
+ }
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return ret;
+}
+
+static int
+usba_udc_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered)
+{
+ struct usba_udc *udc = to_usba_udc(gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ if (is_selfpowered)
+ udc->devstatus |= 1 << USB_DEVICE_SELF_POWERED;
+ else
+ udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
static const struct usb_gadget_ops usba_udc_ops = {
- .get_frame = usba_udc_get_frame,
+ .get_frame = usba_udc_get_frame,
+ .wakeup = usba_udc_wakeup,
+ .set_selfpowered = usba_udc_set_selfpowered,
};
#define EP(nam, idx, maxpkt, maxbk, dma, isoc) \
@@ -1068,8 +1104,11 @@ static void reset_all_endpoints(struct usba_udc *udc)
}
list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
- if (ep->desc)
+ if (ep->desc) {
+ spin_unlock(&udc->lock);
usba_ep_disable(&ep->ep);
+ spin_lock(&udc->lock);
+ }
}
}
@@ -1238,8 +1277,7 @@ static int handle_ep0_setup(struct usba_udc *udc, struct usba_ep *ep,
u16 status;
if (crq->bRequestType == (USB_DIR_IN | USB_RECIP_DEVICE)) {
- /* Self-powered, no remote wakeup */
- status = __constant_cpu_to_le16(1 << 0);
+ status = cpu_to_le16(udc->devstatus);
} else if (crq->bRequestType
== (USB_DIR_IN | USB_RECIP_INTERFACE)) {
status = __constant_cpu_to_le16(0);
@@ -1268,12 +1306,12 @@ static int handle_ep0_setup(struct usba_udc *udc, struct usba_ep *ep,
case USB_REQ_CLEAR_FEATURE: {
if (crq->bRequestType == USB_RECIP_DEVICE) {
- if (feature_is_dev_remote_wakeup(crq)) {
- /* TODO: Handle REMOTE_WAKEUP */
- } else {
+ if (feature_is_dev_remote_wakeup(crq))
+ udc->devstatus
+ &= ~(1 << USB_DEVICE_REMOTE_WAKEUP);
+ else
/* Can't CLEAR_FEATURE TEST_MODE */
goto stall;
- }
} else if (crq->bRequestType == USB_RECIP_ENDPOINT) {
struct usba_ep *target;
@@ -1304,7 +1342,7 @@ static int handle_ep0_setup(struct usba_udc *udc, struct usba_ep *ep,
udc->test_mode = le16_to_cpu(crq->wIndex);
return 0;
} else if (feature_is_dev_remote_wakeup(crq)) {
- /* TODO: Handle REMOTE_WAKEUP */
+ udc->devstatus |= 1 << USB_DEVICE_REMOTE_WAKEUP;
} else {
goto stall;
}
@@ -1791,6 +1829,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
return -EBUSY;
}
+ udc->devstatus = 1 << USB_DEVICE_SELF_POWERED;
udc->driver = driver;
udc->gadget.dev.driver = &driver->driver;
spin_unlock_irqrestore(&udc->lock, flags);
diff --git a/drivers/usb/gadget/atmel_usba_udc.h b/drivers/usb/gadget/atmel_usba_udc.h
index f4f0f8bf812..a68304e31a6 100644
--- a/drivers/usb/gadget/atmel_usba_udc.h
+++ b/drivers/usb/gadget/atmel_usba_udc.h
@@ -320,7 +320,9 @@ struct usba_udc {
struct clk *pclk;
struct clk *hclk;
- int test_mode;
+ u16 devstatus;
+
+ u16 test_mode;
int vbus_prev;
#ifdef CONFIG_USB_GADGET_DEBUG_FS