|
@@ -1013,6 +1013,9 @@ static void r8a66597_check_syssts(struct r8a66597 *r8a66597, int port,
|
|
|
|
|
|
r8a66597_write(r8a66597, ~DTCH, get_intsts_reg(port));
|
|
|
r8a66597_bset(r8a66597, DTCHE, get_intenb_reg(port));
|
|
|
+
|
|
|
+ if (r8a66597->bus_suspended)
|
|
|
+ usb_hcd_resume_root_hub(r8a66597_to_hcd(r8a66597));
|
|
|
}
|
|
|
|
|
|
/* this function must be called with interrupt disabled */
|
|
@@ -1614,6 +1617,11 @@ static irqreturn_t r8a66597_irq(struct usb_hcd *hcd)
|
|
|
r8a66597_bclr(r8a66597, DTCHE, INTENB2);
|
|
|
r8a66597_usb_disconnect(r8a66597, 1);
|
|
|
}
|
|
|
+ if (mask2 & BCHG) {
|
|
|
+ r8a66597_write(r8a66597, ~BCHG, INTSTS2);
|
|
|
+ r8a66597_bclr(r8a66597, BCHGE, INTENB2);
|
|
|
+ usb_hcd_resume_root_hub(r8a66597_to_hcd(r8a66597));
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
if (mask1) {
|
|
@@ -1629,6 +1637,12 @@ static irqreturn_t r8a66597_irq(struct usb_hcd *hcd)
|
|
|
r8a66597_bclr(r8a66597, DTCHE, INTENB1);
|
|
|
r8a66597_usb_disconnect(r8a66597, 0);
|
|
|
}
|
|
|
+ if (mask1 & BCHG) {
|
|
|
+ r8a66597_write(r8a66597, ~BCHG, INTSTS1);
|
|
|
+ r8a66597_bclr(r8a66597, BCHGE, INTENB1);
|
|
|
+ usb_hcd_resume_root_hub(r8a66597_to_hcd(r8a66597));
|
|
|
+ }
|
|
|
+
|
|
|
if (mask1 & SIGN) {
|
|
|
r8a66597_write(r8a66597, ~SIGN, INTSTS1);
|
|
|
status = get_urb_error(r8a66597, 0);
|
|
@@ -2140,7 +2154,7 @@ static int r8a66597_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
|
|
|
|
|
switch (wValue) {
|
|
|
case USB_PORT_FEAT_ENABLE:
|
|
|
- rh->port &= (1 << USB_PORT_FEAT_POWER);
|
|
|
+ rh->port &= ~(1 << USB_PORT_FEAT_POWER);
|
|
|
break;
|
|
|
case USB_PORT_FEAT_SUSPEND:
|
|
|
break;
|
|
@@ -2212,6 +2226,68 @@ error:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+#if defined(CONFIG_PM)
|
|
|
+static int r8a66597_bus_suspend(struct usb_hcd *hcd)
|
|
|
+{
|
|
|
+ struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
|
|
|
+ int port;
|
|
|
+
|
|
|
+ dbg("%s", __func__);
|
|
|
+
|
|
|
+ for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++) {
|
|
|
+ struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
|
|
|
+ unsigned long dvstctr_reg = get_dvstctr_reg(port);
|
|
|
+
|
|
|
+ if (!(rh->port & (1 << USB_PORT_FEAT_ENABLE)))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ dbg("suspend port = %d", port);
|
|
|
+ r8a66597_bclr(r8a66597, UACT, dvstctr_reg); /* suspend */
|
|
|
+ rh->port |= 1 << USB_PORT_FEAT_SUSPEND;
|
|
|
+
|
|
|
+ if (rh->dev->udev->do_remote_wakeup) {
|
|
|
+ msleep(3); /* waiting last SOF */
|
|
|
+ r8a66597_bset(r8a66597, RWUPE, dvstctr_reg);
|
|
|
+ r8a66597_write(r8a66597, ~BCHG, get_intsts_reg(port));
|
|
|
+ r8a66597_bset(r8a66597, BCHGE, get_intenb_reg(port));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ r8a66597->bus_suspended = 1;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int r8a66597_bus_resume(struct usb_hcd *hcd)
|
|
|
+{
|
|
|
+ struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
|
|
|
+ int port;
|
|
|
+
|
|
|
+ dbg("%s", __func__);
|
|
|
+
|
|
|
+ for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++) {
|
|
|
+ struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
|
|
|
+ unsigned long dvstctr_reg = get_dvstctr_reg(port);
|
|
|
+
|
|
|
+ if (!(rh->port & (1 << USB_PORT_FEAT_SUSPEND)))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ dbg("resume port = %d", port);
|
|
|
+ rh->port &= ~(1 << USB_PORT_FEAT_SUSPEND);
|
|
|
+ rh->port |= 1 << USB_PORT_FEAT_C_SUSPEND;
|
|
|
+ r8a66597_mdfy(r8a66597, RESUME, RESUME | UACT, dvstctr_reg);
|
|
|
+ msleep(50);
|
|
|
+ r8a66597_mdfy(r8a66597, UACT, RESUME | UACT, dvstctr_reg);
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+}
|
|
|
+#else
|
|
|
+#define r8a66597_bus_suspend NULL
|
|
|
+#define r8a66597_bus_resume NULL
|
|
|
+#endif
|
|
|
+
|
|
|
static struct hc_driver r8a66597_hc_driver = {
|
|
|
.description = hcd_name,
|
|
|
.hcd_priv_size = sizeof(struct r8a66597),
|
|
@@ -2242,16 +2318,39 @@ static struct hc_driver r8a66597_hc_driver = {
|
|
|
*/
|
|
|
.hub_status_data = r8a66597_hub_status_data,
|
|
|
.hub_control = r8a66597_hub_control,
|
|
|
+ .bus_suspend = r8a66597_bus_suspend,
|
|
|
+ .bus_resume = r8a66597_bus_resume,
|
|
|
};
|
|
|
|
|
|
#if defined(CONFIG_PM)
|
|
|
static int r8a66597_suspend(struct platform_device *pdev, pm_message_t state)
|
|
|
{
|
|
|
+ struct r8a66597 *r8a66597 = dev_get_drvdata(&pdev->dev);
|
|
|
+ int port;
|
|
|
+
|
|
|
+ dbg("%s", __func__);
|
|
|
+
|
|
|
+ disable_controller(r8a66597);
|
|
|
+
|
|
|
+ for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++) {
|
|
|
+ struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
|
|
|
+
|
|
|
+ rh->port = 0x00000000;
|
|
|
+ }
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
static int r8a66597_resume(struct platform_device *pdev)
|
|
|
{
|
|
|
+ struct r8a66597 *r8a66597 = dev_get_drvdata(&pdev->dev);
|
|
|
+ struct usb_hcd *hcd = r8a66597_to_hcd(r8a66597);
|
|
|
+
|
|
|
+ dbg("%s", __func__);
|
|
|
+
|
|
|
+ enable_controller(r8a66597);
|
|
|
+ usb_root_hub_lost_power(hcd->self.root_hub);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
#else /* if defined(CONFIG_PM) */
|