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