mirror of
https://github.com/sipeed/LicheeRV-Nano-Build.git
synced 2026-09-11 05:09:56 -05:00
Added support for USB remote wakeup
This commit is contained in:
@@ -4306,6 +4306,78 @@ static int dwc2_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_hsotg_wakeup - send wakeup signal to the host
|
||||
* @gadget: The usb gadget state
|
||||
*
|
||||
* If the gadget is in device mode and in the L1 or L2 state,
|
||||
* it sends a wakeup signal to the host.
|
||||
*/
|
||||
static int dwc2_hsotg_wakeup(struct usb_gadget *gadget)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = to_hsotg(gadget);
|
||||
int ret = -1;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
|
||||
if (!hsotg->remote_wakeup_allowed) {
|
||||
dev_info(hsotg->dev,
|
||||
"wakeup: signalling skipped: is not allowed by host\n");
|
||||
goto skip;
|
||||
}
|
||||
if (hsotg->lx_state != DWC2_L1 && hsotg->lx_state != DWC2_L2) {
|
||||
dev_info(hsotg->dev,
|
||||
"wakeup: signalling skipped: gadget not in L1/L2 state: %d\n", hsotg->lx_state);
|
||||
goto skip;
|
||||
}
|
||||
if (!dwc2_is_device_mode(hsotg)) {
|
||||
dev_info(hsotg->dev,
|
||||
"wakeup: signalling skipped: gadget not in device mode\n");
|
||||
goto skip;
|
||||
}
|
||||
|
||||
/*if (hsotg->in_ppd) {
|
||||
if (dwc2_exit_partial_power_down(hsotg, 1, true))
|
||||
dev_err(hsotg->dev, "wakeup: exit partial_power_down failed\n");
|
||||
call_gadget(hsotg, resume);
|
||||
}*/
|
||||
if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended) {
|
||||
u32 pcgctl;
|
||||
|
||||
dev_info(hsotg->dev, "wakeup: exiting device clock gating\n");
|
||||
|
||||
/* Clear the Gate hclk. */
|
||||
pcgctl = dwc2_readl(hsotg, PCGCTL);
|
||||
pcgctl &= ~PCGCTL_GATEHCLK;
|
||||
dwc2_writel(hsotg, pcgctl, PCGCTL);
|
||||
udelay(5);
|
||||
|
||||
/* Phy Clock bit. */
|
||||
pcgctl = dwc2_readl(hsotg, PCGCTL);
|
||||
pcgctl &= ~PCGCTL_STOPPCLK;
|
||||
dwc2_writel(hsotg, pcgctl, PCGCTL);
|
||||
udelay(5);
|
||||
|
||||
hsotg->bus_suspended = false;
|
||||
}
|
||||
|
||||
dev_info(hsotg->dev, "wakeup: sending signal to the host");
|
||||
|
||||
dwc2_set_bit(hsotg, DCTL, DCTL_RMTWKUPSIG);
|
||||
mdelay(10);
|
||||
dwc2_clear_bit(hsotg, DCTL, DCTL_RMTWKUPSIG);
|
||||
|
||||
/* After the signalling, the USB core wakes up to L0 */
|
||||
call_gadget(hsotg, resume);
|
||||
hsotg->lx_state = DWC2_L0;
|
||||
|
||||
ret = 0;
|
||||
skip:
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_hsotg_ep_sethalt - set halt on a given endpoint
|
||||
* @ep: The endpoint to set halt.
|
||||
@@ -4688,6 +4760,7 @@ static const struct usb_gadget_ops dwc2_hsotg_gadget_ops = {
|
||||
.pullup = dwc2_hsotg_pullup,
|
||||
.vbus_session = dwc2_hsotg_vbus_session,
|
||||
.vbus_draw = dwc2_hsotg_vbus_draw,
|
||||
.wakeup = dwc2_hsotg_wakeup,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -44,6 +44,8 @@ struct f_hidg {
|
||||
unsigned short report_desc_length;
|
||||
char *report_desc;
|
||||
unsigned short report_length;
|
||||
bool use_out_ep;
|
||||
bool wakeup_on_write;
|
||||
|
||||
/* recv report */
|
||||
struct list_head completed_out_req;
|
||||
@@ -332,10 +334,23 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer,
|
||||
size_t count, loff_t *offp)
|
||||
{
|
||||
struct f_hidg *hidg = file->private_data;
|
||||
struct usb_composite_dev *cdev = hidg->func.config->cdev;
|
||||
struct usb_request *req;
|
||||
unsigned long flags;
|
||||
ssize_t status = -ENOMEM;
|
||||
|
||||
/*
|
||||
* remote wakeup is allowed only when the corresponding bit
|
||||
* in config descriptor is set and wakeup_on_write is enabled.
|
||||
* FIXME: cdev->config can be NULLed on disconnect.
|
||||
*/
|
||||
if (hidg->wakeup_on_write /*&& cdev->config->bmAttributes & 0x20*/){
|
||||
usb_gadget_wakeup(cdev->gadget);
|
||||
ERROR(hidg->func.config->cdev,
|
||||
"usb_gadget_wakeup\n");
|
||||
}
|
||||
|
||||
|
||||
spin_lock_irqsave(&hidg->write_spinlock, flags);
|
||||
|
||||
#define WRITE_COND (!hidg->write_pending)
|
||||
@@ -918,6 +933,7 @@ CONFIGFS_ATTR(f_hid_opts_, name)
|
||||
F_HID_OPT(subclass, 8, 255);
|
||||
F_HID_OPT(protocol, 8, 255);
|
||||
F_HID_OPT(report_length, 16, 65535);
|
||||
F_HID_OPT(wakeup_on_write, 8, 1);
|
||||
|
||||
static ssize_t f_hid_opts_report_desc_show(struct config_item *item, char *page)
|
||||
{
|
||||
@@ -976,6 +992,7 @@ CONFIGFS_ATTR_RO(f_hid_opts_, dev);
|
||||
static struct configfs_attribute *hid_attrs[] = {
|
||||
&f_hid_opts_attr_subclass,
|
||||
&f_hid_opts_attr_protocol,
|
||||
&f_hid_opts_attr_wakeup_on_write,
|
||||
&f_hid_opts_attr_report_length,
|
||||
&f_hid_opts_attr_report_desc,
|
||||
&f_hid_opts_attr_dev,
|
||||
@@ -1106,6 +1123,7 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
}
|
||||
hidg->wakeup_on_write = opts->wakeup_on_write;
|
||||
|
||||
mutex_unlock(&opts->lock);
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ struct f_hid_opts {
|
||||
int minor;
|
||||
unsigned char subclass;
|
||||
unsigned char protocol;
|
||||
unsigned char wakeup_on_write;
|
||||
unsigned short report_length;
|
||||
unsigned short report_desc_length;
|
||||
unsigned char *report_desc;
|
||||
|
||||
@@ -123,7 +123,7 @@ static struct usb_configuration config_driver = {
|
||||
.label = "HID Gadget",
|
||||
.bConfigurationValue = 1,
|
||||
/* .iConfiguration = DYNAMIC */
|
||||
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
||||
.bmAttributes = USB_CONFIG_ATT_SELFPOWER | USB_CONFIG_ATT_WAKEUP,
|
||||
};
|
||||
|
||||
/****************************** Gadget Bind ******************************/
|
||||
|
||||
@@ -499,6 +499,8 @@ int usb_gadget_wakeup(struct usb_gadget *gadget)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
// printf("usb_gadget_wakeup \r\n");
|
||||
// dev_err(dev, "usb_gadget_wakeup\n");
|
||||
if (!gadget->ops->wakeup) {
|
||||
ret = -EOPNOTSUPP;
|
||||
goto out;
|
||||
|
||||
Reference in New Issue
Block a user