Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 58ed7b94 authored by Haavard Skinnemoen's avatar Haavard Skinnemoen Committed by Greg Kroah-Hartman
Browse files

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: default avatarHaavard Skinnemoen <hskinnemoen@atmel.com>
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent d466a919
Loading
Loading
Loading
Loading
+48 −9
Original line number Diff line number Diff line
@@ -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,
	.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);
+3 −1
Original line number Diff line number Diff line
@@ -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