aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKever Yang <kever.yang@rock-chips.com>2014-11-10 08:09:43 -0500
committerFelipe Balbi <balbi@ti.com>2014-11-12 10:12:34 -0500
commit0cf884e819e05437287a668b9bfcc198bab6329c (patch)
tree2c67fee121cc0a8f0f65ea24f3295247eb6e3cfb
parentd3cf6a4b01894e69e9e800ef76f34eaa13357ff1 (diff)
usb: dwc2: add bus suspend/resume for dwc2
Hcd controller needs bus_suspend/resume, dwc2 controller make root hub generate suspend/resume signal with hprt0 register when work in host mode. After the root hub enter suspend, we can make controller enter low power state with PCGCTL register. We also update the lx_state for hsotg state. This patch has tested on rk3288 with suspend/resume. Signed-off-by: Kever Yang <kever.yang@rock-chips.com> Acked-by: Paul Zimmerman <paulz@synopsys.com> Signed-off-by: Felipe Balbi <balbi@ti.com>
-rw-r--r--drivers/usb/dwc2/hcd.c88
1 files changed, 77 insertions, 11 deletions
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 0a0e6f0ad15f..74800786a169 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -1471,6 +1471,30 @@ static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex)
1471 } 1471 }
1472} 1472}
1473 1473
1474static void dwc2_port_resume(struct dwc2_hsotg *hsotg)
1475{
1476 u32 hprt0;
1477
1478 /* After clear the Stop PHY clock bit, we should wait for a moment
1479 * for PLL work stable with clock output.
1480 */
1481 writel(0, hsotg->regs + PCGCTL);
1482 usleep_range(2000, 4000);
1483
1484 hprt0 = dwc2_read_hprt0(hsotg);
1485 hprt0 |= HPRT0_RES;
1486 writel(hprt0, hsotg->regs + HPRT0);
1487 hprt0 &= ~HPRT0_SUSP;
1488 /* according to USB2.0 Spec 7.1.7.7, the host must send the resume
1489 * signal for at least 20ms
1490 */
1491 usleep_range(20000, 25000);
1492
1493 hprt0 &= ~HPRT0_RES;
1494 writel(hprt0, hsotg->regs + HPRT0);
1495 hsotg->lx_state = DWC2_L0;
1496}
1497
1474/* Handles hub class-specific requests */ 1498/* Handles hub class-specific requests */
1475static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, 1499static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
1476 u16 wvalue, u16 windex, char *buf, u16 wlength) 1500 u16 wvalue, u16 windex, char *buf, u16 wlength)
@@ -1516,17 +1540,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
1516 case USB_PORT_FEAT_SUSPEND: 1540 case USB_PORT_FEAT_SUSPEND:
1517 dev_dbg(hsotg->dev, 1541 dev_dbg(hsotg->dev,
1518 "ClearPortFeature USB_PORT_FEAT_SUSPEND\n"); 1542 "ClearPortFeature USB_PORT_FEAT_SUSPEND\n");
1519 writel(0, hsotg->regs + PCGCTL); 1543 dwc2_port_resume(hsotg);
1520 usleep_range(20000, 40000);
1521
1522 hprt0 = dwc2_read_hprt0(hsotg);
1523 hprt0 |= HPRT0_RES;
1524 writel(hprt0, hsotg->regs + HPRT0);
1525 hprt0 &= ~HPRT0_SUSP;
1526 usleep_range(100000, 150000);
1527
1528 hprt0 &= ~HPRT0_RES;
1529 writel(hprt0, hsotg->regs + HPRT0);
1530 break; 1544 break;
1531 1545
1532 case USB_PORT_FEAT_POWER: 1546 case USB_PORT_FEAT_POWER:
@@ -2299,6 +2313,55 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
2299 usleep_range(1000, 3000); 2313 usleep_range(1000, 3000);
2300} 2314}
2301 2315
2316static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
2317{
2318 struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
2319 u32 hprt0;
2320
2321 if (!((hsotg->op_state == OTG_STATE_B_HOST) ||
2322 (hsotg->op_state == OTG_STATE_A_HOST)))
2323 return 0;
2324
2325 /* TODO: We get into suspend from 'on' state, maybe we need to do
2326 * something if we get here from DWC2_L1(LPM sleep) state one day.
2327 */
2328 if (hsotg->lx_state != DWC2_L0)
2329 return 0;
2330
2331 hprt0 = dwc2_read_hprt0(hsotg);
2332 if (hprt0 & HPRT0_CONNSTS) {
2333 dwc2_port_suspend(hsotg, 1);
2334 } else {
2335 u32 pcgctl = readl(hsotg->regs + PCGCTL);
2336
2337 pcgctl |= PCGCTL_STOPPCLK;
2338 writel(pcgctl, hsotg->regs + PCGCTL);
2339 }
2340
2341 return 0;
2342}
2343
2344static int _dwc2_hcd_resume(struct usb_hcd *hcd)
2345{
2346 struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
2347 u32 hprt0;
2348
2349 if (!((hsotg->op_state == OTG_STATE_B_HOST) ||
2350 (hsotg->op_state == OTG_STATE_A_HOST)))
2351 return 0;
2352
2353 if (hsotg->lx_state != DWC2_L2)
2354 return 0;
2355
2356 hprt0 = dwc2_read_hprt0(hsotg);
2357 if ((hprt0 & HPRT0_CONNSTS) && (hprt0 & HPRT0_SUSP))
2358 dwc2_port_resume(hsotg);
2359 else
2360 writel(0, hsotg->regs + PCGCTL);
2361
2362 return 0;
2363}
2364
2302/* Returns the current frame number */ 2365/* Returns the current frame number */
2303static int _dwc2_hcd_get_frame_number(struct usb_hcd *hcd) 2366static int _dwc2_hcd_get_frame_number(struct usb_hcd *hcd)
2304{ 2367{
@@ -2669,6 +2732,9 @@ static struct hc_driver dwc2_hc_driver = {
2669 .hub_status_data = _dwc2_hcd_hub_status_data, 2732 .hub_status_data = _dwc2_hcd_hub_status_data,
2670 .hub_control = _dwc2_hcd_hub_control, 2733 .hub_control = _dwc2_hcd_hub_control,
2671 .clear_tt_buffer_complete = _dwc2_hcd_clear_tt_buffer_complete, 2734 .clear_tt_buffer_complete = _dwc2_hcd_clear_tt_buffer_complete,
2735
2736 .bus_suspend = _dwc2_hcd_suspend,
2737 .bus_resume = _dwc2_hcd_resume,
2672}; 2738};
2673 2739
2674/* 2740/*