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 <pkond...@codeaurora.org>
Signed-off-by: Vamsi Krishna <vskri...@codeaurora.org>
---
 drivers/usb/gadget/msm72k_udc.c  |   57 ++++++++++++++++++++++++++++++++++++++
 include/linux/usb/msm_hsusb_hw.h |    3 ++
 2 files changed, 60 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c
index 84315a2..9922d3d 100644
--- a/drivers/usb/gadget/msm72k_udc.c
+++ b/drivers/usb/gadget/msm72k_udc.c
@@ -167,6 +167,7 @@ struct usb_info {
        struct clk *pclk;
 
        unsigned int ep0_dir;
+       u8 remote_wakeup;
 };
 
 static const struct usb_ep_ops msm72k_ep_ops;
@@ -585,6 +586,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;
                        }
@@ -630,6 +633,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;
                }
        }
 
@@ -1538,12 +1551,49 @@ 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) {
+               ERROR("%s: remote wakeup not supported\n", __func__);
+               return -ENOTSUPP;
+       }
+
+       if (!ui->online) {
+               ERROR("%s: device is not configured\n", __func__);
+               return -ENODEV;
+       }
+
+       spin_lock_irqsave(&ui->lock, flags);
+       if ((readl(USB_PORTSC) & PORTSC_SUSP) == PORTSC_SUSP) {
+               INFO("%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;
@@ -1673,6 +1723,12 @@ int usb_gadget_register_driver(struct usb_gadget_driver 
*driver)
 
        INFO("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)
+               INFO("failed to create sysfs entry: (wakeup) error: (%d)\n",
+                                       retval);
+
        usb_start(ui);
 
        return 0;
@@ -1693,6 +1749,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 ab28be6..ae13342 100644
--- a/include/linux/usb/msm_hsusb_hw.h
+++ b/include/linux/usb/msm_hsusb_hw.h
@@ -200,6 +200,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-arm-msm" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to