From 876e0df902c726408c84b75dab673a90fd492e1d Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Tue, 22 Nov 2011 18:04:45 -0800 Subject: [PATCH 1/4] usb: Remove ehci_reset call from ehci_run Remove the ehci_reset() call done in the ehci_run() routine of the USB EHCI host controller driver and add an ehci_reset() call to the probe processing of all EHCI platform drivers that do not already call ehci_reset(). The call to ehci_reset() from ehci_run() was problematic for several platform drivers, and unnecessary for others. This change moves the decision to call ehci_reset() at driver startup to the platform driver code. Signed-off-by: Geoff Levand Acked-by: Alan Stern --- drivers/usb/host/ehci-au1xxx.c | 1 + drivers/usb/host/ehci-hcd.c | 11 +---------- drivers/usb/host/ehci-octeon.c | 2 ++ drivers/usb/host/ehci-omap.c | 2 ++ drivers/usb/host/ehci-s5p.c | 2 ++ drivers/usb/host/ehci-vt8500.c | 2 ++ drivers/usb/host/ehci-w90x900.c | 2 ++ 7 files changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c index 18bafa99fe57..bf7441afed16 100644 --- a/drivers/usb/host/ehci-au1xxx.c +++ b/drivers/usb/host/ehci-au1xxx.c @@ -23,6 +23,7 @@ static int au1xxx_ehci_setup(struct usb_hcd *hcd) int ret = ehci_init(hcd); ehci->need_io_watchdog = 0; + ehci_reset(ehci); return ret; } diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 3ff9f82f7263..46dccbf85c1a 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -677,22 +677,13 @@ static int ehci_init(struct usb_hcd *hcd) static int ehci_run (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - int retval; u32 temp; u32 hcc_params; hcd->uses_new_polling = 1; /* EHCI spec section 4.1 */ - /* - * TDI driver does the ehci_reset in their reset callback. - * Don't reset here, because configuration settings will - * vanish. - */ - if (!ehci_is_TDI(ehci) && (retval = ehci_reset(ehci)) != 0) { - ehci_mem_cleanup(ehci); - return retval; - } + ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list); ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next); diff --git a/drivers/usb/host/ehci-octeon.c b/drivers/usb/host/ehci-octeon.c index ba1f51361134..c0104882c72d 100644 --- a/drivers/usb/host/ehci-octeon.c +++ b/drivers/usb/host/ehci-octeon.c @@ -155,6 +155,8 @@ static int ehci_octeon_drv_probe(struct platform_device *pdev) /* cache this readonly data; minimize chip reads */ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + ehci_reset(ehci); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) { dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret); diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index e39b0297bad1..e33baf9052cb 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -228,6 +228,8 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) /* cache this readonly data; minimize chip reads */ omap_ehci->hcs_params = readl(&omap_ehci->caps->hcs_params); + ehci_reset(omap_ehci); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) { dev_err(dev, "failed to add hcd with err %d\n", ret); diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index 024b65c4990d..397f827387b7 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -136,6 +136,8 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev) /* cache this readonly data; minimize chip reads */ ehci->hcs_params = readl(&ehci->caps->hcs_params); + ehci_reset(ehci); + err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) { dev_err(&pdev->dev, "Failed to add USB HCD\n"); diff --git a/drivers/usb/host/ehci-vt8500.c b/drivers/usb/host/ehci-vt8500.c index 54d1ab8aec49..c1eda73916cd 100644 --- a/drivers/usb/host/ehci-vt8500.c +++ b/drivers/usb/host/ehci-vt8500.c @@ -132,6 +132,8 @@ static int vt8500_ehci_drv_probe(struct platform_device *pdev) ehci_port_power(ehci, 1); + ehci_reset(ehci); + ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED); if (ret == 0) { diff --git a/drivers/usb/host/ehci-w90x900.c b/drivers/usb/host/ehci-w90x900.c index d661cf7de140..3d2e26cbb34c 100644 --- a/drivers/usb/host/ehci-w90x900.c +++ b/drivers/usb/host/ehci-w90x900.c @@ -78,6 +78,8 @@ static int __devinit usb_w90x900_probe(const struct hc_driver *driver, if (irq < 0) goto err4; + ehci_reset(ehci); + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); if (retval != 0) goto err4; From 9187bef2fa395e85d5e22c4792b553d98410ccd6 Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Tue, 22 Nov 2011 18:33:45 -0800 Subject: [PATCH 2/4] usb: PS3 EHCI HC reset work-around PS3 EHCI HC errata fix 316 - The PS3 EHCI HC will reset its internal INSNREGXX setup regs back to the chip default values on Host Controller Reset (CMD_RESET) or Light Host Controller Reset (CMD_LRESET). The work-around for this is for the HC driver to re-initialise these regs when ever the HC is reset. Adds a new helper routine ps3_ehci_setup_insnreg() which is called from ps3_ehci_hc_reset(). Signed-off-by: Geoff Levand Acked-by: Alan Stern --- drivers/usb/host/ehci-ps3.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index 2dc32da75cfc..a20e496eb479 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c @@ -21,6 +21,34 @@ #include #include +static void ps3_ehci_setup_insnreg(struct ehci_hcd *ehci) +{ + /* PS3 HC internal setup register offsets. */ + + enum ps3_ehci_hc_insnreg { + ps3_ehci_hc_insnreg01 = 0x084, + ps3_ehci_hc_insnreg02 = 0x088, + ps3_ehci_hc_insnreg03 = 0x08c, + }; + + /* PS3 EHCI HC errata fix 316 - The PS3 EHCI HC will reset its + * internal INSNREGXX setup regs back to the chip default values + * on Host Controller Reset (CMD_RESET) or Light Host Controller + * Reset (CMD_LRESET). The work-around for this is for the HC + * driver to re-initialise these regs when ever the HC is reset. + */ + + /* Set burst transfer counts to 256 out, 32 in. */ + + writel_be(0x01000020, (void __iomem *)ehci->regs + + ps3_ehci_hc_insnreg01); + + /* Enable burst transfer counts. */ + + writel_be(0x00000001, (void __iomem *)ehci->regs + + ps3_ehci_hc_insnreg03); +} + static int ps3_ehci_hc_reset(struct usb_hcd *hcd) { int result; @@ -49,6 +77,8 @@ static int ps3_ehci_hc_reset(struct usb_hcd *hcd) ehci_reset(ehci); + ps3_ehci_setup_insnreg(ehci); + return result; } From df7c1ca229ebffe14a6fb3f13d16b1dd2a1731cb Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Wed, 30 Nov 2011 16:40:57 -0800 Subject: [PATCH 3/4] usb: Fix PS3 EHCI suspend The EHCI USB controller of the Cell Super Companion Chip used in the PS3 will stop the root hub after all root hub ports are suspended. When in this condition the ehci-hcd handshake routine will return -ETIMEDOUT and the USB runtime suspend sequence will fail. The STS_HLT bit will not be set, so inspection of the frame index is used to test for the condition. Add a new routine handshake_for_broken_root_hub() that is called after an unsuccessful -ETIMEDOUT handshake. On PS3 handshake_for_broken_root_hub() will test for the condition, and if found will return success to allow the USB suspend to complete. For all other platforms handshake_for_broken_root_hub() will return -ETIMEDOUT Signed-off-by: Geoff Levand Signed-off-by: Alan Stern --- drivers/usb/host/ehci-hcd.c | 50 +++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 46dccbf85c1a..e0ca9955880b 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -48,6 +48,10 @@ #include #include +#if defined(CONFIG_PPC_PS3) +#include +#endif + /*-------------------------------------------------------------------------*/ /* @@ -230,12 +234,58 @@ static int ehci_halt (struct ehci_hcd *ehci) STS_HALT, STS_HALT, 16 * 125); } +#if defined(CONFIG_USB_SUSPEND) && defined(CONFIG_PPC_PS3) + +/* + * The EHCI controller of the Cell Super Companion Chip used in the + * PS3 will stop the root hub after all root hub ports are suspended. + * When in this condition handshake will return -ETIMEDOUT. The + * STS_HLT bit will not be set, so inspection of the frame index is + * used here to test for the condition. If the condition is found + * return success to allow the USB suspend to complete. + */ + +static int handshake_for_broken_root_hub(struct ehci_hcd *ehci, + void __iomem *ptr, u32 mask, u32 done, + int usec) +{ + unsigned int old_index; + int error; + + if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) + return -ETIMEDOUT; + + old_index = ehci_read_frame_index(ehci); + + error = handshake(ehci, ptr, mask, done, usec); + + if (error == -ETIMEDOUT && ehci_read_frame_index(ehci) == old_index) + return 0; + + return error; +} + +#else + +static int handshake_for_broken_root_hub(struct ehci_hcd *ehci, + void __iomem *ptr, u32 mask, u32 done, + int usec) +{ + return -ETIMEDOUT; +} + +#endif + static int handshake_on_error_set_halt(struct ehci_hcd *ehci, void __iomem *ptr, u32 mask, u32 done, int usec) { int error; error = handshake(ehci, ptr, mask, done, usec); + if (error == -ETIMEDOUT) + error = handshake_for_broken_root_hub(ehci, ptr, mask, done, + usec); + if (error) { ehci_halt(ehci); ehci->rh_state = EHCI_RH_HALTED; From aaa0ef289afe9186f81e2340114ea413eef0492a Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Tue, 8 Nov 2011 16:01:18 -0800 Subject: [PATCH 4/4] usb: PS3 EHCI QH read work-around PS3 EHCI HC errata fix 244. The SCC EHCI HC will not correctly perform QH reads that occur near or span a micro-frame boundry. This is due to a problem in the Nak Count Reload Control logic (EHCI Specification 1.0 Section 4.9.1). The work-around for this problem is for the HC driver to set I=1 (inactive) for QHs with H=1 (list head). Signed-off-by: Geoff Levand Acked-by: Alan Stern --- drivers/usb/host/ehci-hcd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index e0ca9955880b..9496b7d7ab25 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -670,6 +670,7 @@ static int ehci_init(struct usb_hcd *hcd) hw = ehci->async->hw; hw->hw_next = QH_NEXT(ehci, ehci->async->qh_dma); hw->hw_info1 = cpu_to_hc32(ehci, QH_HEAD); + hw->hw_info1 |= cpu_to_hc32(ehci, (1 << 7)); /* I = 1 */ hw->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT); hw->hw_qtd_next = EHCI_LIST_END(ehci); ehci->async->qh_state = QH_STATE_LINKED;