[PATCH v2] USB: msm72k_udc: Add Remote wakeup support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Process device remote wakeup set/clear feature requests and implement
wakeup method of usb_gadget_ops.  This patch also provides a sysfs file
to initiate remote wakeup from user space.  Remote wakeup can be generated
by poking into /sys/devices/platform/msm_hsusb/gadget/wakeup file.

Signed-off-by: Pavankumar Kondeti <pkondeti@xxxxxxxxxxxxxx>
Signed-off-by: Vamsi Krishna <vskrishn@xxxxxxxxxxxxxx>
---
 drivers/usb/gadget/msm72k_udc.c  |   62 ++++++++++++++++++++++++++++++++++++++
 include/linux/usb/msm_hsusb_hw.h |    3 ++
 2 files changed, 65 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c
index 6ec1a32..51e9f2e 100644
--- a/drivers/usb/gadget/msm72k_udc.c
+++ b/drivers/usb/gadget/msm72k_udc.c
@@ -179,6 +179,7 @@ static void usb_do_work(struct work_struct *w);
  * @clk: clock struct of usb_hs_clk.
  * @pclk: clock struct of usb_hs_pclk.
  * @ep0_dir: direction of ep0 setup data transfer.
+ * @remote_wakeup: indicates remote wakeup capability enabled by host
  *
  */
 struct usb_info {
@@ -221,6 +222,7 @@ struct usb_info {
 	struct clk *pclk;
 
 	unsigned int ep0_dir;
+	u8 remote_wakeup;
 };
 
 static const struct usb_ep_ops msm72k_ep_ops;
@@ -647,6 +649,8 @@ static void handle_setup(struct usb_info *ui)
 				u16 temp = 0;
 
 				temp = 1 << USB_DEVICE_SELF_POWERED;
+				temp |= (ui->remote_wakeup <<
+						USB_DEVICE_REMOTE_WAKEUP);
 				memcpy(req->buf, &temp, 2);
 				break;
 			}
@@ -695,6 +699,16 @@ static void handle_setup(struct usb_info *ui)
 			*/
 			writel((ctl.wValue << 25) | (1 << 24), USB_DEVICEADDR);
 			goto ack;
+		} else if (ctl.bRequest == USB_REQ_SET_FEATURE) {
+			switch (ctl.wValue) {
+			case USB_DEVICE_REMOTE_WAKEUP:
+				ui->remote_wakeup = 1;
+				goto ack;
+			}
+		} else if ((ctl.bRequest == USB_REQ_CLEAR_FEATURE) &&
+				(ctl.wValue == USB_DEVICE_REMOTE_WAKEUP)) {
+			ui->remote_wakeup = 0;
+			goto ack;
 		}
 	}
 
@@ -900,6 +914,7 @@ static irqreturn_t usb_interrupt(int irq, void *data)
 		writel(readl(USB_ENDPTCOMPLETE), USB_ENDPTCOMPLETE);
 		writel(0xffffffff, USB_ENDPTFLUSH);
 		writel(0, USB_ENDPTCTRL(1));
+		ui->remote_wakeup = 0;
 
 		if (ui->online != 0) {
 			/* marking us offline will cause ept queue attempts
@@ -1660,12 +1675,52 @@ static int msm72k_pullup(struct usb_gadget *_gadget, int is_active)
 	return 0;
 }
 
+static int msm72k_wakeup(struct usb_gadget *_gadget)
+{
+	struct usb_info *ui = container_of(_gadget, struct usb_info, gadget);
+	unsigned long flags;
+
+	if (!ui->remote_wakeup) {
+		dev_err(&ui->pdev->dev, "%s: remote wakeup not supported\n",
+				__func__);
+		return -ENOTSUPP;
+	}
+
+	if (!ui->online) {
+		dev_err(&ui->pdev->dev, "%s: device is not configured\n",
+				__func__);
+		return -ENODEV;
+	}
+
+	spin_lock_irqsave(&ui->lock, flags);
+	if ((readl(USB_PORTSC) & PORTSC_SUSP) == PORTSC_SUSP) {
+		dev_info(&ui->pdev->dev, "%s: enabling force resume\n",
+				__func__);
+		writel(readl(USB_PORTSC) | PORTSC_FPR, USB_PORTSC);
+	}
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	return 0;
+}
+
 static const struct usb_gadget_ops msm72k_ops = {
 	.get_frame	= msm72k_get_frame,
 	.vbus_session	= msm72k_udc_vbus_session,
 	.pullup		= msm72k_pullup,
+	.wakeup		= msm72k_wakeup,
 };
 
+static ssize_t usb_remote_wakeup(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct usb_info *ui = the_usb_info;
+
+	msm72k_wakeup(&ui->gadget);
+
+	return count;
+}
+static DEVICE_ATTR(wakeup, S_IWUSR, 0, usb_remote_wakeup);
+
 static int msm72k_probe(struct platform_device *pdev)
 {
 	struct resource *res;
@@ -1795,6 +1850,12 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
 	dev_info(&ui->pdev->dev, "registered gadget driver '%s'\n",
 			driver->driver.name);
 
+	/* create sysfs node for remote wakeup */
+	retval = device_create_file(&ui->gadget.dev, &dev_attr_wakeup);
+	if (retval != 0)
+		dev_info(&ui->pdev->dev, "failed to create sysfs entry:"
+				"(wakeup) error: (%d)\n", retval);
+
 	usb_start(ui);
 
 	return 0;
@@ -1815,6 +1876,7 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
 	if (!driver || driver != dev->driver || !driver->unbind)
 		return -EINVAL;
 
+	device_remove_file(&dev->gadget.dev, &dev_attr_wakeup);
 	driver->unbind(&dev->gadget);
 	dev->gadget.dev.driver = NULL;
 	dev->driver = NULL;
diff --git a/include/linux/usb/msm_hsusb_hw.h b/include/linux/usb/msm_hsusb_hw.h
index cfbd0aa..6f54268 100644
--- a/include/linux/usb/msm_hsusb_hw.h
+++ b/include/linux/usb/msm_hsusb_hw.h
@@ -171,6 +171,9 @@ struct ept_queue_item {
 #define PORTSC_PSPD_LS        (1 << 26)
 #define PORTSC_PSPD_HS        (2 << 26)
 #define PORTSC_PSPD_MASK      (3 << 26)
+/* suspend and remote wakeup */
+#define PORTSC_FPR             (1 << 6)
+#define PORTSC_SUSP            (1 << 7)
 
 #define PORTSC_PTS_MASK       (3 << 30)
 #define PORTSC_PTS_ULPI       (2 << 30)
-- 
1.7.1

--
Sent by a consultant of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux