diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index 0909b088a284..31ff5348020c 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -4763,6 +4763,78 @@ static void dwc2_gadget_set_speed(struct usb_gadget *g, enum usb_device_speed sp
 	spin_unlock_irqrestore(&hsotg->lock, flags);
 }
 
+/**
+ * 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_dbg(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_dbg(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_dbg(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_dbg(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_dbg(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;
+}
+
 static const struct usb_gadget_ops dwc2_hsotg_gadget_ops = {
 	.get_frame	= dwc2_hsotg_gadget_getframe,
 	.set_selfpowered	= dwc2_hsotg_set_selfpowered,
@@ -4772,6 +4844,7 @@ static const struct usb_gadget_ops dwc2_hsotg_gadget_ops = {
 	.udc_set_speed		= dwc2_gadget_set_speed,
 	.vbus_session		= dwc2_hsotg_vbus_session,
 	.vbus_draw		= dwc2_hsotg_vbus_draw,
+	.wakeup			= dwc2_hsotg_wakeup,
 };
 
 /**
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index ca0a7d9eaa34..cf11ab8e88e0 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -54,6 +54,8 @@ struct f_hidg {
 	 *              will be used to receive reports.
 	 */
 	bool				use_out_ep;
+	/* attempt to wake up the host before write */
+	bool				wakeup_on_write;
 
 	/* recv report */
 	spinlock_t			read_spinlock;
@@ -422,10 +424,19 @@ 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);
+
 	spin_lock_irqsave(&hidg->write_spinlock, flags);
 
 	if (!hidg->req) {
@@ -1101,6 +1112,7 @@ CONFIGFS_ATTR(f_hid_opts_, name)
 F_HID_OPT(subclass, 8, 255);
 F_HID_OPT(protocol, 8, 255);
 F_HID_OPT(no_out_endpoint, 8, 1);
+F_HID_OPT(wakeup_on_write, 8, 1);
 F_HID_OPT(report_length, 16, 65535);
 
 static ssize_t f_hid_opts_report_desc_show(struct config_item *item, char *page)
@@ -1161,6 +1173,7 @@ static struct configfs_attribute *hid_attrs[] = {
 	&f_hid_opts_attr_subclass,
 	&f_hid_opts_attr_protocol,
 	&f_hid_opts_attr_no_out_endpoint,
+	&f_hid_opts_attr_wakeup_on_write,
 	&f_hid_opts_attr_report_length,
 	&f_hid_opts_attr_report_desc,
 	&f_hid_opts_attr_dev,
@@ -1293,6 +1306,7 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi)
 		}
 	}
 	hidg->use_out_ep = !opts->no_out_endpoint;
+	hidg->wakeup_on_write = opts->wakeup_on_write;
 
 	mutex_unlock(&opts->lock);
 
diff --git a/drivers/usb/gadget/function/u_hid.h b/drivers/usb/gadget/function/u_hid.h
index 84bb70292855..f7fcaf1eaf1d 100644
--- a/drivers/usb/gadget/function/u_hid.h
+++ b/drivers/usb/gadget/function/u_hid.h
@@ -21,6 +21,7 @@ struct f_hid_opts {
 	unsigned char			subclass;
 	unsigned char			protocol;
 	unsigned char			no_out_endpoint;
+	unsigned char			wakeup_on_write;
 	unsigned short			report_length;
 	unsigned short			report_desc_length;
 	unsigned char			*report_desc;
