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

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

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
Loading
Loading
Loading
Loading
+95 −29
Original line number Diff line number Diff line
@@ -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,9 +1921,9 @@ __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 (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);
@@ -1929,6 +1937,16 @@ __acquires(udc->lock)
						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;
			}
			break;
		case USB_REQ_GET_STATUS:
			if (type != (USB_DIR_IN|USB_RECIP_DEVICE)   &&
@@ -1952,9 +1970,9 @@ __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 (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);
@@ -1963,9 +1981,18 @@ __acquires(udc->lock)
				spin_unlock(udc->lock);
				err = usb_ep_set_halt(&udc->ci13xxx_ep[num].ep);
				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;
				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++;
+4 −0
Original line number Diff line number Diff line
@@ -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)