From b924b2047045844644e04b3c6e8308b2114afe7e Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 4 Jun 2012 00:56:15 -0700 Subject: ARM: OMAP: Make FS USB omap1 only As the FS USB code is not being actively used for omap2+ there's no point keeping it around for omap2+. Let's make the FS USB platform init code omap1 only so we can remove the last user of omap_read/write for omap2+, and simplify things for further USB, DMA, and device tree related work. While at it, also group the mach includes for the related drivers. Cc: linux-usb@vger.kernel.org Cc: Kyungmin Park Acked-by: Felipe Balbi Signed-off-by: Tony Lindgren --- drivers/usb/host/Kconfig | 2 +- drivers/usb/host/ohci-omap.c | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 83e58df29fe..dcfaaa91a3f 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -308,7 +308,7 @@ config USB_OHCI_HCD config USB_OHCI_HCD_OMAP1 bool "OHCI support for OMAP1/2 chips" - depends on USB_OHCI_HCD && (ARCH_OMAP1 || ARCH_OMAP2) + depends on USB_OHCI_HCD && ARCH_OMAP1 default y ---help--- Enables support for the OHCI controller on OMAP1/2 chips. diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 9ce35d0d9d5..b02c344e2cc 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -20,14 +20,15 @@ #include #include -#include #include #include #include -#include #include -#include + +#include +#include +#include /* OMAP-1510 OHCI has its own MMU for DMA */ -- cgit v1.2.3-70-g09d2 From 20de12ccf900d71eee1344adc7ae0dcd7db9b4ab Mon Sep 17 00:00:00 2001 From: Prashant Gaikwad Date: Tue, 5 Jun 2012 09:59:38 +0530 Subject: USB: ehci-tegra: add clk_prepare/clk_unprepare Use clk_prepare/clk_unprepare as required by the generic clk framework. Cc: stern@rowland.harvard.edu Cc: gregkh@linuxfoundation.org Cc: linux-usb@vger.kernel.org Signed-off-by: Prashant Gaikwad Signed-off-by: Stephen Warren --- drivers/usb/host/ehci-tegra.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 68548236ec4..ab8a3bf628e 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -46,8 +46,8 @@ static void tegra_ehci_power_up(struct usb_hcd *hcd) { struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - clk_enable(tegra->emc_clk); - clk_enable(tegra->clk); + clk_prepare_enable(tegra->emc_clk); + clk_prepare_enable(tegra->clk); tegra_usb_phy_power_on(tegra->phy); tegra->host_resumed = 1; } @@ -58,8 +58,8 @@ static void tegra_ehci_power_down(struct usb_hcd *hcd) tegra->host_resumed = 0; tegra_usb_phy_power_off(tegra->phy); - clk_disable(tegra->clk); - clk_disable(tegra->emc_clk); + clk_disable_unprepare(tegra->clk); + clk_disable_unprepare(tegra->emc_clk); } static int tegra_ehci_internal_port_reset( @@ -671,7 +671,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) goto fail_clk; } - err = clk_enable(tegra->clk); + err = clk_prepare_enable(tegra->clk); if (err) goto fail_clken; @@ -682,7 +682,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) goto fail_emc_clk; } - clk_enable(tegra->emc_clk); + clk_prepare_enable(tegra->emc_clk); clk_set_rate(tegra->emc_clk, 400000000); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -782,10 +782,10 @@ fail: fail_phy: iounmap(hcd->regs); fail_io: - clk_disable(tegra->emc_clk); + clk_disable_unprepare(tegra->emc_clk); clk_put(tegra->emc_clk); fail_emc_clk: - clk_disable(tegra->clk); + clk_disable_unprepare(tegra->clk); fail_clken: clk_put(tegra->clk); fail_clk: @@ -820,10 +820,10 @@ static int tegra_ehci_remove(struct platform_device *pdev) tegra_usb_phy_close(tegra->phy); iounmap(hcd->regs); - clk_disable(tegra->clk); + clk_disable_unprepare(tegra->clk); clk_put(tegra->clk); - clk_disable(tegra->emc_clk); + clk_disable_unprepare(tegra->emc_clk); clk_put(tegra->emc_clk); kfree(tegra); -- cgit v1.2.3-70-g09d2 From 2b97fb5a605635684f757080cdc405d8173838d5 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 25 May 2012 20:14:55 -0300 Subject: usb: imx21-hcd.c: Use clk_prepare_enable/clk_disable_unprepare Prepare the clock before enabling it. Cc: Signed-off-by: Fabio Estevam Acked-by: Sascha Hauer Signed-off-by: Felipe Balbi --- drivers/usb/host/imx21-hcd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c index ff471c1c165..f19e2690c23 100644 --- a/drivers/usb/host/imx21-hcd.c +++ b/drivers/usb/host/imx21-hcd.c @@ -1811,7 +1811,7 @@ static int imx21_remove(struct platform_device *pdev) usb_remove_hcd(hcd); if (res != NULL) { - clk_disable(imx21->clk); + clk_disable_unprepare(imx21->clk); clk_put(imx21->clk); iounmap(imx21->regs); release_mem_region(res->start, resource_size(res)); @@ -1884,7 +1884,7 @@ static int imx21_probe(struct platform_device *pdev) ret = clk_set_rate(imx21->clk, clk_round_rate(imx21->clk, 48000000)); if (ret) goto failed_clock_set; - ret = clk_enable(imx21->clk); + ret = clk_prepare_enable(imx21->clk); if (ret) goto failed_clock_enable; @@ -1900,7 +1900,7 @@ static int imx21_probe(struct platform_device *pdev) return 0; failed_add_hcd: - clk_disable(imx21->clk); + clk_disable_unprepare(imx21->clk); failed_clock_enable: failed_clock_set: clk_put(imx21->clk); -- cgit v1.2.3-70-g09d2 From c2e935a7db6e7354e9dd138b7f6f4c53affc09d9 Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Wed, 13 Jun 2012 20:34:12 +0800 Subject: USB: move transceiver from ehci_hcd and ohci_hcd to hcd and rename it as phy - to decrease redundant since both ehci_hcd and ohci_hcd have the same variable - it helps access phy in usb core code - phy is more meaningful than transceiver Signed-off-by: Richard Zhao Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-fsl.c | 21 ++++++++++----------- drivers/usb/host/ehci-hub.c | 2 +- drivers/usb/host/ehci.h | 4 ---- drivers/usb/host/ohci-omap.c | 27 ++++++++++++++------------- drivers/usb/host/ohci.h | 5 ----- include/linux/usb/hcd.h | 6 ++++++ 6 files changed, 31 insertions(+), 34 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 43362577b54..3379945b095 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -142,19 +142,19 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, if (pdata->operating_mode == FSL_USB2_DR_OTG) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); - ehci->transceiver = usb_get_transceiver(); - dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, transceiver=0x%p\n", - hcd, ehci, ehci->transceiver); + hcd->phy = usb_get_transceiver(); + dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, phy=0x%p\n", + hcd, ehci, hcd->phy); - if (ehci->transceiver) { - retval = otg_set_host(ehci->transceiver->otg, + if (hcd->phy) { + retval = otg_set_host(hcd->phy->otg, &ehci_to_hcd(ehci)->self); if (retval) { - usb_put_transceiver(ehci->transceiver); + usb_put_transceiver(hcd->phy); goto err4; } } else { - dev_err(&pdev->dev, "can't find transceiver\n"); + dev_err(&pdev->dev, "can't find phy\n"); retval = -ENODEV; goto err4; } @@ -190,11 +190,10 @@ static void usb_hcd_fsl_remove(struct usb_hcd *hcd, struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - if (ehci->transceiver) { - otg_set_host(ehci->transceiver->otg, NULL); - usb_put_transceiver(ehci->transceiver); + if (hcd->phy) { + otg_set_host(hcd->phy->otg, NULL); + usb_put_transceiver(hcd->phy); } usb_remove_hcd(hcd); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index fc9e7cc6ac9..dd5eef6af6d 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -724,7 +724,7 @@ static int ehci_hub_control ( #ifdef CONFIG_USB_OTG if ((hcd->self.otg_port == (wIndex + 1)) && hcd->self.b_hnp_enable) { - otg_start_hnp(ehci->transceiver->otg); + otg_start_hnp(hcd->phy->otg); break; } #endif diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 2694ed6558d..85c3572155d 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -175,10 +175,6 @@ struct ehci_hcd { /* one per controller */ #ifdef DEBUG struct dentry *debug_dir; #endif - /* - * OTG controllers and transceivers need software interaction - */ - struct usb_phy *transceiver; }; /* convert between an HCD pointer and the corresponding EHCI_HCD */ diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 9ce35d0d9d5..eccddb46139 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -167,14 +167,15 @@ static int omap_1510_local_bus_init(void) static void start_hnp(struct ohci_hcd *ohci) { - const unsigned port = ohci_to_hcd(ohci)->self.otg_port - 1; + struct usb_hcd *hcd = ohci_to_hcd(ohci); + const unsigned port = hcd->self.otg_port - 1; unsigned long flags; u32 l; - otg_start_hnp(ohci->transceiver->otg); + otg_start_hnp(hcd->phy->otg); local_irq_save(flags); - ohci->transceiver->state = OTG_STATE_A_SUSPEND; + hcd->phy->state = OTG_STATE_A_SUSPEND; writel (RH_PS_PSS, &ohci->regs->roothub.portstatus [port]); l = omap_readl(OTG_CTRL); l &= ~OTG_A_BUSREQ; @@ -211,18 +212,18 @@ static int ohci_omap_init(struct usb_hcd *hcd) #ifdef CONFIG_USB_OTG if (need_transceiver) { - ohci->transceiver = usb_get_transceiver(); - if (ohci->transceiver) { - int status = otg_set_host(ohci->transceiver->otg, + hcd->phy = usb_get_transceiver(); + if (hcd->phy) { + int status = otg_set_host(hcd->phy->otg, &ohci_to_hcd(ohci)->self); - dev_dbg(hcd->self.controller, "init %s transceiver, status %d\n", - ohci->transceiver->label, status); + dev_dbg(hcd->self.controller, "init %s phy, status %d\n", + hcd->phy->label, status); if (status) { - usb_put_transceiver(ohci->transceiver); + usb_put_transceiver(hcd->phy); return status; } } else { - dev_err(hcd->self.controller, "can't find transceiver\n"); + dev_err(hcd->self.controller, "can't find phy\n"); return -ENODEV; } ohci->start_hnp = start_hnp; @@ -403,9 +404,9 @@ usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev) struct ohci_hcd *ohci = hcd_to_ohci (hcd); usb_remove_hcd(hcd); - if (ohci->transceiver) { - (void) otg_set_host(ohci->transceiver->otg, 0); - usb_put_transceiver(ohci->transceiver); + if (hcd->phy) { + (void) otg_set_host(hcd->phy->otg, 0); + usb_put_transceiver(hcd->phy); } if (machine_is_omap_osk()) gpio_free(9); diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 1b19aea25a2..d3299143d9e 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -372,11 +372,6 @@ struct ohci_hcd { struct ed *ed_controltail; /* last in ctrl list */ struct ed *periodic [NUM_INTS]; /* shadow int_table */ - /* - * OTG controllers and transceivers need software interaction; - * other external transceivers should be software-transparent - */ - struct usb_phy *transceiver; void (*start_hnp)(struct ohci_hcd *ohci); /* diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 7f855d50cdf..c532cbeabfb 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -93,6 +93,12 @@ struct usb_hcd { */ const struct hc_driver *driver; /* hw-specific hooks */ + /* + * OTG and some Host controllers need software interaction with phys; + * other external phys should be software-transparent + */ + struct usb_phy *phy; + /* Flags that need to be manipulated atomically because they can * change while the host controller is running. Always use * set_bit() or clear_bit() to change their values. -- cgit v1.2.3-70-g09d2 From 71c421dbd9d87ffe6229816a04b759ec5613014d Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Fri, 15 Jun 2012 12:01:33 +0200 Subject: usb: correct trivial typo in drivers/usb/host/Kconfig Correct "Enbale" -> "Enable", in the desc for USB_HCD_BCMA and USB_HCD_SSB. Signed-off-by: Jesper Dangaard Brouer Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 83e58df29fe..18ba33da34e 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -652,7 +652,7 @@ config USB_HCD_BCMA select USB_OHCI_HCD_PLATFORM if USB_OHCI_HCD select USB_EHCI_HCD_PLATFORM if USB_EHCI_HCD help - Enbale support for the EHCI and OCHI host controller on an bcma bus. + Enable support for the EHCI and OCHI host controller on an bcma bus. It converts the bcma driver into two platform device drivers for ehci and ohci. @@ -664,7 +664,7 @@ config USB_HCD_SSB select USB_OHCI_HCD_PLATFORM if USB_OHCI_HCD select USB_EHCI_HCD_PLATFORM if USB_EHCI_HCD help - Enbale support for the EHCI and OCHI host controller on an bcma bus. + Enable support for the EHCI and OCHI host controller on an bcma bus. It converts the bcma driver into two platform device drivers for ehci and ohci. -- cgit v1.2.3-70-g09d2 From 78091dc2f6f04b03131218df590c877cadcd9379 Mon Sep 17 00:00:00 2001 From: Alexandre Pereira da Silva Date: Wed, 20 Jun 2012 09:02:23 -0300 Subject: USB: ohci-nxp: add usbd and otg clock initialization The ohci-nxp was assuming the clock was enabled by the board init or bootloader and just enabling the pll. This enables the usbd and otg clocks this periferal also needs. Signed-off-by: Alexandre Pereira da Silva Acked-by: Roland Stigge Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-nxp.c | 88 ++++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 33 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ohci-nxp.c b/drivers/usb/host/ohci-nxp.c index 1e364ec962f..a446386bf77 100644 --- a/drivers/usb/host/ohci-nxp.c +++ b/drivers/usb/host/ohci-nxp.c @@ -43,16 +43,6 @@ #define USB_HOST_NEED_CLK_EN (1 << 21) #define PAD_CONTROL_LAST_DRIVEN (1 << 19) -#define USB_OTG_CLK_CTRL IO_ADDRESS(USB_CONFIG_BASE + 0xFF4) -#define USB_OTG_CLK_STAT IO_ADDRESS(USB_CONFIG_BASE + 0xFF8) - -/* USB_OTG_CLK_CTRL bit defines */ -#define AHB_M_CLOCK_ON (1 << 4) -#define OTG_CLOCK_ON (1 << 3) -#define I2C_CLOCK_ON (1 << 2) -#define DEV_CLOCK_ON (1 << 1) -#define HOST_CLOCK_ON (1 << 0) - #define USB_OTG_STAT_CONTROL IO_ADDRESS(USB_CONFIG_BASE + 0x110) /* USB_OTG_STAT_CONTROL bit defines */ @@ -72,7 +62,9 @@ static struct i2c_client *isp1301_i2c_client; extern int usb_disabled(void); -static struct clk *usb_clk; +static struct clk *usb_pll_clk; +static struct clk *usb_dev_clk; +static struct clk *usb_otg_clk; static void isp1301_configure_pnx4008(void) { @@ -249,8 +241,6 @@ static const struct hc_driver ohci_nxp_hc_driver = { .start_port_reset = ohci_start_port_reset, }; -#define USB_CLOCK_MASK (AHB_M_CLOCK_ON| OTG_CLOCK_ON | HOST_CLOCK_ON | I2C_CLOCK_ON) - static void nxp_set_usb_bits(void) { if (machine_is_pnx4008()) { @@ -327,41 +317,63 @@ static int __devinit usb_hcd_nxp_probe(struct platform_device *pdev) /* Enable AHB slave USB clock, needed for further USB clock control */ __raw_writel(USB_SLAVE_HCLK_EN | PAD_CONTROL_LAST_DRIVEN, USB_CTRL); - isp1301_configure(); - /* Enable USB PLL */ - usb_clk = clk_get(&pdev->dev, "ck_pll5"); - if (IS_ERR(usb_clk)) { + usb_pll_clk = clk_get(&pdev->dev, "ck_pll5"); + if (IS_ERR(usb_pll_clk)) { dev_err(&pdev->dev, "failed to acquire USB PLL\n"); - ret = PTR_ERR(usb_clk); + ret = PTR_ERR(usb_pll_clk); goto out1; } - ret = clk_enable(usb_clk); + ret = clk_enable(usb_pll_clk); if (ret < 0) { dev_err(&pdev->dev, "failed to start USB PLL\n"); goto out2; } - ret = clk_set_rate(usb_clk, 48000); + ret = clk_set_rate(usb_pll_clk, 48000); if (ret < 0) { dev_err(&pdev->dev, "failed to set USB clock rate\n"); goto out3; } + /* Enable USB device clock */ + usb_dev_clk = clk_get(&pdev->dev, "ck_usbd"); + if (IS_ERR(usb_dev_clk)) { + dev_err(&pdev->dev, "failed to acquire USB DEV Clock\n"); + ret = PTR_ERR(usb_dev_clk); + goto out4; + } + + ret = clk_enable(usb_dev_clk); + if (ret < 0) { + dev_err(&pdev->dev, "failed to start USB DEV Clock\n"); + goto out5; + } + + /* Enable USB otg clocks */ + usb_otg_clk = clk_get(&pdev->dev, "ck_usb_otg"); + if (IS_ERR(usb_otg_clk)) { + dev_err(&pdev->dev, "failed to acquire USB DEV Clock\n"); + ret = PTR_ERR(usb_dev_clk); + goto out6; + } + __raw_writel(__raw_readl(USB_CTRL) | USB_HOST_NEED_CLK_EN, USB_CTRL); - /* Set to enable all needed USB clocks */ - __raw_writel(USB_CLOCK_MASK, USB_OTG_CLK_CTRL); + ret = clk_enable(usb_otg_clk); + if (ret < 0) { + dev_err(&pdev->dev, "failed to start USB DEV Clock\n"); + goto out7; + } - while ((__raw_readl(USB_OTG_CLK_STAT) & USB_CLOCK_MASK) != - USB_CLOCK_MASK) ; + isp1301_configure(); hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { dev_err(&pdev->dev, "Failed to allocate HC buffer\n"); ret = -ENOMEM; - goto out3; + goto out8; } /* Set all USB bits in the Start Enable register */ @@ -371,14 +383,14 @@ static int __devinit usb_hcd_nxp_probe(struct platform_device *pdev) if (!res) { dev_err(&pdev->dev, "Failed to get MEM resource\n"); ret = -ENOMEM; - goto out4; + goto out8; } hcd->regs = devm_request_and_ioremap(&pdev->dev, res); if (!hcd->regs) { dev_err(&pdev->dev, "Failed to devm_request_and_ioremap\n"); ret = -ENOMEM; - goto out4; + goto out8; } hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); @@ -386,7 +398,7 @@ static int __devinit usb_hcd_nxp_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { ret = -ENXIO; - goto out4; + goto out8; } nxp_start_hc(); @@ -400,13 +412,21 @@ static int __devinit usb_hcd_nxp_probe(struct platform_device *pdev) return ret; nxp_stop_hc(); -out4: +out8: nxp_unset_usb_bits(); usb_put_hcd(hcd); +out7: + clk_disable(usb_otg_clk); +out6: + clk_put(usb_otg_clk); +out5: + clk_disable(usb_dev_clk); +out4: + clk_put(usb_dev_clk); out3: - clk_disable(usb_clk); + clk_disable(usb_pll_clk); out2: - clk_put(usb_clk); + clk_put(usb_pll_clk); out1: isp1301_i2c_client = NULL; out: @@ -422,8 +442,10 @@ static int usb_hcd_nxp_remove(struct platform_device *pdev) release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); nxp_unset_usb_bits(); - clk_disable(usb_clk); - clk_put(usb_clk); + clk_disable(usb_pll_clk); + clk_put(usb_pll_clk); + clk_disable(usb_dev_clk); + clk_put(usb_dev_clk); i2c_unregister_device(isp1301_i2c_client); isp1301_i2c_client = NULL; -- cgit v1.2.3-70-g09d2 From 721002ec1dd55a52425455826af49cf8853b2d4f Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 22 Jun 2012 17:02:45 +0530 Subject: usb: otg: utils: rename function name in OTG utils _transceiver() in otg.c is replaced with _phy. usb_set_transceiver is replaced with usb_add_phy to make it similar to other usb standard function names like usb_add_hcd. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/power/ab8500_charger.c | 6 +++--- drivers/power/isp1704_charger.c | 6 +++--- drivers/power/pda_power.c | 6 +++--- drivers/power/twl4030_charger.c | 6 +++--- drivers/usb/chipidea/udc.c | 8 ++++---- drivers/usb/gadget/fsl_udc_core.c | 2 +- drivers/usb/gadget/mv_udc_core.c | 2 +- drivers/usb/gadget/omap_udc.c | 6 +++--- drivers/usb/gadget/pxa25x_udc.c | 6 +++--- drivers/usb/gadget/pxa27x_udc.c | 4 ++-- drivers/usb/gadget/s3c-hsudc.c | 4 ++-- drivers/usb/host/ehci-fsl.c | 6 +++--- drivers/usb/host/ehci-msm.c | 6 +++--- drivers/usb/host/ehci-mv.c | 6 +++--- drivers/usb/host/ehci-tegra.c | 6 +++--- drivers/usb/host/ohci-omap.c | 6 +++--- drivers/usb/musb/am35x.c | 4 ++-- drivers/usb/musb/blackfin.c | 4 ++-- drivers/usb/musb/da8xx.c | 4 ++-- drivers/usb/musb/davinci.c | 6 +++--- drivers/usb/musb/musb_core.c | 2 +- drivers/usb/musb/musb_dsps.c | 6 +++--- drivers/usb/musb/omap2430.c | 4 ++-- drivers/usb/musb/tusb6010.c | 6 +++--- drivers/usb/musb/ux500.c | 4 ++-- drivers/usb/otg/ab8500-usb.c | 4 ++-- drivers/usb/otg/fsl_otg.c | 6 +++--- drivers/usb/otg/gpio_vbus.c | 4 ++-- drivers/usb/otg/isp1301_omap.c | 4 ++-- drivers/usb/otg/msm_otg.c | 6 +++--- drivers/usb/otg/mv_otg.c | 6 +++--- drivers/usb/otg/nop-usb-xceiv.c | 4 ++-- drivers/usb/otg/otg.c | 32 ++++++++++++++++---------------- drivers/usb/otg/twl4030-usb.c | 2 +- drivers/usb/otg/twl6030-usb.c | 2 +- include/linux/usb/otg.h | 10 +++++----- 36 files changed, 103 insertions(+), 103 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index d2303d0b7c7..cf5ffc4d104 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -2517,7 +2517,7 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev) dev_err(di->dev, "%s mask and set failed\n", __func__); usb_unregister_notifier(di->usb_phy, &di->nb); - usb_put_transceiver(di->usb_phy); + usb_put_phy(di->usb_phy); /* Delete the work queue */ destroy_workqueue(di->charger_wq); @@ -2688,7 +2688,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) goto free_ac; } - di->usb_phy = usb_get_transceiver(); + di->usb_phy = usb_get_phy(); if (!di->usb_phy) { dev_err(di->dev, "failed to get usb transceiver\n"); ret = -EINVAL; @@ -2747,7 +2747,7 @@ free_irq: free_irq(irq, di); } put_usb_phy: - usb_put_transceiver(di->usb_phy); + usb_put_phy(di->usb_phy); free_usb: power_supply_unregister(&di->usb_chg.psy); free_ac: diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c index e5ccd297977..50773ae6f72 100644 --- a/drivers/power/isp1704_charger.c +++ b/drivers/power/isp1704_charger.c @@ -415,7 +415,7 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev) if (!isp) return -ENOMEM; - isp->phy = usb_get_transceiver(); + isp->phy = usb_get_phy(); if (!isp->phy) goto fail0; @@ -475,7 +475,7 @@ fail2: power_supply_unregister(&isp->psy); fail1: isp1704_charger_set_power(isp, 0); - usb_put_transceiver(isp->phy); + usb_put_phy(isp->phy); fail0: kfree(isp); @@ -490,7 +490,7 @@ static int __devexit isp1704_charger_remove(struct platform_device *pdev) usb_unregister_notifier(isp->phy, &isp->nb); power_supply_unregister(&isp->psy); - usb_put_transceiver(isp->phy); + usb_put_phy(isp->phy); isp1704_charger_set_power(isp, 0); kfree(isp); diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c index 214468f4444..e0f206b0775 100644 --- a/drivers/power/pda_power.c +++ b/drivers/power/pda_power.c @@ -321,7 +321,7 @@ static int pda_power_probe(struct platform_device *pdev) } #ifdef CONFIG_USB_OTG_UTILS - transceiver = usb_get_transceiver(); + transceiver = usb_get_phy(); if (transceiver && !pdata->is_usb_online) { pdata->is_usb_online = otg_is_usb_online; } @@ -409,7 +409,7 @@ usb_supply_failed: free_irq(ac_irq->start, &pda_psy_ac); #ifdef CONFIG_USB_OTG_UTILS if (transceiver) - usb_put_transceiver(transceiver); + usb_put_phy(transceiver); #endif ac_irq_failed: if (pdata->is_ac_online) @@ -444,7 +444,7 @@ static int pda_power_remove(struct platform_device *pdev) power_supply_unregister(&pda_psy_ac); #ifdef CONFIG_USB_OTG_UTILS if (transceiver) - usb_put_transceiver(transceiver); + usb_put_phy(transceiver); #endif if (ac_draw) { regulator_put(ac_draw); diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index fdad850c77d..fcddd115cc0 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -479,7 +479,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) INIT_WORK(&bci->work, twl4030_bci_usb_work); - bci->transceiver = usb_get_transceiver(); + bci->transceiver = usb_get_phy(); if (bci->transceiver != NULL) { bci->usb_nb.notifier_call = twl4030_bci_usb_ncb; usb_register_notifier(bci->transceiver, &bci->usb_nb); @@ -509,7 +509,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) fail_unmask_interrupts: if (bci->transceiver != NULL) { usb_unregister_notifier(bci->transceiver, &bci->usb_nb); - usb_put_transceiver(bci->transceiver); + usb_put_phy(bci->transceiver); } free_irq(bci->irq_bci, bci); fail_bci_irq: @@ -540,7 +540,7 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) if (bci->transceiver != NULL) { usb_unregister_notifier(bci->transceiver, &bci->usb_nb); - usb_put_transceiver(bci->transceiver); + usb_put_phy(bci->transceiver); } free_irq(bci->irq_bci, bci); free_irq(bci->irq_chg, bci); diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 51f96942dc5..4468f2c2ddd 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1687,7 +1687,7 @@ static int udc_start(struct ci13xxx *udc) udc->gadget.ep0 = &udc->ep0in->ep; - udc->transceiver = usb_get_transceiver(); + udc->transceiver = usb_get_phy(); if (udc->udc_driver->flags & CI13XXX_REQUIRE_TRANSCEIVER) { if (udc->transceiver == NULL) { @@ -1731,7 +1731,7 @@ static int udc_start(struct ci13xxx *udc) remove_trans: if (udc->transceiver) { otg_set_peripheral(udc->transceiver->otg, &udc->gadget); - usb_put_transceiver(udc->transceiver); + usb_put_phy(udc->transceiver); } dev_err(dev, "error = %i\n", retval); @@ -1741,7 +1741,7 @@ unreg_device: device_unregister(&udc->gadget.dev); put_transceiver: if (udc->transceiver) - usb_put_transceiver(udc->transceiver); + usb_put_phy(udc->transceiver); free_pools: dma_pool_destroy(udc->td_pool); free_qh_pool: @@ -1774,7 +1774,7 @@ static void udc_stop(struct ci13xxx *udc) if (udc->transceiver) { otg_set_peripheral(udc->transceiver->otg, NULL); - usb_put_transceiver(udc->transceiver); + usb_put_phy(udc->transceiver); } dbg_remove_files(&udc->gadget.dev); device_unregister(&udc->gadget.dev); diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 28316858208..d7038509b95 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -2455,7 +2455,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) #ifdef CONFIG_USB_OTG if (pdata->operating_mode == FSL_USB2_DR_OTG) { - udc_controller->transceiver = usb_get_transceiver(); + udc_controller->transceiver = usb_get_phy(); if (!udc_controller->transceiver) { ERR("Can't find OTG driver!\n"); ret = -ENODEV; diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index dbcd1329495..5d779955d5a 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -2180,7 +2180,7 @@ static int __devinit mv_udc_probe(struct platform_device *dev) #ifdef CONFIG_USB_OTG_UTILS if (pdata->mode == MV_USB_MODE_OTG) - udc->transceiver = usb_get_transceiver(); + udc->transceiver = usb_get_phy(); #endif udc->clknum = pdata->clknum; diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 7ba32469c5b..74b9bb8099e 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -2865,7 +2865,7 @@ static int __init omap_udc_probe(struct platform_device *pdev) * use it. Except for OTG, we don't _need_ to talk to one; * but not having one probably means no VBUS detection. */ - xceiv = usb_get_transceiver(); + xceiv = usb_get_phy(); if (xceiv) type = xceiv->label; else if (config->otg) { @@ -3011,7 +3011,7 @@ cleanup1: cleanup0: if (xceiv) - usb_put_transceiver(xceiv); + usb_put_phy(xceiv); if (cpu_is_omap16xx() || cpu_is_omap24xx() || cpu_is_omap7xx()) { clk_disable(hhc_clk); @@ -3041,7 +3041,7 @@ static int __exit omap_udc_remove(struct platform_device *pdev) pullup_disable(udc); if (udc->transceiver) { - usb_put_transceiver(udc->transceiver); + usb_put_phy(udc->transceiver); udc->transceiver = NULL; } omap_writew(0, UDC_SYSCON1); diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index d7c8cb3bf75..a658e446cab 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -2159,7 +2159,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) dev->dev = &pdev->dev; dev->mach = pdev->dev.platform_data; - dev->transceiver = usb_get_transceiver(); + dev->transceiver = usb_get_phy(); if (gpio_is_valid(dev->mach->gpio_pullup)) { if ((retval = gpio_request(dev->mach->gpio_pullup, @@ -2238,7 +2238,7 @@ lubbock_fail0: gpio_free(dev->mach->gpio_pullup); err_gpio_pullup: if (dev->transceiver) { - usb_put_transceiver(dev->transceiver); + usb_put_phy(dev->transceiver); dev->transceiver = NULL; } clk_put(dev->clk); @@ -2280,7 +2280,7 @@ static int __exit pxa25x_udc_remove(struct platform_device *pdev) clk_put(dev->clk); if (dev->transceiver) { - usb_put_transceiver(dev->transceiver); + usb_put_phy(dev->transceiver); dev->transceiver = NULL; } diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 98acb3ab9e1..b982304a49c 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -2464,7 +2464,7 @@ static int __init pxa_udc_probe(struct platform_device *pdev) udc->dev = &pdev->dev; udc->mach = pdev->dev.platform_data; - udc->transceiver = usb_get_transceiver(); + udc->transceiver = usb_get_phy(); gpio = udc->mach->gpio_pullup; if (gpio_is_valid(gpio)) { @@ -2543,7 +2543,7 @@ static int __exit pxa_udc_remove(struct platform_device *_dev) if (gpio_is_valid(gpio)) gpio_free(gpio); - usb_put_transceiver(udc->transceiver); + usb_put_phy(udc->transceiver); udc->transceiver = NULL; platform_set_drvdata(_dev, NULL); diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index 36c6836eeb0..9ad33395f56 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -1282,7 +1282,7 @@ static int __devinit s3c_hsudc_probe(struct platform_device *pdev) hsudc->dev = dev; hsudc->pd = pdev->dev.platform_data; - hsudc->transceiver = usb_get_transceiver(); + hsudc->transceiver = usb_get_phy(); for (i = 0; i < ARRAY_SIZE(hsudc->supplies); i++) hsudc->supplies[i].supply = s3c_hsudc_supply_names[i]; @@ -1386,7 +1386,7 @@ err_remap: release_mem_region(res->start, resource_size(res)); err_res: if (hsudc->transceiver) - usb_put_transceiver(hsudc->transceiver); + usb_put_phy(hsudc->transceiver); regulator_bulk_free(ARRAY_SIZE(hsudc->supplies), hsudc->supplies); err_supplies: diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 43362577b54..0e8976a0ed5 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -142,7 +142,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, if (pdata->operating_mode == FSL_USB2_DR_OTG) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); - ehci->transceiver = usb_get_transceiver(); + ehci->transceiver = usb_get_phy(); dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, transceiver=0x%p\n", hcd, ehci, ehci->transceiver); @@ -150,7 +150,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, retval = otg_set_host(ehci->transceiver->otg, &ehci_to_hcd(ehci)->self); if (retval) { - usb_put_transceiver(ehci->transceiver); + usb_put_phy(ehci->transceiver); goto err4; } } else { @@ -194,7 +194,7 @@ static void usb_hcd_fsl_remove(struct usb_hcd *hcd, if (ehci->transceiver) { otg_set_host(ehci->transceiver->otg, NULL); - usb_put_transceiver(ehci->transceiver); + usb_put_phy(ehci->transceiver); } usb_remove_hcd(hcd); diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index 9803a55fd5f..7badd5db398 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -145,7 +145,7 @@ static int ehci_msm_probe(struct platform_device *pdev) * powering up VBUS, mapping of registers address space and power * management. */ - phy = usb_get_transceiver(); + phy = usb_get_phy(); if (!phy) { dev_err(&pdev->dev, "unable to find transceiver\n"); ret = -ENODEV; @@ -169,7 +169,7 @@ static int ehci_msm_probe(struct platform_device *pdev) return 0; put_transceiver: - usb_put_transceiver(phy); + usb_put_phy(phy); unmap: iounmap(hcd->regs); put_hcd: @@ -187,7 +187,7 @@ static int __devexit ehci_msm_remove(struct platform_device *pdev) pm_runtime_set_suspended(&pdev->dev); otg_set_host(phy->otg, NULL); - usb_put_transceiver(phy); + usb_put_phy(phy); usb_put_hcd(hcd); diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index a936bbcff8f..24f838fe25a 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -253,7 +253,7 @@ static int mv_ehci_probe(struct platform_device *pdev) ehci_mv->mode = pdata->mode; if (ehci_mv->mode == MV_USB_MODE_OTG) { #ifdef CONFIG_USB_OTG_UTILS - ehci_mv->otg = usb_get_transceiver(); + ehci_mv->otg = usb_get_phy(); if (!ehci_mv->otg) { dev_err(&pdev->dev, "unable to find transceiver\n"); @@ -303,7 +303,7 @@ err_set_vbus: #ifdef CONFIG_USB_OTG_UTILS err_put_transceiver: if (ehci_mv->otg) - usb_put_transceiver(ehci_mv->otg); + usb_put_phy(ehci_mv->otg); #endif err_disable_clk: mv_ehci_disable(ehci_mv); @@ -333,7 +333,7 @@ static int mv_ehci_remove(struct platform_device *pdev) if (ehci_mv->otg) { otg_set_host(ehci_mv->otg->otg, NULL); - usb_put_transceiver(ehci_mv->otg); + usb_put_phy(ehci_mv->otg); } if (ehci_mv->mode == MV_USB_MODE_HOST) { diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 68548236ec4..ee17d19b1b8 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -749,7 +749,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) #ifdef CONFIG_USB_OTG_UTILS if (pdata->operating_mode == TEGRA_USB_OTG) { - tegra->transceiver = usb_get_transceiver(); + tegra->transceiver = usb_get_phy(); if (tegra->transceiver) otg_set_host(tegra->transceiver->otg, &hcd->self); } @@ -775,7 +775,7 @@ fail: #ifdef CONFIG_USB_OTG_UTILS if (tegra->transceiver) { otg_set_host(tegra->transceiver->otg, NULL); - usb_put_transceiver(tegra->transceiver); + usb_put_phy(tegra->transceiver); } #endif tegra_usb_phy_close(tegra->phy); @@ -810,7 +810,7 @@ static int tegra_ehci_remove(struct platform_device *pdev) #ifdef CONFIG_USB_OTG_UTILS if (tegra->transceiver) { otg_set_host(tegra->transceiver->otg, NULL); - usb_put_transceiver(tegra->transceiver); + usb_put_phy(tegra->transceiver); } #endif diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 9ce35d0d9d5..c2c1f55889a 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -211,14 +211,14 @@ static int ohci_omap_init(struct usb_hcd *hcd) #ifdef CONFIG_USB_OTG if (need_transceiver) { - ohci->transceiver = usb_get_transceiver(); + ohci->transceiver = usb_get_phy(); if (ohci->transceiver) { int status = otg_set_host(ohci->transceiver->otg, &ohci_to_hcd(ohci)->self); dev_dbg(hcd->self.controller, "init %s transceiver, status %d\n", ohci->transceiver->label, status); if (status) { - usb_put_transceiver(ohci->transceiver); + usb_put_phy(ohci->transceiver); return status; } } else { @@ -405,7 +405,7 @@ usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev) usb_remove_hcd(hcd); if (ohci->transceiver) { (void) otg_set_host(ohci->transceiver->otg, 0); - usb_put_transceiver(ohci->transceiver); + usb_put_phy(ohci->transceiver); } if (machine_is_omap_osk()) gpio_free(9); diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index 9f3eda91ea4..a75989bbb3d 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -364,7 +364,7 @@ static int am35x_musb_init(struct musb *musb) return -ENODEV; usb_nop_xceiv_register(); - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) return -ENODEV; @@ -406,7 +406,7 @@ static int am35x_musb_exit(struct musb *musb) if (data->set_phy_power) data->set_phy_power(0); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return 0; diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index a087ed6c3be..522a4a263df 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -415,7 +415,7 @@ static int bfin_musb_init(struct musb *musb) gpio_direction_output(musb->config->gpio_vrsel, 0); usb_nop_xceiv_register(); - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) { gpio_free(musb->config->gpio_vrsel); return -ENODEV; @@ -440,7 +440,7 @@ static int bfin_musb_exit(struct musb *musb) { gpio_free(musb->config->gpio_vrsel); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return 0; } diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 8bd9566f3fb..61868d604b2 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -425,7 +425,7 @@ static int da8xx_musb_init(struct musb *musb) goto fail; usb_nop_xceiv_register(); - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) goto fail; @@ -458,7 +458,7 @@ static int da8xx_musb_exit(struct musb *musb) phy_off(); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return 0; diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 768b4b55c81..441f776366f 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -384,7 +384,7 @@ static int davinci_musb_init(struct musb *musb) u32 revision; usb_nop_xceiv_register(); - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) goto unregister; @@ -443,7 +443,7 @@ static int davinci_musb_init(struct musb *musb) return 0; fail: - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); unregister: usb_nop_xceiv_unregister(); return -ENODEV; @@ -493,7 +493,7 @@ static int davinci_musb_exit(struct musb *musb) phy_off(); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return 0; diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index db3dff854b7..26f1befb489 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1909,7 +1909,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) /* The musb_platform_init() call: * - adjusts musb->mregs and musb->isr if needed, * - may initialize an integrated tranceiver - * - initializes musb->xceiv, usually by otg_get_transceiver() + * - initializes musb->xceiv, usually by otg_get_phy() * - stops powering VBUS * * There are various transceiver configurations. Blackfin, diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 23db42db761..716c113608f 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -376,7 +376,7 @@ static int dsps_musb_init(struct musb *musb) /* NOP driver needs change if supporting dual instance */ usb_nop_xceiv_register(); - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) return -ENODEV; @@ -409,7 +409,7 @@ static int dsps_musb_init(struct musb *musb) return 0; err0: - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return status; } @@ -430,7 +430,7 @@ static int dsps_musb_exit(struct musb *musb) data->set_phy_power(0); /* NOP driver needs change if supporting dual instance */ - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return 0; diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index c7785e81254..e16dbbf7f30 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -292,7 +292,7 @@ static int omap2430_musb_init(struct musb *musb) * up through ULPI. TWL4030-family PMICs include one, * which needs a driver, drivers aren't always needed. */ - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) { pr_err("HS USB OTG: no transceiver configured\n"); return -ENODEV; @@ -391,7 +391,7 @@ static int omap2430_musb_exit(struct musb *musb) cancel_work_sync(&musb->otg_notifier_work); omap2430_low_level_exit(musb); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); return 0; } diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index de1355946a8..a004736186f 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -1078,7 +1078,7 @@ static int tusb_musb_init(struct musb *musb) int ret; usb_nop_xceiv_register(); - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) return -ENODEV; @@ -1130,7 +1130,7 @@ done: if (sync) iounmap(sync); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); } return ret; @@ -1146,7 +1146,7 @@ static int tusb_musb_exit(struct musb *musb) iounmap(musb->sync_va); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return 0; } diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index aa09dd417b9..53006b113b1 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -37,7 +37,7 @@ struct ux500_glue { static int ux500_musb_init(struct musb *musb) { - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) { pr_err("HS USB OTG: no transceiver configured\n"); return -ENODEV; @@ -48,7 +48,7 @@ static int ux500_musb_init(struct musb *musb) static int ux500_musb_exit(struct musb *musb) { - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); return 0; } diff --git a/drivers/usb/otg/ab8500-usb.c b/drivers/usb/otg/ab8500-usb.c index a84af677dc5..672e28c8143 100644 --- a/drivers/usb/otg/ab8500-usb.c +++ b/drivers/usb/otg/ab8500-usb.c @@ -529,7 +529,7 @@ static int __devinit ab8500_usb_probe(struct platform_device *pdev) if (err < 0) goto fail0; - err = usb_set_transceiver(&ab->phy); + err = usb_add_phy(&ab->phy); if (err) { dev_err(&pdev->dev, "Can't register transceiver\n"); goto fail1; @@ -556,7 +556,7 @@ static int __devexit ab8500_usb_remove(struct platform_device *pdev) cancel_work_sync(&ab->phy_dis_work); - usb_set_transceiver(NULL); + usb_add_phy(NULL); ab8500_usb_host_phy_dis(ab); ab8500_usb_peri_phy_dis(ab); diff --git a/drivers/usb/otg/fsl_otg.c b/drivers/usb/otg/fsl_otg.c index be4a63e8302..73561edd81d 100644 --- a/drivers/usb/otg/fsl_otg.c +++ b/drivers/usb/otg/fsl_otg.c @@ -806,7 +806,7 @@ static int fsl_otg_conf(struct platform_device *pdev) fsl_otg_dev = fsl_otg_tc; /* Store the otg transceiver */ - status = usb_set_transceiver(&fsl_otg_tc->phy); + status = usb_add_phy(&fsl_otg_tc->phy); if (status) { pr_warn(FSL_OTG_NAME ": unable to register OTG transceiver.\n"); goto err; @@ -824,7 +824,7 @@ err: int usb_otg_start(struct platform_device *pdev) { struct fsl_otg *p_otg; - struct usb_phy *otg_trans = usb_get_transceiver(); + struct usb_phy *otg_trans = usb_get_phy(); struct otg_fsm *fsm; int status; struct resource *res; @@ -1134,7 +1134,7 @@ static int __devexit fsl_otg_remove(struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; - usb_set_transceiver(NULL); + usb_add_phy(NULL); free_irq(fsl_otg_dev->irq, fsl_otg_dev); iounmap((void *)usb_dr_regs); diff --git a/drivers/usb/otg/gpio_vbus.c b/drivers/usb/otg/gpio_vbus.c index bde6298a969..9b3c264cdb5 100644 --- a/drivers/usb/otg/gpio_vbus.c +++ b/drivers/usb/otg/gpio_vbus.c @@ -320,7 +320,7 @@ static int __init gpio_vbus_probe(struct platform_device *pdev) } /* only active when a gadget is registered */ - err = usb_set_transceiver(&gpio_vbus->phy); + err = usb_add_phy(&gpio_vbus->phy); if (err) { dev_err(&pdev->dev, "can't register transceiver, err: %d\n", err); @@ -354,7 +354,7 @@ static int __exit gpio_vbus_remove(struct platform_device *pdev) cancel_delayed_work_sync(&gpio_vbus->work); regulator_put(gpio_vbus->vbus_draw); - usb_set_transceiver(NULL); + usb_add_phy(NULL); free_irq(gpio_vbus->irq, pdev); if (gpio_is_valid(pdata->gpio_pullup)) diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c index 33cd709b084..b74df3fec56 100644 --- a/drivers/usb/otg/isp1301_omap.c +++ b/drivers/usb/otg/isp1301_omap.c @@ -1611,7 +1611,7 @@ isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id) dev_dbg(&i2c->dev, "scheduled timer, %d min\n", TIMER_MINUTES); #endif - status = usb_set_transceiver(&isp->phy); + status = usb_add_phy(&isp->phy); if (status < 0) dev_err(&i2c->dev, "can't register transceiver, %d\n", status); @@ -1650,7 +1650,7 @@ subsys_initcall(isp_init); static void __exit isp_exit(void) { if (the_transceiver) - usb_set_transceiver(NULL); + usb_add_phy(NULL); i2c_del_driver(&isp1301_driver); } module_exit(isp_exit); diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c index 1d0347c247d..dd606c01003 100644 --- a/drivers/usb/otg/msm_otg.c +++ b/drivers/usb/otg/msm_otg.c @@ -1555,9 +1555,9 @@ static int __init msm_otg_probe(struct platform_device *pdev) phy->otg->set_host = msm_otg_set_host; phy->otg->set_peripheral = msm_otg_set_peripheral; - ret = usb_set_transceiver(&motg->phy); + ret = usb_add_phy(&motg->phy); if (ret) { - dev_err(&pdev->dev, "usb_set_transceiver failed\n"); + dev_err(&pdev->dev, "usb_add_phy failed\n"); goto free_irq; } @@ -1624,7 +1624,7 @@ static int __devexit msm_otg_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 0); pm_runtime_disable(&pdev->dev); - usb_set_transceiver(NULL); + usb_add_phy(NULL); free_irq(motg->irq, motg); /* diff --git a/drivers/usb/otg/mv_otg.c b/drivers/usb/otg/mv_otg.c index 6cc6c3ffbb8..18e90fe1fbd 100644 --- a/drivers/usb/otg/mv_otg.c +++ b/drivers/usb/otg/mv_otg.c @@ -690,7 +690,7 @@ int mv_otg_remove(struct platform_device *pdev) for (clk_i = 0; clk_i <= mvotg->clknum; clk_i++) clk_put(mvotg->clk[clk_i]); - usb_set_transceiver(NULL); + usb_add_phy(NULL); platform_set_drvdata(pdev, NULL); kfree(mvotg->phy.otg); @@ -853,7 +853,7 @@ static int mv_otg_probe(struct platform_device *pdev) goto err_disable_clk; } - retval = usb_set_transceiver(&mvotg->phy); + retval = usb_add_phy(&mvotg->phy); if (retval < 0) { dev_err(&pdev->dev, "can't register transceiver, %d\n", retval); @@ -880,7 +880,7 @@ static int mv_otg_probe(struct platform_device *pdev) return 0; err_set_transceiver: - usb_set_transceiver(NULL); + usb_add_phy(NULL); err_free_irq: free_irq(mvotg->irq, mvotg); err_disable_clk: diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c index 58b26df6afd..33000dae720 100644 --- a/drivers/usb/otg/nop-usb-xceiv.c +++ b/drivers/usb/otg/nop-usb-xceiv.c @@ -117,7 +117,7 @@ static int __devinit nop_usb_xceiv_probe(struct platform_device *pdev) nop->phy.otg->set_host = nop_set_host; nop->phy.otg->set_peripheral = nop_set_peripheral; - err = usb_set_transceiver(&nop->phy); + err = usb_add_phy(&nop->phy); if (err) { dev_err(&pdev->dev, "can't register transceiver, err: %d\n", err); @@ -139,7 +139,7 @@ static int __devexit nop_usb_xceiv_remove(struct platform_device *pdev) { struct nop_usb_xceiv *nop = platform_get_drvdata(pdev); - usb_set_transceiver(NULL); + usb_add_phy(NULL); platform_set_drvdata(pdev, NULL); kfree(nop->phy.otg); diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index 801e597a154..300a995cfdb 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -18,53 +18,53 @@ static struct usb_phy *phy; /** - * usb_get_transceiver - find the (single) USB transceiver + * usb_get_phy - find the (single) USB PHY * - * Returns the transceiver driver, after getting a refcount to it; or - * null if there is no such transceiver. The caller is responsible for - * calling usb_put_transceiver() to release that count. + * Returns the phy driver, after getting a refcount to it; or + * null if there is no such phy. The caller is responsible for + * calling usb_put_phy() to release that count. * * For use by USB host and peripheral drivers. */ -struct usb_phy *usb_get_transceiver(void) +struct usb_phy *usb_get_phy(void) { if (phy) get_device(phy->dev); return phy; } -EXPORT_SYMBOL(usb_get_transceiver); +EXPORT_SYMBOL(usb_get_phy); /** - * usb_put_transceiver - release the (single) USB transceiver - * @x: the transceiver returned by usb_get_transceiver() + * usb_put_phy - release the (single) USB PHY + * @x: the phy returned by usb_get_phy() * - * Releases a refcount the caller received from usb_get_transceiver(). + * Releases a refcount the caller received from usb_get_phy(). * * For use by USB host and peripheral drivers. */ -void usb_put_transceiver(struct usb_phy *x) +void usb_put_phy(struct usb_phy *x) { if (x) put_device(x->dev); } -EXPORT_SYMBOL(usb_put_transceiver); +EXPORT_SYMBOL(usb_put_phy); /** - * usb_set_transceiver - declare the (single) USB transceiver - * @x: the USB transceiver to be used; or NULL + * usb_add_phy - declare the (single) USB PHY + * @x: the USB phy to be used; or NULL * - * This call is exclusively for use by transceiver drivers, which + * This call is exclusively for use by phy drivers, which * coordinate the activities of drivers for host and peripheral * controllers, and in some cases for VBUS current regulation. */ -int usb_set_transceiver(struct usb_phy *x) +int usb_add_phy(struct usb_phy *x) { if (phy && x) return -EBUSY; phy = x; return 0; } -EXPORT_SYMBOL(usb_set_transceiver); +EXPORT_SYMBOL(usb_add_phy); const char *otg_state_string(enum usb_otg_state state) { diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index 02979306bf8..01022c891e2 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -633,7 +633,7 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev) kfree(twl); return err; } - usb_set_transceiver(&twl->phy); + usb_add_phy(&twl->phy); platform_set_drvdata(pdev, twl); if (device_create_file(&pdev->dev, &dev_attr_vbus)) diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c index d2a9a8e691b..a8be20878eb 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -443,7 +443,7 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev) kfree(twl); return err; } - usb_set_transceiver(&twl->phy); + usb_add_phy(&twl->phy); platform_set_drvdata(pdev, twl); if (device_create_file(&pdev->dev, &dev_attr_vbus)) diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 38ab3f46346..0e739c81052 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -121,7 +121,7 @@ struct usb_phy { /* for board-specific init logic */ -extern int usb_set_transceiver(struct usb_phy *); +extern int usb_add_phy(struct usb_phy *); #if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE)) /* sometimes transceivers are accessed only through e.g. ULPI */ @@ -172,16 +172,16 @@ usb_phy_shutdown(struct usb_phy *x) /* for usb host and peripheral controller drivers */ #ifdef CONFIG_USB_OTG_UTILS -extern struct usb_phy *usb_get_transceiver(void); -extern void usb_put_transceiver(struct usb_phy *); +extern struct usb_phy *usb_get_phy(void); +extern void usb_put_phy(struct usb_phy *); extern const char *otg_state_string(enum usb_otg_state state); #else -static inline struct usb_phy *usb_get_transceiver(void) +static inline struct usb_phy *usb_get_phy(void) { return NULL; } -static inline void usb_put_transceiver(struct usb_phy *x) +static inline void usb_put_phy(struct usb_phy *x) { } -- cgit v1.2.3-70-g09d2 From 662dca54ca67c92b7aa14b9a2ec54acacf33ce45 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 22 Jun 2012 17:02:46 +0530 Subject: usb: otg: support for multiple transceivers by a single controller Add a linked list for keeping multiple PHY instances with different types so that we can have separate USB2 and USB3 PHYs on one single board. _get_phy_ has been changed so that the controller gets the transceiver by type. _remove_phy_ has been added to let the phy be removed from the phy list. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/power/ab8500_charger.c | 2 +- drivers/power/isp1704_charger.c | 2 +- drivers/power/pda_power.c | 2 +- drivers/power/twl4030_charger.c | 2 +- drivers/usb/chipidea/udc.c | 2 +- drivers/usb/gadget/fsl_udc_core.c | 2 +- drivers/usb/gadget/mv_udc_core.c | 2 +- drivers/usb/gadget/omap_udc.c | 2 +- drivers/usb/gadget/pxa25x_udc.c | 2 +- drivers/usb/gadget/pxa27x_udc.c | 2 +- drivers/usb/gadget/s3c-hsudc.c | 2 +- drivers/usb/host/ehci-fsl.c | 2 +- drivers/usb/host/ehci-msm.c | 2 +- drivers/usb/host/ehci-mv.c | 2 +- drivers/usb/host/ehci-tegra.c | 2 +- drivers/usb/host/ohci-omap.c | 2 +- drivers/usb/musb/am35x.c | 2 +- drivers/usb/musb/blackfin.c | 2 +- drivers/usb/musb/da8xx.c | 2 +- drivers/usb/musb/davinci.c | 2 +- drivers/usb/musb/musb_dsps.c | 2 +- drivers/usb/musb/omap2430.c | 2 +- drivers/usb/musb/tusb6010.c | 2 +- drivers/usb/musb/ux500.c | 2 +- drivers/usb/otg/ab8500-usb.c | 4 +- drivers/usb/otg/fsl_otg.c | 6 +-- drivers/usb/otg/gpio_vbus.c | 4 +- drivers/usb/otg/isp1301_omap.c | 4 +- drivers/usb/otg/msm_otg.c | 4 +- drivers/usb/otg/mv_otg.c | 6 +-- drivers/usb/otg/nop-usb-xceiv.c | 4 +- drivers/usb/otg/otg.c | 96 ++++++++++++++++++++++++++++++++++----- drivers/usb/otg/twl4030-usb.c | 2 +- drivers/usb/otg/twl6030-usb.c | 2 +- include/linux/usb/otg.h | 29 ++++++++++-- 35 files changed, 152 insertions(+), 57 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index cf5ffc4d104..6bd6f1c4196 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -2688,7 +2688,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) goto free_ac; } - di->usb_phy = usb_get_phy(); + di->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2); if (!di->usb_phy) { dev_err(di->dev, "failed to get usb transceiver\n"); ret = -EINVAL; diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c index 50773ae6f72..090e5f9e72c 100644 --- a/drivers/power/isp1704_charger.c +++ b/drivers/power/isp1704_charger.c @@ -415,7 +415,7 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev) if (!isp) return -ENOMEM; - isp->phy = usb_get_phy(); + isp->phy = usb_get_phy(USB_PHY_TYPE_USB2); if (!isp->phy) goto fail0; diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c index e0f206b0775..7602d49e4d8 100644 --- a/drivers/power/pda_power.c +++ b/drivers/power/pda_power.c @@ -321,7 +321,7 @@ static int pda_power_probe(struct platform_device *pdev) } #ifdef CONFIG_USB_OTG_UTILS - transceiver = usb_get_phy(); + transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (transceiver && !pdata->is_usb_online) { pdata->is_usb_online = otg_is_usb_online; } diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index fcddd115cc0..13f9db2e853 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -479,7 +479,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) INIT_WORK(&bci->work, twl4030_bci_usb_work); - bci->transceiver = usb_get_phy(); + bci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (bci->transceiver != NULL) { bci->usb_nb.notifier_call = twl4030_bci_usb_ncb; usb_register_notifier(bci->transceiver, &bci->usb_nb); diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 4468f2c2ddd..a06d28b119f 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1687,7 +1687,7 @@ static int udc_start(struct ci13xxx *udc) udc->gadget.ep0 = &udc->ep0in->ep; - udc->transceiver = usb_get_phy(); + udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (udc->udc_driver->flags & CI13XXX_REQUIRE_TRANSCEIVER) { if (udc->transceiver == NULL) { diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index d7038509b95..0808820ba49 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -2455,7 +2455,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) #ifdef CONFIG_USB_OTG if (pdata->operating_mode == FSL_USB2_DR_OTG) { - udc_controller->transceiver = usb_get_phy(); + udc_controller->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (!udc_controller->transceiver) { ERR("Can't find OTG driver!\n"); ret = -ENODEV; diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 5d779955d5a..75ff41a5c95 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -2180,7 +2180,7 @@ static int __devinit mv_udc_probe(struct platform_device *dev) #ifdef CONFIG_USB_OTG_UTILS if (pdata->mode == MV_USB_MODE_OTG) - udc->transceiver = usb_get_phy(); + udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); #endif udc->clknum = pdata->clknum; diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 74b9bb8099e..cf8bf26f12e 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -2865,7 +2865,7 @@ static int __init omap_udc_probe(struct platform_device *pdev) * use it. Except for OTG, we don't _need_ to talk to one; * but not having one probably means no VBUS detection. */ - xceiv = usb_get_phy(); + xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (xceiv) type = xceiv->label; else if (config->otg) { diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index a658e446cab..cc0b1e63dca 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -2159,7 +2159,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) dev->dev = &pdev->dev; dev->mach = pdev->dev.platform_data; - dev->transceiver = usb_get_phy(); + dev->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (gpio_is_valid(dev->mach->gpio_pullup)) { if ((retval = gpio_request(dev->mach->gpio_pullup, diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index b982304a49c..8f744aab962 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -2464,7 +2464,7 @@ static int __init pxa_udc_probe(struct platform_device *pdev) udc->dev = &pdev->dev; udc->mach = pdev->dev.platform_data; - udc->transceiver = usb_get_phy(); + udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); gpio = udc->mach->gpio_pullup; if (gpio_is_valid(gpio)) { diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index 9ad33395f56..22326f27446 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -1282,7 +1282,7 @@ static int __devinit s3c_hsudc_probe(struct platform_device *pdev) hsudc->dev = dev; hsudc->pd = pdev->dev.platform_data; - hsudc->transceiver = usb_get_phy(); + hsudc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); for (i = 0; i < ARRAY_SIZE(hsudc->supplies); i++) hsudc->supplies[i].supply = s3c_hsudc_supply_names[i]; diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 0e8976a0ed5..ba290589d85 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -142,7 +142,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, if (pdata->operating_mode == FSL_USB2_DR_OTG) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); - ehci->transceiver = usb_get_phy(); + ehci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, transceiver=0x%p\n", hcd, ehci, ehci->transceiver); diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index 7badd5db398..c7615fb93db 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -145,7 +145,7 @@ static int ehci_msm_probe(struct platform_device *pdev) * powering up VBUS, mapping of registers address space and power * management. */ - phy = usb_get_phy(); + phy = usb_get_phy(USB_PHY_TYPE_USB2); if (!phy) { dev_err(&pdev->dev, "unable to find transceiver\n"); ret = -ENODEV; diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index 24f838fe25a..ef7aa0df40a 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -253,7 +253,7 @@ static int mv_ehci_probe(struct platform_device *pdev) ehci_mv->mode = pdata->mode; if (ehci_mv->mode == MV_USB_MODE_OTG) { #ifdef CONFIG_USB_OTG_UTILS - ehci_mv->otg = usb_get_phy(); + ehci_mv->otg = usb_get_phy(USB_PHY_TYPE_USB2); if (!ehci_mv->otg) { dev_err(&pdev->dev, "unable to find transceiver\n"); diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index ee17d19b1b8..14df2f5cf6a 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -749,7 +749,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) #ifdef CONFIG_USB_OTG_UTILS if (pdata->operating_mode == TEGRA_USB_OTG) { - tegra->transceiver = usb_get_phy(); + tegra->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (tegra->transceiver) otg_set_host(tegra->transceiver->otg, &hcd->self); } diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index c2c1f55889a..92a77dfd193 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -211,7 +211,7 @@ static int ohci_omap_init(struct usb_hcd *hcd) #ifdef CONFIG_USB_OTG if (need_transceiver) { - ohci->transceiver = usb_get_phy(); + ohci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (ohci->transceiver) { int status = otg_set_host(ohci->transceiver->otg, &ohci_to_hcd(ohci)->self); diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index a75989bbb3d..4a8cbf0e8d5 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -364,7 +364,7 @@ static int am35x_musb_init(struct musb *musb) return -ENODEV; usb_nop_xceiv_register(); - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) return -ENODEV; diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 522a4a263df..452940986d6 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -415,7 +415,7 @@ static int bfin_musb_init(struct musb *musb) gpio_direction_output(musb->config->gpio_vrsel, 0); usb_nop_xceiv_register(); - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) { gpio_free(musb->config->gpio_vrsel); return -ENODEV; diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 61868d604b2..d731c80c4fe 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -425,7 +425,7 @@ static int da8xx_musb_init(struct musb *musb) goto fail; usb_nop_xceiv_register(); - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) goto fail; diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 441f776366f..582268de3fa 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -384,7 +384,7 @@ static int davinci_musb_init(struct musb *musb) u32 revision; usb_nop_xceiv_register(); - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) goto unregister; diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 716c113608f..92603e498e6 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -376,7 +376,7 @@ static int dsps_musb_init(struct musb *musb) /* NOP driver needs change if supporting dual instance */ usb_nop_xceiv_register(); - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) return -ENODEV; diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index e16dbbf7f30..e279cf32772 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -292,7 +292,7 @@ static int omap2430_musb_init(struct musb *musb) * up through ULPI. TWL4030-family PMICs include one, * which needs a driver, drivers aren't always needed. */ - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) { pr_err("HS USB OTG: no transceiver configured\n"); return -ENODEV; diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index a004736186f..8ddf3d5f7cd 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -1078,7 +1078,7 @@ static int tusb_musb_init(struct musb *musb) int ret; usb_nop_xceiv_register(); - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) return -ENODEV; diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index 53006b113b1..46cf80a8cac 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -37,7 +37,7 @@ struct ux500_glue { static int ux500_musb_init(struct musb *musb) { - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) { pr_err("HS USB OTG: no transceiver configured\n"); return -ENODEV; diff --git a/drivers/usb/otg/ab8500-usb.c b/drivers/usb/otg/ab8500-usb.c index 672e28c8143..ae8ad561f08 100644 --- a/drivers/usb/otg/ab8500-usb.c +++ b/drivers/usb/otg/ab8500-usb.c @@ -529,7 +529,7 @@ static int __devinit ab8500_usb_probe(struct platform_device *pdev) if (err < 0) goto fail0; - err = usb_add_phy(&ab->phy); + err = usb_add_phy(&ab->phy, USB_PHY_TYPE_USB2); if (err) { dev_err(&pdev->dev, "Can't register transceiver\n"); goto fail1; @@ -556,7 +556,7 @@ static int __devexit ab8500_usb_remove(struct platform_device *pdev) cancel_work_sync(&ab->phy_dis_work); - usb_add_phy(NULL); + usb_remove_phy(&ab->phy); ab8500_usb_host_phy_dis(ab); ab8500_usb_peri_phy_dis(ab); diff --git a/drivers/usb/otg/fsl_otg.c b/drivers/usb/otg/fsl_otg.c index 73561edd81d..23c798cb2d7 100644 --- a/drivers/usb/otg/fsl_otg.c +++ b/drivers/usb/otg/fsl_otg.c @@ -806,7 +806,7 @@ static int fsl_otg_conf(struct platform_device *pdev) fsl_otg_dev = fsl_otg_tc; /* Store the otg transceiver */ - status = usb_add_phy(&fsl_otg_tc->phy); + status = usb_add_phy(&fsl_otg_tc->phy, USB_PHY_TYPE_USB2); if (status) { pr_warn(FSL_OTG_NAME ": unable to register OTG transceiver.\n"); goto err; @@ -824,7 +824,7 @@ err: int usb_otg_start(struct platform_device *pdev) { struct fsl_otg *p_otg; - struct usb_phy *otg_trans = usb_get_phy(); + struct usb_phy *otg_trans = usb_get_phy(USB_PHY_TYPE_USB2); struct otg_fsm *fsm; int status; struct resource *res; @@ -1134,7 +1134,7 @@ static int __devexit fsl_otg_remove(struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; - usb_add_phy(NULL); + usb_remove_phy(&fsl_otg_dev->phy); free_irq(fsl_otg_dev->irq, fsl_otg_dev); iounmap((void *)usb_dr_regs); diff --git a/drivers/usb/otg/gpio_vbus.c b/drivers/usb/otg/gpio_vbus.c index 9b3c264cdb5..a67ffe22179 100644 --- a/drivers/usb/otg/gpio_vbus.c +++ b/drivers/usb/otg/gpio_vbus.c @@ -320,7 +320,7 @@ static int __init gpio_vbus_probe(struct platform_device *pdev) } /* only active when a gadget is registered */ - err = usb_add_phy(&gpio_vbus->phy); + err = usb_add_phy(&gpio_vbus->phy, USB_PHY_TYPE_USB2); if (err) { dev_err(&pdev->dev, "can't register transceiver, err: %d\n", err); @@ -354,7 +354,7 @@ static int __exit gpio_vbus_remove(struct platform_device *pdev) cancel_delayed_work_sync(&gpio_vbus->work); regulator_put(gpio_vbus->vbus_draw); - usb_add_phy(NULL); + usb_remove_phy(&gpio_vbus->phy); free_irq(gpio_vbus->irq, pdev); if (gpio_is_valid(pdata->gpio_pullup)) diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c index b74df3fec56..75cea4ab098 100644 --- a/drivers/usb/otg/isp1301_omap.c +++ b/drivers/usb/otg/isp1301_omap.c @@ -1611,7 +1611,7 @@ isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id) dev_dbg(&i2c->dev, "scheduled timer, %d min\n", TIMER_MINUTES); #endif - status = usb_add_phy(&isp->phy); + status = usb_add_phy(&isp->phy, USB_PHY_TYPE_USB2); if (status < 0) dev_err(&i2c->dev, "can't register transceiver, %d\n", status); @@ -1650,7 +1650,7 @@ subsys_initcall(isp_init); static void __exit isp_exit(void) { if (the_transceiver) - usb_add_phy(NULL); + usb_remove_phy(&the_transceiver->phy); i2c_del_driver(&isp1301_driver); } module_exit(isp_exit); diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c index dd606c01003..9f5fc906041 100644 --- a/drivers/usb/otg/msm_otg.c +++ b/drivers/usb/otg/msm_otg.c @@ -1555,7 +1555,7 @@ static int __init msm_otg_probe(struct platform_device *pdev) phy->otg->set_host = msm_otg_set_host; phy->otg->set_peripheral = msm_otg_set_peripheral; - ret = usb_add_phy(&motg->phy); + ret = usb_add_phy(&motg->phy, USB_PHY_TYPE_USB2); if (ret) { dev_err(&pdev->dev, "usb_add_phy failed\n"); goto free_irq; @@ -1624,7 +1624,7 @@ static int __devexit msm_otg_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 0); pm_runtime_disable(&pdev->dev); - usb_add_phy(NULL); + usb_remove_phy(phy); free_irq(motg->irq, motg); /* diff --git a/drivers/usb/otg/mv_otg.c b/drivers/usb/otg/mv_otg.c index 18e90fe1fbd..3f124e8f579 100644 --- a/drivers/usb/otg/mv_otg.c +++ b/drivers/usb/otg/mv_otg.c @@ -690,7 +690,7 @@ int mv_otg_remove(struct platform_device *pdev) for (clk_i = 0; clk_i <= mvotg->clknum; clk_i++) clk_put(mvotg->clk[clk_i]); - usb_add_phy(NULL); + usb_remove_phy(&mvotg->phy); platform_set_drvdata(pdev, NULL); kfree(mvotg->phy.otg); @@ -853,7 +853,7 @@ static int mv_otg_probe(struct platform_device *pdev) goto err_disable_clk; } - retval = usb_add_phy(&mvotg->phy); + retval = usb_add_phy(&mvotg->phy, USB_PHY_TYPE_USB2); if (retval < 0) { dev_err(&pdev->dev, "can't register transceiver, %d\n", retval); @@ -880,7 +880,7 @@ static int mv_otg_probe(struct platform_device *pdev) return 0; err_set_transceiver: - usb_add_phy(NULL); + usb_remove_phy(&mvotg->phy); err_free_irq: free_irq(mvotg->irq, mvotg); err_disable_clk: diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c index 33000dae720..803f958f413 100644 --- a/drivers/usb/otg/nop-usb-xceiv.c +++ b/drivers/usb/otg/nop-usb-xceiv.c @@ -117,7 +117,7 @@ static int __devinit nop_usb_xceiv_probe(struct platform_device *pdev) nop->phy.otg->set_host = nop_set_host; nop->phy.otg->set_peripheral = nop_set_peripheral; - err = usb_add_phy(&nop->phy); + err = usb_add_phy(&nop->phy, USB_PHY_TYPE_USB2); if (err) { dev_err(&pdev->dev, "can't register transceiver, err: %d\n", err); @@ -139,7 +139,7 @@ static int __devexit nop_usb_xceiv_remove(struct platform_device *pdev) { struct nop_usb_xceiv *nop = platform_get_drvdata(pdev); - usb_add_phy(NULL); + usb_remove_phy(&nop->phy); platform_set_drvdata(pdev, NULL); kfree(nop->phy.otg); diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index 300a995cfdb..a23065820ea 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -11,14 +11,32 @@ #include #include +#include #include #include -static struct usb_phy *phy; +static LIST_HEAD(phy_list); +static DEFINE_SPINLOCK(phy_lock); + +static struct usb_phy *__usb_find_phy(struct list_head *list, + enum usb_phy_type type) +{ + struct usb_phy *phy = NULL; + + list_for_each_entry(phy, list, head) { + if (phy->type != type) + continue; + + return phy; + } + + return ERR_PTR(-ENODEV); +} /** - * usb_get_phy - find the (single) USB PHY + * usb_get_phy - find the USB PHY + * @type - the type of the phy the controller requires * * Returns the phy driver, after getting a refcount to it; or * null if there is no such phy. The caller is responsible for @@ -26,16 +44,30 @@ static struct usb_phy *phy; * * For use by USB host and peripheral drivers. */ -struct usb_phy *usb_get_phy(void) +struct usb_phy *usb_get_phy(enum usb_phy_type type) { - if (phy) - get_device(phy->dev); + struct usb_phy *phy = NULL; + unsigned long flags; + + spin_lock_irqsave(&phy_lock, flags); + + phy = __usb_find_phy(&phy_list, type); + if (IS_ERR(phy)) { + pr_err("unable to find transceiver of type %s\n", + usb_phy_type_string(type)); + return phy; + } + + get_device(phy->dev); + + spin_unlock_irqrestore(&phy_lock, flags); + return phy; } EXPORT_SYMBOL(usb_get_phy); /** - * usb_put_phy - release the (single) USB PHY + * usb_put_phy - release the USB PHY * @x: the phy returned by usb_get_phy() * * Releases a refcount the caller received from usb_get_phy(). @@ -50,22 +82,62 @@ void usb_put_phy(struct usb_phy *x) EXPORT_SYMBOL(usb_put_phy); /** - * usb_add_phy - declare the (single) USB PHY + * usb_add_phy - declare the USB PHY * @x: the USB phy to be used; or NULL + * @type - the type of this PHY * * This call is exclusively for use by phy drivers, which * coordinate the activities of drivers for host and peripheral * controllers, and in some cases for VBUS current regulation. */ -int usb_add_phy(struct usb_phy *x) +int usb_add_phy(struct usb_phy *x, enum usb_phy_type type) { - if (phy && x) - return -EBUSY; - phy = x; - return 0; + int ret = 0; + unsigned long flags; + struct usb_phy *phy; + + if (x && x->type != USB_PHY_TYPE_UNDEFINED) { + dev_err(x->dev, "not accepting initialized PHY %s\n", x->label); + return -EINVAL; + } + + spin_lock_irqsave(&phy_lock, flags); + + list_for_each_entry(phy, &phy_list, head) { + if (phy->type == type) { + ret = -EBUSY; + dev_err(x->dev, "transceiver type %s already exists\n", + usb_phy_type_string(type)); + goto out; + } + } + + x->type = type; + list_add_tail(&x->head, &phy_list); + +out: + spin_unlock_irqrestore(&phy_lock, flags); + return ret; } EXPORT_SYMBOL(usb_add_phy); +/** + * usb_remove_phy - remove the OTG PHY + * @x: the USB OTG PHY to be removed; + * + * This reverts the effects of usb_add_phy + */ +void usb_remove_phy(struct usb_phy *x) +{ + unsigned long flags; + + spin_lock_irqsave(&phy_lock, flags); + if (x) + list_del(&x->head); + spin_unlock_irqrestore(&phy_lock, flags); +} +EXPORT_SYMBOL(usb_remove_phy); + const char *otg_state_string(enum usb_otg_state state) { switch (state) { diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index 01022c891e2..25a09fabbe3 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -633,7 +633,7 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev) kfree(twl); return err; } - usb_add_phy(&twl->phy); + usb_add_phy(&twl->phy, USB_PHY_TYPE_USB2); platform_set_drvdata(pdev, twl); if (device_create_file(&pdev->dev, &dev_attr_vbus)) diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c index a8be20878eb..dbee00aea75 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -443,7 +443,7 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev) kfree(twl); return err; } - usb_add_phy(&twl->phy); + usb_add_phy(&twl->phy, USB_PHY_TYPE_USB2); platform_set_drvdata(pdev, twl); if (device_create_file(&pdev->dev, &dev_attr_vbus)) diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 0e739c81052..1def65fb57d 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -43,6 +43,13 @@ enum usb_phy_events { USB_EVENT_ENUMERATED, /* gadget driver enumerated */ }; +/* associate a type with PHY */ +enum usb_phy_type { + USB_PHY_TYPE_UNDEFINED, + USB_PHY_TYPE_USB2, + USB_PHY_TYPE_USB3, +}; + struct usb_phy; /* for transceivers connected thru an ULPI interface, the user must @@ -89,6 +96,7 @@ struct usb_phy { const char *label; unsigned int flags; + enum usb_phy_type type; enum usb_otg_state state; enum usb_phy_events last_event; @@ -105,6 +113,9 @@ struct usb_phy { u16 port_status; u16 port_change; + /* to support controllers that have multiple transceivers */ + struct list_head head; + /* initialize/shutdown the OTG controller */ int (*init)(struct usb_phy *x); void (*shutdown)(struct usb_phy *x); @@ -121,7 +132,8 @@ struct usb_phy { /* for board-specific init logic */ -extern int usb_add_phy(struct usb_phy *); +extern int usb_add_phy(struct usb_phy *, enum usb_phy_type type); +extern void usb_remove_phy(struct usb_phy *); #if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE)) /* sometimes transceivers are accessed only through e.g. ULPI */ @@ -172,11 +184,11 @@ usb_phy_shutdown(struct usb_phy *x) /* for usb host and peripheral controller drivers */ #ifdef CONFIG_USB_OTG_UTILS -extern struct usb_phy *usb_get_phy(void); +extern struct usb_phy *usb_get_phy(enum usb_phy_type type); extern void usb_put_phy(struct usb_phy *); extern const char *otg_state_string(enum usb_otg_state state); #else -static inline struct usb_phy *usb_get_phy(void) +static inline struct usb_phy *usb_get_phy(enum usb_phy_type type) { return NULL; } @@ -276,4 +288,15 @@ usb_unregister_notifier(struct usb_phy *x, struct notifier_block *nb) /* for OTG controller drivers (and maybe other stuff) */ extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num); +static inline const char *usb_phy_type_string(enum usb_phy_type type) +{ + switch (type) { + case USB_PHY_TYPE_USB2: + return "USB2 PHY"; + case USB_PHY_TYPE_USB3: + return "USB3 PHY"; + default: + return "UNKNOWN PHY TYPE"; + } +} #endif /* __LINUX_USB_OTG_H */ -- cgit v1.2.3-70-g09d2 From cf61fdb944534ffa84a824bb8c31a3826cdc169d Mon Sep 17 00:00:00 2001 From: Guilherme Maciel Ferreira Date: Sat, 23 Jun 2012 21:24:30 -0300 Subject: USB: FHCI: Reusing QUICC Engine USB Controller registers from immap_qe.h The struct fhci_regs (in drivers/usb/host/fhci.h) is basically a redefinition of the struct qe_usb_ctlr (in arch/powerpc/include/asm/immap_qe.h). The qe_usb_ctlr struct is preferrable once it uses accurately the registers' names found in the Freescale's QUICC Engine Block Reference Manuals (QEIWRM.pdf Rev.4.4 Chapter 19 for MPC836xE series and MPC8323ERM.pdf Rev.2 Chapter 36 for MPC832xE series), making easier to map the FHCI device driver to the hardware manual. Also, as the FHCI driver uses the USB Controller registers, the name qe_usb_ctlr is a more precise representation of the hardware than fhci_regs. Signed-off-by: Guilherme Maciel Ferreira Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/fhci-dbg.c | 12 ++++++------ drivers/usb/host/fhci-hcd.c | 32 ++++++++++++++++---------------- drivers/usb/host/fhci-hub.c | 16 ++++++++-------- drivers/usb/host/fhci-sched.c | 30 +++++++++++++++--------------- drivers/usb/host/fhci-tds.c | 14 +++++++------- drivers/usb/host/fhci.h | 22 ++-------------------- 6 files changed, 54 insertions(+), 72 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/fhci-dbg.c b/drivers/usb/host/fhci-dbg.c index 6fe55004911..f238cb37305 100644 --- a/drivers/usb/host/fhci-dbg.c +++ b/drivers/usb/host/fhci-dbg.c @@ -41,7 +41,7 @@ void fhci_dbg_isr(struct fhci_hcd *fhci, int usb_er) static int fhci_dfs_regs_show(struct seq_file *s, void *v) { struct fhci_hcd *fhci = s->private; - struct fhci_regs __iomem *regs = fhci->regs; + struct qe_usb_ctlr __iomem *regs = fhci->regs; seq_printf(s, "mode: 0x%x\n" "addr: 0x%x\n" @@ -50,11 +50,11 @@ static int fhci_dfs_regs_show(struct seq_file *s, void *v) "status: 0x%x\n" "SOF timer: %d\n" "frame number: %d\n" "lines status: 0x%x\n", - in_8(®s->usb_mod), in_8(®s->usb_addr), - in_8(®s->usb_comm), in_be16(®s->usb_ep[0]), - in_be16(®s->usb_event), in_be16(®s->usb_mask), - in_8(®s->usb_status), in_be16(®s->usb_sof_tmr), - in_be16(®s->usb_frame_num), + in_8(®s->usb_usmod), in_8(®s->usb_usadr), + in_8(®s->usb_uscom), in_be16(®s->usb_usep[0]), + in_be16(®s->usb_usber), in_be16(®s->usb_usbmr), + in_8(®s->usb_usbs), in_be16(®s->usb_ussft), + in_be16(®s->usb_usfrn), fhci_ioports_check_bus_state(fhci)); return 0; diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c index d2623747b48..7da1a26bed2 100644 --- a/drivers/usb/host/fhci-hcd.c +++ b/drivers/usb/host/fhci-hcd.c @@ -40,8 +40,8 @@ void fhci_start_sof_timer(struct fhci_hcd *fhci) /* clear frame_n */ out_be16(&fhci->pram->frame_num, 0); - out_be16(&fhci->regs->usb_sof_tmr, 0); - setbits8(&fhci->regs->usb_mod, USB_MODE_SFTE); + out_be16(&fhci->regs->usb_ussft, 0); + setbits8(&fhci->regs->usb_usmod, USB_MODE_SFTE); fhci_dbg(fhci, "<- %s\n", __func__); } @@ -50,7 +50,7 @@ void fhci_stop_sof_timer(struct fhci_hcd *fhci) { fhci_dbg(fhci, "-> %s\n", __func__); - clrbits8(&fhci->regs->usb_mod, USB_MODE_SFTE); + clrbits8(&fhci->regs->usb_usmod, USB_MODE_SFTE); gtm_stop_timer16(fhci->timer); fhci_dbg(fhci, "<- %s\n", __func__); @@ -58,7 +58,7 @@ void fhci_stop_sof_timer(struct fhci_hcd *fhci) u16 fhci_get_sof_timer_count(struct fhci_usb *usb) { - return be16_to_cpu(in_be16(&usb->fhci->regs->usb_sof_tmr) / 12); + return be16_to_cpu(in_be16(&usb->fhci->regs->usb_ussft) / 12); } /* initialize the endpoint zero */ @@ -88,8 +88,8 @@ void fhci_usb_enable_interrupt(struct fhci_usb *usb) enable_irq(fhci_to_hcd(fhci)->irq); /* initialize the event register and mask register */ - out_be16(&usb->fhci->regs->usb_event, 0xffff); - out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk); + out_be16(&usb->fhci->regs->usb_usber, 0xffff); + out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk); /* enable the timer interrupts */ enable_irq(fhci->timer->irq); @@ -109,7 +109,7 @@ void fhci_usb_disable_interrupt(struct fhci_usb *usb) /* disable the usb interrupt */ disable_irq_nosync(fhci_to_hcd(fhci)->irq); - out_be16(&usb->fhci->regs->usb_mask, 0); + out_be16(&usb->fhci->regs->usb_usbmr, 0); } usb->intr_nesting_cnt++; } @@ -119,9 +119,9 @@ static u32 fhci_usb_enable(struct fhci_hcd *fhci) { struct fhci_usb *usb = fhci->usb_lld; - out_be16(&usb->fhci->regs->usb_event, 0xffff); - out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk); - setbits8(&usb->fhci->regs->usb_mod, USB_MODE_EN); + out_be16(&usb->fhci->regs->usb_usber, 0xffff); + out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk); + setbits8(&usb->fhci->regs->usb_usmod, USB_MODE_EN); mdelay(100); @@ -141,7 +141,7 @@ static u32 fhci_usb_disable(struct fhci_hcd *fhci) usb->port_status == FHCI_PORT_LOW) fhci_device_disconnected_interrupt(fhci); - clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_EN); + clrbits8(&usb->fhci->regs->usb_usmod, USB_MODE_EN); return 0; } @@ -285,13 +285,13 @@ static int fhci_usb_init(struct fhci_hcd *fhci) USB_E_IDLE_MASK | USB_E_RESET_MASK | USB_E_SFT_MASK | USB_E_MSF_MASK); - out_8(&usb->fhci->regs->usb_mod, USB_MODE_HOST | USB_MODE_EN); + out_8(&usb->fhci->regs->usb_usmod, USB_MODE_HOST | USB_MODE_EN); /* clearing the mask register */ - out_be16(&usb->fhci->regs->usb_mask, 0); + out_be16(&usb->fhci->regs->usb_usbmr, 0); /* initialing the event register */ - out_be16(&usb->fhci->regs->usb_event, 0xffff); + out_be16(&usb->fhci->regs->usb_usber, 0xffff); if (endpoint_zero_init(usb, DEFAULT_DATA_MEM, DEFAULT_RING_LEN) != 0) { fhci_usb_free(usb); @@ -745,8 +745,8 @@ static int __devinit of_fhci_probe(struct platform_device *ofdev) } /* Clear and disable any pending interrupts. */ - out_be16(&fhci->regs->usb_event, 0xffff); - out_be16(&fhci->regs->usb_mask, 0); + out_be16(&fhci->regs->usb_usber, 0xffff); + out_be16(&fhci->regs->usb_usbmr, 0); ret = usb_add_hcd(hcd, usb_irq, 0); if (ret < 0) diff --git a/drivers/usb/host/fhci-hub.c b/drivers/usb/host/fhci-hub.c index 348fe62e94f..6af2512f837 100644 --- a/drivers/usb/host/fhci-hub.c +++ b/drivers/usb/host/fhci-hub.c @@ -97,7 +97,7 @@ void fhci_port_disable(struct fhci_hcd *fhci) /* Enable IDLE since we want to know if something comes along */ usb->saved_msk |= USB_E_IDLE_MASK; - out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk); + out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk); /* check if during the disconnection process attached new device */ if (port_status == FHCI_PORT_WAITING) @@ -158,21 +158,21 @@ void fhci_port_reset(void *lld) fhci_stop_sof_timer(fhci); /* disable the USB controller */ - mode = in_8(&fhci->regs->usb_mod); - out_8(&fhci->regs->usb_mod, mode & (~USB_MODE_EN)); + mode = in_8(&fhci->regs->usb_usmod); + out_8(&fhci->regs->usb_usmod, mode & (~USB_MODE_EN)); /* disable idle interrupts */ - mask = in_be16(&fhci->regs->usb_mask); - out_be16(&fhci->regs->usb_mask, mask & (~USB_E_IDLE_MASK)); + mask = in_be16(&fhci->regs->usb_usbmr); + out_be16(&fhci->regs->usb_usbmr, mask & (~USB_E_IDLE_MASK)); fhci_io_port_generate_reset(fhci); /* enable interrupt on this endpoint */ - out_be16(&fhci->regs->usb_mask, mask); + out_be16(&fhci->regs->usb_usbmr, mask); /* enable the USB controller */ - mode = in_8(&fhci->regs->usb_mod); - out_8(&fhci->regs->usb_mod, mode | USB_MODE_EN); + mode = in_8(&fhci->regs->usb_usmod); + out_8(&fhci->regs->usb_usmod, mode | USB_MODE_EN); fhci_start_sof_timer(fhci); fhci_dbg(fhci, "<- %s\n", __func__); diff --git a/drivers/usb/host/fhci-sched.c b/drivers/usb/host/fhci-sched.c index 2df851b4bc7..2dc8a40e39d 100644 --- a/drivers/usb/host/fhci-sched.c +++ b/drivers/usb/host/fhci-sched.c @@ -132,8 +132,8 @@ void fhci_flush_all_transmissions(struct fhci_usb *usb) u8 mode; struct td *td; - mode = in_8(&usb->fhci->regs->usb_mod); - clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_EN); + mode = in_8(&usb->fhci->regs->usb_usmod); + clrbits8(&usb->fhci->regs->usb_usmod, USB_MODE_EN); fhci_flush_bds(usb); @@ -147,9 +147,9 @@ void fhci_flush_all_transmissions(struct fhci_usb *usb) usb->actual_frame->frame_status = FRAME_END_TRANSMISSION; /* reset the event register */ - out_be16(&usb->fhci->regs->usb_event, 0xffff); + out_be16(&usb->fhci->regs->usb_usber, 0xffff); /* enable the USB controller */ - out_8(&usb->fhci->regs->usb_mod, mode | USB_MODE_EN); + out_8(&usb->fhci->regs->usb_usmod, mode | USB_MODE_EN); } /* @@ -414,7 +414,7 @@ static void sof_interrupt(struct fhci_hcd *fhci) usb->port_status = FHCI_PORT_FULL; /* Disable IDLE */ usb->saved_msk &= ~USB_E_IDLE_MASK; - out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk); + out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk); } gtm_set_exact_timer16(fhci->timer, usb->max_frame_usage, false); @@ -433,14 +433,14 @@ void fhci_device_disconnected_interrupt(struct fhci_hcd *fhci) fhci_dbg(fhci, "-> %s\n", __func__); fhci_usb_disable_interrupt(usb); - clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_LSS); + clrbits8(&usb->fhci->regs->usb_usmod, USB_MODE_LSS); usb->port_status = FHCI_PORT_DISABLED; fhci_stop_sof_timer(fhci); /* Enable IDLE since we want to know if something comes along */ usb->saved_msk |= USB_E_IDLE_MASK; - out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk); + out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk); usb->vroot_hub->port.wPortStatus &= ~USB_PORT_STAT_CONNECTION; usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_CONNECTION; @@ -473,7 +473,7 @@ void fhci_device_connected_interrupt(struct fhci_hcd *fhci) } usb->port_status = FHCI_PORT_LOW; - setbits8(&usb->fhci->regs->usb_mod, USB_MODE_LSS); + setbits8(&usb->fhci->regs->usb_usmod, USB_MODE_LSS); usb->vroot_hub->port.wPortStatus |= (USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_CONNECTION); @@ -491,7 +491,7 @@ void fhci_device_connected_interrupt(struct fhci_hcd *fhci) } usb->port_status = FHCI_PORT_FULL; - clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_LSS); + clrbits8(&usb->fhci->regs->usb_usmod, USB_MODE_LSS); usb->vroot_hub->port.wPortStatus &= ~USB_PORT_STAT_LOW_SPEED; usb->vroot_hub->port.wPortStatus |= @@ -535,7 +535,7 @@ static void abort_transmission(struct fhci_usb *usb) /* issue stop Tx command */ qe_issue_cmd(QE_USB_STOP_TX, QE_CR_SUBBLOCK_USB, EP_ZERO, 0); /* flush Tx FIFOs */ - out_8(&usb->fhci->regs->usb_comm, USB_CMD_FLUSH_FIFO | EP_ZERO); + out_8(&usb->fhci->regs->usb_uscom, USB_CMD_FLUSH_FIFO | EP_ZERO); udelay(1000); /* reset Tx BDs */ fhci_flush_bds(usb); @@ -555,11 +555,11 @@ irqreturn_t fhci_irq(struct usb_hcd *hcd) usb = fhci->usb_lld; - usb_er |= in_be16(&usb->fhci->regs->usb_event) & - in_be16(&usb->fhci->regs->usb_mask); + usb_er |= in_be16(&usb->fhci->regs->usb_usber) & + in_be16(&usb->fhci->regs->usb_usbmr); /* clear event bits for next time */ - out_be16(&usb->fhci->regs->usb_event, usb_er); + out_be16(&usb->fhci->regs->usb_usber, usb_er); fhci_dbg_isr(fhci, usb_er); @@ -573,7 +573,7 @@ irqreturn_t fhci_irq(struct usb_hcd *hcd) /* Turn on IDLE since we want to disconnect */ usb->saved_msk |= USB_E_IDLE_MASK; - out_be16(&usb->fhci->regs->usb_event, + out_be16(&usb->fhci->regs->usb_usber, usb->saved_msk); } else if (usb->port_status == FHCI_PORT_DISABLED) { if (fhci_ioports_check_bus_state(fhci) == 1) @@ -611,7 +611,7 @@ irqreturn_t fhci_irq(struct usb_hcd *hcd) /* XXX usb->port_status = FHCI_PORT_WAITING; */ /* Disable IDLE */ usb->saved_msk &= ~USB_E_IDLE_MASK; - out_be16(&usb->fhci->regs->usb_mask, + out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk); } else { fhci_dbg_isr(fhci, -1); diff --git a/drivers/usb/host/fhci-tds.c b/drivers/usb/host/fhci-tds.c index c5ed8819929..1498061f0ae 100644 --- a/drivers/usb/host/fhci-tds.c +++ b/drivers/usb/host/fhci-tds.c @@ -249,7 +249,7 @@ void fhci_init_ep_registers(struct fhci_usb *usb, struct endpoint *ep, u8 rt; /* set the endpoint registers according to the endpoint */ - out_be16(&usb->fhci->regs->usb_ep[0], + out_be16(&usb->fhci->regs->usb_usep[0], USB_TRANS_CTR | USB_EP_MF | USB_EP_RTE); out_be16(&usb->fhci->pram->ep_ptr[0], cpm_muram_offset(ep->ep_pram_ptr)); @@ -463,7 +463,7 @@ u32 fhci_host_transaction(struct fhci_usb *usb, cq_put(&ep->conf_frame_Q, pkt); if (cq_howmany(&ep->conf_frame_Q) == 1) - out_8(&usb->fhci->regs->usb_comm, USB_CMD_STR_FIFO); + out_8(&usb->fhci->regs->usb_uscom, USB_CMD_STR_FIFO); return 0; } @@ -535,8 +535,8 @@ void fhci_flush_actual_frame(struct fhci_usb *usb) struct endpoint *ep = usb->ep0; /* disable the USB controller */ - mode = in_8(&usb->fhci->regs->usb_mod); - out_8(&usb->fhci->regs->usb_mod, mode & ~USB_MODE_EN); + mode = in_8(&usb->fhci->regs->usb_usmod); + out_8(&usb->fhci->regs->usb_usmod, mode & ~USB_MODE_EN); tb_ptr = in_be16(&ep->ep_pram_ptr->tx_bd_ptr); td = cpm_muram_addr(tb_ptr); @@ -571,9 +571,9 @@ void fhci_flush_actual_frame(struct fhci_usb *usb) usb->actual_frame->frame_status = FRAME_TIMER_END_TRANSMISSION; /* reset the event register */ - out_be16(&usb->fhci->regs->usb_event, 0xffff); + out_be16(&usb->fhci->regs->usb_usber, 0xffff); /* enable the USB controller */ - out_8(&usb->fhci->regs->usb_mod, mode | USB_MODE_EN); + out_8(&usb->fhci->regs->usb_usmod, mode | USB_MODE_EN); } /* handles Tx confirm and Tx error interrupt */ @@ -613,7 +613,7 @@ void fhci_host_transmit_actual_frame(struct fhci_usb *usb) /* start transmit only if we have something in the TDs */ if (in_be16(&td->status) & TD_R) - out_8(&usb->fhci->regs->usb_comm, USB_CMD_STR_FIFO); + out_8(&usb->fhci->regs->usb_uscom, USB_CMD_STR_FIFO); if (in_be32(&ep->conf_td->buf_ptr) == DUMMY_BD_BUFFER) { out_be32(&old_td->buf_ptr, 0); diff --git a/drivers/usb/host/fhci.h b/drivers/usb/host/fhci.h index dc6939a44a1..7cc1c32dc36 100644 --- a/drivers/usb/host/fhci.h +++ b/drivers/usb/host/fhci.h @@ -28,6 +28,7 @@ #include #include #include +#include #define USB_CLOCK 48000000 @@ -173,25 +174,6 @@ #define USB_E_TXB_MASK 0x0002 #define USB_E_RXB_MASK 0x0001 -/* Freescale USB Host controller registers */ -struct fhci_regs { - u8 usb_mod; /* mode register */ - u8 usb_addr; /* address register */ - u8 usb_comm; /* command register */ - u8 reserved1[1]; - __be16 usb_ep[4]; /* endpoint register */ - u8 reserved2[4]; - __be16 usb_event; /* event register */ - u8 reserved3[2]; - __be16 usb_mask; /* mask register */ - u8 reserved4[1]; - u8 usb_status; /* status register */ - __be16 usb_sof_tmr; /* Start Of Frame timer */ - u8 reserved5[2]; - __be16 usb_frame_num; /* frame number register */ - u8 reserved6[1]; -}; - /* Freescale USB HOST */ struct fhci_pram { __be16 ep_ptr[4]; /* Endpoint porter reg */ @@ -267,7 +249,7 @@ struct fhci_hcd { int gpios[NUM_GPIOS]; bool alow_gpios[NUM_GPIOS]; - struct fhci_regs __iomem *regs; /* I/O memory used to communicate */ + struct qe_usb_ctlr __iomem *regs; /* I/O memory used to communicate */ struct fhci_pram __iomem *pram; /* Parameter RAM */ struct gtm_timer *timer; -- cgit v1.2.3-70-g09d2 From a46af4ebf9ffec35eea0390e89935197b833dc61 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 25 Jun 2012 12:19:03 -0400 Subject: USB: EHCI: define extension registers like normal ones This patch (as1562) cleans up the definitions of the EHCI extended registers to be consistent with the definitions of the standard registers. This makes the code look a lot nicer, with no functional change. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 15 +++++---------- drivers/usb/host/ehci-hub.c | 26 ++++++++------------------ include/linux/usb/ehci_def.h | 28 +++++++++++++++++++++------- 3 files changed, 34 insertions(+), 35 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 800be38c78b..c49fc1e7895 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -203,11 +203,9 @@ static int handshake (struct ehci_hcd *ehci, void __iomem *ptr, /* check TDI/ARC silicon is in host mode */ static int tdi_in_host_mode (struct ehci_hcd *ehci) { - u32 __iomem *reg_ptr; u32 tmp; - reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + USBMODE); - tmp = ehci_readl(ehci, reg_ptr); + tmp = ehci_readl(ehci, &ehci->regs->usbmode); return (tmp & 3) == USBMODE_CM_HC; } @@ -303,11 +301,9 @@ static int handshake_on_error_set_halt(struct ehci_hcd *ehci, void __iomem *ptr, /* put TDI/ARC silicon into EHCI mode */ static void tdi_reset (struct ehci_hcd *ehci) { - u32 __iomem *reg_ptr; u32 tmp; - reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + USBMODE); - tmp = ehci_readl(ehci, reg_ptr); + tmp = ehci_readl(ehci, &ehci->regs->usbmode); tmp |= USBMODE_CM_HC; /* The default byte access to MMR space is LE after * controller reset. Set the required endian mode @@ -315,7 +311,7 @@ static void tdi_reset (struct ehci_hcd *ehci) */ if (ehci_big_endian_mmio(ehci)) tmp |= USBMODE_BE; - ehci_writel(ehci, tmp, reg_ptr); + ehci_writel(ehci, tmp, &ehci->regs->usbmode); } /* reset a non-running (STS_HALT == 1) controller */ @@ -339,9 +335,8 @@ static int ehci_reset (struct ehci_hcd *ehci) if (ehci->has_hostpc) { ehci_writel(ehci, USBMODE_EX_HC | USBMODE_EX_VBPS, - (u32 __iomem *)(((u8 *)ehci->regs) + USBMODE_EX)); - ehci_writel(ehci, TXFIFO_DEFAULT, - (u32 __iomem *)(((u8 *)ehci->regs) + TXFILLTUNING)); + &ehci->regs->usbmode_ex); + ehci_writel(ehci, TXFIFO_DEFAULT, &ehci->regs->txfill_tuning); } if (retval) return retval; diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index dd5eef6af6d..db05e358677 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -149,10 +149,8 @@ static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, if (ehci->has_hostpc) { port = HCS_N_PORTS(ehci->hcs_params); while (port--) { - u32 __iomem *hostpc_reg; + u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port]; - hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs - + HOSTPC0 + 4 * port); temp = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, temp & ~HOSTPC_PHCD, hostpc_reg); } @@ -185,10 +183,8 @@ static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, if (ehci->has_hostpc) { port = HCS_N_PORTS(ehci->hcs_params); while (port--) { - u32 __iomem *hostpc_reg; + u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port]; - hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs - + HOSTPC0 + 4 * port); temp = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, temp | HOSTPC_PHCD, hostpc_reg); } @@ -285,11 +281,9 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) port = HCS_N_PORTS(ehci->hcs_params); while (port--) { - u32 __iomem *hostpc_reg; + u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port]; u32 t3; - hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs - + HOSTPC0 + 4 * port); t3 = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg); t3 = ehci_readl(ehci, hostpc_reg); @@ -388,10 +382,9 @@ static int ehci_bus_resume (struct usb_hcd *hcd) i = HCS_N_PORTS(ehci->hcs_params); while (i--) { if (test_bit(i, &ehci->bus_suspended)) { - u32 __iomem *hostpc_reg; + u32 __iomem *hostpc_reg = + &ehci->regs->hostpc[i]; - hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs - + HOSTPC0 + 4 * i); temp = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, temp & ~HOSTPC_PHCD, hostpc_reg); @@ -667,7 +660,7 @@ static int ehci_hub_control ( int ports = HCS_N_PORTS (ehci->hcs_params); u32 __iomem *status_reg = &ehci->regs->port_status[ (wIndex & 0xff) - 1]; - u32 __iomem *hostpc_reg = NULL; + u32 __iomem *hostpc_reg = &ehci->regs->hostpc[(wIndex & 0xff) - 1]; u32 temp, temp1, status; unsigned long flags; int retval = 0; @@ -680,9 +673,6 @@ static int ehci_hub_control ( * power, "this is the one", etc. EHCI spec supports this. */ - if (ehci->has_hostpc) - hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs - + HOSTPC0 + 4 * ((wIndex & 0xff) - 1)); spin_lock_irqsave (&ehci->lock, flags); switch (typeReq) { case ClearHubFeature: @@ -734,7 +724,7 @@ static int ehci_hub_control ( goto error; /* clear phy low-power mode before resume */ - if (hostpc_reg) { + if (ehci->has_hostpc) { temp1 = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, temp1 & ~HOSTPC_PHCD, hostpc_reg); @@ -984,7 +974,7 @@ static int ehci_hub_control ( temp &= ~PORT_WKCONN_E; temp |= PORT_WKDISC_E | PORT_WKOC_E; ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); - if (hostpc_reg) { + if (ehci->has_hostpc) { spin_unlock_irqrestore(&ehci->lock, flags); msleep(5);/* 5ms for HCD enter low pwr mode */ spin_lock_irqsave(&ehci->lock, flags); diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h index 7cc95ee3606..de4b9ed5d5d 100644 --- a/include/linux/usb/ehci_def.h +++ b/include/linux/usb/ehci_def.h @@ -111,7 +111,13 @@ struct ehci_regs { /* ASYNCLISTADDR: offset 0x18 */ u32 async_next; /* address of next async queue head */ - u32 reserved[9]; + u32 reserved1[2]; + + /* TXFILLTUNING: offset 0x24 */ + u32 txfill_tuning; /* TX FIFO Tuning register */ +#define TXFIFO_DEFAULT (8<<16) /* FIFO burst threshold 8 */ + + u32 reserved2[6]; /* CONFIGFLAG: offset 0x40 */ u32 configured_flag; @@ -155,26 +161,34 @@ struct ehci_regs { #define PORT_CSC (1<<1) /* connect status change */ #define PORT_CONNECT (1<<0) /* device connected */ #define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) -}; -#define USBMODE 0x68 /* USB Device mode */ + u32 reserved3[9]; + + /* USBMODE: offset 0x68 */ + u32 usbmode; /* USB Device mode */ #define USBMODE_SDIS (1<<3) /* Stream disable */ #define USBMODE_BE (1<<2) /* BE/LE endianness select */ #define USBMODE_CM_HC (3<<0) /* host controller mode */ #define USBMODE_CM_IDLE (0<<0) /* idle state */ + u32 reserved4[7]; + /* Moorestown has some non-standard registers, partially due to the fact that * its EHCI controller has both TT and LPM support. HOSTPCx are extensions to * PORTSCx */ -#define HOSTPC0 0x84 /* HOSTPC extension */ + /* HOSTPC: offset 0x84 */ + u32 hostpc[0]; /* HOSTPC extension */ #define HOSTPC_PHCD (1<<22) /* Phy clock disable */ #define HOSTPC_PSPD (3<<25) /* Port speed detection */ -#define USBMODE_EX 0xc8 /* USB Device mode extension */ + + u32 reserved5[17]; + + /* USBMODE_EX: offset 0xc8 */ + u32 usbmode_ex; /* USB Device mode extension */ #define USBMODE_EX_VBPS (1<<5) /* VBus Power Select On */ #define USBMODE_EX_HC (3<<0) /* host controller mode */ -#define TXFILLTUNING 0x24 /* TX FIFO Tuning register */ -#define TXFIFO_DEFAULT (8<<16) /* FIFO burst threshold 8 */ +}; /* Appendix C, Debug port ... intended for use with special "debug devices" * that can help if there's no serial console. (nonstandard enumeration.) -- cgit v1.2.3-70-g09d2 From 984e97483a143f95d861b7218161ae033df293ab Mon Sep 17 00:00:00 2001 From: Russ Dill Date: Thu, 21 Jun 2012 03:44:31 -0700 Subject: ARM: OMAP: USB: Fixup ehci_hcd_omap_probe error path A recent commit, [PATCH] Fix OMAP EHCI suspend/resume failure (i693) '354ab856' causes ehci probe to fail on omap3xxx. This exposed bugs in the ehci_hcd_omap_probe error path causing an oops. On the error path, call usb_remove_hcd if usb_add_hcd has been called, and call usb_put_hcd if usb_alloc_hcd has been called. Tested on BB-xM. Signed-off-by: Russ.Dill@ti.com Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-omap.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 17cfb8a1131..6e15fc87cf6 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -347,7 +347,7 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) { dev_err(dev, "failed to add hcd with err %d\n", ret); - goto err_add_hcd; + goto err_pm_runtime; } /* root ports should always stay powered */ @@ -424,8 +424,12 @@ err_utmi_p1_fck: clk_put(utmi_p1_fck); err_add_hcd: + usb_remove_hcd(hcd); + +err_pm_runtime: disable_put_regulator(pdata); pm_runtime_put_sync(dev); + usb_put_hcd(hcd); err_io: iounmap(regs); -- cgit v1.2.3-70-g09d2 From ded017ee6c7b90f7356bd8488f8af1c10ba90490 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 26 Jun 2012 17:40:32 +0530 Subject: usb: phy: fix return value check of usb_get_phy usb_get_phy will return -ENODEV if it's not able to find the phy. Hence fixed all the callers of usb_get_phy to check for this error condition instead of relying on a non-zero value as success condition. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/power/ab8500_charger.c | 2 +- drivers/power/isp1704_charger.c | 2 +- drivers/power/pda_power.c | 16 ++++++++-------- drivers/power/twl4030_charger.c | 7 ++++--- drivers/usb/chipidea/udc.c | 9 +++++---- drivers/usb/gadget/fsl_udc_core.c | 15 ++++++++------- drivers/usb/gadget/mv_udc_core.c | 13 +++++++------ drivers/usb/gadget/omap_udc.c | 25 +++++++++++++------------ drivers/usb/gadget/pxa25x_udc.c | 11 ++++++----- drivers/usb/gadget/pxa27x_udc.c | 11 ++++++----- drivers/usb/gadget/s3c-hsudc.c | 9 +++++---- drivers/usb/host/ehci-fsl.c | 5 +++-- drivers/usb/host/ehci-msm.c | 2 +- drivers/usb/host/ehci-mv.c | 7 ++++--- drivers/usb/host/ehci-tegra.c | 7 ++++--- drivers/usb/host/ohci-omap.c | 5 +++-- drivers/usb/musb/am35x.c | 3 ++- drivers/usb/musb/blackfin.c | 3 ++- drivers/usb/musb/da8xx.c | 3 ++- drivers/usb/musb/davinci.c | 3 ++- drivers/usb/musb/musb_dsps.c | 3 ++- drivers/usb/musb/omap2430.c | 2 +- drivers/usb/musb/tusb6010.c | 3 ++- drivers/usb/musb/ux500.c | 3 ++- drivers/usb/otg/otg.c | 4 ++-- 25 files changed, 96 insertions(+), 77 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index 6bd6f1c4196..d4f0c98428c 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -2689,7 +2689,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) } di->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2); - if (!di->usb_phy) { + if (IS_ERR_OR_NULL(di->usb_phy)) { dev_err(di->dev, "failed to get usb transceiver\n"); ret = -EINVAL; goto free_usb; diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c index 090e5f9e72c..122911978da 100644 --- a/drivers/power/isp1704_charger.c +++ b/drivers/power/isp1704_charger.c @@ -416,7 +416,7 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev) return -ENOMEM; isp->phy = usb_get_phy(USB_PHY_TYPE_USB2); - if (!isp->phy) + if (IS_ERR_OR_NULL(isp->phy)) goto fail0; isp->dev = &pdev->dev; diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c index 7602d49e4d8..8dbcd53c5e6 100644 --- a/drivers/power/pda_power.c +++ b/drivers/power/pda_power.c @@ -322,11 +322,11 @@ static int pda_power_probe(struct platform_device *pdev) #ifdef CONFIG_USB_OTG_UTILS transceiver = usb_get_phy(USB_PHY_TYPE_USB2); - if (transceiver && !pdata->is_usb_online) { - pdata->is_usb_online = otg_is_usb_online; - } - if (transceiver && !pdata->is_ac_online) { - pdata->is_ac_online = otg_is_ac_online; + if (!IS_ERR_OR_NULL(transceiver)) { + if (!pdata->is_usb_online) + pdata->is_usb_online = otg_is_usb_online; + if (!pdata->is_ac_online) + pdata->is_ac_online = otg_is_ac_online; } #endif @@ -373,7 +373,7 @@ static int pda_power_probe(struct platform_device *pdev) } #ifdef CONFIG_USB_OTG_UTILS - if (transceiver && pdata->use_otg_notifier) { + if (!IS_ERR_OR_NULL(transceiver) && pdata->use_otg_notifier) { otg_nb.notifier_call = otg_handle_notification; ret = usb_register_notifier(transceiver, &otg_nb); if (ret) { @@ -408,7 +408,7 @@ usb_supply_failed: if (pdata->is_ac_online && ac_irq) free_irq(ac_irq->start, &pda_psy_ac); #ifdef CONFIG_USB_OTG_UTILS - if (transceiver) + if (!IS_ERR_OR_NULL(transceiver)) usb_put_phy(transceiver); #endif ac_irq_failed: @@ -443,7 +443,7 @@ static int pda_power_remove(struct platform_device *pdev) if (pdata->is_ac_online) power_supply_unregister(&pda_psy_ac); #ifdef CONFIG_USB_OTG_UTILS - if (transceiver) + if (!IS_ERR_OR_NULL(transceiver)) usb_put_phy(transceiver); #endif if (ac_draw) { diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 13f9db2e853..7cacbaa68ef 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -480,7 +481,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) INIT_WORK(&bci->work, twl4030_bci_usb_work); bci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); - if (bci->transceiver != NULL) { + if (!IS_ERR_OR_NULL(bci->transceiver)) { bci->usb_nb.notifier_call = twl4030_bci_usb_ncb; usb_register_notifier(bci->transceiver, &bci->usb_nb); } @@ -507,7 +508,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) return 0; fail_unmask_interrupts: - if (bci->transceiver != NULL) { + if (!IS_ERR_OR_NULL(bci->transceiver)) { usb_unregister_notifier(bci->transceiver, &bci->usb_nb); usb_put_phy(bci->transceiver); } @@ -538,7 +539,7 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, TWL4030_INTERRUPTS_BCIIMR2A); - if (bci->transceiver != NULL) { + if (!IS_ERR_OR_NULL(bci->transceiver)) { usb_unregister_notifier(bci->transceiver, &bci->usb_nb); usb_put_phy(bci->transceiver); } diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index a06d28b119f..4688ab71bd2 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -1712,7 +1713,7 @@ static int udc_start(struct ci13xxx *udc) if (retval) goto unreg_device; - if (udc->transceiver) { + if (!IS_ERR_OR_NULL(udc->transceiver)) { retval = otg_set_peripheral(udc->transceiver->otg, &udc->gadget); if (retval) @@ -1729,7 +1730,7 @@ static int udc_start(struct ci13xxx *udc) return retval; remove_trans: - if (udc->transceiver) { + if (!IS_ERR_OR_NULL(udc->transceiver)) { otg_set_peripheral(udc->transceiver->otg, &udc->gadget); usb_put_phy(udc->transceiver); } @@ -1740,7 +1741,7 @@ remove_dbg: unreg_device: device_unregister(&udc->gadget.dev); put_transceiver: - if (udc->transceiver) + if (!IS_ERR_OR_NULL(udc->transceiver)) usb_put_phy(udc->transceiver); free_pools: dma_pool_destroy(udc->td_pool); @@ -1772,7 +1773,7 @@ static void udc_stop(struct ci13xxx *udc) dma_pool_destroy(udc->td_pool); dma_pool_destroy(udc->qh_pool); - if (udc->transceiver) { + if (!IS_ERR_OR_NULL(udc->transceiver)) { otg_set_peripheral(udc->transceiver->otg, NULL); usb_put_phy(udc->transceiver); } diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 0808820ba49..8d8fca635cc 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -1229,7 +1230,7 @@ static int fsl_vbus_draw(struct usb_gadget *gadget, unsigned mA) struct fsl_udc *udc; udc = container_of(gadget, struct fsl_udc, gadget); - if (udc->transceiver) + if (!IS_ERR_OR_NULL(udc->transceiver)) return usb_phy_set_power(udc->transceiver, mA); return -ENOTSUPP; } @@ -1983,13 +1984,13 @@ static int fsl_start(struct usb_gadget_driver *driver, goto out; } - if (udc_controller->transceiver) { + if (!IS_ERR_OR_NULL(udc_controller->transceiver)) { /* Suspend the controller until OTG enable it */ udc_controller->stopped = 1; printk(KERN_INFO "Suspend udc for OTG auto detect\n"); /* connect to bus through transceiver */ - if (udc_controller->transceiver) { + if (!IS_ERR_OR_NULL(udc_controller->transceiver)) { retval = otg_set_peripheral( udc_controller->transceiver->otg, &udc_controller->gadget); @@ -2030,7 +2031,7 @@ static int fsl_stop(struct usb_gadget_driver *driver) if (!driver || driver != udc_controller->driver || !driver->unbind) return -EINVAL; - if (udc_controller->transceiver) + if (!IS_ERR_OR_NULL(udc_controller->transceiver)) otg_set_peripheral(udc_controller->transceiver->otg, NULL); /* stop DR, disable intr */ @@ -2456,7 +2457,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) #ifdef CONFIG_USB_OTG if (pdata->operating_mode == FSL_USB2_DR_OTG) { udc_controller->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); - if (!udc_controller->transceiver) { + if (IS_ERR_OR_NULL(udc_controller->transceiver)) { ERR("Can't find OTG driver!\n"); ret = -ENODEV; goto err_kfree; @@ -2540,7 +2541,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) goto err_free_irq; } - if (!udc_controller->transceiver) { + if (IS_ERR_OR_NULL(udc_controller->transceiver)) { /* initialize usb hw reg except for regs for EP, * leave usbintr reg untouched */ dr_controller_setup(udc_controller); @@ -2564,7 +2565,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) if (ret < 0) goto err_free_irq; - if (udc_controller->transceiver) + if (!IS_ERR_OR_NULL(udc_controller->transceiver)) udc_controller->gadget.is_otg = 1; /* setup QH and epctrl for ep0 */ diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 75ff41a5c95..9305de41af2 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -1381,7 +1382,7 @@ static int mv_udc_start(struct usb_gadget_driver *driver, return retval; } - if (udc->transceiver) { + if (!IS_ERR_OR_NULL(udc->transceiver)) { retval = otg_set_peripheral(udc->transceiver->otg, &udc->gadget); if (retval) { @@ -2107,7 +2108,7 @@ static int __devexit mv_udc_remove(struct platform_device *dev) * then vbus irq will not be requested in udc driver. */ if (udc->pdata && udc->pdata->vbus - && udc->clock_gating && udc->transceiver == NULL) + && udc->clock_gating && IS_ERR_OR_NULL(udc->transceiver)) free_irq(udc->pdata->vbus->irq, &dev->dev); /* free memory allocated in probe */ @@ -2325,7 +2326,7 @@ static int __devinit mv_udc_probe(struct platform_device *dev) eps_init(udc); /* VBUS detect: we can disable/enable clock on demand.*/ - if (udc->transceiver) + if (!IS_ERR_OR_NULL(udc->transceiver)) udc->clock_gating = 1; else if (pdata->vbus) { udc->clock_gating = 1; @@ -2369,7 +2370,7 @@ static int __devinit mv_udc_probe(struct platform_device *dev) err_unregister: if (udc->pdata && udc->pdata->vbus - && udc->clock_gating && udc->transceiver == NULL) + && udc->clock_gating && IS_ERR_OR_NULL(udc->transceiver)) free_irq(pdata->vbus->irq, &dev->dev); device_unregister(&udc->gadget.dev); err_free_irq: @@ -2404,7 +2405,7 @@ static int mv_udc_suspend(struct device *_dev) struct mv_udc *udc = the_controller; /* if OTG is enabled, the following will be done in OTG driver*/ - if (udc->transceiver) + if (!IS_ERR_OR_NULL(udc->transceiver)) return 0; if (udc->pdata->vbus && udc->pdata->vbus->poll) @@ -2437,7 +2438,7 @@ static int mv_udc_resume(struct device *_dev) int retval; /* if OTG is enabled, the following will be done in OTG driver*/ - if (udc->transceiver) + if (!IS_ERR_OR_NULL(udc->transceiver)) return 0; if (!udc->clock_gating) { diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index cf8bf26f12e..7b71295adf6 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -1211,7 +1212,7 @@ static int omap_wakeup(struct usb_gadget *gadget) /* NOTE: non-OTG systems may use SRP TOO... */ } else if (!(udc->devstat & UDC_ATT)) { - if (udc->transceiver) + if (!IS_ERR_OR_NULL(udc->transceiver)) retval = otg_start_srp(udc->transceiver->otg); } spin_unlock_irqrestore(&udc->lock, flags); @@ -1343,7 +1344,7 @@ static int omap_vbus_draw(struct usb_gadget *gadget, unsigned mA) struct omap_udc *udc; udc = container_of(gadget, struct omap_udc, gadget); - if (udc->transceiver) + if (!IS_ERR_OR_NULL(udc->transceiver)) return usb_phy_set_power(udc->transceiver, mA); return -EOPNOTSUPP; } @@ -1792,12 +1793,12 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src) if (devstat & UDC_ATT) { udc->gadget.speed = USB_SPEED_FULL; VDBG("connect\n"); - if (!udc->transceiver) + if (IS_ERR_OR_NULL(udc->transceiver)) pullup_enable(udc); // if (driver->connect) call it } else if (udc->gadget.speed != USB_SPEED_UNKNOWN) { udc->gadget.speed = USB_SPEED_UNKNOWN; - if (!udc->transceiver) + if (IS_ERR_OR_NULL(udc->transceiver)) pullup_disable(udc); DBG("disconnect, gadget %s\n", udc->driver->driver.name); @@ -1837,12 +1838,12 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src) udc->driver->suspend(&udc->gadget); spin_lock(&udc->lock); } - if (udc->transceiver) + if (!IS_ERR_OR_NULL(udc->transceiver)) usb_phy_set_suspend( udc->transceiver, 1); } else { VDBG("resume\n"); - if (udc->transceiver) + if (!IS_ERR_OR_NULL(udc->transceiver)) usb_phy_set_suspend( udc->transceiver, 0); if (udc->gadget.speed == USB_SPEED_FULL @@ -2154,7 +2155,7 @@ static int omap_udc_start(struct usb_gadget_driver *driver, omap_writew(UDC_IRQ_SRC_MASK, UDC_IRQ_SRC); /* connect to bus through transceiver */ - if (udc->transceiver) { + if (!IS_ERR_OR_NULL(udc->transceiver)) { status = otg_set_peripheral(udc->transceiver->otg, &udc->gadget); if (status < 0) { @@ -2201,7 +2202,7 @@ static int omap_udc_stop(struct usb_gadget_driver *driver) if (machine_without_vbus_sense()) omap_vbus_session(&udc->gadget, 0); - if (udc->transceiver) + if (!IS_ERR_OR_NULL(udc->transceiver)) (void) otg_set_peripheral(udc->transceiver->otg, NULL); else pullup_disable(udc); @@ -2866,7 +2867,7 @@ static int __init omap_udc_probe(struct platform_device *pdev) * but not having one probably means no VBUS detection. */ xceiv = usb_get_phy(USB_PHY_TYPE_USB2); - if (xceiv) + if (!IS_ERR_OR_NULL(xceiv)) type = xceiv->label; else if (config->otg) { DBG("OTG requires external transceiver!\n"); @@ -2898,7 +2899,7 @@ static int __init omap_udc_probe(struct platform_device *pdev) case 16: case 19: case 25: - if (!xceiv) { + if (IS_ERR_OR_NULL(xceiv)) { DBG("external transceiver not registered!\n"); type = "unknown"; } @@ -3010,7 +3011,7 @@ cleanup1: udc = NULL; cleanup0: - if (xceiv) + if (!IS_ERR_OR_NULL(xceiv)) usb_put_phy(xceiv); if (cpu_is_omap16xx() || cpu_is_omap24xx() || cpu_is_omap7xx()) { @@ -3040,7 +3041,7 @@ static int __exit omap_udc_remove(struct platform_device *pdev) udc->done = &done; pullup_disable(udc); - if (udc->transceiver) { + if (!IS_ERR_OR_NULL(udc->transceiver)) { usb_put_phy(udc->transceiver); udc->transceiver = NULL; } diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index cc0b1e63dca..fa8e93c2465 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -993,7 +994,7 @@ static int pxa25x_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA) udc = container_of(_gadget, struct pxa25x_udc, gadget); - if (udc->transceiver) + if (!IS_ERR_OR_NULL(udc->transceiver)) return usb_phy_set_power(udc->transceiver, mA); return -EOPNOTSUPP; } @@ -1299,7 +1300,7 @@ fail: DMSG("registered gadget driver '%s'\n", driver->driver.name); /* connect to bus through transceiver */ - if (dev->transceiver) { + if (!IS_ERR_OR_NULL(dev->transceiver)) { retval = otg_set_peripheral(dev->transceiver->otg, &dev->gadget); if (retval) { @@ -1359,7 +1360,7 @@ static int pxa25x_stop(struct usb_gadget_driver *driver) stop_activity(dev, driver); local_irq_enable(); - if (dev->transceiver) + if (!IS_ERR_OR_NULL(dev->transceiver)) (void) otg_set_peripheral(dev->transceiver->otg, NULL); driver->unbind(&dev->gadget); @@ -2237,7 +2238,7 @@ lubbock_fail0: if (gpio_is_valid(dev->mach->gpio_pullup)) gpio_free(dev->mach->gpio_pullup); err_gpio_pullup: - if (dev->transceiver) { + if (!IS_ERR_OR_NULL(dev->transceiver)) { usb_put_phy(dev->transceiver); dev->transceiver = NULL; } @@ -2279,7 +2280,7 @@ static int __exit pxa25x_udc_remove(struct platform_device *pdev) clk_put(dev->clk); - if (dev->transceiver) { + if (!IS_ERR_OR_NULL(dev->transceiver)) { usb_put_phy(dev->transceiver); dev->transceiver = NULL; } diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 8f744aab962..644b4305cb9 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -1573,7 +1574,7 @@ static int should_enable_udc(struct pxa_udc *udc) int put_on; put_on = ((udc->pullup_on) && (udc->driver)); - put_on &= ((udc->vbus_sensed) || (!udc->transceiver)); + put_on &= ((udc->vbus_sensed) || (IS_ERR_OR_NULL(udc->transceiver))); return put_on; } @@ -1594,7 +1595,7 @@ static int should_disable_udc(struct pxa_udc *udc) int put_off; put_off = ((!udc->pullup_on) || (!udc->driver)); - put_off |= ((!udc->vbus_sensed) && (udc->transceiver)); + put_off |= ((!udc->vbus_sensed) && (!IS_ERR_OR_NULL(udc->transceiver))); return put_off; } @@ -1665,7 +1666,7 @@ static int pxa_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA) struct pxa_udc *udc; udc = to_gadget_udc(_gadget); - if (udc->transceiver) + if (!IS_ERR_OR_NULL(udc->transceiver)) return usb_phy_set_power(udc->transceiver, mA); return -EOPNOTSUPP; } @@ -1834,7 +1835,7 @@ static int pxa27x_udc_start(struct usb_gadget_driver *driver, dev_dbg(udc->dev, "registered gadget driver '%s'\n", driver->driver.name); - if (udc->transceiver) { + if (!IS_ERR_OR_NULL(udc->transceiver)) { retval = otg_set_peripheral(udc->transceiver->otg, &udc->gadget); if (retval) { @@ -1908,7 +1909,7 @@ static int pxa27x_udc_stop(struct usb_gadget_driver *driver) dev_info(udc->dev, "unregistered gadget driver '%s'\n", driver->driver.name); - if (udc->transceiver) + if (!IS_ERR_OR_NULL(udc->transceiver)) return otg_set_peripheral(udc->transceiver->otg, NULL); return 0; } diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index 22326f27446..7c915625f24 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -1165,7 +1166,7 @@ static int s3c_hsudc_start(struct usb_gadget *gadget, } /* connect to bus through transceiver */ - if (hsudc->transceiver) { + if (!IS_ERR_OR_NULL(hsudc->transceiver)) { ret = otg_set_peripheral(hsudc->transceiver->otg, &hsudc->gadget); if (ret) { @@ -1220,7 +1221,7 @@ static int s3c_hsudc_stop(struct usb_gadget *gadget, s3c_hsudc_stop_activity(hsudc); spin_unlock_irqrestore(&hsudc->lock, flags); - if (hsudc->transceiver) + if (!IS_ERR_OR_NULL(hsudc->transceiver)) (void) otg_set_peripheral(hsudc->transceiver->otg, NULL); disable_irq(hsudc->irq); @@ -1249,7 +1250,7 @@ static int s3c_hsudc_vbus_draw(struct usb_gadget *gadget, unsigned mA) if (!hsudc) return -ENODEV; - if (hsudc->transceiver) + if (!IS_ERR_OR_NULL(hsudc->transceiver)) return usb_phy_set_power(hsudc->transceiver, mA); return -EOPNOTSUPP; @@ -1385,7 +1386,7 @@ err_irq: err_remap: release_mem_region(res->start, resource_size(res)); err_res: - if (hsudc->transceiver) + if (!IS_ERR_OR_NULL(hsudc->transceiver)) usb_put_phy(hsudc->transceiver); regulator_bulk_free(ARRAY_SIZE(hsudc->supplies), hsudc->supplies); diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index ba290589d85..32865a7145a 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -146,7 +147,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, transceiver=0x%p\n", hcd, ehci, ehci->transceiver); - if (ehci->transceiver) { + if (!IS_ERR_OR_NULL(ehci->transceiver)) { retval = otg_set_host(ehci->transceiver->otg, &ehci_to_hcd(ehci)->self); if (retval) { @@ -192,7 +193,7 @@ static void usb_hcd_fsl_remove(struct usb_hcd *hcd, struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; struct ehci_hcd *ehci = hcd_to_ehci(hcd); - if (ehci->transceiver) { + if (!IS_ERR_OR_NULL(ehci->transceiver)) { otg_set_host(ehci->transceiver->otg, NULL); usb_put_phy(ehci->transceiver); } diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index c7615fb93db..6b4ffb598db 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -146,7 +146,7 @@ static int ehci_msm_probe(struct platform_device *pdev) * management. */ phy = usb_get_phy(USB_PHY_TYPE_USB2); - if (!phy) { + if (IS_ERR_OR_NULL(phy)) { dev_err(&pdev->dev, "unable to find transceiver\n"); ret = -ENODEV; goto unmap; diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index ef7aa0df40a..0e8c168ca24 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -254,7 +255,7 @@ static int mv_ehci_probe(struct platform_device *pdev) if (ehci_mv->mode == MV_USB_MODE_OTG) { #ifdef CONFIG_USB_OTG_UTILS ehci_mv->otg = usb_get_phy(USB_PHY_TYPE_USB2); - if (!ehci_mv->otg) { + if (IS_ERR_OR_NULL(ehci_mv->otg)) { dev_err(&pdev->dev, "unable to find transceiver\n"); retval = -ENODEV; @@ -302,7 +303,7 @@ err_set_vbus: pdata->set_vbus(0); #ifdef CONFIG_USB_OTG_UTILS err_put_transceiver: - if (ehci_mv->otg) + if (!IS_ERR_OR_NULL(ehci_mv->otg)) usb_put_phy(ehci_mv->otg); #endif err_disable_clk: @@ -331,7 +332,7 @@ static int mv_ehci_remove(struct platform_device *pdev) if (hcd->rh_registered) usb_remove_hcd(hcd); - if (ehci_mv->otg) { + if (!IS_ERR_OR_NULL(ehci_mv->otg)) { otg_set_host(ehci_mv->otg->otg, NULL); usb_put_phy(ehci_mv->otg); } diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 14df2f5cf6a..477ecfa0515 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -17,6 +17,7 @@ */ #include +#include #include #include #include @@ -750,7 +751,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) #ifdef CONFIG_USB_OTG_UTILS if (pdata->operating_mode == TEGRA_USB_OTG) { tegra->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); - if (tegra->transceiver) + if (!IS_ERR_OR_NULL(tegra->transceiver)) otg_set_host(tegra->transceiver->otg, &hcd->self); } #endif @@ -773,7 +774,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) fail: #ifdef CONFIG_USB_OTG_UTILS - if (tegra->transceiver) { + if (!IS_ERR_OR_NULL(tegra->transceiver)) { otg_set_host(tegra->transceiver->otg, NULL); usb_put_phy(tegra->transceiver); } @@ -808,7 +809,7 @@ static int tegra_ehci_remove(struct platform_device *pdev) pm_runtime_put_noidle(&pdev->dev); #ifdef CONFIG_USB_OTG_UTILS - if (tegra->transceiver) { + if (!IS_ERR_OR_NULL(tegra->transceiver)) { otg_set_host(tegra->transceiver->otg, NULL); usb_put_phy(tegra->transceiver); } diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 92a77dfd193..c7b06f504c6 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -212,7 +213,7 @@ static int ohci_omap_init(struct usb_hcd *hcd) #ifdef CONFIG_USB_OTG if (need_transceiver) { ohci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); - if (ohci->transceiver) { + if (!IS_ERR_OR_NULL(ohci->transceiver)) { int status = otg_set_host(ohci->transceiver->otg, &ohci_to_hcd(ohci)->self); dev_dbg(hcd->self.controller, "init %s transceiver, status %d\n", @@ -403,7 +404,7 @@ usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev) struct ohci_hcd *ohci = hcd_to_ohci (hcd); usb_remove_hcd(hcd); - if (ohci->transceiver) { + if (!IS_ERR_OR_NULL(ohci->transceiver)) { (void) otg_set_host(ohci->transceiver->otg, 0); usb_put_phy(ohci->transceiver); } diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index 4a8cbf0e8d5..7a95ab87ac0 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -365,7 +366,7 @@ static int am35x_musb_init(struct musb *musb) usb_nop_xceiv_register(); musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); - if (!musb->xceiv) + if (IS_ERR_OR_NULL(musb->xceiv)) return -ENODEV; if (is_host_enabled(musb)) diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 452940986d6..428e6aa3e78 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -416,7 +417,7 @@ static int bfin_musb_init(struct musb *musb) usb_nop_xceiv_register(); musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); - if (!musb->xceiv) { + if (IS_ERR_OR_NULL(musb->xceiv)) { gpio_free(musb->config->gpio_vrsel); return -ENODEV; } diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index d731c80c4fe..0f9fcec4e1d 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -426,7 +427,7 @@ static int da8xx_musb_init(struct musb *musb) usb_nop_xceiv_register(); musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); - if (!musb->xceiv) + if (IS_ERR_OR_NULL(musb->xceiv)) goto fail; if (is_host_enabled(musb)) diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 582268de3fa..f6492043655 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -385,7 +386,7 @@ static int davinci_musb_init(struct musb *musb) usb_nop_xceiv_register(); musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); - if (!musb->xceiv) + if (IS_ERR_OR_NULL(musb->xceiv)) goto unregister; musb->mregs += DAVINCI_BASE_OFFSET; diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 92603e498e6..217808d9fbe 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -377,7 +378,7 @@ static int dsps_musb_init(struct musb *musb) /* NOP driver needs change if supporting dual instance */ usb_nop_xceiv_register(); musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); - if (!musb->xceiv) + if (IS_ERR_OR_NULL(musb->xceiv)) return -ENODEV; /* Returns zero if e.g. not clocked */ diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 2813490ba63..5fdb9da8dd5 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -320,7 +320,7 @@ static int omap2430_musb_init(struct musb *musb) * which needs a driver, drivers aren't always needed. */ musb->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); - if (!musb->xceiv) { + if (IS_ERR_OR_NULL(musb->xceiv)) { pr_err("HS USB OTG: no transceiver configured\n"); return -ENODEV; } diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 8ddf3d5f7cd..1a1bd9cf40c 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -1079,7 +1080,7 @@ static int tusb_musb_init(struct musb *musb) usb_nop_xceiv_register(); musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); - if (!musb->xceiv) + if (IS_ERR_OR_NULL(musb->xceiv)) return -ENODEV; pdev = to_platform_device(musb->controller); diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index 46cf80a8cac..a8c0fadce1b 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -38,7 +39,7 @@ struct ux500_glue { static int ux500_musb_init(struct musb *musb) { musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); - if (!musb->xceiv) { + if (IS_ERR_OR_NULL(musb->xceiv)) { pr_err("HS USB OTG: no transceiver configured\n"); return -ENODEV; } diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index 0fa4d8c1b1e..88d62b16f63 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -67,7 +67,7 @@ struct usb_phy *devm_usb_get_phy(struct device *dev, enum usb_phy_type type) return NULL; phy = usb_get_phy(type); - if (phy) { + if (!IS_ERR(phy)) { *ptr = phy; devres_add(dev, ptr); } else @@ -82,7 +82,7 @@ EXPORT_SYMBOL(devm_usb_get_phy); * @type - the type of the phy the controller requires * * Returns the phy driver, after getting a refcount to it; or - * null if there is no such phy. The caller is responsible for + * -ENODEV if there is no such phy. The caller is responsible for * calling usb_put_phy() to release that count. * * For use by USB host and peripheral drivers. -- cgit v1.2.3-70-g09d2 From e864abed546f9f4b76a3580fb3c53cd389e0c015 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 28 Jun 2012 16:49:42 +0900 Subject: USB: ohci-exynos: add clock gating to suspend/resume This patch adds clock gating to suspend and resume functions. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-exynos.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index 2909621ea19..fedb0eff35d 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -225,6 +225,9 @@ static int exynos_ohci_suspend(struct device *dev) if (pdata && pdata->phy_exit) pdata->phy_exit(pdev, S5P_USB_PHY_HOST); + + clk_disable(exynos_ohci->clk); + fail: spin_unlock_irqrestore(&ohci->lock, flags); @@ -238,6 +241,8 @@ static int exynos_ohci_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data; + clk_enable(exynos_ohci->clk); + if (pdata && pdata->phy_init) pdata->phy_init(pdev, S5P_USB_PHY_HOST); -- cgit v1.2.3-70-g09d2 From 390a0a78067c487609ba5bd18c264f7d5b6f4e96 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 28 Jun 2012 16:30:30 +0900 Subject: USB: ohci-exynos: use devm_ functions The devm_ functions allocate memory that is released when a driver detaches. This makes the code smaller and a bit simpler. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-exynos.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index fedb0eff35d..8bcbdb5ade2 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -87,7 +87,8 @@ static int __devinit exynos_ohci_probe(struct platform_device *pdev) return -EINVAL; } - exynos_ohci = kzalloc(sizeof(struct exynos_ohci_hcd), GFP_KERNEL); + exynos_ohci = devm_kzalloc(&pdev->dev, sizeof(struct exynos_ohci_hcd), + GFP_KERNEL); if (!exynos_ohci) return -ENOMEM; @@ -97,8 +98,7 @@ static int __devinit exynos_ohci_probe(struct platform_device *pdev) dev_name(&pdev->dev)); if (!hcd) { dev_err(&pdev->dev, "Unable to create HCD\n"); - err = -ENOMEM; - goto fail_hcd; + return -ENOMEM; } exynos_ohci->hcd = hcd; @@ -123,7 +123,7 @@ static int __devinit exynos_ohci_probe(struct platform_device *pdev) hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); - hcd->regs = ioremap(res->start, resource_size(res)); + hcd->regs = devm_ioremap(&pdev->dev, res->start, hcd->rsrc_len); if (!hcd->regs) { dev_err(&pdev->dev, "Failed to remap I/O memory\n"); err = -ENOMEM; @@ -134,7 +134,7 @@ static int __devinit exynos_ohci_probe(struct platform_device *pdev) if (!irq) { dev_err(&pdev->dev, "Failed to get IRQ\n"); err = -ENODEV; - goto fail; + goto fail_io; } if (pdata->phy_init) @@ -146,23 +146,19 @@ static int __devinit exynos_ohci_probe(struct platform_device *pdev) err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) { dev_err(&pdev->dev, "Failed to add USB HCD\n"); - goto fail; + goto fail_io; } platform_set_drvdata(pdev, exynos_ohci); return 0; -fail: - iounmap(hcd->regs); fail_io: clk_disable(exynos_ohci->clk); fail_clken: clk_put(exynos_ohci->clk); fail_clk: usb_put_hcd(hcd); -fail_hcd: - kfree(exynos_ohci); return err; } @@ -177,13 +173,10 @@ static int __devexit exynos_ohci_remove(struct platform_device *pdev) if (pdata && pdata->phy_exit) pdata->phy_exit(pdev, S5P_USB_PHY_HOST); - iounmap(hcd->regs); - clk_disable(exynos_ohci->clk); clk_put(exynos_ohci->clk); usb_put_hcd(hcd); - kfree(exynos_ohci); return 0; } -- cgit v1.2.3-70-g09d2 From 9cb07563721cb05f91b517aefd70b57ba8a1d5aa Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 28 Jun 2012 16:29:46 +0900 Subject: USB: ehci-s5p: use devm_ functions The devm_ functions allocate memory that is released when a driver detaches. This makes the code smaller and a bit simpler. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-s5p.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index c474cec064e..1e483f052ff 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -79,7 +79,8 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev) return -EINVAL; } - s5p_ehci = kzalloc(sizeof(struct s5p_ehci_hcd), GFP_KERNEL); + s5p_ehci = devm_kzalloc(&pdev->dev, sizeof(struct s5p_ehci_hcd), + GFP_KERNEL); if (!s5p_ehci) return -ENOMEM; @@ -89,8 +90,7 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev) dev_name(&pdev->dev)); if (!hcd) { dev_err(&pdev->dev, "Unable to create HCD\n"); - err = -ENOMEM; - goto fail_hcd; + return -ENOMEM; } s5p_ehci->hcd = hcd; @@ -115,7 +115,7 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev) hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); - hcd->regs = ioremap(res->start, resource_size(res)); + hcd->regs = devm_ioremap(&pdev->dev, res->start, hcd->rsrc_len); if (!hcd->regs) { dev_err(&pdev->dev, "Failed to remap I/O memory\n"); err = -ENOMEM; @@ -126,7 +126,7 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev) if (!irq) { dev_err(&pdev->dev, "Failed to get IRQ\n"); err = -ENODEV; - goto fail; + goto fail_io; } if (pdata->phy_init) @@ -151,23 +151,19 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev) err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) { dev_err(&pdev->dev, "Failed to add USB HCD\n"); - goto fail; + goto fail_io; } platform_set_drvdata(pdev, s5p_ehci); return 0; -fail: - iounmap(hcd->regs); fail_io: clk_disable(s5p_ehci->clk); fail_clken: clk_put(s5p_ehci->clk); fail_clk: usb_put_hcd(hcd); -fail_hcd: - kfree(s5p_ehci); return err; } @@ -182,13 +178,10 @@ static int __devexit s5p_ehci_remove(struct platform_device *pdev) if (pdata && pdata->phy_exit) pdata->phy_exit(pdev, S5P_USB_PHY_HOST); - iounmap(hcd->regs); - clk_disable(s5p_ehci->clk); clk_put(s5p_ehci->clk); usb_put_hcd(hcd); - kfree(s5p_ehci); return 0; } -- cgit v1.2.3-70-g09d2 From 19181bc50e1b8e92a7a3b3d78637c6dc5c0b5a1b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 4 Jul 2012 09:18:02 +0200 Subject: usbdevfs: Add a USBDEVFS_GET_CAPABILITIES ioctl There are a few (new) usbdevfs capabilities which an application cannot discover in any other way then checking the kernel version. There are 3 problems with this: 1) It is just not very pretty. 2) Given the tendency of enterprise distros to backport stuff it is not reliable. 3) As discussed in length on the mailinglist, USBDEVFS_URB_BULK_CONTINUATION does not work as it should when combined with USBDEVFS_URB_SHORT_NOT_OK (which is its intended use) on devices attached to an XHCI controller. So the availability of these features can be host controller dependent, making depending on them based on the kernel version not a good idea. This patch besides adding the new ioctl also adds flags for the following existing capabilities: USBDEVFS_CAP_ZERO_PACKET, available since 2.6.31 USBDEVFS_CAP_BULK_CONTINUATION, available since 2.6.32, except for XHCI USBDEVFS_CAP_NO_PACKET_SIZE_LIM, available since 3.3 Note that this patch only does not advertise the USBDEVFS_URB_BULK_CONTINUATION cap for XHCI controllers, bulk transfers with this flag set will still be accepted when submitted to XHCI controllers. Returning -EINVAL for them would break existing apps, and in most cases the troublesome scenario wrt USBDEVFS_URB_SHORT_NOT_OK urbs on XHCI controllers will never get hit, so this would break working use cases. The disadvantage of not returning -EINVAL is that cases were it is causing real trouble may go undetected / the cause of the trouble may be unclear, but this is the best we can do. Signed-off-by: Hans de Goede Acked-by: Alan Stern Acked-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 17 +++++++++++++++++ drivers/usb/host/xhci.c | 2 ++ include/linux/usb.h | 5 +++++ include/linux/usbdevice_fs.h | 7 +++++++ 4 files changed, 31 insertions(+) (limited to 'drivers/usb/host') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 62679bc031f..0b387c1a8b7 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1824,6 +1824,20 @@ static int proc_release_port(struct dev_state *ps, void __user *arg) return usb_hub_release_port(ps->dev, portnum, ps); } +static int proc_get_capabilities(struct dev_state *ps, void __user *arg) +{ + __u32 caps; + + caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM; + if (!ps->dev->bus->no_stop_on_short) + caps |= USBDEVFS_CAP_BULK_CONTINUATION; + + if (put_user(caps, (__u32 __user *)arg)) + return -EFAULT; + + return 0; +} + /* * NOTE: All requests here that have interface numbers as parameters * are assuming that somehow the configuration has been prevented from @@ -1994,6 +2008,9 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, snoop(&dev->dev, "%s: RELEASE_PORT\n", __func__); ret = proc_release_port(ps, p); break; + case USBDEVFS_GET_CAPABILITIES: + ret = proc_get_capabilities(ps, p); + break; } usb_unlock_device(dev); if (ret >= 0) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index a979cd0dbe0..7648b2d4b26 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -4450,6 +4450,8 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) /* Accept arbitrarily long scatter-gather lists */ hcd->self.sg_tablesize = ~0; + /* XHCI controllers don't stop the ep queue on short packets :| */ + hcd->self.no_stop_on_short = 1; if (usb_hcd_is_primary_hcd(hcd)) { xhci = kzalloc(sizeof(struct xhci_hcd), GFP_KERNEL); diff --git a/include/linux/usb.h b/include/linux/usb.h index f717fbdaee8..d4f9de1acd4 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -331,6 +331,11 @@ struct usb_bus { u8 otg_port; /* 0, or number of OTG/HNP port */ unsigned is_b_host:1; /* true during some HNP roleswitches */ unsigned b_hnp_enable:1; /* OTG: did A-Host enable HNP? */ + unsigned no_stop_on_short:1; /* + * Quirk: some controllers don't stop + * the ep queue on a short transfer + * with the URB_SHORT_NOT_OK flag set. + */ unsigned sg_tablesize; /* 0 or largest number of sg list entries */ int devnum_next; /* Next open device number in diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h index 15591d2ea40..07b2ceaaad7 100644 --- a/include/linux/usbdevice_fs.h +++ b/include/linux/usbdevice_fs.h @@ -125,6 +125,11 @@ struct usbdevfs_hub_portinfo { char port [127]; /* e.g. port 3 connects to device 27 */ }; +/* Device capability flags */ +#define USBDEVFS_CAP_ZERO_PACKET 0x01 +#define USBDEVFS_CAP_BULK_CONTINUATION 0x02 +#define USBDEVFS_CAP_NO_PACKET_SIZE_LIM 0x04 + #ifdef __KERNEL__ #ifdef CONFIG_COMPAT #include @@ -204,4 +209,6 @@ struct usbdevfs_ioctl32 { #define USBDEVFS_CONNECT _IO('U', 23) #define USBDEVFS_CLAIM_PORT _IOR('U', 24, unsigned int) #define USBDEVFS_RELEASE_PORT _IOR('U', 25, unsigned int) +#define USBDEVFS_GET_CAPABILITIES _IOR('U', 26, __u32) + #endif /* _LINUX_USBDEVICE_FS_H */ -- cgit v1.2.3-70-g09d2 From 2761a63945164ef111062828858a80225222ecd7 Mon Sep 17 00:00:00 2001 From: Russ Dill Date: Thu, 14 Jun 2012 09:24:21 -0700 Subject: mfd: USB: Fix the omap-usb EHCI ULPI PHY reset fix issues. 'ARM: OMAP3: USB: Fix the EHCI ULPI PHY reset issue' (1fcb57d0) fixes an issue where the ULPI PHYs were not held in reset while initializing the EHCI controller. However, it also changes behavior in omap-usb-host.c omap_usbhs_init by releasing reset while the configuration in that function was done. This change caused a regression on BB-xM where USB would not function if 'usb start' had been run from u-boot before booting. A change was made to release reset a little bit earlier which fixed the issue on BB-xM and did not cause any regressions on 3430 sdp, the board for which the fix was originally made. This new fix, 'USB: EHCI: OMAP: Finish ehci omap phy reset cycle before adding hcd.', (3aa2ae74) caused a regression on OMAP5. The original fix to hold the EHCI controller in reset during initialization was correct, however it appears that changing omap_usbhs_init to not hold the PHYs in reset during it's configuration was incorrect. This patch first reverts both fixes, and then changes ehci_hcd_omap_probe in ehci-omap.c to hold the PHYs in reset as the original patch had done. It also is sure to incorporate the _cansleep change that has been made in the meantime. I've tested this on Beagleboard xM, I'd really like to get an ack from the 3430 sdp and OMAP5 guys before getting this merged. v3 - Brown paper bag its too early in the morning actually run git commit amend fix v2 - Put cansleep gpiolib call outside of spinlock Acked-by: Mantesh Sarashetti Tested-by: Mantesh Sarashetti Acked-by: Keshava Munegowda Tested-by: Keshava Munegowda Signed-off-by: Russ Dill Signed-off-by: Samuel Ortiz --- drivers/mfd/omap-usb-host.c | 48 +++++++++++++++++++++++++++++++++++++++++++- drivers/usb/host/ehci-omap.c | 18 ++++++++--------- 2 files changed, 55 insertions(+), 11 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 7e96bb22972..41088ecbb2a 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -500,8 +501,21 @@ static void omap_usbhs_init(struct device *dev) dev_dbg(dev, "starting TI HSUSB Controller\n"); pm_runtime_get_sync(dev); - spin_lock_irqsave(&omap->lock, flags); + if (pdata->ehci_data->phy_reset) { + if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) + gpio_request_one(pdata->ehci_data->reset_gpio_port[0], + GPIOF_OUT_INIT_LOW, "USB1 PHY reset"); + + if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) + gpio_request_one(pdata->ehci_data->reset_gpio_port[1], + GPIOF_OUT_INIT_LOW, "USB2 PHY reset"); + + /* Hold the PHY in RESET for enough time till DIR is high */ + udelay(10); + } + + spin_lock_irqsave(&omap->lock, flags); omap->usbhs_rev = usbhs_read(omap->uhh_base, OMAP_UHH_REVISION); dev_dbg(dev, "OMAP UHH_REVISION 0x%x\n", omap->usbhs_rev); @@ -581,9 +595,39 @@ static void omap_usbhs_init(struct device *dev) } spin_unlock_irqrestore(&omap->lock, flags); + + if (pdata->ehci_data->phy_reset) { + /* Hold the PHY in RESET for enough time till + * PHY is settled and ready + */ + udelay(10); + + if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) + gpio_set_value_cansleep + (pdata->ehci_data->reset_gpio_port[0], 1); + + if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) + gpio_set_value_cansleep + (pdata->ehci_data->reset_gpio_port[1], 1); + } + pm_runtime_put_sync(dev); } +static void omap_usbhs_deinit(struct device *dev) +{ + struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); + struct usbhs_omap_platform_data *pdata = &omap->platdata; + + if (pdata->ehci_data->phy_reset) { + if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) + gpio_free(pdata->ehci_data->reset_gpio_port[0]); + + if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) + gpio_free(pdata->ehci_data->reset_gpio_port[1]); + } +} + /** * usbhs_omap_probe - initialize TI-based HCDs @@ -767,6 +811,7 @@ static int __devinit usbhs_omap_probe(struct platform_device *pdev) goto end_probe; err_alloc: + omap_usbhs_deinit(&pdev->dev); iounmap(omap->tll_base); err_tll: @@ -818,6 +863,7 @@ static int __devexit usbhs_omap_remove(struct platform_device *pdev) { struct usbhs_hcd_omap *omap = platform_get_drvdata(pdev); + omap_usbhs_deinit(&pdev->dev); iounmap(omap->tll_base); iounmap(omap->uhh_base); clk_put(omap->init_60m_fclk); diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 17cfb8a1131..c30435499a0 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -281,14 +281,13 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) } } + /* Hold PHYs in reset while initializing EHCI controller */ if (pdata->phy_reset) { if (gpio_is_valid(pdata->reset_gpio_port[0])) - gpio_request_one(pdata->reset_gpio_port[0], - GPIOF_OUT_INIT_LOW, "USB1 PHY reset"); + gpio_set_value_cansleep(pdata->reset_gpio_port[0], 0); if (gpio_is_valid(pdata->reset_gpio_port[1])) - gpio_request_one(pdata->reset_gpio_port[1], - GPIOF_OUT_INIT_LOW, "USB2 PHY reset"); + gpio_set_value_cansleep(pdata->reset_gpio_port[1], 0); /* Hold the PHY in RESET for enough time till DIR is high */ udelay(10); @@ -330,6 +329,11 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) 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); + goto err_add_hcd; + } if (pdata->phy_reset) { /* Hold the PHY in RESET for enough time till @@ -344,12 +348,6 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) gpio_set_value_cansleep(pdata->reset_gpio_port[1], 1); } - ret = usb_add_hcd(hcd, irq, IRQF_SHARED); - if (ret) { - dev_err(dev, "failed to add hcd with err %d\n", ret); - goto err_add_hcd; - } - /* root ports should always stay powered */ ehci_port_power(omap_ehci, 1); -- cgit v1.2.3-70-g09d2 From c5cf9212a368d88fe1e25797699b167f6daa64a5 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 28 Jun 2012 11:19:02 -0400 Subject: EHCI: centralize controller suspend/resume This patch (as1563) removes a lot of duplicated code by moving the EHCI controller suspend/resume routines into the core driver, where the various platform drivers can invoke them as needed. Not only does this simplify these platform drivers, this also makes it easier for other platform drivers to add suspend/resume support in the future. Note: The patch does not touch the ehci-fsl.c file, because its approach to suspend and resume is so different from all the others. It will have to be handled specially by its maintainer. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-au1xxx.c | 73 ++------------------------------ drivers/usb/host/ehci-hcd.c | 89 ++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/ehci-hub.c | 4 +- drivers/usb/host/ehci-msm.c | 19 ++------- drivers/usb/host/ehci-pci.c | 74 ++------------------------------- drivers/usb/host/ehci-platform.c | 7 ++-- drivers/usb/host/ehci-s5p.c | 61 ++------------------------- drivers/usb/host/ehci-sead3.c | 74 ++------------------------------- drivers/usb/host/ehci-spear.c | 61 ++------------------------- 9 files changed, 114 insertions(+), 348 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c index bf7441afed1..182d3956590 100644 --- a/drivers/usb/host/ehci-au1xxx.c +++ b/drivers/usb/host/ehci-au1xxx.c @@ -158,28 +158,10 @@ static int ehci_hcd_au1xxx_drv_remove(struct platform_device *pdev) static int ehci_hcd_au1xxx_drv_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - unsigned long flags; - int rc = 0; - - if (time_before(jiffies, ehci->next_statechange)) - msleep(10); - - /* Root hub was already suspended. Disable irq emission and - * mark HW unaccessible. The PM and USB cores make sure that - * the root hub is either suspended or stopped. - */ - ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev)); - spin_lock_irqsave(&ehci->lock, flags); - ehci_writel(ehci, 0, &ehci->regs->intr_enable); - (void)ehci_readl(ehci, &ehci->regs->intr_enable); - - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - spin_unlock_irqrestore(&ehci->lock, flags); - - // could save FLADJ in case of Vaux power loss - // ... we'd only use it to handle clock skew + bool do_wakeup = device_may_wakeup(dev); + int rc; + rc = ehci_suspend(hcd, do_wakeup); alchemy_usb_control(ALCHEMY_USB_EHCI0, 0); return rc; @@ -188,56 +170,9 @@ static int ehci_hcd_au1xxx_drv_suspend(struct device *dev) static int ehci_hcd_au1xxx_drv_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - struct ehci_hcd *ehci = hcd_to_ehci(hcd); alchemy_usb_control(ALCHEMY_USB_EHCI0, 1); - - // maybe restore FLADJ - - if (time_before(jiffies, ehci->next_statechange)) - msleep(100); - - /* Mark hardware accessible again as we are out of D3 state by now */ - set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - - /* If CF is still set, we maintained PCI Vaux power. - * Just undo the effect of ehci_pci_suspend(). - */ - if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) { - int mask = INTR_MASK; - - ehci_prepare_ports_for_controller_resume(ehci); - if (!hcd->self.root_hub->do_remote_wakeup) - mask &= ~STS_PCD; - ehci_writel(ehci, mask, &ehci->regs->intr_enable); - ehci_readl(ehci, &ehci->regs->intr_enable); - return 0; - } - - ehci_dbg(ehci, "lost power, restarting\n"); - usb_root_hub_lost_power(hcd->self.root_hub); - - /* Else reset, to cope with power loss or flush-to-storage - * style "resume" having let BIOS kick in during reboot. - */ - (void) ehci_halt(ehci); - (void) ehci_reset(ehci); - - /* emptying the schedule aborts any urbs */ - spin_lock_irq(&ehci->lock); - if (ehci->reclaim) - end_unlink_async(ehci); - ehci_work(ehci); - spin_unlock_irq(&ehci->lock); - - ehci_writel(ehci, ehci->command, &ehci->regs->command); - ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); - ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ - - /* here we "know" root ports should always stay powered */ - ehci_port_power(ehci, 1); - - ehci->rh_state = EHCI_RH_SUSPENDED; + ehci_resume(hcd, false); return 0; } diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index c49fc1e7895..e6823a0cf64 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1242,6 +1242,95 @@ static int ehci_get_frame (struct usb_hcd *hcd) } /*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_PM + +/* suspend/resume, section 4.3 */ + +/* These routines handle the generic parts of controller suspend/resume */ + +static int __maybe_unused ehci_suspend(struct usb_hcd *hcd, bool do_wakeup) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + + if (time_before(jiffies, ehci->next_statechange)) + msleep(10); + + /* + * Root hub was already suspended. Disable IRQ emission and + * mark HW unaccessible. The PM and USB cores make sure that + * the root hub is either suspended or stopped. + */ + ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup); + + spin_lock_irq(&ehci->lock); + ehci_writel(ehci, 0, &ehci->regs->intr_enable); + (void) ehci_readl(ehci, &ehci->regs->intr_enable); + + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + spin_unlock_irq(&ehci->lock); + + return 0; +} + +/* Returns 0 if power was preserved, 1 if power was lost */ +static int __maybe_unused ehci_resume(struct usb_hcd *hcd, bool hibernated) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + + if (time_before(jiffies, ehci->next_statechange)) + msleep(100); + + /* Mark hardware accessible again as we are back to full power by now */ + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + /* + * If CF is still set and we aren't resuming from hibernation + * then we maintained suspend power. + * Just undo the effect of ehci_suspend(). + */ + if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF && + !hibernated) { + int mask = INTR_MASK; + + ehci_prepare_ports_for_controller_resume(ehci); + if (!hcd->self.root_hub->do_remote_wakeup) + mask &= ~STS_PCD; + ehci_writel(ehci, mask, &ehci->regs->intr_enable); + ehci_readl(ehci, &ehci->regs->intr_enable); + return 0; + } + + /* + * Else reset, to cope with power loss or resume from hibernation + * having let the firmware kick in during reboot. + */ + usb_root_hub_lost_power(hcd->self.root_hub); + (void) ehci_halt(ehci); + (void) ehci_reset(ehci); + + /* emptying the schedule aborts any urbs */ + spin_lock_irq(&ehci->lock); + if (ehci->reclaim) + end_unlink_async(ehci); + ehci_work(ehci); + spin_unlock_irq(&ehci->lock); + + ehci_writel(ehci, ehci->command, &ehci->regs->command); + ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); + ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ + + /* here we "know" root ports should always stay powered */ + ehci_port_power(ehci, 1); + + ehci->rh_state = EHCI_RH_SUSPENDED; + return 1; +} + +#endif + +/*-------------------------------------------------------------------------*/ + /* * The EHCI in ChipIdea HDRC cannot be a separate module or device, * because its registers (and irq) are shared between host/gadget/otg diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index db05e358677..b3e2d66e95b 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -107,7 +107,7 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci) ehci->owned_ports = 0; } -static int __maybe_unused ehci_port_change(struct ehci_hcd *ehci) +static int ehci_port_change(struct ehci_hcd *ehci) { int i = HCS_N_PORTS(ehci->hcs_params); @@ -128,7 +128,7 @@ static int __maybe_unused ehci_port_change(struct ehci_hcd *ehci) return 0; } -static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, +static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, bool suspending, bool do_wakeup) { int port; diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index 6b4ffb598db..17dd9e94001 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -198,24 +198,11 @@ static int __devexit ehci_msm_remove(struct platform_device *pdev) static int ehci_msm_pm_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - bool wakeup = device_may_wakeup(dev); + bool do_wakeup = device_may_wakeup(dev); dev_dbg(dev, "ehci-msm PM suspend\n"); - /* - * EHCI helper function has also the same check before manipulating - * port wakeup flags. We do check here the same condition before - * calling the same helper function to avoid bringing hardware - * from Low power mode when there is no need for adjusting port - * wakeup flags. - */ - if (hcd->self.root_hub->do_remote_wakeup && !wakeup) { - pm_runtime_resume(dev); - ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd), - wakeup); - } - - return 0; + return ehci_suspend(hcd, do_wakeup); } static int ehci_msm_pm_resume(struct device *dev) @@ -223,7 +210,7 @@ static int ehci_msm_pm_resume(struct device *dev) struct usb_hcd *hcd = dev_get_drvdata(dev); dev_dbg(dev, "ehci-msm PM resume\n"); - ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd)); + ehci_resume(hcd, false); return 0; } diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 123481793a4..6e767bce060 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -331,29 +331,7 @@ done: static int ehci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) { - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - unsigned long flags; - int rc = 0; - - if (time_before(jiffies, ehci->next_statechange)) - msleep(10); - - /* Root hub was already suspended. Disable irq emission and - * mark HW unaccessible. The PM and USB cores make sure that - * the root hub is either suspended or stopped. - */ - ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup); - spin_lock_irqsave (&ehci->lock, flags); - ehci_writel(ehci, 0, &ehci->regs->intr_enable); - (void)ehci_readl(ehci, &ehci->regs->intr_enable); - - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - spin_unlock_irqrestore (&ehci->lock, flags); - - // could save FLADJ in case of Vaux power loss - // ... we'd only use it to handle clock skew - - return rc; + return ehci_suspend(hcd, do_wakeup); } static bool usb_is_intel_switchable_ehci(struct pci_dev *pdev) @@ -402,54 +380,8 @@ static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated) if (usb_is_intel_switchable_ehci(pdev)) ehci_enable_xhci_companion(); - // maybe restore FLADJ - - if (time_before(jiffies, ehci->next_statechange)) - msleep(100); - - /* Mark hardware accessible again as we are out of D3 state by now */ - set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - - /* If CF is still set and we aren't resuming from hibernation - * then we maintained PCI Vaux power. - * Just undo the effect of ehci_pci_suspend(). - */ - if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF && - !hibernated) { - int mask = INTR_MASK; - - ehci_prepare_ports_for_controller_resume(ehci); - if (!hcd->self.root_hub->do_remote_wakeup) - mask &= ~STS_PCD; - ehci_writel(ehci, mask, &ehci->regs->intr_enable); - ehci_readl(ehci, &ehci->regs->intr_enable); - return 0; - } - - usb_root_hub_lost_power(hcd->self.root_hub); - - /* Else reset, to cope with power loss or flush-to-storage - * style "resume" having let BIOS kick in during reboot. - */ - (void) ehci_halt(ehci); - (void) ehci_reset(ehci); - (void) ehci_pci_reinit(ehci, pdev); - - /* emptying the schedule aborts any urbs */ - spin_lock_irq(&ehci->lock); - if (ehci->reclaim) - end_unlink_async(ehci); - ehci_work(ehci); - spin_unlock_irq(&ehci->lock); - - ehci_writel(ehci, ehci->command, &ehci->regs->command); - ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); - ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ - - /* here we "know" root ports should always stay powered */ - ehci_port_power(ehci, 1); - - ehci->rh_state = EHCI_RH_SUSPENDED; + if (ehci_resume(hcd, hibernated) != 0) + (void) ehci_pci_reinit(ehci, pdev); return 0; } #endif diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index dfe881a34ae..4b1d896d5a2 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -153,17 +153,16 @@ static int __devexit ehci_platform_remove(struct platform_device *dev) static int ehci_platform_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - bool wakeup = device_may_wakeup(dev); + bool do_wakeup = device_may_wakeup(dev); - ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd), wakeup); - return 0; + return ehci_suspend(hcd, do_wakeup); } static int ehci_platform_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd)); + ehci_resume(hcd, false); return 0; } diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index 1e483f052ff..c7e0936d4a7 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -200,27 +200,12 @@ static int s5p_ehci_suspend(struct device *dev) { struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev); struct usb_hcd *hcd = s5p_ehci->hcd; - struct ehci_hcd *ehci = hcd_to_ehci(hcd); + bool do_wakeup = device_may_wakeup(dev); struct platform_device *pdev = to_platform_device(dev); struct s5p_ehci_platdata *pdata = pdev->dev.platform_data; - unsigned long flags; - int rc = 0; + int rc; - if (time_before(jiffies, ehci->next_statechange)) - msleep(20); - - /* - * Root hub was already suspended. Disable irq emission and - * mark HW unaccessible. The PM and USB cores make sure that - * the root hub is either suspended or stopped. - */ - ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev)); - spin_lock_irqsave(&ehci->lock, flags); - ehci_writel(ehci, 0, &ehci->regs->intr_enable); - (void)ehci_readl(ehci, &ehci->regs->intr_enable); - - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - spin_unlock_irqrestore(&ehci->lock, flags); + rc = ehci_suspend(hcd, do_wakeup); if (pdata && pdata->phy_exit) pdata->phy_exit(pdev, S5P_USB_PHY_HOST); @@ -234,7 +219,6 @@ static int s5p_ehci_resume(struct device *dev) { struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev); struct usb_hcd *hcd = s5p_ehci->hcd; - struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct platform_device *pdev = to_platform_device(dev); struct s5p_ehci_platdata *pdata = pdev->dev.platform_data; @@ -246,44 +230,7 @@ static int s5p_ehci_resume(struct device *dev) /* DMA burst Enable */ writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs)); - if (time_before(jiffies, ehci->next_statechange)) - msleep(100); - - /* Mark hardware accessible again as we are out of D3 state by now */ - set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - - if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) { - int mask = INTR_MASK; - - ehci_prepare_ports_for_controller_resume(ehci); - if (!hcd->self.root_hub->do_remote_wakeup) - mask &= ~STS_PCD; - ehci_writel(ehci, mask, &ehci->regs->intr_enable); - ehci_readl(ehci, &ehci->regs->intr_enable); - return 0; - } - - usb_root_hub_lost_power(hcd->self.root_hub); - - (void) ehci_halt(ehci); - (void) ehci_reset(ehci); - - /* emptying the schedule aborts any urbs */ - spin_lock_irq(&ehci->lock); - if (ehci->reclaim) - end_unlink_async(ehci); - ehci_work(ehci); - spin_unlock_irq(&ehci->lock); - - ehci_writel(ehci, ehci->command, &ehci->regs->command); - ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); - ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ - - /* here we "know" root ports should always stay powered */ - ehci_port_power(ehci, 1); - - ehci->rh_state = EHCI_RH_SUSPENDED; - + ehci_resume(hcd, false); return 0; } #else diff --git a/drivers/usb/host/ehci-sead3.c b/drivers/usb/host/ehci-sead3.c index cc199e87a7a..58c96bd50d2 100644 --- a/drivers/usb/host/ehci-sead3.c +++ b/drivers/usb/host/ehci-sead3.c @@ -160,84 +160,16 @@ static int ehci_hcd_sead3_drv_remove(struct platform_device *pdev) static int ehci_hcd_sead3_drv_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - unsigned long flags; - int rc = 0; - - if (time_before(jiffies, ehci->next_statechange)) - msleep(20); - - /* Root hub was already suspended. Disable irq emission and - * mark HW unaccessible. The PM and USB cores make sure that - * the root hub is either suspended or stopped. - */ - ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev)); - spin_lock_irqsave(&ehci->lock, flags); - ehci_writel(ehci, 0, &ehci->regs->intr_enable); - (void)ehci_readl(ehci, &ehci->regs->intr_enable); - - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - spin_unlock_irqrestore(&ehci->lock, flags); + bool do_wakeup = device_may_wakeup(dev); - /* could save FLADJ in case of Vaux power loss - * ... we'd only use it to handle clock skew - */ - - return rc; + return ehci_suspend(hcd, do_wakeup); } static int ehci_hcd_sead3_drv_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - - /* maybe restore FLADJ. */ - - if (time_before(jiffies, ehci->next_statechange)) - msleep(100); - - /* Mark hardware accessible again as we are out of D3 state by now */ - set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - - /* If CF is still set, we maintained PCI Vaux power. - * Just undo the effect of ehci_pci_suspend(). - */ - if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) { - int mask = INTR_MASK; - - ehci_prepare_ports_for_controller_resume(ehci); - if (!hcd->self.root_hub->do_remote_wakeup) - mask &= ~STS_PCD; - ehci_writel(ehci, mask, &ehci->regs->intr_enable); - ehci_readl(ehci, &ehci->regs->intr_enable); - return 0; - } - - ehci_dbg(ehci, "lost power, restarting\n"); - usb_root_hub_lost_power(hcd->self.root_hub); - - /* Else reset, to cope with power loss or flush-to-storage - * style "resume" having let BIOS kick in during reboot. - */ - (void) ehci_halt(ehci); - (void) ehci_reset(ehci); - - /* emptying the schedule aborts any urbs */ - spin_lock_irq(&ehci->lock); - if (ehci->reclaim) - end_unlink_async(ehci); - ehci_work(ehci); - spin_unlock_irq(&ehci->lock); - - ehci_writel(ehci, ehci->command, &ehci->regs->command); - ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); - ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ - - /* here we "know" root ports should always stay powered */ - ehci_port_power(ehci, 1); - - ehci->rh_state = EHCI_RH_SUSPENDED; + ehci_resume(hcd, false); return 0; } diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c index 37ba8c8d2fd..7ed533e6cca 100644 --- a/drivers/usb/host/ehci-spear.c +++ b/drivers/usb/host/ehci-spear.c @@ -97,71 +97,16 @@ static const struct hc_driver ehci_spear_hc_driver = { static int ehci_spear_drv_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - unsigned long flags; - int rc = 0; - - if (time_before(jiffies, ehci->next_statechange)) - msleep(10); - - /* - * Root hub was already suspended. Disable irq emission and mark HW - * unaccessible. The PM and USB cores make sure that the root hub is - * either suspended or stopped. - */ - spin_lock_irqsave(&ehci->lock, flags); - ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev)); - ehci_writel(ehci, 0, &ehci->regs->intr_enable); - ehci_readl(ehci, &ehci->regs->intr_enable); - spin_unlock_irqrestore(&ehci->lock, flags); + bool do_wakeup = device_may_wakeup(dev); - return rc; + return ehci_suspend(hcd, do_wakeup); } static int ehci_spear_drv_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - - if (time_before(jiffies, ehci->next_statechange)) - msleep(100); - - if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) { - int mask = INTR_MASK; - - ehci_prepare_ports_for_controller_resume(ehci); - - if (!hcd->self.root_hub->do_remote_wakeup) - mask &= ~STS_PCD; - - ehci_writel(ehci, mask, &ehci->regs->intr_enable); - ehci_readl(ehci, &ehci->regs->intr_enable); - return 0; - } - - usb_root_hub_lost_power(hcd->self.root_hub); - - /* - * Else reset, to cope with power loss or flush-to-storage style - * "resume" having let BIOS kick in during reboot. - */ - ehci_halt(ehci); - ehci_reset(ehci); - - /* emptying the schedule aborts any urbs */ - spin_lock_irq(&ehci->lock); - if (ehci->reclaim) - end_unlink_async(ehci); - - ehci_work(ehci); - spin_unlock_irq(&ehci->lock); - - ehci_writel(ehci, ehci->command, &ehci->regs->command); - ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); - ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ - /* here we "know" root ports should always stay powered */ - ehci_port_power(ehci, 1); + ehci_resume(hcd, false); return 0; } #endif /* CONFIG_PM */ -- cgit v1.2.3-70-g09d2 From 1a49e2ac9651df7349867a5cf44e2c83de1046af Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 9 Jul 2012 15:55:14 -0400 Subject: EHCI: centralize controller initialization This patch (as1564c) converts the EHCI platform drivers to use the central ehci_setup() routine for generic controller initialization rather than each having its own idiosyncratic approach. The major point of difficulty lies in ehci-pci's many vendor- and device-specific workarounds. Some of them have to be applied before calling ehci_setup() and some after, which necessitates a fair amount of code motion. The other platform drivers require much smaller changes. One point not addressed by the patch is whether ports should be powered on or off following initialization. The different drivers appear to handle this pretty much at random. In fact it shouldn't matter, because the hub driver turns on power to all ports when it binds to the root hub. Straightening that out will be left for another day. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-atmel.c | 19 +---- drivers/usb/host/ehci-au1xxx.c | 14 +--- drivers/usb/host/ehci-cns3xxx.c | 6 +- drivers/usb/host/ehci-fsl.c | 18 +---- drivers/usb/host/ehci-grlib.c | 15 +--- drivers/usb/host/ehci-hcd.c | 5 +- drivers/usb/host/ehci-ixp4xx.c | 6 +- drivers/usb/host/ehci-mv.c | 23 +----- drivers/usb/host/ehci-mxc.c | 17 +--- drivers/usb/host/ehci-octeon.c | 8 +- drivers/usb/host/ehci-omap.c | 104 ++++++++++++------------- drivers/usb/host/ehci-orion.c | 17 +--- drivers/usb/host/ehci-pci.c | 160 +++++++++++++++++++------------------- drivers/usb/host/ehci-pmcmsp.c | 17 +--- drivers/usb/host/ehci-ppc-of.c | 25 +----- drivers/usb/host/ehci-ps3.c | 18 +---- drivers/usb/host/ehci-s5p.c | 12 +-- drivers/usb/host/ehci-sh.c | 16 +--- drivers/usb/host/ehci-spear.c | 10 +-- drivers/usb/host/ehci-tegra.c | 18 +---- drivers/usb/host/ehci-vt8500.c | 14 +--- drivers/usb/host/ehci-w90x900.c | 9 +-- drivers/usb/host/ehci-xilinx-of.c | 31 +------- drivers/usb/host/ehci-xls.c | 21 +---- 24 files changed, 164 insertions(+), 439 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c index cf14c95a670..a47e2cffaaf 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -53,30 +53,15 @@ static void atmel_stop_ehci(struct platform_device *pdev) static int ehci_atmel_setup(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); - int retval = 0; + int retval; /* registers start at offset 0x0 */ ehci->caps = hcd->regs; - ehci->regs = hcd->regs + - HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - dbg_hcs_params(ehci, "reset"); - dbg_hcc_params(ehci, "reset"); - - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); - - retval = ehci_halt(ehci); - if (retval) - return retval; - /* data structure init */ - retval = ehci_init(hcd); + retval = ehci_setup(hcd); if (retval) return retval; - ehci->sbrn = 0x20; - - ehci_reset(ehci); ehci_port_power(ehci, 0); return retval; diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c index 182d3956590..cba10d625a5 100644 --- a/drivers/usb/host/ehci-au1xxx.c +++ b/drivers/usb/host/ehci-au1xxx.c @@ -20,10 +20,12 @@ extern int usb_disabled(void); static int au1xxx_ehci_setup(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); - int ret = ehci_init(hcd); + int ret; + + ehci->caps = hcd->regs; + ret = ehci_setup(hcd); ehci->need_io_watchdog = 0; - ehci_reset(ehci); return ret; } @@ -78,7 +80,6 @@ static const struct hc_driver ehci_au1xxx_hc_driver = { static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev) { struct usb_hcd *hcd; - struct ehci_hcd *ehci; struct resource *res; int ret; @@ -116,13 +117,6 @@ static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev) goto err3; } - ehci = hcd_to_ehci(hcd); - ehci->caps = hcd->regs; - ehci->regs = hcd->regs + - HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = readl(&ehci->caps->hcs_params); - ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED); if (ret == 0) { diff --git a/drivers/usb/host/ehci-cns3xxx.c b/drivers/usb/host/ehci-cns3xxx.c index 6536abdea6e..caaa3e5be33 100644 --- a/drivers/usb/host/ehci-cns3xxx.c +++ b/drivers/usb/host/ehci-cns3xxx.c @@ -33,14 +33,10 @@ static int cns3xxx_ehci_init(struct usb_hcd *hcd) } ehci->caps = hcd->regs; - ehci->regs = hcd->regs - + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); hcd->has_tt = 0; - ehci_reset(ehci); - retval = ehci_init(hcd); + retval = ehci_setup(hcd); if (retval) return retval; diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 74914de8b9b..ab52db684b6 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -348,29 +348,13 @@ static int ehci_fsl_setup(struct usb_hcd *hcd) /* EHCI registers start at offset 0x100 */ ehci->caps = hcd->regs + 0x100; - ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - dbg_hcs_params(ehci, "reset"); - dbg_hcc_params(ehci, "reset"); - - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); hcd->has_tt = 1; - retval = ehci_halt(ehci); - if (retval) - return retval; - - /* data structure init */ - retval = ehci_init(hcd); + retval = ehci_setup(hcd); if (retval) return retval; - ehci->sbrn = 0x20; - - ehci_reset(ehci); - if (of_device_is_compatible(dev->parent->of_node, "fsl,mpc5121-usb2-dr")) { /* diff --git a/drivers/usb/host/ehci-grlib.c b/drivers/usb/host/ehci-grlib.c index fdfd8c5b639..22ca45c079a 100644 --- a/drivers/usb/host/ehci-grlib.c +++ b/drivers/usb/host/ehci-grlib.c @@ -40,18 +40,13 @@ static int ehci_grlib_setup(struct usb_hcd *hcd) struct ehci_hcd *ehci = hcd_to_ehci(hcd); int retval; - retval = ehci_halt(ehci); + retval = ehci_setup(hcd); if (retval) return retval; - retval = ehci_init(hcd); - if (retval) - return retval; - - ehci->sbrn = 0x20; ehci_port_power(ehci, 1); - return ehci_reset(ehci); + return retval; } @@ -164,12 +159,6 @@ static int __devinit ehci_hcd_grlib_probe(struct platform_device *op) ehci->big_endian_capbase = 1; } - ehci->regs = hcd->regs + - HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); - rv = usb_add_hcd(hcd, irq, 0); if (rv) goto err_ehci; diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index e6823a0cf64..f9a783bfa1f 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -808,7 +808,7 @@ static int ehci_run (struct usb_hcd *hcd) return 0; } -static int __maybe_unused ehci_setup (struct usb_hcd *hcd) +static int ehci_setup(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); int retval; @@ -832,6 +832,9 @@ static int __maybe_unused ehci_setup (struct usb_hcd *hcd) if (retval) return retval; + if (ehci_is_TDI(ehci)) + tdi_reset(ehci); + ehci_reset(ehci); return 0; diff --git a/drivers/usb/host/ehci-ixp4xx.c b/drivers/usb/host/ehci-ixp4xx.c index c4460f3d009..488d401942e 100644 --- a/drivers/usb/host/ehci-ixp4xx.c +++ b/drivers/usb/host/ehci-ixp4xx.c @@ -22,14 +22,10 @@ static int ixp4xx_ehci_init(struct usb_hcd *hcd) ehci->big_endian_mmio = 1; ehci->caps = hcd->regs + 0x100; - ehci->regs = hcd->regs + 0x100 - + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); hcd->has_tt = 1; - ehci_reset(ehci); - retval = ehci_init(hcd); + retval = ehci_setup(hcd); if (retval) return retval; diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index 0e8c168ca24..f6df1ccc961 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -77,7 +77,6 @@ static void mv_ehci_disable(struct ehci_hcd_mv *ehci_mv) static int mv_ehci_reset(struct usb_hcd *hcd) { - struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct device *dev = hcd->self.controller; struct ehci_hcd_mv *ehci_mv = dev_get_drvdata(dev); int retval; @@ -87,25 +86,13 @@ static int mv_ehci_reset(struct usb_hcd *hcd) return -ENODEV; } - /* - * data structure init - */ - retval = ehci_init(hcd); - if (retval) { - dev_err(dev, "ehci_init failed %d\n", retval); - return retval; - } - hcd->has_tt = 1; - ehci->sbrn = 0x20; - retval = ehci_reset(ehci); - if (retval) { - dev_err(dev, "ehci_reset failed %d\n", retval); - return retval; - } + retval = ehci_setup(hcd); + if (retval) + dev_err(dev, "ehci_setup failed %d\n", retval); - return 0; + return retval; } static const struct hc_driver mv_ehci_hc_driver = { @@ -248,8 +235,6 @@ static int mv_ehci_probe(struct platform_device *pdev) ehci = hcd_to_ehci(hcd); ehci->caps = (struct ehci_caps *) ehci_mv->cap_regs; - ehci->regs = (struct ehci_regs *) ehci_mv->op_regs; - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); ehci_mv->mode = pdata->mode; if (ehci_mv->mode == MV_USB_MODE_OTG) { diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index c778ffe4e4e..34201372c85 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -42,27 +42,12 @@ static int ehci_mxc_setup(struct usb_hcd *hcd) struct ehci_hcd *ehci = hcd_to_ehci(hcd); int retval; - dbg_hcs_params(ehci, "reset"); - dbg_hcc_params(ehci, "reset"); - - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); - hcd->has_tt = 1; - retval = ehci_halt(ehci); + retval = ehci_setup(hcd); if (retval) return retval; - /* data structure init */ - retval = ehci_init(hcd); - if (retval) - return retval; - - ehci->sbrn = 0x20; - - ehci_reset(ehci); - ehci_port_power(ehci, 0); return 0; } diff --git a/drivers/usb/host/ehci-octeon.c b/drivers/usb/host/ehci-octeon.c index c0104882c72..ba26957abf4 100644 --- a/drivers/usb/host/ehci-octeon.c +++ b/drivers/usb/host/ehci-octeon.c @@ -56,7 +56,7 @@ static const struct hc_driver ehci_octeon_hc_driver = { /* * basic lifecycle operations */ - .reset = ehci_init, + .reset = ehci_setup, .start = ehci_run, .stop = ehci_stop, .shutdown = ehci_shutdown, @@ -150,12 +150,6 @@ static int ehci_octeon_drv_probe(struct platform_device *pdev) #endif ehci->caps = hcd->regs; - ehci->regs = hcd->regs + - HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - /* 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) { diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 6e15fc87cf6..6133d93808d 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -145,6 +145,56 @@ static void omap_ehci_soft_phy_reset(struct platform_device *pdev, u8 port) } } +static int omap_ehci_init(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int rc; + struct ehci_hcd_omap_platform_data *pdata; + + pdata = hcd->self.controller->platform_data; + if (pdata->phy_reset) { + if (gpio_is_valid(pdata->reset_gpio_port[0])) + gpio_request_one(pdata->reset_gpio_port[0], + GPIOF_OUT_INIT_LOW, "USB1 PHY reset"); + + if (gpio_is_valid(pdata->reset_gpio_port[1])) + gpio_request_one(pdata->reset_gpio_port[1], + GPIOF_OUT_INIT_LOW, "USB2 PHY reset"); + + /* Hold the PHY in RESET for enough time till DIR is high */ + udelay(10); + } + + /* Soft reset the PHY using PHY reset command over ULPI */ + if (pdata->port_mode[0] == OMAP_EHCI_PORT_MODE_PHY) + omap_ehci_soft_phy_reset(pdev, 0); + if (pdata->port_mode[1] == OMAP_EHCI_PORT_MODE_PHY) + omap_ehci_soft_phy_reset(pdev, 1); + + /* we know this is the memory we want, no need to ioremap again */ + ehci->caps = hcd->regs; + + rc = ehci_setup(hcd); + + if (pdata->phy_reset) { + /* Hold the PHY in RESET for enough time till + * PHY is settled and ready + */ + udelay(10); + + if (gpio_is_valid(pdata->reset_gpio_port[0])) + gpio_set_value_cansleep(pdata->reset_gpio_port[0], 1); + + if (gpio_is_valid(pdata->reset_gpio_port[1])) + gpio_set_value_cansleep(pdata->reset_gpio_port[1], 1); + } + + /* root ports should always stay powered */ + ehci_port_power(ehci, 1); + + return rc; +} + static int omap_ehci_hub_control( struct usb_hcd *hcd, u16 typeReq, @@ -219,7 +269,6 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) struct resource *res; struct usb_hcd *hcd; void __iomem *regs; - struct ehci_hcd *omap_ehci; int ret = -ENODEV; int irq; int i; @@ -281,19 +330,6 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) } } - if (pdata->phy_reset) { - if (gpio_is_valid(pdata->reset_gpio_port[0])) - gpio_request_one(pdata->reset_gpio_port[0], - GPIOF_OUT_INIT_LOW, "USB1 PHY reset"); - - if (gpio_is_valid(pdata->reset_gpio_port[1])) - gpio_request_one(pdata->reset_gpio_port[1], - GPIOF_OUT_INIT_LOW, "USB2 PHY reset"); - - /* Hold the PHY in RESET for enough time till DIR is high */ - udelay(10); - } - pm_runtime_enable(dev); pm_runtime_get_sync(dev); @@ -309,50 +345,12 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) ehci_write(regs, EHCI_INSNREG04, EHCI_INSNREG04_DISABLE_UNSUSPEND); - /* Soft reset the PHY using PHY reset command over ULPI */ - if (pdata->port_mode[0] == OMAP_EHCI_PORT_MODE_PHY) - omap_ehci_soft_phy_reset(pdev, 0); - if (pdata->port_mode[1] == OMAP_EHCI_PORT_MODE_PHY) - omap_ehci_soft_phy_reset(pdev, 1); - - omap_ehci = hcd_to_ehci(hcd); - omap_ehci->sbrn = 0x20; - - /* we know this is the memory we want, no need to ioremap again */ - omap_ehci->caps = hcd->regs; - omap_ehci->regs = hcd->regs - + HC_LENGTH(ehci, readl(&omap_ehci->caps->hc_capbase)); - - dbg_hcs_params(omap_ehci, "reset"); - dbg_hcc_params(omap_ehci, "reset"); - - /* cache this readonly data; minimize chip reads */ - omap_ehci->hcs_params = readl(&omap_ehci->caps->hcs_params); - - ehci_reset(omap_ehci); - - if (pdata->phy_reset) { - /* Hold the PHY in RESET for enough time till - * PHY is settled and ready - */ - udelay(10); - - if (gpio_is_valid(pdata->reset_gpio_port[0])) - gpio_set_value_cansleep(pdata->reset_gpio_port[0], 1); - - if (gpio_is_valid(pdata->reset_gpio_port[1])) - gpio_set_value_cansleep(pdata->reset_gpio_port[1], 1); - } - ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) { dev_err(dev, "failed to add hcd with err %d\n", ret); goto err_pm_runtime; } - /* root ports should always stay powered */ - ehci_port_power(omap_ehci, 1); - /* get clocks */ utmi_p1_fck = clk_get(dev, "utmi_p1_gfclk"); if (IS_ERR(utmi_p1_fck)) { @@ -512,7 +510,7 @@ static const struct hc_driver ehci_omap_hc_driver = { /* * basic lifecycle operations */ - .reset = ehci_init, + .reset = omap_ehci_init, .start = ehci_run, .stop = ehci_stop, .shutdown = ehci_shutdown, diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 82de1073aa5..3e411230953 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -106,21 +106,10 @@ static int ehci_orion_setup(struct usb_hcd *hcd) struct ehci_hcd *ehci = hcd_to_ehci(hcd); int retval; - hcd->has_tt = 1; - - retval = ehci_halt(ehci); - if (retval) - return retval; - - /* - * data structure init - */ - retval = ehci_init(hcd); + retval = ehci_setup(ehci); if (retval) return retval; - ehci_reset(ehci); - ehci_port_power(ehci, 0); return retval; @@ -261,11 +250,7 @@ static int __devinit ehci_orion_drv_probe(struct platform_device *pdev) ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs + 0x100; - ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); hcd->has_tt = 1; - ehci->sbrn = 0x20; /* * (Re-)program MBUS remapping windows if we are asked to. diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 6e767bce060..21e5f963f33 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -54,6 +54,17 @@ static int ehci_pci_setup(struct usb_hcd *hcd) u32 temp; int retval; + ehci->caps = hcd->regs; + + /* + * ehci_init() causes memory for DMA transfers to be + * allocated. Thus, any vendor-specific workarounds based on + * limiting the type of memory used for DMA transfers must + * happen before ehci_setup() is called. + * + * Most other workarounds can be done either before or after + * init and reset; they are located here too. + */ switch (pdev->vendor) { case PCI_VENDOR_ID_TOSHIBA_2: /* celleb's companion chip */ @@ -66,20 +77,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd) #endif } break; - } - - ehci->caps = hcd->regs; - ehci->regs = hcd->regs + - HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - - dbg_hcs_params(ehci, "reset"); - dbg_hcc_params(ehci, "reset"); - - /* ehci_init() causes memory for DMA transfers to be - * allocated. Thus, any vendor-specific workarounds based on - * limiting the type of memory used for DMA transfers must - * happen before ehci_init() is called. */ - switch (pdev->vendor) { case PCI_VENDOR_ID_NVIDIA: /* NVidia reports that certain chips don't handle * QH, ITD, or SITD addresses above 2GB. (But TD, @@ -95,61 +92,28 @@ static int ehci_pci_setup(struct usb_hcd *hcd) ehci_warn(ehci, "can't enable NVidia " "workaround for >2GB RAM\n"); break; - } - break; - } - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); - - retval = ehci_halt(ehci); - if (retval) - return retval; - - if ((pdev->vendor == PCI_VENDOR_ID_AMD && pdev->device == 0x7808) || - (pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x4396)) { - /* EHCI controller on AMD SB700/SB800/Hudson-2/3 platforms may - * read/write memory space which does not belong to it when - * there is NULL pointer with T-bit set to 1 in the frame list - * table. To avoid the issue, the frame list link pointer - * should always contain a valid pointer to a inactive qh. + /* Some NForce2 chips have problems with selective suspend; + * fixed in newer silicon. */ - ehci->use_dummy_qh = 1; - ehci_info(ehci, "applying AMD SB700/SB800/Hudson-2/3 EHCI " - "dummy qh workaround\n"); - } - - /* data structure init */ - retval = ehci_init(hcd); - if (retval) - return retval; - - switch (pdev->vendor) { - case PCI_VENDOR_ID_NEC: - ehci->need_io_watchdog = 0; + case 0x0068: + if (pdev->revision < 0xa4) + ehci->no_selective_suspend = 1; + break; + } break; case PCI_VENDOR_ID_INTEL: - ehci->need_io_watchdog = 0; ehci->fs_i_thresh = 1; if (pdev->device == 0x27cc) { ehci->broken_periodic = 1; ehci_info(ehci, "using broken periodic workaround\n"); } - if (pdev->device == 0x0806 || pdev->device == 0x0811 - || pdev->device == 0x0829) { - ehci_info(ehci, "disable lpm for langwell/penwell\n"); - ehci->has_lpm = 0; - } - if (pdev->device == PCI_DEVICE_ID_INTEL_CE4100_USB) { + if (pdev->device == PCI_DEVICE_ID_INTEL_CE4100_USB) hcd->has_tt = 1; - tdi_reset(ehci); - } break; case PCI_VENDOR_ID_TDI: - if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) { + if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) hcd->has_tt = 1; - tdi_reset(ehci); - } break; case PCI_VENDOR_ID_AMD: /* AMD PLL quirk */ @@ -161,28 +125,17 @@ static int ehci_pci_setup(struct usb_hcd *hcd) retval = -EIO; goto done; } - break; - case PCI_VENDOR_ID_NVIDIA: - switch (pdev->device) { - /* Some NForce2 chips have problems with selective suspend; - * fixed in newer silicon. - */ - case 0x0068: - if (pdev->revision < 0xa4) - ehci->no_selective_suspend = 1; - break; - /* MCP89 chips on the MacBookAir3,1 give EPROTO when - * fetching device descriptors unless LPM is disabled. - * There are also intermittent problems enumerating - * devices with PPCD enabled. + /* + * EHCI controller on AMD SB700/SB800/Hudson-2/3 platforms may + * read/write memory space which does not belong to it when + * there is NULL pointer with T-bit set to 1 in the frame list + * table. To avoid the issue, the frame list link pointer + * should always contain a valid pointer to a inactive qh. */ - case 0x0d9d: - ehci_info(ehci, "disable lpm/ppcd for nvidia mcp89"); - ehci->has_lpm = 0; - ehci->has_ppcd = 0; - ehci->command &= ~CMD_PPCEE; - break; + if (pdev->device == 0x7808) { + ehci->use_dummy_qh = 1; + ehci_info(ehci, "applying AMD SB700/SB800/Hudson-2/3 EHCI dummy qh workaround\n"); } break; case PCI_VENDOR_ID_VIA: @@ -203,6 +156,18 @@ static int ehci_pci_setup(struct usb_hcd *hcd) /* AMD PLL quirk */ if (usb_amd_find_chipset_info()) ehci->amd_pll_fix = 1; + + /* + * EHCI controller on AMD SB700/SB800/Hudson-2/3 platforms may + * read/write memory space which does not belong to it when + * there is NULL pointer with T-bit set to 1 in the frame list + * table. To avoid the issue, the frame list link pointer + * should always contain a valid pointer to a inactive qh. + */ + if (pdev->device == 0x4396) { + ehci->use_dummy_qh = 1; + ehci_info(ehci, "applying AMD SB700/SB800/Hudson-2/3 EHCI dummy qh workaround\n"); + } /* SB600 and old version of SB700 have a bug in EHCI controller, * which causes usb devices lose response in some cases. */ @@ -231,6 +196,40 @@ static int ehci_pci_setup(struct usb_hcd *hcd) break; } + retval = ehci_setup(hcd); + if (retval) + return retval; + + /* These workarounds need to be applied after ehci_setup() */ + switch (pdev->vendor) { + case PCI_VENDOR_ID_NEC: + ehci->need_io_watchdog = 0; + break; + case PCI_VENDOR_ID_INTEL: + ehci->need_io_watchdog = 0; + if (pdev->device == 0x0806 || pdev->device == 0x0811 + || pdev->device == 0x0829) { + ehci_info(ehci, "disable lpm for langwell/penwell\n"); + ehci->has_lpm = 0; + } + break; + case PCI_VENDOR_ID_NVIDIA: + switch (pdev->device) { + /* MCP89 chips on the MacBookAir3,1 give EPROTO when + * fetching device descriptors unless LPM is disabled. + * There are also intermittent problems enumerating + * devices with PPCD enabled. + */ + case 0x0d9d: + ehci_info(ehci, "disable lpm/ppcd for nvidia mcp89"); + ehci->has_lpm = 0; + ehci->has_ppcd = 0; + ehci->command &= ~CMD_PPCEE; + break; + } + break; + } + /* optional debug port, normally in the first BAR */ temp = pci_find_capability(pdev, 0x0a); if (temp) { @@ -238,7 +237,7 @@ static int ehci_pci_setup(struct usb_hcd *hcd) temp >>= 16; if ((temp & (3 << 13)) == (1 << 13)) { temp &= 0x1fff; - ehci->debug = ehci_to_hcd(ehci)->regs + temp; + ehci->debug = hcd->regs + temp; temp = ehci_readl(ehci, &ehci->debug->control); ehci_info(ehci, "debug port %d%s\n", HCS_DEBUG_PORT(ehci->hcs_params), @@ -250,8 +249,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd) } } - ehci_reset(ehci); - /* at least the Genesys GL880S needs fixup here */ temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params); temp &= 0x0f; @@ -275,10 +272,11 @@ static int ehci_pci_setup(struct usb_hcd *hcd) } /* Serial Bus Release Number is at PCI 0x60 offset */ - pci_read_config_byte(pdev, 0x60, &ehci->sbrn); if (pdev->vendor == PCI_VENDOR_ID_STMICRO && pdev->device == PCI_DEVICE_ID_STMICRO_USB_HOST) - ehci->sbrn = 0x20; /* ConneXT has no sbrn register */ + ; /* ConneXT has no sbrn register */ + else + pci_read_config_byte(pdev, 0x60, &ehci->sbrn); /* Keep this around for a while just in case some EHCI * implementation uses legacy PCI PM support. This test diff --git a/drivers/usb/host/ehci-pmcmsp.c b/drivers/usb/host/ehci-pmcmsp.c index e8d54de44ac..087aee2a904 100644 --- a/drivers/usb/host/ehci-pmcmsp.c +++ b/drivers/usb/host/ehci-pmcmsp.c @@ -78,27 +78,14 @@ static int ehci_msp_setup(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); int retval; + ehci->big_endian_mmio = 1; ehci->big_endian_desc = 1; ehci->caps = hcd->regs; - ehci->regs = hcd->regs + - HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - dbg_hcs_params(ehci, "reset"); - dbg_hcc_params(ehci, "reset"); - - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); hcd->has_tt = 1; - retval = ehci_halt(ehci); - if (retval) - return retval; - - ehci_reset(ehci); - - /* data structure init */ - retval = ehci_init(hcd); + retval = ehci_setup(hcd); if (retval) return retval; diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c index 41d11fe1425..bbbe89dfd88 100644 --- a/drivers/usb/host/ehci-ppc-of.c +++ b/drivers/usb/host/ehci-ppc-of.c @@ -17,24 +17,6 @@ #include #include -/* called during probe() after chip reset completes */ -static int ehci_ppc_of_setup(struct usb_hcd *hcd) -{ - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - int retval; - - retval = ehci_halt(ehci); - if (retval) - return retval; - - retval = ehci_init(hcd); - if (retval) - return retval; - - ehci->sbrn = 0x20; - return ehci_reset(ehci); -} - static const struct hc_driver ehci_ppc_of_hc_driver = { .description = hcd_name, @@ -50,7 +32,7 @@ static const struct hc_driver ehci_ppc_of_hc_driver = { /* * basic lifecycle operations */ - .reset = ehci_ppc_of_setup, + .reset = ehci_setup, .start = ehci_run, .stop = ehci_stop, .shutdown = ehci_shutdown, @@ -178,11 +160,6 @@ static int __devinit ehci_hcd_ppc_of_probe(struct platform_device *op) ehci->big_endian_desc = 1; ehci->caps = hcd->regs; - ehci->regs = hcd->regs + - HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); if (of_device_is_compatible(dn, "ibm,usb-ehci-440epx")) { rv = ppc44x_enable_bmt(dn); diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index a20e496eb47..45a356e9f13 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c @@ -55,28 +55,12 @@ static int ps3_ehci_hc_reset(struct usb_hcd *hcd) struct ehci_hcd *ehci = hcd_to_ehci(hcd); ehci->big_endian_mmio = 1; - ehci->caps = hcd->regs; - ehci->regs = hcd->regs + HC_LENGTH(ehci, ehci_readl(ehci, - &ehci->caps->hc_capbase)); - - dbg_hcs_params(ehci, "reset"); - dbg_hcc_params(ehci, "reset"); - - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); - - result = ehci_halt(ehci); + result = ehci_setup(hcd); if (result) return result; - result = ehci_init(hcd); - - if (result) - return result; - - ehci_reset(ehci); - ps3_ehci_setup_insnreg(ehci); return result; diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index c7e0936d4a7..13c179fb2ee 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -40,7 +40,7 @@ static const struct hc_driver s5p_ehci_hc_driver = { .irq = ehci_irq, .flags = HCD_MEMORY | HCD_USB2, - .reset = ehci_init, + .reset = ehci_setup, .start = ehci_run, .stop = ehci_stop, .shutdown = ehci_shutdown, @@ -134,20 +134,10 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev) ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs; - ehci->regs = hcd->regs + - HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); /* DMA burst Enable */ writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs)); - dbg_hcs_params(ehci, "reset"); - dbg_hcc_params(ehci, "reset"); - - /* 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-sh.c b/drivers/usb/host/ehci-sh.c index e7cb3925abf..b3f1e3650da 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -24,25 +24,11 @@ static int ehci_sh_reset(struct usb_hcd *hcd) int ret; ehci->caps = hcd->regs; - ehci->regs = hcd->regs + HC_LENGTH(ehci, ehci_readl(ehci, - &ehci->caps->hc_capbase)); - dbg_hcs_params(ehci, "reset"); - dbg_hcc_params(ehci, "reset"); - - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); - - ret = ehci_halt(ehci); - if (unlikely(ret)) - return ret; - - ret = ehci_init(hcd); + ret = ehci_setup(hcd); if (unlikely(ret)) return ret; - ehci->sbrn = 0x20; - - ehci_reset(ehci); ehci_port_power(ehci, 0); return ret; diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c index 7ed533e6cca..c718a065e15 100644 --- a/drivers/usb/host/ehci-spear.c +++ b/drivers/usb/host/ehci-spear.c @@ -41,19 +41,11 @@ static int ehci_spear_setup(struct usb_hcd *hcd) /* registers start at offset 0x0 */ ehci->caps = hcd->regs; - ehci->regs = hcd->regs + HC_LENGTH(ehci, ehci_readl(ehci, - &ehci->caps->hc_capbase)); - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); - retval = ehci_halt(ehci); - if (retval) - return retval; - retval = ehci_init(hcd); + retval = ehci_setup(hcd); if (retval) return retval; - ehci_reset(ehci); ehci_port_power(ehci, 0); return retval; diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 477ecfa0515..f7f3ce3275b 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -281,30 +281,14 @@ static int tegra_ehci_setup(struct usb_hcd *hcd) /* EHCI registers start at offset 0x100 */ ehci->caps = hcd->regs + 0x100; - ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); - - dbg_hcs_params(ehci, "reset"); - dbg_hcc_params(ehci, "reset"); - - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = readl(&ehci->caps->hcs_params); /* switch to host mode */ hcd->has_tt = 1; - ehci_reset(ehci); - retval = ehci_halt(ehci); + retval = ehci_setup(ehci); if (retval) return retval; - /* data structure init */ - retval = ehci_init(hcd); - if (retval) - return retval; - - ehci->sbrn = 0x20; - ehci_port_power(ehci, 1); return retval; } diff --git a/drivers/usb/host/ehci-vt8500.c b/drivers/usb/host/ehci-vt8500.c index c1eda73916c..4d147c4e33f 100644 --- a/drivers/usb/host/ehci-vt8500.c +++ b/drivers/usb/host/ehci-vt8500.c @@ -48,7 +48,7 @@ static const struct hc_driver vt8500_ehci_hc_driver = { /* * basic lifecycle operations */ - .reset = ehci_init, + .reset = ehci_setup, .start = ehci_run, .stop = ehci_stop, .shutdown = ehci_shutdown, @@ -121,18 +121,6 @@ static int vt8500_ehci_drv_probe(struct platform_device *pdev) ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs; - ehci->regs = hcd->regs + - HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); - - dbg_hcs_params(ehci, "reset"); - dbg_hcc_params(ehci, "reset"); - - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = readl(&ehci->caps->hcs_params); - - ehci_port_power(ehci, 1); - - ehci_reset(ehci); ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED); diff --git a/drivers/usb/host/ehci-w90x900.c b/drivers/usb/host/ehci-w90x900.c index 3d2e26cbb34..ec598082c14 100644 --- a/drivers/usb/host/ehci-w90x900.c +++ b/drivers/usb/host/ehci-w90x900.c @@ -71,21 +71,14 @@ static int __devinit usb_w90x900_probe(const struct hc_driver *driver, val |= ENPHY; __raw_writel(val, ehci->regs+PHY1_CTR); - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); - ehci->sbrn = 0x20; - irq = platform_get_irq(pdev, 0); if (irq < 0) goto err4; - ehci_reset(ehci); - retval = usb_add_hcd(hcd, irq, IRQF_SHARED); if (retval != 0) goto err4; - ehci_writel(ehci, 1, &ehci->regs->configured_flag); - return retval; err4: iounmap(hcd->regs); @@ -120,7 +113,7 @@ static const struct hc_driver ehci_w90x900_hc_driver = { /* * basic lifecycle operations */ - .reset = ehci_init, + .reset = ehci_setup, .start = ehci_run, .stop = ehci_stop, diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c index e9713d589e3..39f24fa37eb 100644 --- a/drivers/usb/host/ehci-xilinx-of.c +++ b/drivers/usb/host/ehci-xilinx-of.c @@ -31,30 +31,6 @@ #include #include -/** - * ehci_xilinx_of_setup - Initialize the device for ehci_reset() - * @hcd: Pointer to the usb_hcd device to which the host controller bound - * - * called during probe() after chip reset completes. - */ -static int ehci_xilinx_of_setup(struct usb_hcd *hcd) -{ - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - int retval; - - retval = ehci_halt(ehci); - if (retval) - return retval; - - retval = ehci_init(hcd); - if (retval) - return retval; - - ehci->sbrn = 0x20; - - return ehci_reset(ehci); -} - /** * ehci_xilinx_port_handed_over - hand the port out if failed to enable it * @hcd: Pointer to the usb_hcd device to which the host controller bound @@ -107,7 +83,7 @@ static const struct hc_driver ehci_xilinx_of_hc_driver = { /* * basic lifecycle operations */ - .reset = ehci_xilinx_of_setup, + .reset = ehci_setup, .start = ehci_run, .stop = ehci_stop, .shutdown = ehci_shutdown, @@ -219,11 +195,6 @@ static int __devinit ehci_hcd_xilinx_of_probe(struct platform_device *op) /* Debug registers are at the first 0x100 region */ ehci->caps = hcd->regs + 0x100; - ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); rv = usb_add_hcd(hcd, irq, 0); if (rv == 0) diff --git a/drivers/usb/host/ehci-xls.c b/drivers/usb/host/ehci-xls.c index 72f08196f8c..99c353a85ae 100644 --- a/drivers/usb/host/ehci-xls.c +++ b/drivers/usb/host/ehci-xls.c @@ -14,30 +14,11 @@ static int ehci_xls_setup(struct usb_hcd *hcd) { - int retval; struct ehci_hcd *ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs; - ehci->regs = hcd->regs + - HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - dbg_hcs_params(ehci, "reset"); - dbg_hcc_params(ehci, "reset"); - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); - - retval = ehci_halt(ehci); - if (retval) - return retval; - - /* data structure init */ - retval = ehci_init(hcd); - if (retval) - return retval; - - ehci_reset(ehci); - - return retval; + return ehci_setup(ehci); } int ehci_xls_probe_internal(const struct hc_driver *driver, -- cgit v1.2.3-70-g09d2 From af3a23ef4e5fbaf33f0afdda7d26442f036ba795 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 25 Jun 2012 08:24:30 -0700 Subject: xhci: Export Latency Tolerance Messaging capabilities. Some xHCI host controllers may have optional support for Latency Tolerance Messaging (LTM). This allows USB 3.0 devices that support LTM to pass information about how much latency they can tolerate to the xHC. A PCI xHCI host will use this information to update the PCI Latency Tolerance Request (LTR) info. The goal of this is to gather latency information for the system, to enable hardware-driven C states, and the shutting down of PLLs. Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-hub.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 2732ef660c5..6d21030e2b7 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -508,12 +508,18 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if (hcd->speed != HCD_USB3) goto error; + /* Set the U1 and U2 exit latencies. */ memcpy(buf, &usb_bos_descriptor, USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE); temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params3); buf[12] = HCS_U1_LATENCY(temp); put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]); + /* Indicate whether the host has LTM support. */ + temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params); + if (HCC_LTC(temp)) + buf[8] |= USB_LTM_SUPPORT; + spin_unlock_irqrestore(&xhci->lock, flags); return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE; case GetPortStatus: -- cgit v1.2.3-70-g09d2 From 631fe9d9d20e28fffdf750d12dd2cd275bd654e9 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:21:09 -0400 Subject: USB: EHCI: initialize data before resetting hardware Currently, EHCI initialization turns off the controller (in case it was left running by the firmware) before setting up the ehci_hcd data structure. This patch (as1565) reverses that order. Although it doesn't matter now, it will matter later on when future additions to ehci_halt() will want to acquire a spinlock that gets initialized by ehci_init(). Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index f9a783bfa1f..8727f4ea343 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -823,12 +823,12 @@ static int ehci_setup(struct usb_hcd *hcd) ehci->sbrn = HCD_USB2; - retval = ehci_halt(ehci); + /* data structure init */ + retval = ehci_init(hcd); if (retval) return retval; - /* data structure init */ - retval = ehci_init(hcd); + retval = ehci_halt(ehci); if (retval) return retval; -- cgit v1.2.3-70-g09d2 From 15be105b4a18c461b95fa683907f6da6deae1b75 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:21:17 -0400 Subject: USB: EHCI: remove unneeded suspend/resume code This patch (as1566) removes the code in ehci-hcd's resume routines which tries to restart or cancel any transfers left active while the root hub or controller was asleep. This code isn't necessary, because all URBs are terminated before the root hub is suspended. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 7 ------- drivers/usb/host/ehci-hub.c | 13 +------------ 2 files changed, 1 insertion(+), 19 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 8727f4ea343..ab7306de8d1 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1312,13 +1312,6 @@ static int __maybe_unused ehci_resume(struct usb_hcd *hcd, bool hibernated) (void) ehci_halt(ehci); (void) ehci_reset(ehci); - /* emptying the schedule aborts any urbs */ - spin_lock_irq(&ehci->lock); - if (ehci->reclaim) - end_unlink_async(ehci); - ehci_work(ehci); - spin_unlock_irq(&ehci->lock); - ehci_writel(ehci, ehci->command, &ehci->regs->command); ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index b3e2d66e95b..1daaa96f527 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -424,23 +424,12 @@ static int ehci_bus_resume (struct usb_hcd *hcd) ehci_vdbg (ehci, "resumed port %d\n", i + 1); } } - (void) ehci_readl(ehci, &ehci->regs->command); - - /* maybe re-activate the schedule(s) */ - temp = 0; - if (ehci->async->qh_next.qh) - temp |= CMD_ASE; - if (ehci->periodic_sched) - temp |= CMD_PSE; - if (temp) { - ehci->command |= temp; - ehci_writel(ehci, ehci->command, &ehci->regs->command); - } ehci->next_statechange = jiffies + msecs_to_jiffies(5); /* Now we can safely re-enable irqs */ ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); + (void) ehci_readl(ehci, &ehci->regs->intr_enable); spin_unlock_irq (&ehci->lock); ehci_handover_companion_ports(ehci); -- cgit v1.2.3-70-g09d2 From c83e1a9ff68a6535b81c40dc8fda99348ab480fb Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:21:25 -0400 Subject: USB: EHCI: don't refcount QHs This patch (as1567) removes ehci-hcd's reference counting of QH structures. It's not necessary to refcount these things because they always get deallocated at exactly one spot in ehci_endpoint_disable() (except for two special QHs, ehci->async and ehci->dummy) and are never used again. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 2 +- drivers/usb/host/ehci-mem.c | 24 +++--------------------- drivers/usb/host/ehci-q.c | 12 +++--------- drivers/usb/host/ehci-sched.c | 5 +---- drivers/usb/host/ehci.h | 9 --------- 5 files changed, 8 insertions(+), 44 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index ab7306de8d1..1f8f792eec8 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1179,7 +1179,7 @@ idle_timeout: if (qh->clearing_tt) goto idle_timeout; if (list_empty (&qh->qtd_list)) { - qh_put (qh); + qh_destroy(ehci, qh); break; } /* else FALL THROUGH */ diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c index 12f70c302b0..93132d8ad36 100644 --- a/drivers/usb/host/ehci-mem.c +++ b/drivers/usb/host/ehci-mem.c @@ -64,10 +64,8 @@ static inline void ehci_qtd_free (struct ehci_hcd *ehci, struct ehci_qtd *qtd) } -static void qh_destroy(struct ehci_qh *qh) +static void qh_destroy(struct ehci_hcd *ehci, struct ehci_qh *qh) { - struct ehci_hcd *ehci = qh->ehci; - /* clean qtds first, and know this is not linked */ if (!list_empty (&qh->qtd_list) || qh->qh_next.ptr) { ehci_dbg (ehci, "unused qh not empty!\n"); @@ -92,8 +90,6 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags) if (!qh->hw) goto fail; memset(qh->hw, 0, sizeof *qh->hw); - qh->refcount = 1; - qh->ehci = ehci; qh->qh_dma = dma; // INIT_LIST_HEAD (&qh->qh_list); INIT_LIST_HEAD (&qh->qtd_list); @@ -113,20 +109,6 @@ fail: return NULL; } -/* to share a qh (cpu threads, or hc) */ -static inline struct ehci_qh *qh_get (struct ehci_qh *qh) -{ - WARN_ON(!qh->refcount); - qh->refcount++; - return qh; -} - -static inline void qh_put (struct ehci_qh *qh) -{ - if (!--qh->refcount) - qh_destroy(qh); -} - /*-------------------------------------------------------------------------*/ /* The queue heads and transfer descriptors are managed from pools tied @@ -138,11 +120,11 @@ static void ehci_mem_cleanup (struct ehci_hcd *ehci) { free_cached_lists(ehci); if (ehci->async) - qh_put (ehci->async); + qh_destroy(ehci, ehci->async); ehci->async = NULL; if (ehci->dummy) - qh_put(ehci->dummy); + qh_destroy(ehci, ehci->dummy); ehci->dummy = NULL; /* DMA consistent memory and pools */ diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 4378bf72bba..7d117bbffac 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -265,7 +265,6 @@ __acquires(ehci->lock) /* ... update hc-wide periodic stats (for usbfs) */ ehci_to_hcd(ehci)->self.bandwidth_int_reqs--; } - qh_put (qh); } if (unlikely(urb->unlinked)) { @@ -946,7 +945,7 @@ qh_make ( ehci_dbg(ehci, "bogus dev %p speed %d\n", urb->dev, urb->dev->speed); done: - qh_put (qh); + qh_destroy(ehci, qh); return NULL; } @@ -1003,7 +1002,6 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) head->qh_next.qh = qh; head->hw->hw_next = dma; - qh_get(qh); qh->xacterrs = 0; qh->qh_state = QH_STATE_LINKED; /* qtd completions reported later by interrupt */ @@ -1090,7 +1088,7 @@ static struct ehci_qh *qh_append_tds ( wmb (); dummy->hw_token = token; - urb->hcpriv = qh_get (qh); + urb->hcpriv = qh; } } return qh; @@ -1167,7 +1165,6 @@ static void end_unlink_async (struct ehci_hcd *ehci) // qh->hw_next = cpu_to_hc32(qh->qh_dma); qh->qh_state = QH_STATE_IDLE; qh->qh_next.qh = NULL; - qh_put (qh); // refcount from reclaim /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */ next = qh->reclaim; @@ -1186,7 +1183,6 @@ static void end_unlink_async (struct ehci_hcd *ehci) && ehci->async->qh_next.qh == NULL) timer_action (ehci, TIMER_ASYNC_OFF); } - qh_put(qh); /* refcount from async list */ if (next) { ehci->reclaim = NULL; @@ -1230,7 +1226,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) } qh->qh_state = QH_STATE_UNLINK; - ehci->reclaim = qh = qh_get (qh); + ehci->reclaim = qh; prev = ehci->async; while (prev->qh_next.qh != qh) @@ -1283,12 +1279,10 @@ static void scan_async (struct ehci_hcd *ehci) * gets unlinked then ehci->qh_scan_next is adjusted * in start_unlink_async(). */ - qh = qh_get(qh); temp = qh_completions(ehci, qh); if (qh->needs_rescan) unlink_async(ehci, qh); qh->unlink_time = jiffies + EHCI_SHRINK_JIFFIES; - qh_put(qh); if (temp != 0) goto rescan; } diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 33182c6d1ff..027df3de2dc 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -606,7 +606,6 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh) } qh->qh_state = QH_STATE_LINKED; qh->xacterrs = 0; - qh_get (qh); /* update per-qh bandwidth for usbfs */ ehci_to_hcd(ehci)->self.bandwidth_allocated += qh->period @@ -650,7 +649,6 @@ static int qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) /* qh->qh_next still "live" to HC */ qh->qh_state = QH_STATE_UNLINK; qh->qh_next.ptr = NULL; - qh_put (qh); /* maybe turn off periodic schedule */ return disable_periodic(ehci); @@ -2340,7 +2338,7 @@ restart: switch (hc32_to_cpu(ehci, type)) { case Q_TYPE_QH: /* handle any completions */ - temp.qh = qh_get (q.qh); + temp.qh = q.qh; type = Q_NEXT_TYPE(ehci, q.qh->hw->hw_next); q = q.qh->qh_next; if (temp.qh->stamp != ehci->periodic_stamp) { @@ -2351,7 +2349,6 @@ restart: temp.qh->needs_rescan)) intr_deschedule(ehci, temp.qh); } - qh_put (temp.qh); break; case Q_TYPE_FSTN: /* for "save place" FSTNs, look at QH entries diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 85c3572155d..f7a59f5d70c 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -350,16 +350,7 @@ struct ehci_qh { struct ehci_qtd *dummy; struct ehci_qh *reclaim; /* next to reclaim */ - struct ehci_hcd *ehci; unsigned long unlink_time; - - /* - * Do NOT use atomic operations for QH refcounting. On some CPUs - * (PPC7448 for example), atomic operations cannot be performed on - * memory that is cache-inhibited (i.e. being used for DMA). - * Spinlocks are used to protect all QH fields. - */ - u32 refcount; unsigned stamp; u8 needs_rescan; /* Dequeue during giveback */ -- cgit v1.2.3-70-g09d2 From 4c53de72109759c931744734ca75f9cecb3baef1 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:21:32 -0400 Subject: USB: EHCI: add symbolic constants for QHs This patch (as1568) introduces symbolic constants for some of the less-frequently used bitfields in the QH structure. This makes the code a little easier to read and understand. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-dbg.c | 6 +++--- drivers/usb/host/ehci-hcd.c | 2 +- drivers/usb/host/ehci-q.c | 12 ++++++------ drivers/usb/host/ehci.h | 8 +++++++- 4 files changed, 17 insertions(+), 11 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 7561966fbdc..97338abff29 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -404,9 +404,9 @@ struct debug_buffer { #define speed_char(info1) ({ char tmp; \ switch (info1 & (3 << 12)) { \ - case 0 << 12: tmp = 'f'; break; \ - case 1 << 12: tmp = 'l'; break; \ - case 2 << 12: tmp = 'h'; break; \ + case QH_FULL_SPEED: tmp = 'f'; break; \ + case QH_LOW_SPEED: tmp = 'l'; break; \ + case QH_HIGH_SPEED: tmp = 'h'; break; \ default: tmp = '?'; break; \ }; tmp; }) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 1f8f792eec8..cdb15769468 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -667,7 +667,7 @@ static int ehci_init(struct usb_hcd *hcd) hw->hw_next = QH_NEXT(ehci, ehci->async->qh_dma); hw->hw_info1 = cpu_to_hc32(ehci, QH_HEAD); #if defined(CONFIG_PPC_PS3) - hw->hw_info1 |= cpu_to_hc32(ehci, (1 << 7)); /* I = 1 */ + hw->hw_info1 |= cpu_to_hc32(ehci, QH_INACTIVATE); #endif hw->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT); hw->hw_qtd_next = EHCI_LIST_END(ehci); diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 7d117bbffac..15650478771 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -100,7 +100,7 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd) * and set the pseudo-toggle in udev. Only usb_clear_halt() will * ever clear it. */ - if (!(hw->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) { + if (!(hw->hw_info1 & cpu_to_hc32(ehci, QH_TOGGLE_CTL))) { unsigned is_out, epnum; is_out = qh->is_out; @@ -886,7 +886,7 @@ qh_make ( /* using TT? */ switch (urb->dev->speed) { case USB_SPEED_LOW: - info1 |= (1 << 12); /* EPS "low" */ + info1 |= QH_LOW_SPEED; /* FALL THROUGH */ case USB_SPEED_FULL: @@ -894,8 +894,8 @@ qh_make ( if (type != PIPE_INTERRUPT) info1 |= (EHCI_TUNE_RL_TT << 28); if (type == PIPE_CONTROL) { - info1 |= (1 << 27); /* for TT */ - info1 |= 1 << 14; /* toggle from qtd */ + info1 |= QH_CONTROL_EP; /* for TT */ + info1 |= QH_TOGGLE_CTL; /* toggle from qtd */ } info1 |= maxp << 16; @@ -920,11 +920,11 @@ qh_make ( break; case USB_SPEED_HIGH: /* no TT involved */ - info1 |= (2 << 12); /* EPS "high" */ + info1 |= QH_HIGH_SPEED; if (type == PIPE_CONTROL) { info1 |= (EHCI_TUNE_RL_HS << 28); info1 |= 64 << 16; /* usb2 fixed maxpacket */ - info1 |= 1 << 14; /* toggle from qtd */ + info1 |= QH_TOGGLE_CTL; /* toggle from qtd */ info2 |= (EHCI_TUNE_MULT_HS << 30); } else if (type == PIPE_BULK) { info1 |= (EHCI_TUNE_RL_HS << 28); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index f7a59f5d70c..195449db1b1 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -324,7 +324,13 @@ union ehci_shadow { struct ehci_qh_hw { __hc32 hw_next; /* see EHCI 3.6.1 */ __hc32 hw_info1; /* see EHCI 3.6.2 */ -#define QH_HEAD 0x00008000 +#define QH_CONTROL_EP (1 << 27) /* FS/LS control endpoint */ +#define QH_HEAD (1 << 15) /* Head of async reclamation list */ +#define QH_TOGGLE_CTL (1 << 14) /* Data toggle control */ +#define QH_HIGH_SPEED (2 << 12) /* Endpoint speed */ +#define QH_LOW_SPEED (1 << 12) +#define QH_FULL_SPEED (0 << 12) +#define QH_INACTIVATE (1 << 7) /* Inactivate on next transaction */ __hc32 hw_info2; /* see EHCI 3.6.2 */ #define QH_SMASK 0x000000ff #define QH_CMASK 0x0000ff00 -- cgit v1.2.3-70-g09d2 From 99ac5b1e9536f142461681fa6143a947d66b4279 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:21:38 -0400 Subject: USB: EHCI: rename "reclaim" This patch (as1569) renames the ehci->reclaim list in ehci-hcd. The word "reclaim" is used in the EHCI specification to mean something quite different, and "unlink_next" is more descriptive of the list's purpose anyway. Similarly, the "reclaim" field in the ehci_stats structure is renamed "iaa", which is more meaningful (to experts, anyway) and is a better match for the "lost_iaa" field. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-dbg.c | 16 +++++++++------- drivers/usb/host/ehci-hcd.c | 25 ++++++++++++------------- drivers/usb/host/ehci-hub.c | 2 +- drivers/usb/host/ehci-q.c | 22 +++++++++++----------- drivers/usb/host/ehci.h | 10 +++++----- 5 files changed, 38 insertions(+), 37 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 97338abff29..76120957d60 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -538,12 +538,13 @@ static ssize_t fill_async_buffer(struct debug_buffer *buf) spin_lock_irqsave (&ehci->lock, flags); for (qh = ehci->async->qh_next.qh; size > 0 && qh; qh = qh->qh_next.qh) qh_lines (ehci, qh, &next, &size); - if (ehci->reclaim && size > 0) { - temp = scnprintf (next, size, "\nreclaim =\n"); + if (ehci->async_unlink && size > 0) { + temp = scnprintf(next, size, "\nunlink =\n"); size -= temp; next += temp; - for (qh = ehci->reclaim; size > 0 && qh; qh = qh->reclaim) + for (qh = ehci->async_unlink; size > 0 && qh; + qh = qh->unlink_next) qh_lines (ehci, qh, &next, &size); } spin_unlock_irqrestore (&ehci->lock, flags); @@ -841,16 +842,17 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) } } - if (ehci->reclaim) { - temp = scnprintf(next, size, "reclaim qh %p\n", ehci->reclaim); + if (ehci->async_unlink) { + temp = scnprintf(next, size, "async unlink qh %p\n", + ehci->async_unlink); size -= temp; next += temp; } #ifdef EHCI_STATS temp = scnprintf (next, size, - "irq normal %ld err %ld reclaim %ld (lost %ld)\n", - ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim, + "irq normal %ld err %ld iaa %ld (lost %ld)\n", + ehci->stats.normal, ehci->stats.error, ehci->stats.iaa, ehci->stats.lost_iaa); size -= temp; next += temp; diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index cdb15769468..efee426a246 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -403,7 +403,7 @@ static void ehci_iaa_watchdog(unsigned long param) * (a) SMP races against real IAA firing and retriggering, and * (b) clean HC shutdown, when IAA watchdog was pending. */ - if (ehci->reclaim + if (ehci->async_unlink && !timer_pending(&ehci->iaa_watchdog) && ehci->rh_state == EHCI_RH_RUNNING) { u32 cmd, status; @@ -583,8 +583,8 @@ static void ehci_stop (struct usb_hcd *hcd) usb_amd_dev_put(); #ifdef EHCI_STATS - ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld (lost %ld)\n", - ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim, + ehci_dbg(ehci, "irq normal %ld err %ld iaa %ld (lost %ld)\n", + ehci->stats.normal, ehci->stats.error, ehci->stats.iaa, ehci->stats.lost_iaa); ehci_dbg (ehci, "complete %ld unlink %ld\n", ehci->stats.complete, ehci->stats.unlink); @@ -651,7 +651,6 @@ static int ehci_init(struct usb_hcd *hcd) else // N microframes cached ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params); - ehci->reclaim = NULL; ehci->next_uframe = -1; ehci->clock_frame = -1; @@ -896,11 +895,11 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) /* guard against (alleged) silicon errata */ if (cmd & CMD_IAAD) ehci_dbg(ehci, "IAA with IAAD still set?\n"); - if (ehci->reclaim) { - COUNT(ehci->stats.reclaim); + if (ehci->async_unlink) { + COUNT(ehci->stats.iaa); end_unlink_async(ehci); } else - ehci_dbg(ehci, "IAA with nothing to reclaim?\n"); + ehci_dbg(ehci, "IAA with nothing unlinked?\n"); } /* remote wakeup [4.3.1] */ @@ -1027,7 +1026,7 @@ static int ehci_urb_enqueue ( static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) { /* failfast */ - if (ehci->rh_state != EHCI_RH_RUNNING && ehci->reclaim) + if (ehci->rh_state != EHCI_RH_RUNNING && ehci->async_unlink) end_unlink_async(ehci); /* If the QH isn't linked then there's nothing we can do @@ -1041,15 +1040,15 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) } /* defer till later if busy */ - if (ehci->reclaim) { + if (ehci->async_unlink) { struct ehci_qh *last; - for (last = ehci->reclaim; - last->reclaim; - last = last->reclaim) + for (last = ehci->async_unlink; + last->unlink_next; + last = last->unlink_next) continue; qh->qh_state = QH_STATE_UNLINK_WAIT; - last->reclaim = qh; + last->unlink_next = qh; /* start IAA cycle */ } else diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 1daaa96f527..77d3324b4b2 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -301,7 +301,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) ehci_halt (ehci); ehci->rh_state = EHCI_RH_SUSPENDED; - if (ehci->reclaim) + if (ehci->async_unlink) end_unlink_async(ehci); /* allow remote wakeup */ diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 15650478771..8e80cde8c35 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1153,11 +1153,11 @@ submit_async ( /*-------------------------------------------------------------------------*/ -/* the async qh for the qtds being reclaimed are now unlinked from the HC */ +/* the async qh for the qtds being unlinked are now gone from the HC */ static void end_unlink_async (struct ehci_hcd *ehci) { - struct ehci_qh *qh = ehci->reclaim; + struct ehci_qh *qh = ehci->async_unlink; struct ehci_qh *next; iaa_watchdog_done(ehci); @@ -1167,9 +1167,9 @@ static void end_unlink_async (struct ehci_hcd *ehci) qh->qh_next.qh = NULL; /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */ - next = qh->reclaim; - ehci->reclaim = next; - qh->reclaim = NULL; + next = qh->unlink_next; + ehci->async_unlink = next; + qh->unlink_next = NULL; qh_completions (ehci, qh); @@ -1185,7 +1185,7 @@ static void end_unlink_async (struct ehci_hcd *ehci) } if (next) { - ehci->reclaim = NULL; + ehci->async_unlink = NULL; start_unlink_async (ehci, next); } @@ -1203,7 +1203,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) #ifdef DEBUG assert_spin_locked(&ehci->lock); - if (ehci->reclaim + if (ehci->async_unlink || (qh->qh_state != QH_STATE_LINKED && qh->qh_state != QH_STATE_UNLINK_WAIT) ) @@ -1214,7 +1214,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) if (unlikely (qh == ehci->async)) { /* can't get here without STS_ASS set */ if (ehci->rh_state != EHCI_RH_HALTED - && !ehci->reclaim) { + && !ehci->async_unlink) { /* ... and CMD_IAAD clear */ ehci->command &= ~CMD_ASE; ehci_writel(ehci, ehci->command, &ehci->regs->command); @@ -1226,7 +1226,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) } qh->qh_state = QH_STATE_UNLINK; - ehci->reclaim = qh; + ehci->async_unlink = qh; prev = ehci->async; while (prev->qh_next.qh != qh) @@ -1240,7 +1240,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) /* If the controller isn't running, we don't have to wait for it */ if (unlikely(ehci->rh_state != EHCI_RH_RUNNING)) { - /* if (unlikely (qh->reclaim != 0)) + /* if (unlikely (qh->unlink_next != 0)) * this will recurse, probably not much */ end_unlink_async (ehci); @@ -1295,7 +1295,7 @@ static void scan_async (struct ehci_hcd *ehci) */ if (list_empty(&qh->qtd_list) && qh->qh_state == QH_STATE_LINKED) { - if (!ehci->reclaim && (stopped || + if (!ehci->async_unlink && (stopped || time_after_eq(jiffies, qh->unlink_time))) start_unlink_async(ehci, qh); else diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 195449db1b1..3c6c07c0956 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -42,7 +42,7 @@ struct ehci_stats { /* irq usage */ unsigned long normal; unsigned long error; - unsigned long reclaim; + unsigned long iaa; unsigned long lost_iaa; /* termination of urbs from core */ @@ -51,7 +51,7 @@ struct ehci_stats { }; /* ehci_hcd->lock guards shared data against other CPUs: - * ehci_hcd: async, reclaim, periodic (and shadow), ... + * ehci_hcd: async, unlink, periodic (and shadow), ... * usb_host_endpoint: hcpriv * ehci_qh: qh_next, qtd_list * ehci_qtd: qtd_list @@ -81,7 +81,7 @@ struct ehci_hcd { /* one per controller */ /* async schedule support */ struct ehci_qh *async; struct ehci_qh *dummy; /* For AMD quirk use */ - struct ehci_qh *reclaim; + struct ehci_qh *async_unlink; struct ehci_qh *qh_scan_next; unsigned scanning : 1; @@ -354,7 +354,7 @@ struct ehci_qh { union ehci_shadow qh_next; /* ptr to qh; or periodic */ struct list_head qtd_list; /* sw qtd list */ struct ehci_qtd *dummy; - struct ehci_qh *reclaim; /* next to reclaim */ + struct ehci_qh *unlink_next; /* next on unlink list */ unsigned long unlink_time; unsigned stamp; @@ -364,7 +364,7 @@ struct ehci_qh { #define QH_STATE_LINKED 1 /* HC sees this */ #define QH_STATE_UNLINK 2 /* HC may still see this */ #define QH_STATE_IDLE 3 /* HC doesn't see this */ -#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on reclaim q */ +#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on unlink q */ #define QH_STATE_COMPLETING 5 /* don't touch token.HALT */ u8 xacterrs; /* XactErr retry counter */ -- cgit v1.2.3-70-g09d2 From 2f5bb665ba7a14c5842fa2e1cde2be039843a2a2 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:21:43 -0400 Subject: USB: EHCI: add pointer to end of async-unlink list This patch (as1570) adds a pointer for the end of ehci-hcd's async-unlink list. The list (which is actually a queue) is singly linked, so having a pointer to its end makes adding new entries easier -- there's no longer any need to scan through the whole list. In principle it could be changed to a standard doubly-linked list. It turns out that doing so actually makes the code less clear, so I'm leaving it as is. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 9 ++------- drivers/usb/host/ehci-q.c | 2 ++ drivers/usb/host/ehci.h | 1 + 3 files changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index efee426a246..8b75e4279a4 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1041,14 +1041,9 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) /* defer till later if busy */ if (ehci->async_unlink) { - struct ehci_qh *last; - - for (last = ehci->async_unlink; - last->unlink_next; - last = last->unlink_next) - continue; qh->qh_state = QH_STATE_UNLINK_WAIT; - last->unlink_next = qh; + ehci->async_unlink_last->unlink_next = qh; + ehci->async_unlink_last = qh; /* start IAA cycle */ } else diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 8e80cde8c35..5193612c96e 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1227,6 +1227,8 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) qh->qh_state = QH_STATE_UNLINK; ehci->async_unlink = qh; + if (!qh->unlink_next) + ehci->async_unlink_last = qh; prev = ehci->async; while (prev->qh_next.qh != qh) diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 3c6c07c0956..475f23e10bb 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -82,6 +82,7 @@ struct ehci_hcd { /* one per controller */ struct ehci_qh *async; struct ehci_qh *dummy; /* For AMD quirk use */ struct ehci_qh *async_unlink; + struct ehci_qh *async_unlink_last; struct ehci_qh *qh_scan_next; unsigned scanning : 1; -- cgit v1.2.3-70-g09d2 From c0c53dbc32ea05a1e1dd9dba4772327da9a11750 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:21:48 -0400 Subject: USB: EHCI: add new root-hub state: STOPPING This patch (as1571) adds a new state for ehci-hcd's root hubs: EHCI_RH_STOPPING. This value is used at times when the root hub is being stopped and we don't know whether or not the hardware has finished all its DMA yet. Although the purpose may not be apparent, this distinction will come in useful later on. Future patches will avoid actions that depend on the root hub being operational (like turning on the async or periodic schedules) when they see the state is EHCI_RH_STOPPING. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-dbg.c | 2 ++ drivers/usb/host/ehci-hcd.c | 13 ++++++------- drivers/usb/host/ehci-hub.c | 3 +-- drivers/usb/host/ehci-q.c | 8 ++++---- drivers/usb/host/ehci-sched.c | 8 ++++---- drivers/usb/host/ehci.h | 7 ++++++- 6 files changed, 23 insertions(+), 18 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 76120957d60..f0c00de035e 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -706,6 +706,8 @@ static const char *rh_state_string(struct ehci_hcd *ehci) return "suspended"; case EHCI_RH_RUNNING: return "running"; + case EHCI_RH_STOPPING: + return "stopping"; } return "?"; } diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 8b75e4279a4..bc94822f4c5 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -357,10 +357,8 @@ static void ehci_quiesce (struct ehci_hcd *ehci) { u32 temp; -#ifdef DEBUG if (ehci->rh_state != EHCI_RH_RUNNING) - BUG (); -#endif + return; /* wait for any schedule enables/disables to take effect */ temp = (ehci->command << 10) & (STS_ASS | STS_PSS); @@ -494,6 +492,7 @@ static void ehci_shutdown(struct usb_hcd *hcd) del_timer_sync(&ehci->iaa_watchdog); spin_lock_irq(&ehci->lock); + ehci->rh_state = EHCI_RH_STOPPING; ehci_silence_controller(ehci); spin_unlock_irq(&ehci->lock); } @@ -562,8 +561,7 @@ static void ehci_stop (struct usb_hcd *hcd) del_timer_sync(&ehci->iaa_watchdog); spin_lock_irq(&ehci->lock); - if (ehci->rh_state == EHCI_RH_RUNNING) - ehci_quiesce (ehci); + ehci_quiesce(ehci); ehci_silence_controller(ehci); ehci_reset (ehci); @@ -951,6 +949,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) /* PCI errors [4.15.2.4] */ if (unlikely ((status & STS_FATAL) != 0)) { ehci_err(ehci, "fatal error\n"); + ehci->rh_state = EHCI_RH_STOPPING; dbg_cmd(ehci, "fatal", cmd); dbg_status(ehci, "fatal", status); ehci_halt(ehci); @@ -1026,7 +1025,7 @@ static int ehci_urb_enqueue ( static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) { /* failfast */ - if (ehci->rh_state != EHCI_RH_RUNNING && ehci->async_unlink) + if (ehci->rh_state < EHCI_RH_RUNNING && ehci->async_unlink) end_unlink_async(ehci); /* If the QH isn't linked then there's nothing we can do @@ -1148,7 +1147,7 @@ rescan: goto idle_timeout; } - if (ehci->rh_state != EHCI_RH_RUNNING) + if (ehci->rh_state < EHCI_RH_RUNNING) qh->qh_state = QH_STATE_IDLE; switch (qh->qh_state) { case QH_STATE_LINKED: diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 77d3324b4b2..fb1b99e7493 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -227,8 +227,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) } /* stop schedules, clean any completed work */ - if (ehci->rh_state == EHCI_RH_RUNNING) - ehci_quiesce (ehci); + ehci_quiesce(ehci); ehci_work(ehci); /* Unlike other USB host controller types, EHCI doesn't have diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 5193612c96e..285d5a0f3f7 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -433,7 +433,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) /* stop scanning when we reach qtds the hc is using */ } else if (likely (!stopped - && ehci->rh_state == EHCI_RH_RUNNING)) { + && ehci->rh_state >= EHCI_RH_RUNNING)) { break; /* scan the whole queue for unlinks whenever it stops */ @@ -441,7 +441,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) stopped = 1; /* cancel everything if we halt, suspend, etc */ - if (ehci->rh_state != EHCI_RH_RUNNING) + if (ehci->rh_state < EHCI_RH_RUNNING) last_status = -ESHUTDOWN; /* this qtd is active; skip it unless a previous qtd @@ -1241,7 +1241,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) wmb (); /* If the controller isn't running, we don't have to wait for it */ - if (unlikely(ehci->rh_state != EHCI_RH_RUNNING)) { + if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) { /* if (unlikely (qh->unlink_next != 0)) * this will recurse, probably not much */ @@ -1263,7 +1263,7 @@ static void scan_async (struct ehci_hcd *ehci) enum ehci_timer_action action = TIMER_IO_WATCHDOG; timer_action_done (ehci, TIMER_ASYNC_SHRINK); - stopped = (ehci->rh_state != EHCI_RH_RUNNING); + stopped = (ehci->rh_state < EHCI_RH_RUNNING); ehci->qh_scan_next = ehci->async->qh_next.qh; while (ehci->qh_scan_next) { diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 027df3de2dc..3429b8a33c5 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -2299,7 +2299,7 @@ scan_periodic (struct ehci_hcd *ehci) * Touches as few pages as possible: cache-friendly. */ now_uframe = ehci->next_uframe; - if (ehci->rh_state == EHCI_RH_RUNNING) { + if (ehci->rh_state >= EHCI_RH_RUNNING) { clock = ehci_read_frame_index(ehci); clock_frame = (clock >> 3) & (ehci->periodic_size - 1); } else { @@ -2334,7 +2334,7 @@ restart: union ehci_shadow temp; int live; - live = (ehci->rh_state == EHCI_RH_RUNNING); + live = (ehci->rh_state >= EHCI_RH_RUNNING); switch (hc32_to_cpu(ehci, type)) { case Q_TYPE_QH: /* handle any completions */ @@ -2459,7 +2459,7 @@ restart: * We can't advance our scan without collecting the ISO * transfers that are still pending in this frame. */ - if (incomplete && ehci->rh_state == EHCI_RH_RUNNING) { + if (incomplete && ehci->rh_state >= EHCI_RH_RUNNING) { ehci->next_uframe = now_uframe; break; } @@ -2475,7 +2475,7 @@ restart: if (now_uframe == clock) { unsigned now; - if (ehci->rh_state != EHCI_RH_RUNNING + if (ehci->rh_state < EHCI_RH_RUNNING || ehci->periodic_sched == 0) break; ehci->next_uframe = now_uframe; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 475f23e10bb..9e8e82ecce5 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -62,10 +62,15 @@ struct ehci_stats { #define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */ +/* + * ehci_rh_state values of EHCI_RH_RUNNING or above mean that the + * controller may be doing DMA. Lower values mean there's no DMA. + */ enum ehci_rh_state { EHCI_RH_HALTED, EHCI_RH_SUSPENDED, - EHCI_RH_RUNNING + EHCI_RH_RUNNING, + EHCI_RH_STOPPING }; struct ehci_hcd { /* one per controller */ -- cgit v1.2.3-70-g09d2 From d58b4bcc6df8046cf9c3c59f9ff84d2cd86b93eb Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:21:54 -0400 Subject: USB: EHCI: introduce high-res timer This patch (as1572) begins the conversion of ehci-hcd over to using high-resolution timers rather than old-fashioned low-resolution kernel timers. This reduces overhead caused by timer roundoff on systems where HZ is smaller than 1000. Also, the new timer framework introduced here is much more logical and easily extended than the ad-hoc approach ehci-hcd currently uses for timers. An hrtimer structure is added to ehci_hcd, along with a bitflag array and an array of ktime_t values, to keep track of which timing events are pending and what their expiration times are. Only the infrastructure for the timing operations is added in this patch. Later patches will add routines for handling each of the various timing events the driver needs. In some cases the new hrtimer handlers will replace the existing handlers for ehci-hcd's kernel timers; as this happens the old timers will be removed. In other cases the new timing events will replace busy-wait loops. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 15 +++++- drivers/usb/host/ehci-hub.c | 3 ++ drivers/usb/host/ehci-timer.c | 106 ++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/ehci.h | 16 +++++++ 4 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 drivers/usb/host/ehci-timer.c (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index bc94822f4c5..f8fed163a23 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -30,8 +30,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -380,6 +379,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci) static void end_unlink_async(struct ehci_hcd *ehci); static void ehci_work(struct ehci_hcd *ehci); +#include "ehci-timer.c" #include "ehci-hub.c" #include "ehci-lpm.c" #include "ehci-mem.c" @@ -494,7 +494,10 @@ static void ehci_shutdown(struct usb_hcd *hcd) spin_lock_irq(&ehci->lock); ehci->rh_state = EHCI_RH_STOPPING; ehci_silence_controller(ehci); + ehci->enabled_hrtimer_events = 0; spin_unlock_irq(&ehci->lock); + + hrtimer_cancel(&ehci->hrtimer); } static void ehci_port_power (struct ehci_hcd *ehci, int is_on) @@ -561,12 +564,14 @@ static void ehci_stop (struct usb_hcd *hcd) del_timer_sync(&ehci->iaa_watchdog); spin_lock_irq(&ehci->lock); + ehci->enabled_hrtimer_events = 0; ehci_quiesce(ehci); ehci_silence_controller(ehci); ehci_reset (ehci); spin_unlock_irq(&ehci->lock); + hrtimer_cancel(&ehci->hrtimer); remove_sysfs_files(ehci); remove_debug_files (ehci); @@ -615,6 +620,10 @@ static int ehci_init(struct usb_hcd *hcd) ehci->iaa_watchdog.function = ehci_iaa_watchdog; ehci->iaa_watchdog.data = (unsigned long) ehci; + hrtimer_init(&ehci->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + ehci->hrtimer.function = ehci_hrtimer_func; + ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT; + hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params); /* @@ -954,6 +963,8 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) dbg_status(ehci, "fatal", status); ehci_halt(ehci); dead: + ehci->enabled_hrtimer_events = 0; + hrtimer_try_to_cancel(&ehci->hrtimer); ehci_reset(ehci); ehci_writel(ehci, 0, &ehci->regs->configured_flag); usb_hc_died(hcd); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index fb1b99e7493..25329e4b844 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -311,12 +311,15 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) ehci_readl(ehci, &ehci->regs->intr_enable); ehci->next_statechange = jiffies + msecs_to_jiffies(10); + ehci->enabled_hrtimer_events = 0; + ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT; spin_unlock_irq (&ehci->lock); /* ehci_work() may have re-enabled the watchdog timer, which we do not * want, and so we must delete any pending watchdog timer events. */ del_timer_sync(&ehci->watchdog); + hrtimer_cancel(&ehci->hrtimer); return 0; } diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c new file mode 100644 index 00000000000..f6279e2883a --- /dev/null +++ b/drivers/usb/host/ehci-timer.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2012 by Alan Stern + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/* This file is part of ehci-hcd.c */ + +/*-------------------------------------------------------------------------*/ + +/* + * EHCI timer support... Now using hrtimers. + * + * Lots of different events are triggered from ehci->hrtimer. Whenever + * the timer routine runs, it checks each possible event; events that are + * currently enabled and whose expiration time has passed get handled. + * The set of enabled events is stored as a collection of bitflags in + * ehci->enabled_hrtimer_events, and they are numbered in order of + * increasing delay values (ranging between 1 ms and 100 ms). + * + * Rather than implementing a sorted list or tree of all pending events, + * we keep track only of the lowest-numbered pending event, in + * ehci->next_hrtimer_event. Whenever ehci->hrtimer gets restarted, its + * expiration time is set to the timeout value for this event. + * + * As a result, events might not get handled right away; the actual delay + * could be anywhere up to twice the requested delay. This doesn't + * matter, because none of the events are especially time-critical. The + * ones that matter most all have a delay of 1 ms, so they will be + * handled after 2 ms at most, which is okay. In addition to this, we + * allow for an expiration range of 1 ms. + */ + +/* + * Delay lengths for the hrtimer event types. + * Keep this list sorted by delay length, in the same order as + * the event types indexed by enum ehci_hrtimer_event in ehci.h. + */ +static unsigned event_delays_ns[] = { +}; + +/* Enable a pending hrtimer event */ +static void ehci_enable_event(struct ehci_hcd *ehci, unsigned event, + bool resched) +{ + ktime_t *timeout = &ehci->hr_timeouts[event]; + + if (resched) + *timeout = ktime_add(ktime_get(), + ktime_set(0, event_delays_ns[event])); + ehci->enabled_hrtimer_events |= (1 << event); + + /* Track only the lowest-numbered pending event */ + if (event < ehci->next_hrtimer_event) { + ehci->next_hrtimer_event = event; + hrtimer_start_range_ns(&ehci->hrtimer, *timeout, + NSEC_PER_MSEC, HRTIMER_MODE_ABS); + } +} + + +/* + * Handler functions for the hrtimer event types. + * Keep this array in the same order as the event types indexed by + * enum ehci_hrtimer_event in ehci.h. + */ +static void (*event_handlers[])(struct ehci_hcd *) = { +}; + +static enum hrtimer_restart ehci_hrtimer_func(struct hrtimer *t) +{ + struct ehci_hcd *ehci = container_of(t, struct ehci_hcd, hrtimer); + ktime_t now; + unsigned long events; + unsigned long flags; + unsigned e; + + spin_lock_irqsave(&ehci->lock, flags); + + events = ehci->enabled_hrtimer_events; + ehci->enabled_hrtimer_events = 0; + ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT; + + /* + * Check each pending event. If its time has expired, handle + * the event; otherwise re-enable it. + */ + now = ktime_get(); + for_each_set_bit(e, &events, EHCI_HRTIMER_NUM_EVENTS) { + if (now.tv64 >= ehci->hr_timeouts[e].tv64) + event_handlers[e](ehci); + else + ehci_enable_event(ehci, e, false); + } + + spin_unlock_irqrestore(&ehci->lock, flags); + return HRTIMER_NORESTART; +} diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 9e8e82ecce5..070be83028d 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -73,7 +73,23 @@ enum ehci_rh_state { EHCI_RH_STOPPING }; +/* + * Timer events, ordered by increasing delay length. + * Always update event_delays_ns[] and event_handlers[] (defined in + * ehci-timer.c) in parallel with this list. + */ +enum ehci_hrtimer_event { + EHCI_HRTIMER_NUM_EVENTS /* Must come last */ +}; +#define EHCI_HRTIMER_NO_EVENT 99 + struct ehci_hcd { /* one per controller */ + /* timing support */ + enum ehci_hrtimer_event next_hrtimer_event; + unsigned enabled_hrtimer_events; + ktime_t hr_timeouts[EHCI_HRTIMER_NUM_EVENTS]; + struct hrtimer hrtimer; + /* glue to PCI and HCD framework */ struct ehci_caps __iomem *caps; struct ehci_regs __iomem *regs; -- cgit v1.2.3-70-g09d2 From 3ca9aebac2ebb8f56d2d097636b8c568320a9f87 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:22:05 -0400 Subject: USB: EHCI: use hrtimer for the periodic schedule This patch (as1573) adds hrtimer support for managing ehci-hcd's periodic schedule. There are two issues to deal with. First, the schedule's state (on or off) must not be changed until the hardware status has caught up with the current command. This is handled by an hrtimer event that polls at 1-ms intervals to see when the Periodic Schedule Status (PSS) flag matches the Periodic Schedule Enable (PSE) value. Second, the schedule should not be turned off as soon as it becomes empty. Turning the schedule on and off takes time, so we want to wait until the schedule has been empty for a suitable period before turning it off. This is handled by an hrtimer event that gets set to expire 10 ms after the periodic schedule becomes empty. The existing code polls (for up to 1125 us and with interrupts disabled!) to check the status, and doesn't implement a delay before turning off the schedule. Furthermore, if the polling fails then the driver decides that the controller has died. This has caused problems for several people; some controllers can take 10 ms or more to turn off their periodic schedules. This patch fixes these issues. It also makes the "broken_periodic" workaround unnecessary; there is no longer any danger of turning off the periodic schedule after it has been on for less than 1 ms. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 2 +- drivers/usb/host/ehci-pci.c | 4 --- drivers/usb/host/ehci-sched.c | 69 ++++++++----------------------------- drivers/usb/host/ehci-timer.c | 80 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/ehci.h | 7 ++-- 5 files changed, 101 insertions(+), 61 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index f8fed163a23..7e00ca095ce 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -546,7 +546,7 @@ static void ehci_work (struct ehci_hcd *ehci) */ if (ehci->rh_state == EHCI_RH_RUNNING && (ehci->async->qh_next.ptr != NULL || - ehci->periodic_sched != 0)) + ehci->periodic_count != 0)) timer_action (ehci, TIMER_IO_WATCHDOG); } diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 21e5f963f33..2cb7d370c4e 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -104,10 +104,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd) break; case PCI_VENDOR_ID_INTEL: ehci->fs_i_thresh = 1; - if (pdev->device == 0x27cc) { - ehci->broken_periodic = 1; - ehci_info(ehci, "using broken periodic workaround\n"); - } if (pdev->device == PCI_DEVICE_ID_INTEL_CE4100_USB) hcd->has_tt = 1; break; diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 3429b8a33c5..f5c15880c65 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -481,67 +481,26 @@ static int tt_no_collision ( static int enable_periodic (struct ehci_hcd *ehci) { - int status; - - if (ehci->periodic_sched++) + if (ehci->periodic_count++) return 0; - /* did clearing PSE did take effect yet? - * takes effect only at frame boundaries... - */ - status = handshake_on_error_set_halt(ehci, &ehci->regs->status, - STS_PSS, 0, 9 * 125); - if (status) { - usb_hc_died(ehci_to_hcd(ehci)); - return status; - } - - ehci->command |= CMD_PSE; - ehci_writel(ehci, ehci->command, &ehci->regs->command); - /* posted write ... PSS happens later */ + /* Stop waiting to turn off the periodic schedule */ + ehci->enabled_hrtimer_events &= ~BIT(EHCI_HRTIMER_DISABLE_PERIODIC); - /* make sure ehci_work scans these */ - ehci->next_uframe = ehci_read_frame_index(ehci) - % (ehci->periodic_size << 3); - if (unlikely(ehci->broken_periodic)) - ehci->last_periodic_enable = ktime_get_real(); + /* Don't start the schedule until PSS is 0 */ + ehci_poll_PSS(ehci); return 0; } static int disable_periodic (struct ehci_hcd *ehci) { - int status; - - if (--ehci->periodic_sched) + if (--ehci->periodic_count) return 0; - if (unlikely(ehci->broken_periodic)) { - /* delay experimentally determined */ - ktime_t safe = ktime_add_us(ehci->last_periodic_enable, 1000); - ktime_t now = ktime_get_real(); - s64 delay = ktime_us_delta(safe, now); - - if (unlikely(delay > 0)) - udelay(delay); - } - - /* did setting PSE not take effect yet? - * takes effect only at frame boundaries... - */ - status = handshake_on_error_set_halt(ehci, &ehci->regs->status, - STS_PSS, STS_PSS, 9 * 125); - if (status) { - usb_hc_died(ehci_to_hcd(ehci)); - return status; - } - - ehci->command &= ~CMD_PSE; - ehci_writel(ehci, ehci->command, &ehci->regs->command); - /* posted write ... */ - - free_cached_lists(ehci); + ehci->next_uframe = -1; /* the periodic schedule is empty */ - ehci->next_uframe = -1; + /* Don't turn off the schedule until PSS is 1 */ + ehci_poll_PSS(ehci); return 0; } @@ -650,8 +609,7 @@ static int qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) qh->qh_state = QH_STATE_UNLINK; qh->qh_next.ptr = NULL; - /* maybe turn off periodic schedule */ - return disable_periodic(ehci); + return 0; } static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) @@ -706,6 +664,9 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) ehci_err(ehci, "can't reschedule qh %p, err %d\n", qh, rc); } + + /* maybe turn off periodic schedule */ + disable_periodic(ehci); } /*-------------------------------------------------------------------------*/ @@ -2447,7 +2408,7 @@ restart: /* assume completion callbacks modify the queue */ if (unlikely (modified)) { - if (likely(ehci->periodic_sched > 0)) + if (likely(ehci->periodic_count > 0)) goto restart; /* short-circuit this scan */ now_uframe = clock; @@ -2476,7 +2437,7 @@ restart: unsigned now; if (ehci->rh_state < EHCI_RH_RUNNING - || ehci->periodic_sched == 0) + || ehci->periodic_count == 0) break; ehci->next_uframe = now_uframe; now = ehci_read_frame_index(ehci) & (mod - 1); diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index f6279e2883a..ecd3296157c 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -16,6 +16,28 @@ /*-------------------------------------------------------------------------*/ +/* Set a bit in the USBCMD register */ +static void ehci_set_command_bit(struct ehci_hcd *ehci, u32 bit) +{ + ehci->command |= bit; + ehci_writel(ehci, ehci->command, &ehci->regs->command); + + /* unblock posted write */ + ehci_readl(ehci, &ehci->regs->command); +} + +/* Clear a bit in the USBCMD register */ +static void ehci_clear_command_bit(struct ehci_hcd *ehci, u32 bit) +{ + ehci->command &= ~bit; + ehci_writel(ehci, ehci->command, &ehci->regs->command); + + /* unblock posted write */ + ehci_readl(ehci, &ehci->regs->command); +} + +/*-------------------------------------------------------------------------*/ + /* * EHCI timer support... Now using hrtimers. * @@ -45,6 +67,8 @@ * the event types indexed by enum ehci_hrtimer_event in ehci.h. */ static unsigned event_delays_ns[] = { + 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_PSS */ + 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ }; /* Enable a pending hrtimer event */ @@ -67,12 +91,68 @@ static void ehci_enable_event(struct ehci_hcd *ehci, unsigned event, } +/* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */ +static void ehci_poll_PSS(struct ehci_hcd *ehci) +{ + unsigned actual, want; + + /* Don't do anything if the controller isn't running (e.g., died) */ + if (ehci->rh_state != EHCI_RH_RUNNING) + return; + + want = (ehci->command & CMD_PSE) ? STS_PSS : 0; + actual = ehci_readl(ehci, &ehci->regs->status) & STS_PSS; + + if (want != actual) { + + /* Poll again later, but give up after about 20 ms */ + if (ehci->PSS_poll_count++ < 20) { + ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true); + return; + } + ehci_warn(ehci, "Waited too long for the periodic schedule status, giving up\n"); + } + ehci->PSS_poll_count = 0; + + /* The status is up-to-date; restart or stop the schedule as needed */ + if (want == 0) { /* Stopped */ + free_cached_lists(ehci); + if (ehci->periodic_count > 0) { + + /* make sure ehci_work scans these */ + ehci->next_uframe = ehci_read_frame_index(ehci) + & ((ehci->periodic_size << 3) - 1); + ehci_set_command_bit(ehci, CMD_PSE); + } + + } else { /* Running */ + if (ehci->periodic_count == 0) { + + /* Turn off the schedule after a while */ + ehci_enable_event(ehci, EHCI_HRTIMER_DISABLE_PERIODIC, + true); + } + } +} + +/* Turn off the periodic schedule after a brief delay */ +static void ehci_disable_PSE(struct ehci_hcd *ehci) +{ + ehci_clear_command_bit(ehci, CMD_PSE); + + /* Poll to see when it actually stops */ + ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true); +} + + /* * Handler functions for the hrtimer event types. * Keep this array in the same order as the event types indexed by * enum ehci_hrtimer_event in ehci.h. */ static void (*event_handlers[])(struct ehci_hcd *) = { + ehci_poll_PSS, /* EHCI_HRTIMER_POLL_PSS */ + ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ }; static enum hrtimer_restart ehci_hrtimer_func(struct hrtimer *t) diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 070be83028d..da2e0ab2385 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -79,6 +79,8 @@ enum ehci_rh_state { * ehci-timer.c) in parallel with this list. */ enum ehci_hrtimer_event { + EHCI_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */ + EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ EHCI_HRTIMER_NUM_EVENTS /* Must come last */ }; #define EHCI_HRTIMER_NO_EVENT 99 @@ -90,6 +92,8 @@ struct ehci_hcd { /* one per controller */ ktime_t hr_timeouts[EHCI_HRTIMER_NUM_EVENTS]; struct hrtimer hrtimer; + int PSS_poll_count; + /* glue to PCI and HCD framework */ struct ehci_caps __iomem *caps; struct ehci_regs __iomem *regs; @@ -116,7 +120,7 @@ struct ehci_hcd { /* one per controller */ union ehci_shadow *pshadow; /* mirror hw periodic table */ int next_uframe; /* scan periodic, start here */ - unsigned periodic_sched; /* periodic activity count */ + unsigned periodic_count; /* periodic activity count */ unsigned uframe_periodic_max; /* max periodic time per uframe */ @@ -165,7 +169,6 @@ struct ehci_hcd { /* one per controller */ unsigned big_endian_capbase:1; unsigned has_amcc_usb23:1; unsigned need_io_watchdog:1; - unsigned broken_periodic:1; unsigned amd_pll_fix:1; unsigned fs_i_thresh:1; /* Intel iso scheduling */ unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/ -- cgit v1.2.3-70-g09d2 From b015cb79ce84944076a8a849055f898f2a4d6be8 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:22:10 -0400 Subject: USB: EHCI: return void instead of 0 This patch (as1574) changes the return type of multiple functions in ehci-sched.c from int to void. The values they return are now always 0, so there's no reason for them to return any value at all. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-sched.c | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index f5c15880c65..69b1861e432 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -479,29 +479,27 @@ static int tt_no_collision ( /*-------------------------------------------------------------------------*/ -static int enable_periodic (struct ehci_hcd *ehci) +static void enable_periodic(struct ehci_hcd *ehci) { if (ehci->periodic_count++) - return 0; + return; /* Stop waiting to turn off the periodic schedule */ ehci->enabled_hrtimer_events &= ~BIT(EHCI_HRTIMER_DISABLE_PERIODIC); /* Don't start the schedule until PSS is 0 */ ehci_poll_PSS(ehci); - return 0; } -static int disable_periodic (struct ehci_hcd *ehci) +static void disable_periodic(struct ehci_hcd *ehci) { if (--ehci->periodic_count) - return 0; + return; ehci->next_uframe = -1; /* the periodic schedule is empty */ /* Don't turn off the schedule until PSS is 1 */ ehci_poll_PSS(ehci); - return 0; } /*-------------------------------------------------------------------------*/ @@ -512,7 +510,7 @@ static int disable_periodic (struct ehci_hcd *ehci) * this just links in a qh; caller guarantees uframe masks are set right. * no FSTN support (yet; ehci 0.96+) */ -static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh) +static void qh_link_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) { unsigned i; unsigned period = qh->period; @@ -572,10 +570,10 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh) : (qh->usecs * 8); /* maybe enable periodic schedule processing */ - return enable_periodic(ehci); + enable_periodic(ehci); } -static int qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) +static void qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) { unsigned i; unsigned period; @@ -608,8 +606,6 @@ static int qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) /* qh->qh_next still "live" to HC */ qh->qh_state = QH_STATE_UNLINK; qh->qh_next.ptr = NULL; - - return 0; } static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) @@ -843,7 +839,7 @@ static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh) ehci_dbg (ehci, "reused qh %p schedule\n", qh); /* stuff into the periodic schedule */ - status = qh_link_periodic (ehci, qh); + qh_link_periodic(ehci, qh); done: return status; } @@ -1574,8 +1570,7 @@ itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd) } /* fit urb's itds into the selected schedule slot; activate as needed */ -static int -itd_link_urb ( +static void itd_link_urb( struct ehci_hcd *ehci, struct urb *urb, unsigned mod, @@ -1646,7 +1641,7 @@ itd_link_urb ( urb->hcpriv = NULL; timer_action (ehci, TIMER_IO_WATCHDOG); - return enable_periodic(ehci); + enable_periodic(ehci); } #define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR) @@ -1726,7 +1721,7 @@ itd_complete ( ehci_urb_done(ehci, urb, 0); retval = true; urb = NULL; - (void) disable_periodic(ehci); + disable_periodic(ehci); ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) { @@ -1993,8 +1988,7 @@ sitd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd) } /* fit urb's sitds into the selected schedule slot; activate as needed */ -static int -sitd_link_urb ( +static void sitd_link_urb( struct ehci_hcd *ehci, struct urb *urb, unsigned mod, @@ -2056,7 +2050,7 @@ sitd_link_urb ( urb->hcpriv = NULL; timer_action (ehci, TIMER_IO_WATCHDOG); - return enable_periodic(ehci); + enable_periodic(ehci); } /*-------------------------------------------------------------------------*/ @@ -2122,7 +2116,7 @@ sitd_complete ( ehci_urb_done(ehci, urb, 0); retval = true; urb = NULL; - (void) disable_periodic(ehci); + disable_periodic(ehci); ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) { -- cgit v1.2.3-70-g09d2 From 9671cd7a91059bcd27665a884ee6568d31ef6857 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:22:16 -0400 Subject: USB: EHCI: remove PS3 status polling This patch (as1575) removes special code added for status polling of the EHCI controller in PS3 systems. While the controller is running, the polling is now carried out by an hrtimer handler. When the controller is suspending or stopping, we use the same polling routine as the old code -- but in neither case do we need to conclude that the controller has died if the polling goes on for too long. As a result the entire handshake_on_error_set_halt() routine is now unused, so it is removed from the driver. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 69 ++------------------------------------------- 1 file changed, 2 insertions(+), 67 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 7e00ca095ce..fd7ae16f77b 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -235,68 +235,6 @@ 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; - ehci_err(ehci, "force halt; handshake %p %08x %08x -> %d\n", - ptr, mask, done, error); - } - - return error; -} - /* put TDI/ARC silicon into EHCI mode */ static void tdi_reset (struct ehci_hcd *ehci) { @@ -361,17 +299,14 @@ static void ehci_quiesce (struct ehci_hcd *ehci) /* wait for any schedule enables/disables to take effect */ temp = (ehci->command << 10) & (STS_ASS | STS_PSS); - if (handshake_on_error_set_halt(ehci, &ehci->regs->status, - STS_ASS | STS_PSS, temp, 16 * 125)) - return; + handshake(ehci, &ehci->regs->status, STS_ASS | STS_PSS, temp, 16 * 125); /* then disable anything that's still active */ ehci->command &= ~(CMD_ASE | CMD_PSE); ehci_writel(ehci, ehci->command, &ehci->regs->command); /* hardware can take 16 microframes to turn off ... */ - handshake_on_error_set_halt(ehci, &ehci->regs->status, - STS_ASS | STS_PSS, 0, 16 * 125); + handshake(ehci, &ehci->regs->status, STS_ASS | STS_PSS, 0, 16 * 125); } /*-------------------------------------------------------------------------*/ -- cgit v1.2.3-70-g09d2 From 314466101c6ae14f6f5db8a86eda1509ba2c02a8 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:22:21 -0400 Subject: USB: EHCI: use hrtimer for async schedule This patch (as1576) adds hrtimer support for managing ehci-hcd's async schedule. Just as with the earlier change to the periodic schedule management, two new hrtimer events take care of everything. One event polls at 1-ms intervals to see when the Asynchronous Schedule Status (ASS) flag matches the Asynchronous Schedule Enable (ASE) value; the schedule's state must not be changed until it does. The other event delays for 15 ms after the async schedule becomes empty before turning it off. The new events replace a busy-wait poll and a kernel timer usage. They also replace the rather illogical method currently used for indicating the async schedule should be turned off: attempting to unlink the dedicated QH at the head of the async list. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 13 ++------- drivers/usb/host/ehci-q.c | 68 +++++++++++++++++++------------------------ drivers/usb/host/ehci-timer.c | 49 +++++++++++++++++++++++++++++++ drivers/usb/host/ehci.h | 5 +++- 4 files changed, 86 insertions(+), 49 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index fd7ae16f77b..21d6fbc0a32 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -95,7 +95,6 @@ static const char hcd_name [] = "ehci_hcd"; #define EHCI_IAA_MSECS 10 /* arbitrary */ #define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ -#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */ #define EHCI_SHRINK_JIFFIES (DIV_ROUND_UP(HZ, 200) + 1) /* 5-ms async qh unlink delay */ @@ -137,7 +136,7 @@ timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action) * SHRINK were pending, OFF would never be requested. */ if (timer_pending(&ehci->watchdog) - && ((BIT(TIMER_ASYNC_SHRINK) | BIT(TIMER_ASYNC_OFF)) + && (BIT(TIMER_ASYNC_SHRINK) & ehci->actions)) return; @@ -150,9 +149,6 @@ timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action) return; t = EHCI_IO_JIFFIES; break; - case TIMER_ASYNC_OFF: - t = EHCI_ASYNC_JIFFIES; - break; /* case TIMER_ASYNC_SHRINK: */ default: t = EHCI_SHRINK_JIFFIES; @@ -376,10 +372,6 @@ static void ehci_watchdog(unsigned long param) spin_lock_irqsave(&ehci->lock, flags); - /* stop async processing after it's idled a bit */ - if (test_bit (TIMER_ASYNC_OFF, &ehci->actions)) - start_unlink_async (ehci, ehci->async); - /* ehci could run by timer, without IRQs ... */ ehci_work (ehci); @@ -470,7 +462,8 @@ static void ehci_work (struct ehci_hcd *ehci) if (ehci->scanning) return; ehci->scanning = 1; - scan_async (ehci); + if (ehci->async_count) + scan_async(ehci); if (ehci->next_uframe != -1) scan_periodic (ehci); ehci->scanning = 0; diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 285d5a0f3f7..d68764ef447 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -964,6 +964,30 @@ done: /*-------------------------------------------------------------------------*/ +static void enable_async(struct ehci_hcd *ehci) +{ + if (ehci->async_count++) + return; + + /* Stop waiting to turn off the async schedule */ + ehci->enabled_hrtimer_events &= ~BIT(EHCI_HRTIMER_DISABLE_ASYNC); + + /* Don't start the schedule until ASS is 0 */ + ehci_poll_ASS(ehci); +} + +static void disable_async(struct ehci_hcd *ehci) +{ + if (--ehci->async_count) + return; + + /* The async schedule and async_unlink list are supposed to be empty */ + WARN_ON(ehci->async->qh_next.qh || ehci->async_unlink); + + /* Don't turn off the schedule until ASS is 1 */ + ehci_poll_ASS(ehci); +} + /* move qh (and its qtds) onto async queue; maybe enable queue. */ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) @@ -977,24 +1001,11 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) WARN_ON(qh->qh_state != QH_STATE_IDLE); - /* (re)start the async schedule? */ - head = ehci->async; - timer_action_done (ehci, TIMER_ASYNC_OFF); - if (!head->qh_next.qh) { - if (!(ehci->command & CMD_ASE)) { - /* in case a clear of CMD_ASE didn't take yet */ - (void)handshake(ehci, &ehci->regs->status, - STS_ASS, 0, 150); - ehci->command |= CMD_ASE; - ehci_writel(ehci, ehci->command, &ehci->regs->command); - /* posted write need not be known to HC yet ... */ - } - } - /* clear halt and/or toggle; and maybe recover from silicon quirk */ qh_refresh(ehci, qh); /* splice right after start */ + head = ehci->async; qh->qh_next = head->qh_next; qh->hw->hw_next = head->hw->hw_next; wmb (); @@ -1005,6 +1016,8 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) qh->xacterrs = 0; qh->qh_state = QH_STATE_LINKED; /* qtd completions reported later by interrupt */ + + enable_async(ehci); } /*-------------------------------------------------------------------------*/ @@ -1173,16 +1186,10 @@ static void end_unlink_async (struct ehci_hcd *ehci) qh_completions (ehci, qh); - if (!list_empty(&qh->qtd_list) && ehci->rh_state == EHCI_RH_RUNNING) { + if (!list_empty(&qh->qtd_list) && ehci->rh_state == EHCI_RH_RUNNING) qh_link_async (ehci, qh); - } else { - /* it's not free to turn the async schedule on/off; leave it - * active but idle for a while once it empties. - */ - if (ehci->rh_state == EHCI_RH_RUNNING - && ehci->async->qh_next.qh == NULL) - timer_action (ehci, TIMER_ASYNC_OFF); - } + + disable_async(ehci); if (next) { ehci->async_unlink = NULL; @@ -1210,21 +1217,6 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) BUG (); #endif - /* stop async schedule right now? */ - if (unlikely (qh == ehci->async)) { - /* can't get here without STS_ASS set */ - if (ehci->rh_state != EHCI_RH_HALTED - && !ehci->async_unlink) { - /* ... and CMD_IAAD clear */ - ehci->command &= ~CMD_ASE; - ehci_writel(ehci, ehci->command, &ehci->regs->command); - wmb (); - // handshake later, if we need to - timer_action_done (ehci, TIMER_ASYNC_OFF); - } - return; - } - qh->qh_state = QH_STATE_UNLINK; ehci->async_unlink = qh; if (!qh->unlink_next) diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index ecd3296157c..1e907dd3bb1 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -67,8 +67,10 @@ static void ehci_clear_command_bit(struct ehci_hcd *ehci, u32 bit) * the event types indexed by enum ehci_hrtimer_event in ehci.h. */ static unsigned event_delays_ns[] = { + 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_ASS */ 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_PSS */ 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ + 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */ }; /* Enable a pending hrtimer event */ @@ -91,6 +93,51 @@ static void ehci_enable_event(struct ehci_hcd *ehci, unsigned event, } +/* Poll the STS_ASS status bit; see when it agrees with CMD_ASE */ +static void ehci_poll_ASS(struct ehci_hcd *ehci) +{ + unsigned actual, want; + + /* Don't enable anything if the controller isn't running (e.g., died) */ + if (ehci->rh_state != EHCI_RH_RUNNING) + return; + + want = (ehci->command & CMD_ASE) ? STS_ASS : 0; + actual = ehci_readl(ehci, &ehci->regs->status) & STS_ASS; + + if (want != actual) { + + /* Poll again later, but give up after about 20 ms */ + if (ehci->ASS_poll_count++ < 20) { + ehci_enable_event(ehci, EHCI_HRTIMER_POLL_ASS, true); + return; + } + ehci_warn(ehci, "Waited too long for the async schedule status, giving up\n"); + } + ehci->ASS_poll_count = 0; + + /* The status is up-to-date; restart or stop the schedule as needed */ + if (want == 0) { /* Stopped */ + if (ehci->async_count > 0) + ehci_set_command_bit(ehci, CMD_ASE); + + } else { /* Running */ + if (ehci->async_count == 0) { + + /* Turn off the schedule after a while */ + ehci_enable_event(ehci, EHCI_HRTIMER_DISABLE_ASYNC, + true); + } + } +} + +/* Turn off the async schedule after a brief delay */ +static void ehci_disable_ASE(struct ehci_hcd *ehci) +{ + ehci_clear_command_bit(ehci, CMD_ASE); +} + + /* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */ static void ehci_poll_PSS(struct ehci_hcd *ehci) { @@ -151,8 +198,10 @@ static void ehci_disable_PSE(struct ehci_hcd *ehci) * enum ehci_hrtimer_event in ehci.h. */ static void (*event_handlers[])(struct ehci_hcd *) = { + ehci_poll_ASS, /* EHCI_HRTIMER_POLL_ASS */ ehci_poll_PSS, /* EHCI_HRTIMER_POLL_PSS */ ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ + ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */ }; static enum hrtimer_restart ehci_hrtimer_func(struct hrtimer *t) diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index da2e0ab2385..bf06bbb77ba 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -79,8 +79,10 @@ enum ehci_rh_state { * ehci-timer.c) in parallel with this list. */ enum ehci_hrtimer_event { + EHCI_HRTIMER_POLL_ASS, /* Poll for async schedule off */ EHCI_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */ EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ + EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ EHCI_HRTIMER_NUM_EVENTS /* Must come last */ }; #define EHCI_HRTIMER_NO_EVENT 99 @@ -93,6 +95,7 @@ struct ehci_hcd { /* one per controller */ struct hrtimer hrtimer; int PSS_poll_count; + int ASS_poll_count; /* glue to PCI and HCD framework */ struct ehci_caps __iomem *caps; @@ -110,6 +113,7 @@ struct ehci_hcd { /* one per controller */ struct ehci_qh *async_unlink_last; struct ehci_qh *qh_scan_next; unsigned scanning : 1; + unsigned async_count; /* async activity count */ /* periodic schedule support */ #define DEFAULT_I_TDPS 1024 /* some HCs can do less */ @@ -229,7 +233,6 @@ static inline void iaa_watchdog_done(struct ehci_hcd *ehci) enum ehci_timer_action { TIMER_IO_WATCHDOG, TIMER_ASYNC_SHRINK, - TIMER_ASYNC_OFF, }; static inline void -- cgit v1.2.3-70-g09d2 From df2022553dd8d34d49e16c19d851ea619438f0ef Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:22:26 -0400 Subject: USB: EHCI: use hrtimer for interrupt QH unlink This patch (as1577) adds hrtimer support for unlinking interrupt QHs in ehci-hcd. The current code relies on a fixed delay of either 2 or 55 us, which is not always adequate and in any case is totally bogus. Thanks to internal caching, the EHCI hardware may continue to access an interrupt QH for more than a millisecond after it has been unlinked. In fact, the EHCI spec doesn't say how long to wait before using an unlinked interrupt QH. The patch sets the delay to 9 microframes minimum, which ought to be adequate. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 6 ++-- drivers/usb/host/ehci-hub.c | 1 + drivers/usb/host/ehci-sched.c | 71 ++++++++++++++++++++++++++++--------------- drivers/usb/host/ehci-timer.c | 34 +++++++++++++++++++++ drivers/usb/host/ehci.h | 10 +++++- 5 files changed, 94 insertions(+), 28 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 21d6fbc0a32..edcfd2c4295 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -309,6 +309,8 @@ static void ehci_quiesce (struct ehci_hcd *ehci) static void end_unlink_async(struct ehci_hcd *ehci); static void ehci_work(struct ehci_hcd *ehci); +static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh); +static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh); #include "ehci-timer.c" #include "ehci-hub.c" @@ -1034,7 +1036,7 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) switch (qh->qh_state) { case QH_STATE_LINKED: case QH_STATE_COMPLETING: - intr_deschedule (ehci, qh); + start_unlink_intr(ehci, qh); break; case QH_STATE_IDLE: qh_completions (ehci, qh); @@ -1164,7 +1166,7 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) if (eptype == USB_ENDPOINT_XFER_BULK) unlink_async(ehci, qh); else - intr_deschedule(ehci, qh); + start_unlink_intr(ehci, qh); } } spin_unlock_irqrestore(&ehci->lock, flags); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 25329e4b844..8aa740dc510 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -302,6 +302,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) if (ehci->async_unlink) end_unlink_async(ehci); + ehci_handle_intr_unlinks(ehci); /* allow remote wakeup */ mask = INTR_MASK; diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 69b1861e432..eec8446f8de 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -578,12 +578,20 @@ static void qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) unsigned i; unsigned period; - // FIXME: - // IF this isn't high speed - // and this qh is active in the current uframe - // (and overlay token SplitXstate is false?) - // THEN - // qh->hw_info1 |= cpu_to_hc32(1 << 7 /* "ignore" */); + /* + * If qh is for a low/full-speed device, simply unlinking it + * could interfere with an ongoing split transaction. To unlink + * it safely would require setting the QH_INACTIVATE bit and + * waiting at least one frame, as described in EHCI 4.12.2.5. + * + * We won't bother with any of this. Instead, we assume that the + * only reason for unlinking an interrupt QH while the current URB + * is still active is to dequeue all the URBs (flush the whole + * endpoint queue). + * + * If rebalancing the periodic schedule is ever implemented, this + * approach will no longer be valid. + */ /* high bandwidth, or otherwise part of every microframe */ if ((period = qh->period) == 0) @@ -608,12 +616,8 @@ static void qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) qh->qh_next.ptr = NULL; } -static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) +static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) { - unsigned wait; - struct ehci_qh_hw *hw = qh->hw; - int rc; - /* If the QH isn't linked then there's nothing we can do * unless we were called during a giveback, in which case * qh_completions() has to deal with it. @@ -626,28 +630,45 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) qh_unlink_periodic (ehci, qh); - /* simple/paranoid: always delay, expecting the HC needs to read - * qh->hw_next or finish a writeback after SPLIT/CSPLIT ... and - * expect khubd to clean up after any CSPLITs we won't issue. - * active high speed queues may need bigger delays... + /* Make sure the unlinks are visible before starting the timer */ + wmb(); + + /* + * The EHCI spec doesn't say how long it takes the controller to + * stop accessing an unlinked interrupt QH. The timer delay is + * 9 uframes; presumably that will be long enough. */ - if (list_empty (&qh->qtd_list) - || (cpu_to_hc32(ehci, QH_CMASK) - & hw->hw_info2) != 0) - wait = 2; + qh->unlink_cycle = ehci->intr_unlink_cycle; + + /* New entries go at the end of the intr_unlink list */ + if (ehci->intr_unlink) + ehci->intr_unlink_last->unlink_next = qh; else - wait = 55; /* worst case: 3 * 1024 */ + ehci->intr_unlink = qh; + ehci->intr_unlink_last = qh; + + if (ehci->intr_unlinking) + ; /* Avoid recursive calls */ + else if (ehci->rh_state < EHCI_RH_RUNNING) + ehci_handle_intr_unlinks(ehci); + else if (ehci->intr_unlink == qh) { + ehci_enable_event(ehci, EHCI_HRTIMER_UNLINK_INTR, true); + ++ehci->intr_unlink_cycle; + } +} + +static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) +{ + struct ehci_qh_hw *hw = qh->hw; + int rc; - udelay (wait); qh->qh_state = QH_STATE_IDLE; hw->hw_next = EHCI_LIST_END(ehci); - wmb (); qh_completions(ehci, qh); /* reschedule QH iff another request is queued */ - if (!list_empty(&qh->qtd_list) && - ehci->rh_state == EHCI_RH_RUNNING) { + if (!list_empty(&qh->qtd_list) && ehci->rh_state == EHCI_RH_RUNNING) { rc = qh_schedule(ehci, qh); /* An error here likely indicates handshake failure @@ -2302,7 +2323,7 @@ restart: temp.qh->stamp = ehci->periodic_stamp; if (unlikely(list_empty(&temp.qh->qtd_list) || temp.qh->needs_rescan)) - intr_deschedule(ehci, temp.qh); + start_unlink_intr(ehci, temp.qh); } break; case Q_TYPE_FSTN: diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index 1e907dd3bb1..bd8b591771b 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -69,6 +69,7 @@ static void ehci_clear_command_bit(struct ehci_hcd *ehci, u32 bit) static unsigned event_delays_ns[] = { 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_ASS */ 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_PSS */ + 1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */ 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */ }; @@ -192,6 +193,38 @@ static void ehci_disable_PSE(struct ehci_hcd *ehci) } +/* Handle unlinked interrupt QHs once they are gone from the hardware */ +static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci) +{ + bool stopped = (ehci->rh_state < EHCI_RH_RUNNING); + + /* + * Process all the QHs on the intr_unlink list that were added + * before the current unlink cycle began. The list is in + * temporal order, so stop when we reach the first entry in the + * current cycle. But if the root hub isn't running then + * process all the QHs on the list. + */ + ehci->intr_unlinking = true; + while (ehci->intr_unlink) { + struct ehci_qh *qh = ehci->intr_unlink; + + if (!stopped && qh->unlink_cycle == ehci->intr_unlink_cycle) + break; + ehci->intr_unlink = qh->unlink_next; + qh->unlink_next = NULL; + end_unlink_intr(ehci, qh); + } + + /* Handle remaining entries later */ + if (ehci->intr_unlink) { + ehci_enable_event(ehci, EHCI_HRTIMER_UNLINK_INTR, true); + ++ehci->intr_unlink_cycle; + } + ehci->intr_unlinking = false; +} + + /* * Handler functions for the hrtimer event types. * Keep this array in the same order as the event types indexed by @@ -200,6 +233,7 @@ static void ehci_disable_PSE(struct ehci_hcd *ehci) static void (*event_handlers[])(struct ehci_hcd *) = { ehci_poll_ASS, /* EHCI_HRTIMER_POLL_ASS */ ehci_poll_PSS, /* EHCI_HRTIMER_POLL_PSS */ + ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */ ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */ }; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index bf06bbb77ba..f36f1f85d7f 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -81,6 +81,7 @@ enum ehci_rh_state { enum ehci_hrtimer_event { EHCI_HRTIMER_POLL_ASS, /* Poll for async schedule off */ EHCI_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */ + EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ EHCI_HRTIMER_NUM_EVENTS /* Must come last */ @@ -106,13 +107,16 @@ struct ehci_hcd { /* one per controller */ spinlock_t lock; enum ehci_rh_state rh_state; + /* general schedule support */ + unsigned scanning:1; + bool intr_unlinking:1; + /* async schedule support */ struct ehci_qh *async; struct ehci_qh *dummy; /* For AMD quirk use */ struct ehci_qh *async_unlink; struct ehci_qh *async_unlink_last; struct ehci_qh *qh_scan_next; - unsigned scanning : 1; unsigned async_count; /* async activity count */ /* periodic schedule support */ @@ -123,6 +127,9 @@ struct ehci_hcd { /* one per controller */ unsigned i_thresh; /* uframes HC might cache */ union ehci_shadow *pshadow; /* mirror hw periodic table */ + struct ehci_qh *intr_unlink; + struct ehci_qh *intr_unlink_last; + unsigned intr_unlink_cycle; int next_uframe; /* scan periodic, start here */ unsigned periodic_count; /* periodic activity count */ unsigned uframe_periodic_max; /* max periodic time per uframe */ @@ -385,6 +392,7 @@ struct ehci_qh { struct ehci_qh *unlink_next; /* next on unlink list */ unsigned long unlink_time; + unsigned unlink_cycle; unsigned stamp; u8 needs_rescan; /* Dequeue during giveback */ -- cgit v1.2.3-70-g09d2 From bf6387bcd16975ba8952b094f262a359d74e1c8a Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:22:31 -0400 Subject: USB: EHCI: use hrtimer for controller death This patch (as1578) adds an hrtimer event to handle the death of an EHCI controller. When a controller dies, it doesn't necessarily stop running right away. The new event polls at 1-ms intervals to see when all activity has safely stopped. This replaces a busy-wait polling loop in the current code. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 20 ++++++++++---------- drivers/usb/host/ehci-timer.c | 26 ++++++++++++++++++++++++++ drivers/usb/host/ehci.h | 2 ++ 3 files changed, 38 insertions(+), 10 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index edcfd2c4295..1676c66b853 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -888,20 +888,20 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) /* PCI errors [4.15.2.4] */ if (unlikely ((status & STS_FATAL) != 0)) { ehci_err(ehci, "fatal error\n"); - ehci->rh_state = EHCI_RH_STOPPING; dbg_cmd(ehci, "fatal", cmd); dbg_status(ehci, "fatal", status); - ehci_halt(ehci); dead: - ehci->enabled_hrtimer_events = 0; - hrtimer_try_to_cancel(&ehci->hrtimer); - ehci_reset(ehci); - ehci_writel(ehci, 0, &ehci->regs->configured_flag); usb_hc_died(hcd); - /* generic layer kills/unlinks all urbs, then - * uses ehci_stop to clean up the rest - */ - bh = 1; + + /* Don't let the controller do anything more */ + ehci->rh_state = EHCI_RH_STOPPING; + ehci->command &= ~(CMD_RUN | CMD_ASE | CMD_PSE); + ehci_writel(ehci, ehci->command, &ehci->regs->command); + ehci_writel(ehci, 0, &ehci->regs->intr_enable); + ehci_handle_controller_death(ehci); + + /* Handle completions when the controller stops */ + bh = 0; } if (bh) diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index bd8b591771b..0c5326a8883 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -69,6 +69,7 @@ static void ehci_clear_command_bit(struct ehci_hcd *ehci, u32 bit) static unsigned event_delays_ns[] = { 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_ASS */ 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_PSS */ + 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_DEAD */ 1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */ 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */ @@ -193,6 +194,30 @@ static void ehci_disable_PSE(struct ehci_hcd *ehci) } +/* Poll the STS_HALT status bit; see when a dead controller stops */ +static void ehci_handle_controller_death(struct ehci_hcd *ehci) +{ + if (!(ehci_readl(ehci, &ehci->regs->status) & STS_HALT)) { + + /* Give up after a few milliseconds */ + if (ehci->died_poll_count++ < 5) { + /* Try again later */ + ehci_enable_event(ehci, EHCI_HRTIMER_POLL_DEAD, true); + return; + } + ehci_warn(ehci, "Waited too long for the controller to stop, giving up\n"); + } + + /* Clean up the mess */ + ehci->rh_state = EHCI_RH_HALTED; + ehci_writel(ehci, 0, &ehci->regs->configured_flag); + ehci_writel(ehci, 0, &ehci->regs->intr_enable); + ehci_work(ehci); + + /* Not in process context, so don't try to reset the controller */ +} + + /* Handle unlinked interrupt QHs once they are gone from the hardware */ static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci) { @@ -233,6 +258,7 @@ static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci) static void (*event_handlers[])(struct ehci_hcd *) = { ehci_poll_ASS, /* EHCI_HRTIMER_POLL_ASS */ ehci_poll_PSS, /* EHCI_HRTIMER_POLL_PSS */ + ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */ ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */ ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */ diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index f36f1f85d7f..6874d89b0b6 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -81,6 +81,7 @@ enum ehci_rh_state { enum ehci_hrtimer_event { EHCI_HRTIMER_POLL_ASS, /* Poll for async schedule off */ EHCI_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */ + EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ @@ -97,6 +98,7 @@ struct ehci_hcd { /* one per controller */ int PSS_poll_count; int ASS_poll_count; + int died_poll_count; /* glue to PCI and HCD framework */ struct ehci_caps __iomem *caps; -- cgit v1.2.3-70-g09d2 From 55934eb3b9fa52eb53b9d7342267fc73c38206aa Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:22:35 -0400 Subject: USB: EHCI: use hrtimer for (s)iTD deallocation This patch (as1579) adds an hrtimer event to handle deallocation of iTDs and siTDs in ehci-hcd. Because of the frame-oriented approach used by the EHCI periodic schedule, the hardware can continue to access the Transfer Descriptor for isochronous (or split-isochronous) transactions for up to a millisecond after the transaction completes. The iTD (or siTD) must not be reused before then. The strategy currently used involves putting completed iTDs on a list of cached entries and every so often returning them to the endpoint's free list. The new strategy reduces overhead by putting completed iTDs back on the free list immediately, although they are not reused until it is safe to do so. When the isochronous endpoint stops (its queue becomes empty), the iTDs on its free list get moved to a global list, from which they will be deallocated after a minimum of 2 ms. This delay is what the new hrtimer event is for. Overall this may not be a tremendous improvement over the current code, but to me it seems a lot more clear and logical. In addition, it removes the need for each iTD to keep a reference to the ehci_iso_stream it belongs to, since the iTD never needs to be moved back to the stream's free list from the global list. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 1 + drivers/usb/host/ehci-hub.c | 1 + drivers/usb/host/ehci-mem.c | 1 - drivers/usb/host/ehci-sched.c | 137 ++++++++++++------------------------------ drivers/usb/host/ehci-timer.c | 50 +++++++++++++-- drivers/usb/host/ehci.h | 5 +- 6 files changed, 90 insertions(+), 105 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 1676c66b853..98b945840c9 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -509,6 +509,7 @@ static void ehci_stop (struct usb_hcd *hcd) spin_lock_irq (&ehci->lock); if (ehci->async) ehci_work (ehci); + end_free_itds(ehci); spin_unlock_irq (&ehci->lock); ehci_mem_cleanup (ehci); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 8aa740dc510..6ad806bbe46 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -303,6 +303,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) if (ehci->async_unlink) end_unlink_async(ehci); ehci_handle_intr_unlinks(ehci); + end_free_itds(ehci); /* allow remote wakeup */ mask = INTR_MASK; diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c index 93132d8ad36..ef2c3a1eca4 100644 --- a/drivers/usb/host/ehci-mem.c +++ b/drivers/usb/host/ehci-mem.c @@ -118,7 +118,6 @@ fail: static void ehci_mem_cleanup (struct ehci_hcd *ehci) { - free_cached_lists(ehci); if (ehci->async) qh_destroy(ehci, ehci->async); ehci->async = NULL; diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index eec8446f8de..1e4f13c11b6 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1045,31 +1045,6 @@ iso_stream_put(struct ehci_hcd *ehci, struct ehci_iso_stream *stream) if (stream->refcount == 1) { // BUG_ON (!list_empty(&stream->td_list)); - while (!list_empty (&stream->free_list)) { - struct list_head *entry; - - entry = stream->free_list.next; - list_del (entry); - - /* knows about ITD vs SITD */ - if (stream->highspeed) { - struct ehci_itd *itd; - - itd = list_entry (entry, struct ehci_itd, - itd_list); - dma_pool_free (ehci->itd_pool, itd, - itd->itd_dma); - } else { - struct ehci_sitd *sitd; - - sitd = list_entry (entry, struct ehci_sitd, - sitd_list); - dma_pool_free (ehci->sitd_pool, sitd, - sitd->sitd_dma); - } - } - - stream->bEndpointAddress &= 0x0f; if (stream->ep) stream->ep->hcpriv = NULL; @@ -1230,17 +1205,19 @@ itd_urb_transaction ( spin_lock_irqsave (&ehci->lock, flags); for (i = 0; i < num_itds; i++) { - /* free_list.next might be cache-hot ... but maybe - * the HC caches it too. avoid that issue for now. + /* + * Use iTDs from the free list, but not iTDs that may + * still be in use by the hardware. */ - - /* prefer previously-allocated itds */ - if (likely (!list_empty(&stream->free_list))) { - itd = list_entry (stream->free_list.prev, + if (likely(!list_empty(&stream->free_list))) { + itd = list_first_entry(&stream->free_list, struct ehci_itd, itd_list); + if (itd->frame == ehci->clock_frame) + goto alloc_itd; list_del (&itd->itd_list); itd_dma = itd->itd_dma; } else { + alloc_itd: spin_unlock_irqrestore (&ehci->lock, flags); itd = dma_pool_alloc (ehci->itd_pool, mem_flags, &itd_dma); @@ -1762,24 +1739,18 @@ itd_complete ( done: itd->urb = NULL; - if (ehci->clock_frame != itd->frame || itd->index[7] != -1) { - /* OK to recycle this ITD now. */ - itd->stream = NULL; - list_move(&itd->itd_list, &stream->free_list); - iso_stream_put(ehci, stream); - } else { - /* HW might remember this ITD, so we can't recycle it yet. - * Move it to a safe place until a new frame starts. - */ - list_move(&itd->itd_list, &ehci->cached_itd_list); - if (stream->refcount == 2) { - /* If iso_stream_put() were called here, stream - * would be freed. Instead, just prevent reuse. - */ - stream->ep->hcpriv = NULL; - stream->ep = NULL; - } + + /* Add to the end of the free list for later reuse */ + list_move_tail(&itd->itd_list, &stream->free_list); + + /* Recycle the iTDs when the pipeline is empty (ep no longer in use) */ + if (list_empty(&stream->td_list)) { + list_splice_tail_init(&stream->free_list, + &ehci->cached_itd_list); + start_free_itds(ehci); } + + iso_stream_put(ehci, stream); return retval; } @@ -1930,17 +1901,19 @@ sitd_urb_transaction ( * means we never need two sitds for full speed packets. */ - /* free_list.next might be cache-hot ... but maybe - * the HC caches it too. avoid that issue for now. + /* + * Use siTDs from the free list, but not siTDs that may + * still be in use by the hardware. */ - - /* prefer previously-allocated sitds */ - if (!list_empty(&stream->free_list)) { - sitd = list_entry (stream->free_list.prev, + if (likely(!list_empty(&stream->free_list))) { + sitd = list_first_entry(&stream->free_list, struct ehci_sitd, sitd_list); + if (sitd->frame == ehci->clock_frame) + goto alloc_sitd; list_del (&sitd->sitd_list); sitd_dma = sitd->sitd_dma; } else { + alloc_sitd: spin_unlock_irqrestore (&ehci->lock, flags); sitd = dma_pool_alloc (ehci->sitd_pool, mem_flags, &sitd_dma); @@ -2157,24 +2130,18 @@ sitd_complete ( done: sitd->urb = NULL; - if (ehci->clock_frame != sitd->frame) { - /* OK to recycle this SITD now. */ - sitd->stream = NULL; - list_move(&sitd->sitd_list, &stream->free_list); - iso_stream_put(ehci, stream); - } else { - /* HW might remember this SITD, so we can't recycle it yet. - * Move it to a safe place until a new frame starts. - */ - list_move(&sitd->sitd_list, &ehci->cached_sitd_list); - if (stream->refcount == 2) { - /* If iso_stream_put() were called here, stream - * would be freed. Instead, just prevent reuse. - */ - stream->ep->hcpriv = NULL; - stream->ep = NULL; - } + + /* Add to the end of the free list for later reuse */ + list_move_tail(&sitd->sitd_list, &stream->free_list); + + /* Recycle the siTDs when the pipeline is empty (ep no longer in use) */ + if (list_empty(&stream->td_list)) { + list_splice_tail_init(&stream->free_list, + &ehci->cached_sitd_list); + start_free_itds(ehci); } + + iso_stream_put(ehci, stream); return retval; } @@ -2239,28 +2206,6 @@ done: /*-------------------------------------------------------------------------*/ -static void free_cached_lists(struct ehci_hcd *ehci) -{ - struct ehci_itd *itd, *n; - struct ehci_sitd *sitd, *sn; - - list_for_each_entry_safe(itd, n, &ehci->cached_itd_list, itd_list) { - struct ehci_iso_stream *stream = itd->stream; - itd->stream = NULL; - list_move(&itd->itd_list, &stream->free_list); - iso_stream_put(ehci, stream); - } - - list_for_each_entry_safe(sitd, sn, &ehci->cached_sitd_list, sitd_list) { - struct ehci_iso_stream *stream = sitd->stream; - sitd->stream = NULL; - list_move(&sitd->sitd_list, &stream->free_list); - iso_stream_put(ehci, stream); - } -} - -/*-------------------------------------------------------------------------*/ - static void scan_periodic (struct ehci_hcd *ehci) { @@ -2282,10 +2227,7 @@ scan_periodic (struct ehci_hcd *ehci) clock = now_uframe + mod - 1; clock_frame = -1; } - if (ehci->clock_frame != clock_frame) { - free_cached_lists(ehci); - ehci->clock_frame = clock_frame; - } + ehci->clock_frame = clock_frame; clock &= mod - 1; clock_frame = clock >> 3; ++ehci->periodic_stamp; @@ -2463,7 +2405,6 @@ restart: clock = now; clock_frame = clock >> 3; if (ehci->clock_frame != clock_frame) { - free_cached_lists(ehci); ehci->clock_frame = clock_frame; ++ehci->periodic_stamp; } diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index 0c5326a8883..8feb60ff422 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -71,6 +71,7 @@ static unsigned event_delays_ns[] = { 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_PSS */ 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_DEAD */ 1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */ + 2 * NSEC_PER_MSEC, /* EHCI_HRTIMER_FREE_ITDS */ 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */ }; @@ -165,7 +166,6 @@ static void ehci_poll_PSS(struct ehci_hcd *ehci) /* The status is up-to-date; restart or stop the schedule as needed */ if (want == 0) { /* Stopped */ - free_cached_lists(ehci); if (ehci->periodic_count > 0) { /* make sure ehci_work scans these */ @@ -188,9 +188,6 @@ static void ehci_poll_PSS(struct ehci_hcd *ehci) static void ehci_disable_PSE(struct ehci_hcd *ehci) { ehci_clear_command_bit(ehci, CMD_PSE); - - /* Poll to see when it actually stops */ - ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true); } @@ -250,6 +247,50 @@ static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci) } +/* Start another free-iTDs/siTDs cycle */ +static void start_free_itds(struct ehci_hcd *ehci) +{ + if (!(ehci->enabled_hrtimer_events & BIT(EHCI_HRTIMER_FREE_ITDS))) { + ehci->last_itd_to_free = list_entry( + ehci->cached_itd_list.prev, + struct ehci_itd, itd_list); + ehci->last_sitd_to_free = list_entry( + ehci->cached_sitd_list.prev, + struct ehci_sitd, sitd_list); + ehci_enable_event(ehci, EHCI_HRTIMER_FREE_ITDS, true); + } +} + +/* Wait for controller to stop using old iTDs and siTDs */ +static void end_free_itds(struct ehci_hcd *ehci) +{ + struct ehci_itd *itd, *n; + struct ehci_sitd *sitd, *sn; + + if (ehci->rh_state < EHCI_RH_RUNNING) { + ehci->last_itd_to_free = NULL; + ehci->last_sitd_to_free = NULL; + } + + list_for_each_entry_safe(itd, n, &ehci->cached_itd_list, itd_list) { + list_del(&itd->itd_list); + dma_pool_free(ehci->itd_pool, itd, itd->itd_dma); + if (itd == ehci->last_itd_to_free) + break; + } + list_for_each_entry_safe(sitd, sn, &ehci->cached_sitd_list, sitd_list) { + list_del(&sitd->sitd_list); + dma_pool_free(ehci->sitd_pool, sitd, sitd->sitd_dma); + if (sitd == ehci->last_sitd_to_free) + break; + } + + if (!list_empty(&ehci->cached_itd_list) || + !list_empty(&ehci->cached_sitd_list)) + start_free_itds(ehci); +} + + /* * Handler functions for the hrtimer event types. * Keep this array in the same order as the event types indexed by @@ -260,6 +301,7 @@ static void (*event_handlers[])(struct ehci_hcd *) = { ehci_poll_PSS, /* EHCI_HRTIMER_POLL_PSS */ ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */ ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */ + end_free_itds, /* EHCI_HRTIMER_FREE_ITDS */ ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */ }; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 6874d89b0b6..bcfbb175e2b 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -83,6 +83,7 @@ enum ehci_hrtimer_event { EHCI_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */ EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ + EHCI_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */ EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ EHCI_HRTIMER_NUM_EVENTS /* Must come last */ @@ -139,7 +140,9 @@ struct ehci_hcd { /* one per controller */ /* list of itds & sitds completed while clock_frame was still active */ struct list_head cached_itd_list; + struct ehci_itd *last_itd_to_free; struct list_head cached_sitd_list; + struct ehci_sitd *last_sitd_to_free; unsigned clock_frame; /* per root hub port */ @@ -250,8 +253,6 @@ timer_action_done (struct ehci_hcd *ehci, enum ehci_timer_action action) clear_bit (action, &ehci->actions); } -static void free_cached_lists(struct ehci_hcd *ehci); - /*-------------------------------------------------------------------------*/ #include -- cgit v1.2.3-70-g09d2 From 8c5bf7be56f1a8aecc1f802f132d53f556a9bc45 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:22:39 -0400 Subject: USB: EHCI: don't refcount iso_stream structures This patch (as1580) makes ehci_iso_stream structures behave more like QHs, in that they will remain allocated until their isochronous endpoint is disabled. This will come in useful in the future, when periodic bandwidth gets allocated as an altsetting is installed rather than on-the-fly. For now, the change to the ehci_iso_stream lifetimes means that each structure is always deallocated at exactly one spot in ehci_endpoint_disable() and never used again. As a result, it is no longer necessary to use reference counting on these things, and the patch removes it. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 12 +++++++--- drivers/usb/host/ehci-sched.c | 53 +++++-------------------------------------- drivers/usb/host/ehci.h | 3 +-- 3 files changed, 16 insertions(+), 52 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 98b945840c9..fb1966cf564 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1085,8 +1085,14 @@ rescan: * accelerate iso completions ... so spin a while. */ if (qh->hw == NULL) { - ehci_vdbg (ehci, "iso delay\n"); - goto idle_timeout; + struct ehci_iso_stream *stream = ep->hcpriv; + + if (!list_empty(&stream->td_list)) + goto idle_timeout; + + /* BUG_ON(!list_empty(&stream->free_list)); */ + kfree(stream); + goto done; } if (ehci->rh_state < EHCI_RH_RUNNING) @@ -1127,8 +1133,8 @@ idle_timeout: list_empty (&qh->qtd_list) ? "" : "(has tds)"); break; } + done: ep->hcpriv = NULL; -done: spin_unlock_irqrestore (&ehci->lock, flags); } diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 1e4f13c11b6..11b2f21d7ac 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -934,7 +934,6 @@ iso_stream_alloc (gfp_t mem_flags) INIT_LIST_HEAD(&stream->td_list); INIT_LIST_HEAD(&stream->free_list); stream->next_uframe = -1; - stream->refcount = 1; } return stream; } @@ -1034,32 +1033,6 @@ iso_stream_init ( stream->maxp = maxp; } -static void -iso_stream_put(struct ehci_hcd *ehci, struct ehci_iso_stream *stream) -{ - stream->refcount--; - - /* free whenever just a dev->ep reference remains. - * not like a QH -- no persistent state (toggle, halt) - */ - if (stream->refcount == 1) { - // BUG_ON (!list_empty(&stream->td_list)); - - if (stream->ep) - stream->ep->hcpriv = NULL; - - kfree(stream); - } -} - -static inline struct ehci_iso_stream * -iso_stream_get (struct ehci_iso_stream *stream) -{ - if (likely (stream != NULL)) - stream->refcount++; - return stream; -} - static struct ehci_iso_stream * iso_stream_find (struct ehci_hcd *ehci, struct urb *urb) { @@ -1080,7 +1053,6 @@ iso_stream_find (struct ehci_hcd *ehci, struct urb *urb) if (unlikely (stream == NULL)) { stream = iso_stream_alloc(GFP_ATOMIC); if (likely (stream != NULL)) { - /* dev->ep owns the initial refcount */ ep->hcpriv = stream; stream->ep = ep; iso_stream_init(ehci, stream, urb->dev, urb->pipe, @@ -1095,9 +1067,6 @@ iso_stream_find (struct ehci_hcd *ehci, struct urb *urb) stream = NULL; } - /* caller guarantees an eventual matching iso_stream_put */ - stream = iso_stream_get (stream); - spin_unlock_irqrestore (&ehci->lock, flags); return stream; } @@ -1611,7 +1580,7 @@ static void itd_link_urb( itd = list_entry (iso_sched->td_list.next, struct ehci_itd, itd_list); list_move_tail (&itd->itd_list, &stream->td_list); - itd->stream = iso_stream_get (stream); + itd->stream = stream; itd->urb = urb; itd_init (ehci, stream, itd); } @@ -1735,7 +1704,6 @@ itd_complete ( dev->devpath, stream->bEndpointAddress & 0x0f, (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); } - iso_stream_put (ehci, stream); done: itd->urb = NULL; @@ -1750,7 +1718,6 @@ done: start_free_itds(ehci); } - iso_stream_put(ehci, stream); return retval; } @@ -1807,12 +1774,9 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, itd_link_urb (ehci, urb, ehci->periodic_size << 3, stream); else usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); -done_not_linked: + done_not_linked: spin_unlock_irqrestore (&ehci->lock, flags); - -done: - if (unlikely (status < 0)) - iso_stream_put (ehci, stream); + done: return status; } @@ -2028,7 +1992,7 @@ static void sitd_link_urb( sitd = list_entry (sched->td_list.next, struct ehci_sitd, sitd_list); list_move_tail (&sitd->sitd_list, &stream->td_list); - sitd->stream = iso_stream_get (stream); + sitd->stream = stream; sitd->urb = urb; sitd_patch(ehci, stream, sitd, sched, packet); @@ -2126,7 +2090,6 @@ sitd_complete ( dev->devpath, stream->bEndpointAddress & 0x0f, (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); } - iso_stream_put (ehci, stream); done: sitd->urb = NULL; @@ -2141,7 +2104,6 @@ done: start_free_itds(ehci); } - iso_stream_put(ehci, stream); return retval; } @@ -2195,12 +2157,9 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream); else usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); -done_not_linked: + done_not_linked: spin_unlock_irqrestore (&ehci->lock, flags); - -done: - if (status < 0) - iso_stream_put (ehci, stream); + done: return status; } diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index bcfbb175e2b..755e30b0000 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -386,7 +386,7 @@ struct ehci_qh_hw { } __attribute__ ((aligned(32))); struct ehci_qh { - struct ehci_qh_hw *hw; + struct ehci_qh_hw *hw; /* Must come first */ /* the rest is HCD-private */ dma_addr_t qh_dma; /* address of qh */ union ehci_shadow qh_next; /* ptr to qh; or periodic */ @@ -453,7 +453,6 @@ struct ehci_iso_stream { /* first field matches ehci_hq, but is NULL */ struct ehci_qh_hw *hw; - u32 refcount; u8 bEndpointAddress; u8 highspeed; struct list_head td_list; /* queued itds/sitds */ -- cgit v1.2.3-70-g09d2 From 9d9387475af261949f61a5ec465e1f762d7be08a Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:22:44 -0400 Subject: USB: EHCI: use hrtimer for the IAA watchdog This patch (as1581) replaces the iaa_watchdog kernel timer used by ehci-hcd with an hrtimer event, in keeping with the general conversion to high-res timers. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 66 +++++++++---------------------------------- drivers/usb/host/ehci-hub.c | 1 - drivers/usb/host/ehci-q.c | 4 +-- drivers/usb/host/ehci-timer.c | 45 +++++++++++++++++++++++++++++ drivers/usb/host/ehci.h | 16 +---------- 5 files changed, 61 insertions(+), 71 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index fb1966cf564..f74ba277c22 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -93,7 +93,6 @@ static const char hcd_name [] = "ehci_hcd"; */ #define EHCI_TUNE_FLS 1 /* (medium) 512-frame schedule */ -#define EHCI_IAA_MSECS 10 /* arbitrary */ #define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ #define EHCI_SHRINK_JIFFIES (DIV_ROUND_UP(HZ, 200) + 1) /* 5-ms async qh unlink delay */ @@ -322,51 +321,6 @@ static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh); /*-------------------------------------------------------------------------*/ -static void ehci_iaa_watchdog(unsigned long param) -{ - struct ehci_hcd *ehci = (struct ehci_hcd *) param; - unsigned long flags; - - spin_lock_irqsave (&ehci->lock, flags); - - /* Lost IAA irqs wedge things badly; seen first with a vt8235. - * So we need this watchdog, but must protect it against both - * (a) SMP races against real IAA firing and retriggering, and - * (b) clean HC shutdown, when IAA watchdog was pending. - */ - if (ehci->async_unlink - && !timer_pending(&ehci->iaa_watchdog) - && ehci->rh_state == EHCI_RH_RUNNING) { - u32 cmd, status; - - /* If we get here, IAA is *REALLY* late. It's barely - * conceivable that the system is so busy that CMD_IAAD - * is still legitimately set, so let's be sure it's - * clear before we read STS_IAA. (The HC should clear - * CMD_IAAD when it sets STS_IAA.) - */ - cmd = ehci_readl(ehci, &ehci->regs->command); - - /* If IAA is set here it either legitimately triggered - * before we cleared IAAD above (but _way_ late, so we'll - * still count it as lost) ... or a silicon erratum: - * - VIA seems to set IAA without triggering the IRQ; - * - IAAD potentially cleared without setting IAA. - */ - status = ehci_readl(ehci, &ehci->regs->status); - if ((status & STS_IAA) || !(cmd & CMD_IAAD)) { - COUNT (ehci->stats.lost_iaa); - ehci_writel(ehci, STS_IAA, &ehci->regs->status); - } - - ehci_vdbg(ehci, "IAA watchdog: status %x cmd %x\n", - status, cmd); - end_unlink_async(ehci); - } - - spin_unlock_irqrestore(&ehci->lock, flags); -} - static void ehci_watchdog(unsigned long param) { struct ehci_hcd *ehci = (struct ehci_hcd *) param; @@ -418,7 +372,6 @@ static void ehci_shutdown(struct usb_hcd *hcd) struct ehci_hcd *ehci = hcd_to_ehci(hcd); del_timer_sync(&ehci->watchdog); - del_timer_sync(&ehci->iaa_watchdog); spin_lock_irq(&ehci->lock); ehci->rh_state = EHCI_RH_STOPPING; @@ -491,7 +444,6 @@ static void ehci_stop (struct usb_hcd *hcd) /* no more interrupts ... */ del_timer_sync (&ehci->watchdog); - del_timer_sync(&ehci->iaa_watchdog); spin_lock_irq(&ehci->lock); ehci->enabled_hrtimer_events = 0; @@ -547,10 +499,6 @@ static int ehci_init(struct usb_hcd *hcd) ehci->watchdog.function = ehci_watchdog; ehci->watchdog.data = (unsigned long) ehci; - init_timer(&ehci->iaa_watchdog); - ehci->iaa_watchdog.function = ehci_iaa_watchdog; - ehci->iaa_watchdog.data = (unsigned long) ehci; - hrtimer_init(&ehci->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); ehci->hrtimer.function = ehci_hrtimer_func; ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT; @@ -830,6 +778,20 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) /* complete the unlinking of some qh [4.15.2.3] */ if (status & STS_IAA) { + + /* Turn off the IAA watchdog */ + ehci->enabled_hrtimer_events &= ~BIT(EHCI_HRTIMER_IAA_WATCHDOG); + + /* + * Mild optimization: Allow another IAAD to reset the + * hrtimer, if one occurs before the next expiration. + * In theory we could always cancel the hrtimer, but + * tests show that about half the time it will be reset + * for some other event anyway. + */ + if (ehci->next_hrtimer_event == EHCI_HRTIMER_IAA_WATCHDOG) + ++ehci->next_hrtimer_event; + /* guard against (alleged) silicon errata */ if (cmd & CMD_IAAD) ehci_dbg(ehci, "IAA with IAAD still set?\n"); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 6ad806bbe46..3097872b1e7 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -209,7 +209,6 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) if (time_before (jiffies, ehci->next_statechange)) msleep(5); del_timer_sync(&ehci->watchdog); - del_timer_sync(&ehci->iaa_watchdog); spin_lock_irq (&ehci->lock); diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index d68764ef447..7b35933d91a 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1173,8 +1173,6 @@ static void end_unlink_async (struct ehci_hcd *ehci) struct ehci_qh *qh = ehci->async_unlink; struct ehci_qh *next; - iaa_watchdog_done(ehci); - // qh->hw_next = cpu_to_hc32(qh->qh_dma); qh->qh_state = QH_STATE_IDLE; qh->qh_next.qh = NULL; @@ -1243,7 +1241,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) ehci_writel(ehci, ehci->command | CMD_IAAD, &ehci->regs->command); (void)ehci_readl(ehci, &ehci->regs->command); - iaa_watchdog_start(ehci); + ehci_enable_event(ehci, EHCI_HRTIMER_IAA_WATCHDOG, true); } /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index 8feb60ff422..aab04206422 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -72,6 +72,7 @@ static unsigned event_delays_ns[] = { 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_DEAD */ 1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */ 2 * NSEC_PER_MSEC, /* EHCI_HRTIMER_FREE_ITDS */ + 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_IAA_WATCHDOG */ 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */ }; @@ -291,6 +292,49 @@ static void end_free_itds(struct ehci_hcd *ehci) } +/* Handle lost (or very late) IAA interrupts */ +static void ehci_iaa_watchdog(struct ehci_hcd *ehci) +{ + if (ehci->rh_state != EHCI_RH_RUNNING) + return; + + /* + * Lost IAA irqs wedge things badly; seen first with a vt8235. + * So we need this watchdog, but must protect it against both + * (a) SMP races against real IAA firing and retriggering, and + * (b) clean HC shutdown, when IAA watchdog was pending. + */ + if (ehci->async_unlink) { + u32 cmd, status; + + /* If we get here, IAA is *REALLY* late. It's barely + * conceivable that the system is so busy that CMD_IAAD + * is still legitimately set, so let's be sure it's + * clear before we read STS_IAA. (The HC should clear + * CMD_IAAD when it sets STS_IAA.) + */ + cmd = ehci_readl(ehci, &ehci->regs->command); + + /* + * If IAA is set here it either legitimately triggered + * after the watchdog timer expired (_way_ late, so we'll + * still count it as lost) ... or a silicon erratum: + * - VIA seems to set IAA without triggering the IRQ; + * - IAAD potentially cleared without setting IAA. + */ + status = ehci_readl(ehci, &ehci->regs->status); + if ((status & STS_IAA) || !(cmd & CMD_IAAD)) { + COUNT(ehci->stats.lost_iaa); + ehci_writel(ehci, STS_IAA, &ehci->regs->status); + } + + ehci_vdbg(ehci, "IAA watchdog: status %x cmd %x\n", + status, cmd); + end_unlink_async(ehci); + } +} + + /* * Handler functions for the hrtimer event types. * Keep this array in the same order as the event types indexed by @@ -302,6 +346,7 @@ static void (*event_handlers[])(struct ehci_hcd *) = { ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */ ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */ end_free_itds, /* EHCI_HRTIMER_FREE_ITDS */ + ehci_iaa_watchdog, /* EHCI_HRTIMER_IAA_WATCHDOG */ ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */ }; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 755e30b0000..d9414aae610 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -84,6 +84,7 @@ enum ehci_hrtimer_event { EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ EHCI_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */ + EHCI_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */ EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ EHCI_HRTIMER_NUM_EVENTS /* Must come last */ @@ -168,7 +169,6 @@ struct ehci_hcd { /* one per controller */ struct dma_pool *itd_pool; /* itd per iso urb */ struct dma_pool *sitd_pool; /* sitd per split iso urb */ - struct timer_list iaa_watchdog; struct timer_list watchdog; unsigned long actions; unsigned periodic_stamp; @@ -228,20 +228,6 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci) return container_of ((void *) ehci, struct usb_hcd, hcd_priv); } - -static inline void -iaa_watchdog_start(struct ehci_hcd *ehci) -{ - WARN_ON(timer_pending(&ehci->iaa_watchdog)); - mod_timer(&ehci->iaa_watchdog, - jiffies + msecs_to_jiffies(EHCI_IAA_MSECS)); -} - -static inline void iaa_watchdog_done(struct ehci_hcd *ehci) -{ - del_timer(&ehci->iaa_watchdog); -} - enum ehci_timer_action { TIMER_IO_WATCHDOG, TIMER_ASYNC_SHRINK, -- cgit v1.2.3-70-g09d2 From 3c273a056bf46167f0a1309c2ba72282a17d2541 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:22:49 -0400 Subject: USB: EHCI: unlink multiple async QHs together This patch (as1582) changes ehci-hcd's strategy for unlinking async QHs. Currently the driver never unlinks more than one QH at a time. This can be inefficient and cause unnecessary delays, since a QH cannot be reused while it is waiting to be unlinked. The new strategy unlinks all the waiting QHs at once. In practice the improvement won't be very big, because it's somewhat uncommon to have two or more QHs waiting to be unlinked at any time. But it does happen, and in any case, doing things this way makes more sense IMO. The change requires the async unlinking code to be refactored slightly. Now in addition to the routines for starting and ending an unlink, there are new routines for unlinking a single QH and starting an IAA cycle. This approach is needed because there are two separate paths for unlinking async QHs: When a transfer error occurs or an URB is cancelled, the QH must be unlinked right away; When a QH has been idle sufficiently long, it is unlinked to avoid consuming DMA bandwidth uselessly. In the first case we want the unlink to proceed as quickly as possible, whereas in the second case we can afford to batch several QHs together and unlink them all at once. Hence the division of labor. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 35 ++-------- drivers/usb/host/ehci-hub.c | 3 +- drivers/usb/host/ehci-q.c | 149 +++++++++++++++++++++++++----------------- drivers/usb/host/ehci-timer.c | 3 +- drivers/usb/host/ehci.h | 2 + 5 files changed, 98 insertions(+), 94 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index f74ba277c22..86e8ee169c6 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -795,7 +795,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) /* guard against (alleged) silicon errata */ if (cmd & CMD_IAAD) ehci_dbg(ehci, "IAA with IAAD still set?\n"); - if (ehci->async_unlink) { + if (ehci->async_iaa) { COUNT(ehci->stats.iaa); end_unlink_async(ehci); } else @@ -926,33 +926,6 @@ static int ehci_urb_enqueue ( } } -static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) -{ - /* failfast */ - if (ehci->rh_state < EHCI_RH_RUNNING && ehci->async_unlink) - end_unlink_async(ehci); - - /* If the QH isn't linked then there's nothing we can do - * unless we were called during a giveback, in which case - * qh_completions() has to deal with it. - */ - if (qh->qh_state != QH_STATE_LINKED) { - if (qh->qh_state == QH_STATE_COMPLETING) - qh->needs_rescan = 1; - return; - } - - /* defer till later if busy */ - if (ehci->async_unlink) { - qh->qh_state = QH_STATE_UNLINK_WAIT; - ehci->async_unlink_last->unlink_next = qh; - ehci->async_unlink_last = qh; - - /* start IAA cycle */ - } else - start_unlink_async (ehci, qh); -} - /* remove from hardware lists * completions normally happen asynchronously */ @@ -979,7 +952,7 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) switch (qh->qh_state) { case QH_STATE_LINKED: case QH_STATE_COMPLETING: - unlink_async(ehci, qh); + start_unlink_async(ehci, qh); break; case QH_STATE_UNLINK: case QH_STATE_UNLINK_WAIT: @@ -1070,7 +1043,7 @@ rescan: * may already be unlinked. */ if (tmp) - unlink_async(ehci, qh); + start_unlink_async(ehci, qh); /* FALL THROUGH */ case QH_STATE_UNLINK: /* wait for hw to finish? */ case QH_STATE_UNLINK_WAIT: @@ -1133,7 +1106,7 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) * re-linking will call qh_refresh(). */ if (eptype == USB_ENDPOINT_XFER_BULK) - unlink_async(ehci, qh); + start_unlink_async(ehci, qh); else start_unlink_intr(ehci, qh); } diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 3097872b1e7..a3822700e49 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -299,8 +299,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) ehci_halt (ehci); ehci->rh_state = EHCI_RH_SUSPENDED; - if (ehci->async_unlink) - end_unlink_async(ehci); + end_unlink_async(ehci); ehci_handle_intr_unlinks(ehci); end_free_itds(ehci); diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 7b35933d91a..181832921c5 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -293,9 +293,6 @@ __acquires(ehci->lock) spin_lock (&ehci->lock); } -static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh); -static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh); - static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh); /* @@ -1166,82 +1163,114 @@ submit_async ( /*-------------------------------------------------------------------------*/ -/* the async qh for the qtds being unlinked are now gone from the HC */ - -static void end_unlink_async (struct ehci_hcd *ehci) +static void single_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh) { - struct ehci_qh *qh = ehci->async_unlink; - struct ehci_qh *next; + struct ehci_qh *prev; - // qh->hw_next = cpu_to_hc32(qh->qh_dma); - qh->qh_state = QH_STATE_IDLE; - qh->qh_next.qh = NULL; + /* Add to the end of the list of QHs waiting for the next IAAD */ + qh->qh_state = QH_STATE_UNLINK; + if (ehci->async_unlink) + ehci->async_unlink_last->unlink_next = qh; + else + ehci->async_unlink = qh; + ehci->async_unlink_last = qh; + + /* Unlink it from the schedule */ + prev = ehci->async; + while (prev->qh_next.qh != qh) + prev = prev->qh_next.qh; + + prev->hw->hw_next = qh->hw->hw_next; + prev->qh_next = qh->qh_next; + if (ehci->qh_scan_next == qh) + ehci->qh_scan_next = qh->qh_next.qh; +} - /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */ - next = qh->unlink_next; - ehci->async_unlink = next; - qh->unlink_next = NULL; +static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested) +{ + /* + * Do nothing if an IAA cycle is already running or + * if one will be started shortly. + */ + if (ehci->async_iaa || ehci->async_unlinking) + return; - qh_completions (ehci, qh); + /* Do all the waiting QHs at once */ + ehci->async_iaa = ehci->async_unlink; + ehci->async_unlink = NULL; - if (!list_empty(&qh->qtd_list) && ehci->rh_state == EHCI_RH_RUNNING) - qh_link_async (ehci, qh); + /* If the controller isn't running, we don't have to wait for it */ + if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) { + if (!nested) /* Avoid recursion */ + end_unlink_async(ehci); - disable_async(ehci); + /* Otherwise start a new IAA cycle */ + } else { + /* Make sure the unlinks are all visible to the hardware */ + wmb(); - if (next) { - ehci->async_unlink = NULL; - start_unlink_async (ehci, next); + ehci_writel(ehci, ehci->command | CMD_IAAD, + &ehci->regs->command); + ehci_readl(ehci, &ehci->regs->command); + ehci_enable_event(ehci, EHCI_HRTIMER_IAA_WATCHDOG, true); } +} + +/* the async qh for the qtds being unlinked are now gone from the HC */ + +static void end_unlink_async(struct ehci_hcd *ehci) +{ + struct ehci_qh *qh; if (ehci->has_synopsys_hc_bug) ehci_writel(ehci, (u32) ehci->async->qh_dma, &ehci->regs->async_next); + + /* Process the idle QHs */ + restart: + ehci->async_unlinking = true; + while (ehci->async_iaa) { + qh = ehci->async_iaa; + ehci->async_iaa = qh->unlink_next; + qh->unlink_next = NULL; + + qh->qh_state = QH_STATE_IDLE; + qh->qh_next.qh = NULL; + + qh_completions(ehci, qh); + if (!list_empty(&qh->qtd_list) && + ehci->rh_state == EHCI_RH_RUNNING) + qh_link_async(ehci, qh); + disable_async(ehci); + } + ehci->async_unlinking = false; + + /* Start a new IAA cycle if any QHs are waiting for it */ + if (ehci->async_unlink) { + start_iaa_cycle(ehci, true); + if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) + goto restart; + } } /* makes sure the async qh will become idle */ /* caller must own ehci->lock */ -static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) +static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh) { - struct ehci_qh *prev; - -#ifdef DEBUG - assert_spin_locked(&ehci->lock); - if (ehci->async_unlink - || (qh->qh_state != QH_STATE_LINKED - && qh->qh_state != QH_STATE_UNLINK_WAIT) - ) - BUG (); -#endif - - qh->qh_state = QH_STATE_UNLINK; - ehci->async_unlink = qh; - if (!qh->unlink_next) - ehci->async_unlink_last = qh; - - prev = ehci->async; - while (prev->qh_next.qh != qh) - prev = prev->qh_next.qh; - - prev->hw->hw_next = qh->hw->hw_next; - prev->qh_next = qh->qh_next; - if (ehci->qh_scan_next == qh) - ehci->qh_scan_next = qh->qh_next.qh; - wmb (); - - /* If the controller isn't running, we don't have to wait for it */ - if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) { - /* if (unlikely (qh->unlink_next != 0)) - * this will recurse, probably not much - */ - end_unlink_async (ehci); + /* + * If the QH isn't linked then there's nothing we can do + * unless we were called during a giveback, in which case + * qh_completions() has to deal with it. + */ + if (qh->qh_state != QH_STATE_LINKED) { + if (qh->qh_state == QH_STATE_COMPLETING) + qh->needs_rescan = 1; return; } - ehci_writel(ehci, ehci->command | CMD_IAAD, &ehci->regs->command); - (void)ehci_readl(ehci, &ehci->regs->command); - ehci_enable_event(ehci, EHCI_HRTIMER_IAA_WATCHDOG, true); + single_unlink_async(ehci, qh); + start_iaa_cycle(ehci, false); } /*-------------------------------------------------------------------------*/ @@ -1269,11 +1298,11 @@ static void scan_async (struct ehci_hcd *ehci) * drops the lock. That's why ehci->qh_scan_next * always holds the next qh to scan; if the next qh * gets unlinked then ehci->qh_scan_next is adjusted - * in start_unlink_async(). + * in single_unlink_async(). */ temp = qh_completions(ehci, qh); if (qh->needs_rescan) - unlink_async(ehci, qh); + start_unlink_async(ehci, qh); qh->unlink_time = jiffies + EHCI_SHRINK_JIFFIES; if (temp != 0) goto rescan; diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index aab04206422..8ca5f152f5b 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -211,6 +211,7 @@ static void ehci_handle_controller_death(struct ehci_hcd *ehci) ehci_writel(ehci, 0, &ehci->regs->configured_flag); ehci_writel(ehci, 0, &ehci->regs->intr_enable); ehci_work(ehci); + end_unlink_async(ehci); /* Not in process context, so don't try to reset the controller */ } @@ -304,7 +305,7 @@ static void ehci_iaa_watchdog(struct ehci_hcd *ehci) * (a) SMP races against real IAA firing and retriggering, and * (b) clean HC shutdown, when IAA watchdog was pending. */ - if (ehci->async_unlink) { + if (ehci->async_iaa) { u32 cmd, status; /* If we get here, IAA is *REALLY* late. It's barely diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index d9414aae610..1a782775881 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -114,12 +114,14 @@ struct ehci_hcd { /* one per controller */ /* general schedule support */ unsigned scanning:1; bool intr_unlinking:1; + bool async_unlinking:1; /* async schedule support */ struct ehci_qh *async; struct ehci_qh *dummy; /* For AMD quirk use */ struct ehci_qh *async_unlink; struct ehci_qh *async_unlink_last; + struct ehci_qh *async_iaa; struct ehci_qh *qh_scan_next; unsigned async_count; /* async activity count */ -- cgit v1.2.3-70-g09d2 From 32830f207691176234b4c4dd17f0d7ab6d87d94b Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:22:53 -0400 Subject: USB: EHCI: use hrtimer for unlinking empty async QHs This patch (as1583) changes ehci-hcd to use an hrtimer event for unlinking empty (unused) async QHs instead of using a kernel timer. The check for empty QHs is moved to a new routine, where it doesn't require going through an entire scan of both the async and periodic schedules. And it can unlink multiple QHs at once, unlike the current code. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 16 +-------- drivers/usb/host/ehci-hub.c | 1 + drivers/usb/host/ehci-q.c | 78 +++++++++++++++++++++++++++++-------------- drivers/usb/host/ehci-timer.c | 2 ++ drivers/usb/host/ehci.h | 4 +-- 5 files changed, 59 insertions(+), 42 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 86e8ee169c6..74ffd20edff 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -94,8 +94,6 @@ static const char hcd_name [] = "ehci_hcd"; #define EHCI_TUNE_FLS 1 /* (medium) 512-frame schedule */ #define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ -#define EHCI_SHRINK_JIFFIES (DIV_ROUND_UP(HZ, 200) + 1) - /* 5-ms async qh unlink delay */ /* Initial IRQ latency: faster than hw default */ static int log2_irq_thresh = 0; // 0 to 6 @@ -130,15 +128,6 @@ MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us"); static void timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action) { - /* Don't override timeouts which shrink or (later) disable - * the async ring; just the I/O watchdog. Note that if a - * SHRINK were pending, OFF would never be requested. - */ - if (timer_pending(&ehci->watchdog) - && (BIT(TIMER_ASYNC_SHRINK) - & ehci->actions)) - return; - if (!test_and_set_bit(action, &ehci->actions)) { unsigned long t; @@ -148,10 +137,6 @@ timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action) return; t = EHCI_IO_JIFFIES; break; - /* case TIMER_ASYNC_SHRINK: */ - default: - t = EHCI_SHRINK_JIFFIES; - break; } mod_timer(&ehci->watchdog, t + jiffies); } @@ -307,6 +292,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci) /*-------------------------------------------------------------------------*/ static void end_unlink_async(struct ehci_hcd *ehci); +static void unlink_empty_async(struct ehci_hcd *ehci); static void ehci_work(struct ehci_hcd *ehci); static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh); static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index a3822700e49..5d84562e271 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -300,6 +300,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) ehci->rh_state = EHCI_RH_SUSPENDED; end_unlink_async(ehci); + unlink_empty_async(ehci); ehci_handle_intr_unlinks(ehci); end_free_itds(ehci); diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 181832921c5..bae93176782 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1205,7 +1205,7 @@ static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested) end_unlink_async(ehci); /* Otherwise start a new IAA cycle */ - } else { + } else if (likely(ehci->rh_state == EHCI_RH_RUNNING)) { /* Make sure the unlinks are all visible to the hardware */ wmb(); @@ -1253,6 +1253,39 @@ static void end_unlink_async(struct ehci_hcd *ehci) } } +static void unlink_empty_async(struct ehci_hcd *ehci) +{ + struct ehci_qh *qh, *next; + bool stopped = (ehci->rh_state < EHCI_RH_RUNNING); + bool check_unlinks_later = false; + + /* Unlink all the async QHs that have been empty for a timer cycle */ + next = ehci->async->qh_next.qh; + while (next) { + qh = next; + next = qh->qh_next.qh; + + if (list_empty(&qh->qtd_list) && + qh->qh_state == QH_STATE_LINKED) { + if (!stopped && qh->unlink_cycle == + ehci->async_unlink_cycle) + check_unlinks_later = true; + else + single_unlink_async(ehci, qh); + } + } + + /* Start a new IAA cycle if any QHs are waiting for it */ + if (ehci->async_unlink) + start_iaa_cycle(ehci, false); + + /* QHs that haven't been empty for long enough will be handled later */ + if (check_unlinks_later) { + ehci_enable_event(ehci, EHCI_HRTIMER_ASYNC_UNLINKS, true); + ++ehci->async_unlink_cycle; + } +} + /* makes sure the async qh will become idle */ /* caller must own ehci->lock */ @@ -1277,12 +1310,8 @@ static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh) static void scan_async (struct ehci_hcd *ehci) { - bool stopped; struct ehci_qh *qh; - enum ehci_timer_action action = TIMER_IO_WATCHDOG; - - timer_action_done (ehci, TIMER_ASYNC_SHRINK); - stopped = (ehci->rh_state < EHCI_RH_RUNNING); + bool check_unlinks_later = false; ehci->qh_scan_next = ehci->async->qh_next.qh; while (ehci->qh_scan_next) { @@ -1301,28 +1330,27 @@ static void scan_async (struct ehci_hcd *ehci) * in single_unlink_async(). */ temp = qh_completions(ehci, qh); - if (qh->needs_rescan) + if (qh->needs_rescan) { start_unlink_async(ehci, qh); - qh->unlink_time = jiffies + EHCI_SHRINK_JIFFIES; - if (temp != 0) + } else if (list_empty(&qh->qtd_list) + && qh->qh_state == QH_STATE_LINKED) { + qh->unlink_cycle = ehci->async_unlink_cycle; + check_unlinks_later = true; + } else if (temp != 0) goto rescan; } + } - /* unlink idle entries, reducing DMA usage as well - * as HCD schedule-scanning costs. delay for any qh - * we just scanned, there's a not-unusual case that it - * doesn't stay idle for long. - * (plus, avoids some kind of re-activation race.) - */ - if (list_empty(&qh->qtd_list) - && qh->qh_state == QH_STATE_LINKED) { - if (!ehci->async_unlink && (stopped || - time_after_eq(jiffies, qh->unlink_time))) - start_unlink_async(ehci, qh); - else - action = TIMER_ASYNC_SHRINK; - } + /* + * Unlink empty entries, reducing DMA usage as well + * as HCD schedule-scanning costs. Delay for any qh + * we just scanned, there's a not-unusual case that it + * doesn't stay idle for long. + */ + if (check_unlinks_later && ehci->rh_state == EHCI_RH_RUNNING && + !(ehci->enabled_hrtimer_events & + BIT(EHCI_HRTIMER_ASYNC_UNLINKS))) { + ehci_enable_event(ehci, EHCI_HRTIMER_ASYNC_UNLINKS, true); + ++ehci->async_unlink_cycle; } - if (action == TIMER_ASYNC_SHRINK) - timer_action (ehci, TIMER_ASYNC_SHRINK); } diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index 8ca5f152f5b..a823290b513 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -72,6 +72,7 @@ static unsigned event_delays_ns[] = { 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_DEAD */ 1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */ 2 * NSEC_PER_MSEC, /* EHCI_HRTIMER_FREE_ITDS */ + 6 * NSEC_PER_MSEC, /* EHCI_HRTIMER_ASYNC_UNLINKS */ 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_IAA_WATCHDOG */ 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */ @@ -347,6 +348,7 @@ static void (*event_handlers[])(struct ehci_hcd *) = { ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */ ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */ end_free_itds, /* EHCI_HRTIMER_FREE_ITDS */ + unlink_empty_async, /* EHCI_HRTIMER_ASYNC_UNLINKS */ ehci_iaa_watchdog, /* EHCI_HRTIMER_IAA_WATCHDOG */ ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */ diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 1a782775881..303c36cc99c 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -84,6 +84,7 @@ enum ehci_hrtimer_event { EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ EHCI_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */ + EHCI_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */ EHCI_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */ EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ @@ -123,6 +124,7 @@ struct ehci_hcd { /* one per controller */ struct ehci_qh *async_unlink_last; struct ehci_qh *async_iaa; struct ehci_qh *qh_scan_next; + unsigned async_unlink_cycle; unsigned async_count; /* async activity count */ /* periodic schedule support */ @@ -232,7 +234,6 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci) enum ehci_timer_action { TIMER_IO_WATCHDOG, - TIMER_ASYNC_SHRINK, }; static inline void @@ -382,7 +383,6 @@ struct ehci_qh { struct ehci_qtd *dummy; struct ehci_qh *unlink_next; /* next on unlink list */ - unsigned long unlink_time; unsigned unlink_cycle; unsigned stamp; -- cgit v1.2.3-70-g09d2 From 361aabf395e4a23cf554cf4ec0c0c6963b8beb01 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:22:57 -0400 Subject: USB: EHCI: don't lose events during a scan This patch (as1584) fixes a minor bug that has been present in ehci-hcd since the beginning. Scanning the schedules for URB completions is single-threaded. If a completion interrupt occurs while an URB is being given back, the interrupt handler realizes that a scan is in progress on another CPU and avoids starting a new one. This means that completion events can be lost. If an URB completes after it has been scanned but while a scan is still in progress, the driver won't notice and won't rescan the completed URB. The patch fixes the problem by adding a new flag to indicate that another scan is needed after the current scan is done. The flag gets set whenever a completion interrupt occurs while a scan is in progress. The rescan will see the completion, thus preventing it from getting lost. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 13 ++++++++++--- drivers/usb/host/ehci.h | 3 ++- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 74ffd20edff..f6cf1d17810 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -400,14 +400,21 @@ static void ehci_work (struct ehci_hcd *ehci) * it reports urb completions. this flag guards against bogus * attempts at re-entrant schedule scanning. */ - if (ehci->scanning) + if (ehci->scanning) { + ehci->need_rescan = true; return; - ehci->scanning = 1; + } + ehci->scanning = true; + + rescan: + ehci->need_rescan = false; if (ehci->async_count) scan_async(ehci); if (ehci->next_uframe != -1) scan_periodic (ehci); - ehci->scanning = 0; + if (ehci->need_rescan) + goto rescan; + ehci->scanning = false; /* the IO watchdog guards against hardware or driver bugs that * misplace IRQs, and should let us run completely without IRQs. diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 303c36cc99c..c462d52ac57 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -113,7 +113,8 @@ struct ehci_hcd { /* one per controller */ enum ehci_rh_state rh_state; /* general schedule support */ - unsigned scanning:1; + bool scanning:1; + bool need_rescan:1; bool intr_unlinking:1; bool async_unlinking:1; -- cgit v1.2.3-70-g09d2 From 569b394f53f0abd177cc665c9b4ace89e3f4c7fb Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:23:00 -0400 Subject: USB: EHCI: always scan each interrupt QH This patch (as1585) fixes a bug in ehci-hcd's scheme for scanning interrupt QHs. Currently a single routine takes care of scanning everything on the periodic schedule. Whenever an interrupt occurs, it scans all isochronous and interrupt URBs scheduled for frames that have elapsed since the last scan. This has two disadvantages. The first is relatively minor: An interrupt QH is likely to end up getting scanned multiple times, particularly if the last scan was not fairly recent. (The current code avoids this by maintaining a periodic_stamp in each interrupt QH.) The second is more serious. The periodic schedule wraps around. If the last scan occurred during frame N, and the next scan occurs when the schedule has gone through an entire cycle and is back at frame N, the scanning code won't look at any frames other than N. Consequently it won't see any QHs that completed during frame N-1 or earlier. The patch replaces the entire frame-based approach for scanning interrupt QHs with a new routine using a list-based approach, the same as for async QHs. This has a slight disadvantage, because it means that all interrupt QHs have to be scanned every time. But it is more robust than the current approach. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 7 +++- drivers/usb/host/ehci-q.c | 3 +- drivers/usb/host/ehci-sched.c | 96 ++++++++++++++++++++++++++----------------- drivers/usb/host/ehci-timer.c | 7 +--- drivers/usb/host/ehci.h | 8 ++-- 5 files changed, 71 insertions(+), 50 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index f6cf1d17810..c13dad8a850 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -410,8 +410,10 @@ static void ehci_work (struct ehci_hcd *ehci) ehci->need_rescan = false; if (ehci->async_count) scan_async(ehci); - if (ehci->next_uframe != -1) - scan_periodic (ehci); + if (ehci->intr_count > 0) + scan_intr(ehci); + if (ehci->isoc_count > 0) + scan_isoc(ehci); if (ehci->need_rescan) goto rescan; ehci->scanning = false; @@ -509,6 +511,7 @@ static int ehci_init(struct usb_hcd *hcd) * periodic_size can shrink by USBCMD update if hcc_params allows. */ ehci->periodic_size = DEFAULT_I_TDPS; + INIT_LIST_HEAD(&ehci->intr_qh_list); INIT_LIST_HEAD(&ehci->cached_itd_list); INIT_LIST_HEAD(&ehci->cached_sitd_list); diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index bae93176782..c9c7f7b3b7d 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -322,7 +322,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) * * It's a bug for qh->qh_state to be anything other than * QH_STATE_IDLE, unless our caller is scan_async() or - * scan_periodic(). + * scan_intr(). */ state = qh->qh_state; qh->qh_state = QH_STATE_COMPLETING; @@ -832,7 +832,6 @@ qh_make ( is_input, 0, hb_mult(maxp) * max_packet(maxp))); qh->start = NO_FRAME; - qh->stamp = ehci->periodic_stamp; if (urb->dev->speed == USB_SPEED_HIGH) { qh->c_usecs = 0; diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 11b2f21d7ac..263b542985c 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -569,7 +569,10 @@ static void qh_link_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) ? ((qh->usecs + qh->c_usecs) / qh->period) : (qh->usecs * 8); + list_add(&qh->intr_node, &ehci->intr_qh_list); + /* maybe enable periodic schedule processing */ + ++ehci->intr_count; enable_periodic(ehci); } @@ -614,6 +617,11 @@ static void qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) /* qh->qh_next still "live" to HC */ qh->qh_state = QH_STATE_UNLINK; qh->qh_next.ptr = NULL; + + if (ehci->qh_scan_next == qh) + ehci->qh_scan_next = list_entry(qh->intr_node.next, + struct ehci_qh, intr_node); + list_del(&qh->intr_node); } static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) @@ -683,6 +691,7 @@ static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) } /* maybe turn off periodic schedule */ + --ehci->intr_count; disable_periodic(ehci); } @@ -920,6 +929,35 @@ done_not_linked: return status; } +static void scan_intr(struct ehci_hcd *ehci) +{ + struct ehci_qh *qh; + + list_for_each_entry_safe(qh, ehci->qh_scan_next, &ehci->intr_qh_list, + intr_node) { + rescan: + /* clean any finished work for this qh */ + if (!list_empty(&qh->qtd_list)) { + int temp; + + /* + * Unlinks could happen here; completion reporting + * drops the lock. That's why ehci->qh_scan_next + * always holds the next qh to scan; if the next qh + * gets unlinked then ehci->qh_scan_next is adjusted + * in qh_unlink_periodic(). + */ + temp = qh_completions(ehci, qh); + if (unlikely(qh->needs_rescan || + (list_empty(&qh->qtd_list) && + qh->qh_state == QH_STATE_LINKED))) + start_unlink_intr(ehci, qh); + else if (temp != 0) + goto rescan; + } + } +} + /*-------------------------------------------------------------------------*/ /* ehci_iso_stream ops work with both ITD and SITD */ @@ -1450,6 +1488,10 @@ iso_stream_schedule ( urb->start_frame = stream->next_uframe; if (!stream->highspeed) urb->start_frame >>= 3; + + /* Make sure scan_isoc() sees these */ + if (ehci->isoc_count == 0) + ehci->next_uframe = now; return 0; fail: @@ -1608,6 +1650,7 @@ static void itd_link_urb( urb->hcpriv = NULL; timer_action (ehci, TIMER_IO_WATCHDOG); + ++ehci->isoc_count; enable_periodic(ehci); } @@ -1688,9 +1731,11 @@ itd_complete ( ehci_urb_done(ehci, urb, 0); retval = true; urb = NULL; + + --ehci->isoc_count; disable_periodic(ehci); - ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; + ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) { if (ehci->amd_pll_fix == 1) usb_amd_quirk_pll_enable(); @@ -2008,6 +2053,7 @@ static void sitd_link_urb( urb->hcpriv = NULL; timer_action (ehci, TIMER_IO_WATCHDOG); + ++ehci->isoc_count; enable_periodic(ehci); } @@ -2074,9 +2120,11 @@ sitd_complete ( ehci_urb_done(ehci, urb, 0); retval = true; urb = NULL; + + --ehci->isoc_count; disable_periodic(ehci); - ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; + ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) { if (ehci->amd_pll_fix == 1) usb_amd_quirk_pll_enable(); @@ -2165,8 +2213,7 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, /*-------------------------------------------------------------------------*/ -static void -scan_periodic (struct ehci_hcd *ehci) +static void scan_isoc(struct ehci_hcd *ehci) { unsigned now_uframe, frame, clock, clock_frame, mod; unsigned modified; @@ -2189,7 +2236,6 @@ scan_periodic (struct ehci_hcd *ehci) ehci->clock_frame = clock_frame; clock &= mod - 1; clock_frame = clock >> 3; - ++ehci->periodic_stamp; for (;;) { union ehci_shadow q, *q_p; @@ -2208,36 +2254,10 @@ restart: while (q.ptr != NULL) { unsigned uf; - union ehci_shadow temp; int live; live = (ehci->rh_state >= EHCI_RH_RUNNING); switch (hc32_to_cpu(ehci, type)) { - case Q_TYPE_QH: - /* handle any completions */ - temp.qh = q.qh; - type = Q_NEXT_TYPE(ehci, q.qh->hw->hw_next); - q = q.qh->qh_next; - if (temp.qh->stamp != ehci->periodic_stamp) { - modified = qh_completions(ehci, temp.qh); - if (!modified) - temp.qh->stamp = ehci->periodic_stamp; - if (unlikely(list_empty(&temp.qh->qtd_list) || - temp.qh->needs_rescan)) - start_unlink_intr(ehci, temp.qh); - } - break; - case Q_TYPE_FSTN: - /* for "save place" FSTNs, look at QH entries - * in the previous frame for completions. - */ - if (q.fstn->hw_prev != EHCI_LIST_END(ehci)) { - ehci_dbg(ehci, - "ignoring completions from FSTNs\n"); - } - type = Q_NEXT_TYPE(ehci, q.fstn->hw_next); - q = q.fstn->fstn_next; - break; case Q_TYPE_ITD: /* If this ITD is still active, leave it for * later processing ... check the next entry. @@ -2319,12 +2339,17 @@ restart: ehci_dbg(ehci, "corrupt type %d frame %d shadow %p\n", type, frame, q.ptr); // BUG (); + /* FALL THROUGH */ + case Q_TYPE_QH: + case Q_TYPE_FSTN: + /* End of the iTDs and siTDs */ q.ptr = NULL; + break; } /* assume completion callbacks modify the queue */ if (unlikely (modified)) { - if (likely(ehci->periodic_count > 0)) + if (likely(ehci->isoc_count > 0)) goto restart; /* short-circuit this scan */ now_uframe = clock; @@ -2353,7 +2378,7 @@ restart: unsigned now; if (ehci->rh_state < EHCI_RH_RUNNING - || ehci->periodic_count == 0) + || ehci->isoc_count == 0) break; ehci->next_uframe = now_uframe; now = ehci_read_frame_index(ehci) & (mod - 1); @@ -2363,10 +2388,7 @@ restart: /* rescan the rest of this frame, then ... */ clock = now; clock_frame = clock >> 3; - if (ehci->clock_frame != clock_frame) { - ehci->clock_frame = clock_frame; - ++ehci->periodic_stamp; - } + ehci->clock_frame = clock_frame; } else { now_uframe++; now_uframe &= mod - 1; diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index a823290b513..0e28bae78d1 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -168,13 +168,8 @@ static void ehci_poll_PSS(struct ehci_hcd *ehci) /* The status is up-to-date; restart or stop the schedule as needed */ if (want == 0) { /* Stopped */ - if (ehci->periodic_count > 0) { - - /* make sure ehci_work scans these */ - ehci->next_uframe = ehci_read_frame_index(ehci) - & ((ehci->periodic_size << 3) - 1); + if (ehci->periodic_count > 0) ehci_set_command_bit(ehci, CMD_PSE); - } } else { /* Running */ if (ehci->periodic_count == 0) { diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index c462d52ac57..08637183aad 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -117,6 +117,7 @@ struct ehci_hcd { /* one per controller */ bool need_rescan:1; bool intr_unlinking:1; bool async_unlinking:1; + struct ehci_qh *qh_scan_next; /* async schedule support */ struct ehci_qh *async; @@ -124,7 +125,6 @@ struct ehci_hcd { /* one per controller */ struct ehci_qh *async_unlink; struct ehci_qh *async_unlink_last; struct ehci_qh *async_iaa; - struct ehci_qh *qh_scan_next; unsigned async_unlink_cycle; unsigned async_count; /* async activity count */ @@ -133,6 +133,7 @@ struct ehci_hcd { /* one per controller */ unsigned periodic_size; __hc32 *periodic; /* hw periodic table */ dma_addr_t periodic_dma; + struct list_head intr_qh_list; unsigned i_thresh; /* uframes HC might cache */ union ehci_shadow *pshadow; /* mirror hw periodic table */ @@ -140,6 +141,8 @@ struct ehci_hcd { /* one per controller */ struct ehci_qh *intr_unlink_last; unsigned intr_unlink_cycle; int next_uframe; /* scan periodic, start here */ + unsigned intr_count; /* intr activity count */ + unsigned isoc_count; /* isoc activity count */ unsigned periodic_count; /* periodic activity count */ unsigned uframe_periodic_max; /* max periodic time per uframe */ @@ -176,7 +179,6 @@ struct ehci_hcd { /* one per controller */ struct timer_list watchdog; unsigned long actions; - unsigned periodic_stamp; unsigned random_frame; unsigned long next_statechange; ktime_t last_periodic_enable; @@ -381,11 +383,11 @@ struct ehci_qh { dma_addr_t qh_dma; /* address of qh */ union ehci_shadow qh_next; /* ptr to qh; or periodic */ struct list_head qtd_list; /* sw qtd list */ + struct list_head intr_node; /* list of intr QHs */ struct ehci_qtd *dummy; struct ehci_qh *unlink_next; /* next on unlink list */ unsigned unlink_cycle; - unsigned stamp; u8 needs_rescan; /* Dequeue during giveback */ u8 qh_state; -- cgit v1.2.3-70-g09d2 From 18aafe64d75d0e27dae206cacf4171e4e485d285 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:23:04 -0400 Subject: USB: EHCI: use hrtimer for the I/O watchdog This patch (as1586) replaces the kernel timer used by ehci-hcd as an I/O watchdog with an hrtimer event. Unlike in the current code, the watchdog event is now always enabled whenever any isochronous URBs are active. This will prevent bugs caused by the periodic schedule wrapping around with no completion interrupts; the watchdog handler is guaranteed to scan the isochronous transfers at least once during each iteration of the schedule. The extra overhead will be negligible: one timer interrupt every 100 ms. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 47 +------------------------------------------ drivers/usb/host/ehci-hub.c | 5 ----- drivers/usb/host/ehci-q.c | 1 + drivers/usb/host/ehci-sched.c | 3 +-- drivers/usb/host/ehci-timer.c | 21 +++++++++++++++++++ drivers/usb/host/ehci.h | 13 +----------- 6 files changed, 25 insertions(+), 65 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index c13dad8a850..9f26080889f 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -93,8 +93,6 @@ static const char hcd_name [] = "ehci_hcd"; */ #define EHCI_TUNE_FLS 1 /* (medium) 512-frame schedule */ -#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ - /* Initial IRQ latency: faster than hw default */ static int log2_irq_thresh = 0; // 0 to 6 module_param (log2_irq_thresh, int, S_IRUGO); @@ -125,25 +123,6 @@ MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us"); /*-------------------------------------------------------------------------*/ -static void -timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action) -{ - if (!test_and_set_bit(action, &ehci->actions)) { - unsigned long t; - - switch (action) { - case TIMER_IO_WATCHDOG: - if (!ehci->need_io_watchdog) - return; - t = EHCI_IO_JIFFIES; - break; - } - mod_timer(&ehci->watchdog, t + jiffies); - } -} - -/*-------------------------------------------------------------------------*/ - /* * handshake - spin reading hc until handshake completes or fails * @ptr: address of hc register to be read @@ -307,19 +286,6 @@ static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh); /*-------------------------------------------------------------------------*/ -static void ehci_watchdog(unsigned long param) -{ - struct ehci_hcd *ehci = (struct ehci_hcd *) param; - unsigned long flags; - - spin_lock_irqsave(&ehci->lock, flags); - - /* ehci could run by timer, without IRQs ... */ - ehci_work (ehci); - - spin_unlock_irqrestore (&ehci->lock, flags); -} - /* On some systems, leaving remote wakeup enabled prevents system shutdown. * The firmware seems to think that powering off is a wakeup event! * This routine turns off remote wakeup and everything else, on all ports. @@ -357,8 +323,6 @@ static void ehci_shutdown(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); - del_timer_sync(&ehci->watchdog); - spin_lock_irq(&ehci->lock); ehci->rh_state = EHCI_RH_STOPPING; ehci_silence_controller(ehci); @@ -394,8 +358,6 @@ static void ehci_port_power (struct ehci_hcd *ehci, int is_on) */ static void ehci_work (struct ehci_hcd *ehci) { - timer_action_done (ehci, TIMER_IO_WATCHDOG); - /* another CPU may drop ehci->lock during a schedule scan while * it reports urb completions. this flag guards against bogus * attempts at re-entrant schedule scanning. @@ -422,10 +384,7 @@ static void ehci_work (struct ehci_hcd *ehci) * misplace IRQs, and should let us run completely without IRQs. * such lossage has been observed on both VT6202 and VT8235. */ - if (ehci->rh_state == EHCI_RH_RUNNING && - (ehci->async->qh_next.ptr != NULL || - ehci->periodic_count != 0)) - timer_action (ehci, TIMER_IO_WATCHDOG); + turn_on_io_watchdog(ehci); } /* @@ -438,7 +397,6 @@ static void ehci_stop (struct usb_hcd *hcd) ehci_dbg (ehci, "stop\n"); /* no more interrupts ... */ - del_timer_sync (&ehci->watchdog); spin_lock_irq(&ehci->lock); ehci->enabled_hrtimer_events = 0; @@ -490,9 +448,6 @@ static int ehci_init(struct usb_hcd *hcd) * keep io watchdog by default, those good HCDs could turn off it later */ ehci->need_io_watchdog = 1; - init_timer(&ehci->watchdog); - ehci->watchdog.function = ehci_watchdog; - ehci->watchdog.data = (unsigned long) ehci; hrtimer_init(&ehci->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); ehci->hrtimer.function = ehci_hrtimer_func; diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 5d84562e271..05490d387fd 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -208,7 +208,6 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) if (time_before (jiffies, ehci->next_statechange)) msleep(5); - del_timer_sync(&ehci->watchdog); spin_lock_irq (&ehci->lock); @@ -316,10 +315,6 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT; spin_unlock_irq (&ehci->lock); - /* ehci_work() may have re-enabled the watchdog timer, which we do not - * want, and so we must delete any pending watchdog timer events. - */ - del_timer_sync(&ehci->watchdog); hrtimer_cancel(&ehci->hrtimer); return 0; } diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index c9c7f7b3b7d..9bc39ca460c 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -970,6 +970,7 @@ static void enable_async(struct ehci_hcd *ehci) /* Don't start the schedule until ASS is 0 */ ehci_poll_ASS(ehci); + turn_on_io_watchdog(ehci); } static void disable_async(struct ehci_hcd *ehci) diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 263b542985c..26ce8fef0e5 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -489,6 +489,7 @@ static void enable_periodic(struct ehci_hcd *ehci) /* Don't start the schedule until PSS is 0 */ ehci_poll_PSS(ehci); + turn_on_io_watchdog(ehci); } static void disable_periodic(struct ehci_hcd *ehci) @@ -1649,7 +1650,6 @@ static void itd_link_urb( iso_sched_free (stream, iso_sched); urb->hcpriv = NULL; - timer_action (ehci, TIMER_IO_WATCHDOG); ++ehci->isoc_count; enable_periodic(ehci); } @@ -2052,7 +2052,6 @@ static void sitd_link_urb( iso_sched_free (stream, sched); urb->hcpriv = NULL; - timer_action (ehci, TIMER_IO_WATCHDOG); ++ehci->isoc_count; enable_periodic(ehci); } diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index 0e28bae78d1..eb896a2c8f2 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -76,6 +76,7 @@ static unsigned event_delays_ns[] = { 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_IAA_WATCHDOG */ 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */ + 100 * NSEC_PER_MSEC, /* EHCI_HRTIMER_IO_WATCHDOG */ }; /* Enable a pending hrtimer event */ @@ -332,6 +333,25 @@ static void ehci_iaa_watchdog(struct ehci_hcd *ehci) } +/* Enable the I/O watchdog, if appropriate */ +static void turn_on_io_watchdog(struct ehci_hcd *ehci) +{ + /* Not needed if the controller isn't running or it's already enabled */ + if (ehci->rh_state != EHCI_RH_RUNNING || + (ehci->enabled_hrtimer_events & + BIT(EHCI_HRTIMER_IO_WATCHDOG))) + return; + + /* + * Isochronous transfers always need the watchdog. + * For other sorts we use it only if the flag is set. + */ + if (ehci->isoc_count > 0 || (ehci->need_io_watchdog && + ehci->async_count + ehci->intr_count > 0)) + ehci_enable_event(ehci, EHCI_HRTIMER_IO_WATCHDOG, true); +} + + /* * Handler functions for the hrtimer event types. * Keep this array in the same order as the event types indexed by @@ -347,6 +367,7 @@ static void (*event_handlers[])(struct ehci_hcd *) = { ehci_iaa_watchdog, /* EHCI_HRTIMER_IAA_WATCHDOG */ ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */ + ehci_work, /* EHCI_HRTIMER_IO_WATCHDOG */ }; static enum hrtimer_restart ehci_hrtimer_func(struct hrtimer *t) diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 08637183aad..254f414bd0b 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -88,6 +88,7 @@ enum ehci_hrtimer_event { EHCI_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */ EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ + EHCI_HRTIMER_IO_WATCHDOG, /* Check for missing IRQs */ EHCI_HRTIMER_NUM_EVENTS /* Must come last */ }; #define EHCI_HRTIMER_NO_EVENT 99 @@ -177,8 +178,6 @@ struct ehci_hcd { /* one per controller */ struct dma_pool *itd_pool; /* itd per iso urb */ struct dma_pool *sitd_pool; /* sitd per split iso urb */ - struct timer_list watchdog; - unsigned long actions; unsigned random_frame; unsigned long next_statechange; ktime_t last_periodic_enable; @@ -235,16 +234,6 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci) return container_of ((void *) ehci, struct usb_hcd, hcd_priv); } -enum ehci_timer_action { - TIMER_IO_WATCHDOG, -}; - -static inline void -timer_action_done (struct ehci_hcd *ehci, enum ehci_timer_action action) -{ - clear_bit (action, &ehci->actions); -} - /*-------------------------------------------------------------------------*/ #include -- cgit v1.2.3-70-g09d2 From f42890782241a60d107f23d08089a4a12b507a11 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:23:07 -0400 Subject: USB: EHCI: simplify isochronous scanning This patch (as1587) simplifies ehci-hcd's scan_isoc() routine by eliminating some local variables, declaring boolean-valued values as bool rather than unsigned, changing variable names to make more sense, and so on. The logic at the end of the routine is cut down significantly. The scanning doesn't have to catch up all the way to where the hardware is; it merely has to catch up to where the hardware was when the last interrupt occurred. If the hardware has made more progress since then and issued another interrupt, a rescan will catch up to it. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 3 -- drivers/usb/host/ehci-sched.c | 114 +++++++++++------------------------------- drivers/usb/host/ehci.h | 6 +-- 3 files changed, 32 insertions(+), 91 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 9f26080889f..340c9c4894b 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -488,9 +488,6 @@ static int ehci_init(struct usb_hcd *hcd) else // N microframes cached ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params); - ehci->next_uframe = -1; - ehci->clock_frame = -1; - /* * dedicate a qh for the async ring head, since we couldn't unlink * a 'real' qh without stopping the async schedule [4.8]. use it diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 26ce8fef0e5..7cf3da7babf 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -497,8 +497,6 @@ static void disable_periodic(struct ehci_hcd *ehci) if (--ehci->periodic_count) return; - ehci->next_uframe = -1; /* the periodic schedule is empty */ - /* Don't turn off the schedule until PSS is 1 */ ehci_poll_PSS(ehci); } @@ -1220,7 +1218,7 @@ itd_urb_transaction ( if (likely(!list_empty(&stream->free_list))) { itd = list_first_entry(&stream->free_list, struct ehci_itd, itd_list); - if (itd->frame == ehci->clock_frame) + if (itd->frame == ehci->now_frame) goto alloc_itd; list_del (&itd->itd_list); itd_dma = itd->itd_dma; @@ -1492,7 +1490,7 @@ iso_stream_schedule ( /* Make sure scan_isoc() sees these */ if (ehci->isoc_count == 0) - ehci->next_uframe = now; + ehci->next_frame = now >> 3; return 0; fail: @@ -1666,11 +1664,8 @@ static void itd_link_urb( * (b) only this endpoint's completions submit URBs. It seems some silicon * corrupts things if you reuse completed descriptors very quickly... */ -static unsigned -itd_complete ( - struct ehci_hcd *ehci, - struct ehci_itd *itd -) { +static bool itd_complete(struct ehci_hcd *ehci, struct ehci_itd *itd) +{ struct urb *urb = itd->urb; struct usb_iso_packet_descriptor *desc; u32 t; @@ -1678,7 +1673,7 @@ itd_complete ( int urb_index = -1; struct ehci_iso_stream *stream = itd->stream; struct usb_device *dev; - unsigned retval = false; + bool retval = false; /* for each uframe with a packet */ for (uframe = 0; uframe < 8; uframe++) { @@ -1917,7 +1912,7 @@ sitd_urb_transaction ( if (likely(!list_empty(&stream->free_list))) { sitd = list_first_entry(&stream->free_list, struct ehci_sitd, sitd_list); - if (sitd->frame == ehci->clock_frame) + if (sitd->frame == ehci->now_frame) goto alloc_sitd; list_del (&sitd->sitd_list); sitd_dma = sitd->sitd_dma; @@ -2071,18 +2066,15 @@ static void sitd_link_urb( * (b) only this endpoint's completions submit URBs. It seems some silicon * corrupts things if you reuse completed descriptors very quickly... */ -static unsigned -sitd_complete ( - struct ehci_hcd *ehci, - struct ehci_sitd *sitd -) { +static bool sitd_complete(struct ehci_hcd *ehci, struct ehci_sitd *sitd) +{ struct urb *urb = sitd->urb; struct usb_iso_packet_descriptor *desc; u32 t; int urb_index = -1; struct ehci_iso_stream *stream = sitd->stream; struct usb_device *dev; - unsigned retval = false; + bool retval = false; urb_index = sitd->index; desc = &urb->iso_frame_desc [urb_index]; @@ -2214,34 +2206,29 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, static void scan_isoc(struct ehci_hcd *ehci) { - unsigned now_uframe, frame, clock, clock_frame, mod; - unsigned modified; - - mod = ehci->periodic_size << 3; + unsigned uf, now_frame, frame; + unsigned fmask = ehci->periodic_size - 1; + bool modified, live; /* * When running, scan from last scan point up to "now" * else clean up by scanning everything that's left. * Touches as few pages as possible: cache-friendly. */ - now_uframe = ehci->next_uframe; if (ehci->rh_state >= EHCI_RH_RUNNING) { - clock = ehci_read_frame_index(ehci); - clock_frame = (clock >> 3) & (ehci->periodic_size - 1); + uf = ehci_read_frame_index(ehci); + now_frame = (uf >> 3) & fmask; + live = true; } else { - clock = now_uframe + mod - 1; - clock_frame = -1; + now_frame = (ehci->next_frame - 1) & fmask; + live = false; } - ehci->clock_frame = clock_frame; - clock &= mod - 1; - clock_frame = clock >> 3; + ehci->now_frame = now_frame; + frame = ehci->next_frame; for (;;) { union ehci_shadow q, *q_p; __hc32 type, *hw_p; - unsigned incomplete = false; - - frame = now_uframe >> 3; restart: /* scan each element in frame's queue for completions */ @@ -2249,13 +2236,9 @@ restart: hw_p = &ehci->periodic [frame]; q.ptr = q_p->ptr; type = Q_NEXT_TYPE(ehci, *hw_p); - modified = 0; + modified = false; while (q.ptr != NULL) { - unsigned uf; - int live; - - live = (ehci->rh_state >= EHCI_RH_RUNNING); switch (hc32_to_cpu(ehci, type)) { case Q_TYPE_ITD: /* If this ITD is still active, leave it for @@ -2263,7 +2246,7 @@ restart: * No need to check for activity unless the * frame is current. */ - if (frame == clock_frame && live) { + if (frame == now_frame && live) { rmb(); for (uf = 0; uf < 8; uf++) { if (q.itd->hw_transaction[uf] & @@ -2271,7 +2254,6 @@ restart: break; } if (uf < 8) { - incomplete = true; q_p = &q.itd->itd_next; hw_p = &q.itd->hw_next; type = Q_NEXT_TYPE(ehci, @@ -2303,14 +2285,12 @@ restart: * No need to check for activity unless the * frame is current. */ - if (((frame == clock_frame) || - (((frame + 1) & (ehci->periodic_size - 1)) - == clock_frame)) + if (((frame == now_frame) || + (((frame + 1) & fmask) == now_frame)) && live && (q.sitd->hw_results & SITD_ACTIVE(ehci))) { - incomplete = true; q_p = &q.sitd->sitd_next; hw_p = &q.sitd->hw_next; type = Q_NEXT_TYPE(ehci, @@ -2347,50 +2327,14 @@ restart: } /* assume completion callbacks modify the queue */ - if (unlikely (modified)) { - if (likely(ehci->isoc_count > 0)) - goto restart; - /* short-circuit this scan */ - now_uframe = clock; - break; - } + if (unlikely(modified && ehci->isoc_count > 0)) + goto restart; } - /* If we can tell we caught up to the hardware, stop now. - * We can't advance our scan without collecting the ISO - * transfers that are still pending in this frame. - */ - if (incomplete && ehci->rh_state >= EHCI_RH_RUNNING) { - ehci->next_uframe = now_uframe; + /* Stop when we have reached the current frame */ + if (frame == now_frame) break; - } - - // FIXME: this assumes we won't get lapped when - // latencies climb; that should be rare, but... - // detect it, and just go all the way around. - // FLR might help detect this case, so long as latencies - // don't exceed periodic_size msec (default 1.024 sec). - - // FIXME: likewise assumes HC doesn't halt mid-scan - - if (now_uframe == clock) { - unsigned now; - - if (ehci->rh_state < EHCI_RH_RUNNING - || ehci->isoc_count == 0) - break; - ehci->next_uframe = now_uframe; - now = ehci_read_frame_index(ehci) & (mod - 1); - if (now_uframe == now) - break; - - /* rescan the rest of this frame, then ... */ - clock = now; - clock_frame = clock >> 3; - ehci->clock_frame = clock_frame; - } else { - now_uframe++; - now_uframe &= mod - 1; - } + frame = (frame + 1) & fmask; } + ehci->next_frame = now_frame; } diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 254f414bd0b..7de58fe52d5 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -141,19 +141,19 @@ struct ehci_hcd { /* one per controller */ struct ehci_qh *intr_unlink; struct ehci_qh *intr_unlink_last; unsigned intr_unlink_cycle; - int next_uframe; /* scan periodic, start here */ + unsigned now_frame; /* frame from HC hardware */ + unsigned next_frame; /* scan periodic, start here */ unsigned intr_count; /* intr activity count */ unsigned isoc_count; /* isoc activity count */ unsigned periodic_count; /* periodic activity count */ unsigned uframe_periodic_max; /* max periodic time per uframe */ - /* list of itds & sitds completed while clock_frame was still active */ + /* list of itds & sitds completed while now_frame was still active */ struct list_head cached_itd_list; struct ehci_itd *last_itd_to_free; struct list_head cached_sitd_list; struct ehci_sitd *last_sitd_to_free; - unsigned clock_frame; /* per root hub port */ unsigned long reset_done [EHCI_MAX_ROOT_PORTS]; -- cgit v1.2.3-70-g09d2 From c4f3476436f7452b97c8accb5dd7d53219a11a3f Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:23:10 -0400 Subject: USB: EHCI: fix up locking This patch (as1588) adjusts the locking in ehci-hcd's various halt, shutdown, and suspend/resume pathways. We want to hold the spinlock while writing device registers and accessing shared variables, but not while polling in a loop. In addition, there's no need to call ehci_work() at times when no URBs can be active, i.e., in ehci_stop() and ehci_bus_suspend(). Finally, ehci_adjust_port_wakeup_flags() is called only in situations where interrupts are enabled; therefore it can use spin_lock_irq rather than spin_lock_irqsave. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 48 +++++++++++++++++++++++++++++-------------- drivers/usb/host/ehci-hub.c | 41 +++++++++++++++++++++++++----------- drivers/usb/host/ehci-tegra.c | 5 ++--- 3 files changed, 64 insertions(+), 30 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 340c9c4894b..ac4c8ddde20 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -167,21 +167,24 @@ static int tdi_in_host_mode (struct ehci_hcd *ehci) return (tmp & 3) == USBMODE_CM_HC; } -/* force HC to halt state from unknown (EHCI spec section 2.3) */ +/* + * Force HC to halt state from unknown (EHCI spec section 2.3). + * Must be called with interrupts enabled and the lock not held. + */ static int ehci_halt (struct ehci_hcd *ehci) { - u32 temp = ehci_readl(ehci, &ehci->regs->status); + u32 temp; + + spin_lock_irq(&ehci->lock); /* disable any irqs left enabled by previous code */ ehci_writel(ehci, 0, &ehci->regs->intr_enable); - if (ehci_is_TDI(ehci) && tdi_in_host_mode(ehci) == 0) { + if (ehci_is_TDI(ehci) && !tdi_in_host_mode(ehci)) { + spin_unlock_irq(&ehci->lock); return 0; } - if ((temp & STS_HALT) != 0) - return 0; - /* * This routine gets called during probe before ehci->command * has been initialized, so we can't rely on its value. @@ -190,7 +193,11 @@ static int ehci_halt (struct ehci_hcd *ehci) temp = ehci_readl(ehci, &ehci->regs->command); temp &= ~(CMD_RUN | CMD_IAAD); ehci_writel(ehci, temp, &ehci->regs->command); - return handshake (ehci, &ehci->regs->status, + + spin_unlock_irq(&ehci->lock); + synchronize_irq(ehci_to_hcd(ehci)->irq); + + return handshake(ehci, &ehci->regs->status, STS_HALT, STS_HALT, 16 * 125); } @@ -210,7 +217,10 @@ static void tdi_reset (struct ehci_hcd *ehci) ehci_writel(ehci, tmp, &ehci->regs->usbmode); } -/* reset a non-running (STS_HALT == 1) controller */ +/* + * Reset a non-running (STS_HALT == 1) controller. + * Must be called with interrupts enabled and the lock not held. + */ static int ehci_reset (struct ehci_hcd *ehci) { int retval; @@ -248,7 +258,10 @@ static int ehci_reset (struct ehci_hcd *ehci) return retval; } -/* idle the controller (from running) */ +/* + * Idle the controller (turn off the schedules). + * Must be called with interrupts enabled and the lock not held. + */ static void ehci_quiesce (struct ehci_hcd *ehci) { u32 temp; @@ -261,8 +274,10 @@ static void ehci_quiesce (struct ehci_hcd *ehci) handshake(ehci, &ehci->regs->status, STS_ASS | STS_PSS, temp, 16 * 125); /* then disable anything that's still active */ + spin_lock_irq(&ehci->lock); ehci->command &= ~(CMD_ASE | CMD_PSE); ehci_writel(ehci, ehci->command, &ehci->regs->command); + spin_unlock_irq(&ehci->lock); /* hardware can take 16 microframes to turn off ... */ handshake(ehci, &ehci->regs->status, STS_ASS | STS_PSS, 0, 16 * 125); @@ -301,11 +316,14 @@ static void ehci_turn_off_all_ports(struct ehci_hcd *ehci) /* * Halt HC, turn off all ports, and let the BIOS use the companion controllers. - * Should be called with ehci->lock held. + * Must be called with interrupts enabled and the lock not held. */ static void ehci_silence_controller(struct ehci_hcd *ehci) { ehci_halt(ehci); + + spin_lock_irq(&ehci->lock); + ehci->rh_state = EHCI_RH_HALTED; ehci_turn_off_all_ports(ehci); /* make BIOS/etc use companion controller during reboot */ @@ -313,6 +331,7 @@ static void ehci_silence_controller(struct ehci_hcd *ehci) /* unblock posted writes */ ehci_readl(ehci, &ehci->regs->configured_flag); + spin_unlock_irq(&ehci->lock); } /* ehci_shutdown kick in for silicon on any bus (not just pci, etc). @@ -325,10 +344,11 @@ static void ehci_shutdown(struct usb_hcd *hcd) spin_lock_irq(&ehci->lock); ehci->rh_state = EHCI_RH_STOPPING; - ehci_silence_controller(ehci); ehci->enabled_hrtimer_events = 0; spin_unlock_irq(&ehci->lock); + ehci_silence_controller(ehci); + hrtimer_cancel(&ehci->hrtimer); } @@ -400,11 +420,11 @@ static void ehci_stop (struct usb_hcd *hcd) spin_lock_irq(&ehci->lock); ehci->enabled_hrtimer_events = 0; - ehci_quiesce(ehci); + spin_unlock_irq(&ehci->lock); + ehci_quiesce(ehci); ehci_silence_controller(ehci); ehci_reset (ehci); - spin_unlock_irq(&ehci->lock); hrtimer_cancel(&ehci->hrtimer); remove_sysfs_files(ehci); @@ -412,8 +432,6 @@ static void ehci_stop (struct usb_hcd *hcd) /* root hub is shut down separately (first, when possible) */ spin_lock_irq (&ehci->lock); - if (ehci->async) - ehci_work (ehci); end_free_itds(ehci); spin_unlock_irq (&ehci->lock); ehci_mem_cleanup (ehci); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 05490d387fd..ffc5f27df72 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -59,6 +59,7 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci) /* Give the connections some time to appear */ msleep(20); + spin_lock_irq(&ehci->lock); port = HCS_N_PORTS(ehci->hcs_params); while (port--) { if (test_bit(port, &ehci->owned_ports)) { @@ -70,23 +71,30 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci) clear_bit(port, &ehci->owned_ports); else if (test_bit(port, &ehci->companion_ports)) ehci_writel(ehci, status & ~PORT_PE, reg); - else + else { + spin_unlock_irq(&ehci->lock); ehci_hub_control(hcd, SetPortFeature, USB_PORT_FEAT_RESET, port + 1, NULL, 0); + spin_lock_irq(&ehci->lock); + } } } + spin_unlock_irq(&ehci->lock); if (!ehci->owned_ports) return; msleep(90); /* Wait for resets to complete */ + spin_lock_irq(&ehci->lock); port = HCS_N_PORTS(ehci->hcs_params); while (port--) { if (test_bit(port, &ehci->owned_ports)) { + spin_unlock_irq(&ehci->lock); ehci_hub_control(hcd, GetPortStatus, 0, port + 1, (char *) &buf, sizeof(buf)); + spin_lock_irq(&ehci->lock); /* The companion should now own the port, * but if something went wrong the port must not @@ -105,6 +113,7 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci) } ehci->owned_ports = 0; + spin_unlock_irq(&ehci->lock); } static int ehci_port_change(struct ehci_hcd *ehci) @@ -133,7 +142,6 @@ static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, { int port; u32 temp; - unsigned long flags; /* If remote wakeup is enabled for the root hub but disabled * for the controller, we must adjust all the port wakeup flags @@ -143,7 +151,7 @@ static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, if (!ehci_to_hcd(ehci)->self.root_hub->do_remote_wakeup || do_wakeup) return; - spin_lock_irqsave(&ehci->lock, flags); + spin_lock_irq(&ehci->lock); /* clear phy low-power mode before changing wakeup flags */ if (ehci->has_hostpc) { @@ -154,9 +162,9 @@ static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, temp = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, temp & ~HOSTPC_PHCD, hostpc_reg); } - spin_unlock_irqrestore(&ehci->lock, flags); + spin_unlock_irq(&ehci->lock); msleep(5); - spin_lock_irqsave(&ehci->lock, flags); + spin_lock_irq(&ehci->lock); } port = HCS_N_PORTS(ehci->hcs_params); @@ -194,7 +202,7 @@ static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, if (!suspending && ehci_port_change(ehci)) usb_hcd_resume_root_hub(ehci_to_hcd(ehci)); - spin_unlock_irqrestore(&ehci->lock, flags); + spin_unlock_irq(&ehci->lock); } static int ehci_bus_suspend (struct usb_hcd *hcd) @@ -209,6 +217,9 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) if (time_before (jiffies, ehci->next_statechange)) msleep(5); + /* stop the schedules */ + ehci_quiesce(ehci); + spin_lock_irq (&ehci->lock); /* Once the controller is stopped, port resumes that are already @@ -224,10 +235,6 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) } } - /* stop schedules, clean any completed work */ - ehci_quiesce(ehci); - ehci_work(ehci); - /* Unlike other USB host controller types, EHCI doesn't have * any notion of "global" or bus-wide suspend. The driver has * to manually suspend all the active unsuspended ports, and @@ -289,6 +296,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) "succeeded" : "failed"); } } + spin_unlock_irq(&ehci->lock); /* Apparently some devices need a >= 1-uframe delay here */ if (ehci->bus_suspended) @@ -296,6 +304,8 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) /* turn off now-idle HC */ ehci_halt (ehci); + + spin_lock_irq(&ehci->lock); ehci->rh_state = EHCI_RH_SUSPENDED; end_unlink_async(ehci); @@ -424,13 +434,14 @@ static int ehci_bus_resume (struct usb_hcd *hcd) } ehci->next_statechange = jiffies + msecs_to_jiffies(5); + spin_unlock_irq(&ehci->lock); + + ehci_handover_companion_ports(ehci); /* Now we can safely re-enable irqs */ ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); (void) ehci_readl(ehci, &ehci->regs->intr_enable); - spin_unlock_irq (&ehci->lock); - ehci_handover_companion_ports(ehci); return 0; } @@ -1018,7 +1029,9 @@ static int ehci_hub_control ( case USB_PORT_FEAT_TEST: if (!selector || selector > 5) goto error; + spin_unlock_irqrestore(&ehci->lock, flags); ehci_quiesce(ehci); + spin_lock_irqsave(&ehci->lock, flags); /* Put all enabled ports into suspend */ while (ports--) { @@ -1030,7 +1043,11 @@ static int ehci_hub_control ( ehci_writel(ehci, temp | PORT_SUSPEND, sreg); } + + spin_unlock_irqrestore(&ehci->lock, flags); ehci_halt(ehci); + spin_lock_irqsave(&ehci->lock, flags); + temp = ehci_readl(ehci, status_reg); temp |= selector << 16; ehci_writel(ehci, temp, status_reg); diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index f7f3ce3275b..65360945df7 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -445,12 +445,11 @@ static int controller_suspend(struct device *dev) if (time_before(jiffies, ehci->next_statechange)) msleep(10); - spin_lock_irqsave(&ehci->lock, flags); + ehci_halt(ehci); + spin_lock_irqsave(&ehci->lock, flags); tegra->port_speed = (readl(&hw->port_status[0]) >> 26) & 0x3; - ehci_halt(ehci); clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - spin_unlock_irqrestore(&ehci->lock, flags); tegra_ehci_power_down(hcd); -- cgit v1.2.3-70-g09d2 From 43fe3a99d9caf10b25f9c596e9854cdae30db418 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:23:16 -0400 Subject: USB: EHCI: resolve some unlikely races This patch (as1589) resolves some unlikely races involving system shutdown or controller death in ehci-hcd: Shutdown races with both root-hub resume and controller resume. Controller death races with root-hub suspend. A new bitflag is added to indicate that the controller has been shut down (whether for system shutdown or because it died). Tests are added in the suspend and resume pathways to avoid reactivating the controller after any sort of shutdown. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 20 +++++++++++++++++++- drivers/usb/host/ehci-hub.c | 27 +++++++++++++++++++++++---- drivers/usb/host/ehci.h | 1 + 3 files changed, 43 insertions(+), 5 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index ac4c8ddde20..e44ca5453aa 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -343,6 +343,7 @@ static void ehci_shutdown(struct usb_hcd *hcd) struct ehci_hcd *ehci = hcd_to_ehci(hcd); spin_lock_irq(&ehci->lock); + ehci->shutdown = true; ehci->rh_state = EHCI_RH_STOPPING; ehci->enabled_hrtimer_events = 0; spin_unlock_irq(&ehci->lock); @@ -823,6 +824,7 @@ dead: usb_hc_died(hcd); /* Don't let the controller do anything more */ + ehci->shutdown = true; ehci->rh_state = EHCI_RH_STOPPING; ehci->command &= ~(CMD_RUN | CMD_ASE | CMD_PSE); ehci_writel(ehci, ehci->command, &ehci->regs->command); @@ -1129,6 +1131,9 @@ static int __maybe_unused ehci_resume(struct usb_hcd *hcd, bool hibernated) /* Mark hardware accessible again as we are back to full power by now */ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + if (ehci->shutdown) + return 0; /* Controller is dead */ + /* * If CF is still set and we aren't resuming from hibernation * then we maintained suspend power. @@ -1139,10 +1144,17 @@ static int __maybe_unused ehci_resume(struct usb_hcd *hcd, bool hibernated) int mask = INTR_MASK; ehci_prepare_ports_for_controller_resume(ehci); + + spin_lock_irq(&ehci->lock); + if (ehci->shutdown) + goto skip; + if (!hcd->self.root_hub->do_remote_wakeup) mask &= ~STS_PCD; ehci_writel(ehci, mask, &ehci->regs->intr_enable); ehci_readl(ehci, &ehci->regs->intr_enable); + skip: + spin_unlock_irq(&ehci->lock); return 0; } @@ -1154,14 +1166,20 @@ static int __maybe_unused ehci_resume(struct usb_hcd *hcd, bool hibernated) (void) ehci_halt(ehci); (void) ehci_reset(ehci); + spin_lock_irq(&ehci->lock); + if (ehci->shutdown) + goto skip; + ehci_writel(ehci, ehci->command, &ehci->regs->command); ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ + ehci->rh_state = EHCI_RH_SUSPENDED; + spin_unlock_irq(&ehci->lock); + /* here we "know" root ports should always stay powered */ ehci_port_power(ehci, 1); - ehci->rh_state = EHCI_RH_SUSPENDED; return 1; } diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index ffc5f27df72..c7880223738 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -221,6 +221,8 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) ehci_quiesce(ehci); spin_lock_irq (&ehci->lock); + if (ehci->rh_state < EHCI_RH_RUNNING) + goto done; /* Once the controller is stopped, port resumes that are already * in progress won't complete. Hence if remote wakeup is enabled @@ -306,6 +308,10 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) ehci_halt (ehci); spin_lock_irq(&ehci->lock); + if (ehci->enabled_hrtimer_events & BIT(EHCI_HRTIMER_POLL_DEAD)) + ehci_handle_controller_death(ehci); + if (ehci->rh_state != EHCI_RH_RUNNING) + goto done; ehci->rh_state = EHCI_RH_SUSPENDED; end_unlink_async(ehci); @@ -320,6 +326,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) ehci_writel(ehci, mask, &ehci->regs->intr_enable); ehci_readl(ehci, &ehci->regs->intr_enable); + done: ehci->next_statechange = jiffies + msecs_to_jiffies(10); ehci->enabled_hrtimer_events = 0; ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT; @@ -342,10 +349,8 @@ static int ehci_bus_resume (struct usb_hcd *hcd) if (time_before (jiffies, ehci->next_statechange)) msleep(5); spin_lock_irq (&ehci->lock); - if (!HCD_HW_ACCESSIBLE(hcd)) { - spin_unlock_irq(&ehci->lock); - return -ESHUTDOWN; - } + if (!HCD_HW_ACCESSIBLE(hcd) || ehci->shutdown) + goto shutdown; if (unlikely(ehci->debug)) { if (!dbgp_reset_prep()) @@ -384,6 +389,8 @@ static int ehci_bus_resume (struct usb_hcd *hcd) spin_unlock_irq(&ehci->lock); msleep(8); spin_lock_irq(&ehci->lock); + if (ehci->shutdown) + goto shutdown; /* clear phy low-power mode before resume */ if (ehci->bus_suspended && ehci->has_hostpc) { @@ -401,6 +408,8 @@ static int ehci_bus_resume (struct usb_hcd *hcd) spin_unlock_irq(&ehci->lock); msleep(5); spin_lock_irq(&ehci->lock); + if (ehci->shutdown) + goto shutdown; } /* manually resume the ports we suspended during bus_suspend() */ @@ -421,6 +430,8 @@ static int ehci_bus_resume (struct usb_hcd *hcd) spin_unlock_irq(&ehci->lock); msleep(20); spin_lock_irq(&ehci->lock); + if (ehci->shutdown) + goto shutdown; } i = HCS_N_PORTS (ehci->hcs_params); @@ -439,10 +450,18 @@ static int ehci_bus_resume (struct usb_hcd *hcd) ehci_handover_companion_ports(ehci); /* Now we can safely re-enable irqs */ + spin_lock_irq(&ehci->lock); + if (ehci->shutdown) + goto shutdown; ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); (void) ehci_readl(ehci, &ehci->regs->intr_enable); + spin_unlock_irq(&ehci->lock); return 0; + + shutdown: + spin_unlock_irq(&ehci->lock); + return -ESHUTDOWN; } #else diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 7de58fe52d5..da07d98f7d1 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -118,6 +118,7 @@ struct ehci_hcd { /* one per controller */ bool need_rescan:1; bool intr_unlinking:1; bool async_unlinking:1; + bool shutdown:1; struct ehci_qh *qh_scan_next; /* async schedule support */ -- cgit v1.2.3-70-g09d2 From 08d7660d45bc3e14b0fafe2e891ca50b4b18591a Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Tue, 10 Jul 2012 06:58:48 -0500 Subject: USB: ehci-fsl: Update ifdef check to work on 64-bit ppc We need to use CONFIG_FSL_SOC_BOOKE instead of CONFIG_PPC_85xx as CONFIG_PPC_85xx isn't defined when we build support for 64-bit embedded FSL PPC SoCs. Signed-off-by: Kumar Gala Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-fsl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index ab52db684b6..b7451b29c5a 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -313,7 +313,7 @@ static void ehci_fsl_usb_setup(struct ehci_hcd *ehci) } if (pdata->have_sysif_regs) { -#ifdef CONFIG_PPC_85xx +#ifdef CONFIG_FSL_SOC_BOOKE out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x00000008); out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000080); #else -- cgit v1.2.3-70-g09d2 From 72119743651054ceddd4fdf0cce161bdb4cc4aac Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Thu, 12 Jul 2012 10:34:56 -0400 Subject: usb: host: tegra: pass correct pointer in ehci_setup() The ehci_setup() require the pointer of usb_hcd. Passing the correct pointer in place of ehci_hcd pointer. This is side effect of change: commit 1a49e2ac9651df7349867a5cf44e2c83de1046af Author: Alan Stern EHCI: centralize controller initialization [Although I checked for this specifically, obviously I missed some of the calls. In addition to the mistake in ehci-tegra.c that Laxman fixed here, the same thing needs to be fixed in ehci-orion.c and ehci-xls.c. -- Alan Stern] Signed-off-by: Laxman Dewangan Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-orion.c | 2 +- drivers/usb/host/ehci-tegra.c | 2 +- drivers/usb/host/ehci-xls.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 3e411230953..9408da83eaf 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -106,7 +106,7 @@ static int ehci_orion_setup(struct usb_hcd *hcd) struct ehci_hcd *ehci = hcd_to_ehci(hcd); int retval; - retval = ehci_setup(ehci); + retval = ehci_setup(hcd); if (retval) return retval; diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 65360945df7..562c80e2bfe 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -285,7 +285,7 @@ static int tegra_ehci_setup(struct usb_hcd *hcd) /* switch to host mode */ hcd->has_tt = 1; - retval = ehci_setup(ehci); + retval = ehci_setup(hcd); if (retval) return retval; diff --git a/drivers/usb/host/ehci-xls.c b/drivers/usb/host/ehci-xls.c index 99c353a85ae..8dc6a22d90b 100644 --- a/drivers/usb/host/ehci-xls.c +++ b/drivers/usb/host/ehci-xls.c @@ -18,7 +18,7 @@ static int ehci_xls_setup(struct usb_hcd *hcd) ehci->caps = hcd->regs; - return ehci_setup(ehci); + return ehci_setup(hcd); } int ehci_xls_probe_internal(const struct hc_driver *driver, -- cgit v1.2.3-70-g09d2 From ba5952e0711b14d8d4fe172671f8aa6091ace3ee Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 13 Jul 2012 17:25:24 +0800 Subject: USB: ehci-omap: fix compile failure(v1) The omap_ehci_init() is introduced in the below commit: commit 1a49e2ac9651df7349867a5cf44e2c83de1046af(EHCI: centralize controller initialization) the local variable of 'pdev' inside omap_ehci_init() is used but not defined, so fix the compiling failure. Signed-off-by: Ming Lei Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-omap.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 6133d93808d..ec21f4a4a05 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -115,9 +115,8 @@ static void omap_ehci_erratum_i693(struct ehci_hcd *ehci) clk_disable(usbhost_p2_fck); } -static void omap_ehci_soft_phy_reset(struct platform_device *pdev, u8 port) +static void omap_ehci_soft_phy_reset(struct usb_hcd *hcd, u8 port) { - struct usb_hcd *hcd = dev_get_drvdata(&pdev->dev); unsigned long timeout = jiffies + msecs_to_jiffies(1000); unsigned reg = 0; @@ -139,7 +138,8 @@ static void omap_ehci_soft_phy_reset(struct platform_device *pdev, u8 port) cpu_relax(); if (time_after(jiffies, timeout)) { - dev_dbg(&pdev->dev, "phy reset operation timed out\n"); + dev_dbg(hcd->self.controller, + "phy reset operation timed out\n"); break; } } @@ -167,9 +167,9 @@ static int omap_ehci_init(struct usb_hcd *hcd) /* Soft reset the PHY using PHY reset command over ULPI */ if (pdata->port_mode[0] == OMAP_EHCI_PORT_MODE_PHY) - omap_ehci_soft_phy_reset(pdev, 0); + omap_ehci_soft_phy_reset(hcd, 0); if (pdata->port_mode[1] == OMAP_EHCI_PORT_MODE_PHY) - omap_ehci_soft_phy_reset(pdev, 1); + omap_ehci_soft_phy_reset(hcd, 1); /* we know this is the memory we want, no need to ioremap again */ ehci->caps = hcd->regs; -- cgit v1.2.3-70-g09d2 From d51389306f598d635c55e72f42609b543efd9469 Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Mon, 16 Jul 2012 11:25:36 +0530 Subject: USB: ohci-exynos: Add support for device tree This patch adds support to parse probe data for ohci driver for exynos using device tree. Signed-off-by: Thomas Abraham Signed-off-by: Abhilash Kesavan Signed-off-by: Vivek Gautam Acked-by: Jingoo Han Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-exynos.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index 8bcbdb5ade2..fc3091bd237 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -12,6 +12,7 @@ */ #include +#include #include #include #include @@ -71,6 +72,8 @@ static const struct hc_driver exynos_ohci_hc_driver = { .start_port_reset = ohci_start_port_reset, }; +static u64 ohci_exynos_dma_mask = DMA_BIT_MASK(32); + static int __devinit exynos_ohci_probe(struct platform_device *pdev) { struct exynos4_ohci_platdata *pdata; @@ -87,6 +90,16 @@ static int __devinit exynos_ohci_probe(struct platform_device *pdev) return -EINVAL; } + /* + * Right now device-tree probed devices don't get dma_mask set. + * Since shared usb code relies on it, set it here for now. + * Once we move to full device tree support this will vanish off. + */ + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &ohci_exynos_dma_mask; + if (!pdev->dev.coherent_dma_mask) + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + exynos_ohci = devm_kzalloc(&pdev->dev, sizeof(struct exynos_ohci_hcd), GFP_KERNEL); if (!exynos_ohci) @@ -256,6 +269,14 @@ static const struct dev_pm_ops exynos_ohci_pm_ops = { .resume = exynos_ohci_resume, }; +#ifdef CONFIG_OF +static const struct of_device_id exynos_ohci_match[] = { + { .compatible = "samsung,exynos-ohci" }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_ohci_match); +#endif + static struct platform_driver exynos_ohci_driver = { .probe = exynos_ohci_probe, .remove = __devexit_p(exynos_ohci_remove), @@ -264,6 +285,7 @@ static struct platform_driver exynos_ohci_driver = { .name = "exynos-ohci", .owner = THIS_MODULE, .pm = &exynos_ohci_pm_ops, + .of_match_table = of_match_ptr(exynos_ohci_match), } }; -- cgit v1.2.3-70-g09d2 From 2c026e2b36e8e934de9726398a3e1eeb3fa6055c Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Mon, 16 Jul 2012 11:25:37 +0530 Subject: USB: ehci-s5p: Add support for device tree This patch adds support to parse probe data for ehci driver for exynos using device tree Signed-off-by: Thomas Abraham Signed-off-by: Abhilash Kesavan Signed-off-by: Vivek Gautam Acked-by: Jingoo Han Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-s5p.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index 13c179fb2ee..37d84cf9a40 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -13,6 +13,7 @@ */ #include +#include #include #include #include @@ -63,6 +64,8 @@ static const struct hc_driver s5p_ehci_hc_driver = { .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; +static u64 ehci_s5p_dma_mask = DMA_BIT_MASK(32); + static int __devinit s5p_ehci_probe(struct platform_device *pdev) { struct s5p_ehci_platdata *pdata; @@ -79,6 +82,16 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev) return -EINVAL; } + /* + * Right now device-tree probed devices don't get dma_mask set. + * Since shared usb code relies on it, set it here for now. + * Once we move to full device tree support this will vanish off. + */ + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &ehci_s5p_dma_mask; + if (!pdev->dev.coherent_dma_mask) + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + s5p_ehci = devm_kzalloc(&pdev->dev, sizeof(struct s5p_ehci_hcd), GFP_KERNEL); if (!s5p_ehci) @@ -233,6 +246,14 @@ static const struct dev_pm_ops s5p_ehci_pm_ops = { .resume = s5p_ehci_resume, }; +#ifdef CONFIG_OF +static const struct of_device_id exynos_ehci_match[] = { + { .compatible = "samsung,exynos-ehci" }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_ehci_match); +#endif + static struct platform_driver s5p_ehci_driver = { .probe = s5p_ehci_probe, .remove = __devexit_p(s5p_ehci_remove), @@ -241,6 +262,7 @@ static struct platform_driver s5p_ehci_driver = { .name = "s5p-ehci", .owner = THIS_MODULE, .pm = &s5p_ehci_pm_ops, + .of_match_table = of_match_ptr(exynos_ehci_match), } }; -- cgit v1.2.3-70-g09d2 From fd81d59c90d38661e4891ee2f4c9b7c051e93022 Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Tue, 17 Jul 2012 10:10:50 +0530 Subject: USB: ehci-s5p: Add vbus setup function to the s5p ehci glue layer This patch retrieves and configures the vbus control gpio via the device tree. The suspend/resume callbacks will be later modified for vbus control. Signed-off-by: Abhilash Kesavan Signed-off-by: Vivek Gautam Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-s5p.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index 37d84cf9a40..9d8f1dd57cb 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -64,6 +65,24 @@ static const struct hc_driver s5p_ehci_hc_driver = { .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; +static void s5p_setup_vbus_gpio(struct platform_device *pdev) +{ + int err; + int gpio; + + if (!pdev->dev.of_node) + return; + + gpio = of_get_named_gpio(pdev->dev.of_node, + "samsung,vbus-gpio", 0); + if (!gpio_is_valid(gpio)) + return; + + err = gpio_request_one(gpio, GPIOF_OUT_INIT_HIGH, "ehci_vbus_gpio"); + if (err) + dev_err(&pdev->dev, "can't request ehci vbus gpio %d", gpio); +} + static u64 ehci_s5p_dma_mask = DMA_BIT_MASK(32); static int __devinit s5p_ehci_probe(struct platform_device *pdev) @@ -92,6 +111,8 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev) if (!pdev->dev.coherent_dma_mask) pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + s5p_setup_vbus_gpio(pdev); + s5p_ehci = devm_kzalloc(&pdev->dev, sizeof(struct s5p_ehci_hcd), GFP_KERNEL); if (!s5p_ehci) -- cgit v1.2.3-70-g09d2 From 47fc28bff82a4dd5f6b41c97e335d10fc78a8e9a Mon Sep 17 00:00:00 2001 From: Chris Metcalf Date: Wed, 9 May 2012 13:58:14 -0400 Subject: usb: add host support for the tilegx architecture This change adds OHCI and EHCI support for the tilegx's on-chip USB hardware. Signed-off-by: Chris Metcalf --- arch/tile/Kconfig | 10 ++ arch/tile/kernel/Makefile | 1 + arch/tile/kernel/usb.c | 69 +++++++++++++ drivers/usb/host/ehci-hcd.c | 5 + drivers/usb/host/ehci-tilegx.c | 214 +++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/ohci-hcd.c | 5 + drivers/usb/host/ohci-tilegx.c | 203 ++++++++++++++++++++++++++++++++++++++ include/linux/usb/tilegx.h | 34 +++++++ 8 files changed, 541 insertions(+) create mode 100644 arch/tile/kernel/usb.c create mode 100644 drivers/usb/host/ehci-tilegx.c create mode 100644 drivers/usb/host/ohci-tilegx.c create mode 100644 include/linux/usb/tilegx.h (limited to 'drivers/usb/host') diff --git a/arch/tile/Kconfig b/arch/tile/Kconfig index 557e3a381ca..cf4bb69ea8e 100644 --- a/arch/tile/Kconfig +++ b/arch/tile/Kconfig @@ -396,6 +396,16 @@ config NO_IOPORT source "drivers/pci/Kconfig" +config TILE_USB + tristate "Tilera USB host adapter support" + default y + depends on USB + depends on TILEGX + select TILE_GXIO_USB_HOST + ---help--- + Provides USB host adapter support for the built-in EHCI and OHCI + interfaces on TILE-Gx chips. + config HOTPLUG bool "Support for hot-pluggable devices" ---help--- diff --git a/arch/tile/kernel/Makefile b/arch/tile/kernel/Makefile index 49d4ce3cd7f..5334be8e253 100644 --- a/arch/tile/kernel/Makefile +++ b/arch/tile/kernel/Makefile @@ -19,3 +19,4 @@ obj-$(CONFIG_PCI) += pci_gx.o else obj-$(CONFIG_PCI) += pci.o endif +obj-$(CONFIG_TILE_USB) += usb.o diff --git a/arch/tile/kernel/usb.c b/arch/tile/kernel/usb.c new file mode 100644 index 00000000000..5af8debc6a7 --- /dev/null +++ b/arch/tile/kernel/usb.c @@ -0,0 +1,69 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, version 2. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for + * more details. + * + * Register the Tile-Gx USB interfaces as platform devices. + * + * The actual USB driver is just some glue (in + * drivers/usb/host/[eo]hci-tilegx.c) which makes the registers available + * to the standard kernel EHCI and OHCI drivers. + */ + +#include +#include +#include +#include + +static u64 ehci_dmamask = DMA_BIT_MASK(32); + +#define USB_HOST_DEF(unit, type, dmamask) \ + static struct \ + tilegx_usb_platform_data tilegx_usb_platform_data_ ## type ## \ + hci ## unit = { \ + .dev_index = unit, \ + }; \ + \ + static struct platform_device tilegx_usb_ ## type ## hci ## unit = { \ + .name = "tilegx-" #type "hci", \ + .id = unit, \ + .dev = { \ + .dma_mask = dmamask, \ + .coherent_dma_mask = DMA_BIT_MASK(32), \ + .platform_data = \ + &tilegx_usb_platform_data_ ## type ## hci ## \ + unit, \ + }, \ + }; + +USB_HOST_DEF(0, e, &ehci_dmamask) +USB_HOST_DEF(0, o, NULL) +USB_HOST_DEF(1, e, &ehci_dmamask) +USB_HOST_DEF(1, o, NULL) + +#undef USB_HOST_DEF + +static struct platform_device *tilegx_usb_devices[] __initdata = { + &tilegx_usb_ehci0, + &tilegx_usb_ehci1, + &tilegx_usb_ohci0, + &tilegx_usb_ohci1, +}; + +/** Add our set of possible USB devices. */ +static int __init tilegx_usb_init(void) +{ + platform_add_devices(tilegx_usb_devices, + ARRAY_SIZE(tilegx_usb_devices)); + + return 0; +} +arch_initcall(tilegx_usb_init); diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 800be38c78b..1d9401e0990 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1349,6 +1349,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_msm_driver #endif +#ifdef CONFIG_TILE_USB +#include "ehci-tilegx.c" +#define PLATFORM_DRIVER ehci_hcd_tilegx_driver +#endif + #ifdef CONFIG_USB_EHCI_HCD_PMC_MSP #include "ehci-pmcmsp.c" #define PLATFORM_DRIVER ehci_hcd_msp_driver diff --git a/drivers/usb/host/ehci-tilegx.c b/drivers/usb/host/ehci-tilegx.c new file mode 100644 index 00000000000..1d215cdb9de --- /dev/null +++ b/drivers/usb/host/ehci-tilegx.c @@ -0,0 +1,214 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, version 2. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for + * more details. + */ + +/* + * Tilera TILE-Gx USB EHCI host controller driver. + */ + +#include +#include +#include +#include + +#include + +#include +#include + +static void tilegx_start_ehc(void) +{ +} + +static void tilegx_stop_ehc(void) +{ +} + +static int tilegx_ehci_setup(struct usb_hcd *hcd) +{ + int ret = ehci_init(hcd); + + /* + * Some drivers do: + * + * struct ehci_hcd *ehci = hcd_to_ehci(hcd); + * ehci->need_io_watchdog = 0; + * + * here, but since this is a new driver we're going to leave the + * watchdog enabled. Later we may try to turn it off and see + * whether we run into any problems. + */ + + return ret; +} + +static const struct hc_driver ehci_tilegx_hc_driver = { + .description = hcd_name, + .product_desc = "Tile-Gx EHCI", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * Generic hardware linkage. + */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * Basic lifecycle operations. + */ + .reset = tilegx_ehci_setup, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* + * Managing I/O requests and associated device resources. + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, + + /* + * Scheduling support. + */ + .get_frame_number = ehci_get_frame, + + /* + * Root hub support. + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +}; + +static int ehci_hcd_tilegx_drv_probe(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + struct ehci_hcd *ehci; + struct tilegx_usb_platform_data *pdata = pdev->dev.platform_data; + pte_t pte = { 0 }; + int my_cpu = smp_processor_id(); + int ret; + + if (usb_disabled()) + return -ENODEV; + + /* + * Try to initialize our GXIO context; if we can't, the device + * doesn't exist. + */ + if (gxio_usb_host_init(&pdata->usb_ctx, pdata->dev_index, 1) != 0) + return -ENXIO; + + hcd = usb_create_hcd(&ehci_tilegx_hc_driver, &pdev->dev, + dev_name(&pdev->dev)); + if (!hcd) + return -ENOMEM; + + /* + * We don't use rsrc_start to map in our registers, but seems like + * we ought to set it to something, so we use the register VA. + */ + hcd->rsrc_start = + (ulong) gxio_usb_host_get_reg_start(&pdata->usb_ctx); + hcd->rsrc_len = gxio_usb_host_get_reg_len(&pdata->usb_ctx); + hcd->regs = gxio_usb_host_get_reg_start(&pdata->usb_ctx); + + tilegx_start_ehc(); + + ehci = hcd_to_ehci(hcd); + ehci->caps = hcd->regs; + ehci->regs = + hcd->regs + HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); + /* cache this readonly data; minimize chip reads */ + ehci->hcs_params = readl(&ehci->caps->hcs_params); + + /* Create our IRQs and register them. */ + pdata->irq = create_irq(); + if (pdata->irq < 0) { + ret = -ENXIO; + goto err_no_irq; + } + + tile_irq_activate(pdata->irq, TILE_IRQ_PERCPU); + + /* Configure interrupts. */ + ret = gxio_usb_host_cfg_interrupt(&pdata->usb_ctx, + cpu_x(my_cpu), cpu_y(my_cpu), + KERNEL_PL, pdata->irq); + if (ret) { + ret = -ENXIO; + goto err_have_irq; + } + + /* Register all of our memory. */ + pte = pte_set_home(pte, PAGE_HOME_HASH); + ret = gxio_usb_host_register_client_memory(&pdata->usb_ctx, pte, 0); + if (ret) { + ret = -ENXIO; + goto err_have_irq; + } + + ret = usb_add_hcd(hcd, pdata->irq, IRQF_SHARED); + if (ret == 0) { + platform_set_drvdata(pdev, hcd); + return ret; + } + +err_have_irq: + destroy_irq(pdata->irq); +err_no_irq: + tilegx_stop_ehc(); + usb_put_hcd(hcd); + gxio_usb_host_destroy(&pdata->usb_ctx); + return ret; +} + +static int ehci_hcd_tilegx_drv_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct tilegx_usb_platform_data *pdata = pdev->dev.platform_data; + + usb_remove_hcd(hcd); + usb_put_hcd(hcd); + tilegx_stop_ehc(); + gxio_usb_host_destroy(&pdata->usb_ctx); + destroy_irq(pdata->irq); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static void ehci_hcd_tilegx_drv_shutdown(struct platform_device *pdev) +{ + usb_hcd_platform_shutdown(pdev); + ehci_hcd_tilegx_drv_remove(pdev); +} + +static struct platform_driver ehci_hcd_tilegx_driver = { + .probe = ehci_hcd_tilegx_drv_probe, + .remove = ehci_hcd_tilegx_drv_remove, + .shutdown = ehci_hcd_tilegx_drv_shutdown, + .driver = { + .name = "tilegx-ehci", + .owner = THIS_MODULE, + } +}; + +MODULE_ALIAS("platform:tilegx-ehci"); diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index e0adf5c0cf5..2b1e8d84c87 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1100,6 +1100,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ohci_octeon_driver #endif +#ifdef CONFIG_TILE_USB +#include "ohci-tilegx.c" +#define PLATFORM_DRIVER ohci_hcd_tilegx_driver +#endif + #ifdef CONFIG_USB_CNS3XXX_OHCI #include "ohci-cns3xxx.c" #define PLATFORM_DRIVER ohci_hcd_cns3xxx_driver diff --git a/drivers/usb/host/ohci-tilegx.c b/drivers/usb/host/ohci-tilegx.c new file mode 100644 index 00000000000..1ae7b28a71c --- /dev/null +++ b/drivers/usb/host/ohci-tilegx.c @@ -0,0 +1,203 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, version 2. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for + * more details. + */ + +/* + * Tilera TILE-Gx USB OHCI host controller driver. + */ + +#include +#include +#include +#include + +#include + +#include +#include + +static void tilegx_start_ohc(void) +{ +} + +static void tilegx_stop_ohc(void) +{ +} + +static int tilegx_ohci_start(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + int ret; + + ret = ohci_init(ohci); + if (ret < 0) + return ret; + + ret = ohci_run(ohci); + if (ret < 0) { + dev_err(hcd->self.controller, "can't start %s\n", + hcd->self.bus_name); + ohci_stop(hcd); + return ret; + } + + return 0; +} + +static const struct hc_driver ohci_tilegx_hc_driver = { + .description = hcd_name, + .product_desc = "Tile-Gx OHCI", + .hcd_priv_size = sizeof(struct ohci_hcd), + + /* + * Generic hardware linkage. + */ + .irq = ohci_irq, + .flags = HCD_MEMORY | HCD_LOCAL_MEM | HCD_USB11, + + /* + * Basic lifecycle operations. + */ + .start = tilegx_ohci_start, + .stop = ohci_stop, + .shutdown = ohci_shutdown, + + /* + * Managing I/O requests and associated device resources. + */ + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + /* + * Scheduling support. + */ + .get_frame_number = ohci_get_frame, + + /* + * Root hub support. + */ + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, + .start_port_reset = ohci_start_port_reset, +}; + +static int ohci_hcd_tilegx_drv_probe(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + struct tilegx_usb_platform_data *pdata = pdev->dev.platform_data; + pte_t pte = { 0 }; + int my_cpu = smp_processor_id(); + int ret; + + if (usb_disabled()) + return -ENODEV; + + /* + * Try to initialize our GXIO context; if we can't, the device + * doesn't exist. + */ + if (gxio_usb_host_init(&pdata->usb_ctx, pdata->dev_index, 0) != 0) + return -ENXIO; + + hcd = usb_create_hcd(&ohci_tilegx_hc_driver, &pdev->dev, + dev_name(&pdev->dev)); + if (!hcd) + return -ENOMEM; + + /* + * We don't use rsrc_start to map in our registers, but seems like + * we ought to set it to something, so we use the register VA. + */ + hcd->rsrc_start = + (ulong) gxio_usb_host_get_reg_start(&pdata->usb_ctx); + hcd->rsrc_len = gxio_usb_host_get_reg_len(&pdata->usb_ctx); + hcd->regs = gxio_usb_host_get_reg_start(&pdata->usb_ctx); + + tilegx_start_ohc(); + + /* Create our IRQs and register them. */ + pdata->irq = create_irq(); + if (pdata->irq < 0) { + ret = -ENXIO; + goto err_no_irq; + } + + tile_irq_activate(pdata->irq, TILE_IRQ_PERCPU); + + /* Configure interrupts. */ + ret = gxio_usb_host_cfg_interrupt(&pdata->usb_ctx, + cpu_x(my_cpu), cpu_y(my_cpu), + KERNEL_PL, pdata->irq); + if (ret) { + ret = -ENXIO; + goto err_have_irq; + } + + /* Register all of our memory. */ + pte = pte_set_home(pte, PAGE_HOME_HASH); + ret = gxio_usb_host_register_client_memory(&pdata->usb_ctx, pte, 0); + if (ret) { + ret = -ENXIO; + goto err_have_irq; + } + + ohci_hcd_init(hcd_to_ohci(hcd)); + + ret = usb_add_hcd(hcd, pdata->irq, IRQF_SHARED); + if (ret == 0) { + platform_set_drvdata(pdev, hcd); + return ret; + } + +err_have_irq: + destroy_irq(pdata->irq); +err_no_irq: + tilegx_stop_ohc(); + usb_put_hcd(hcd); + gxio_usb_host_destroy(&pdata->usb_ctx); + return ret; +} + +static int ohci_hcd_tilegx_drv_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct tilegx_usb_platform_data* pdata = pdev->dev.platform_data; + + usb_remove_hcd(hcd); + usb_put_hcd(hcd); + tilegx_stop_ohc(); + gxio_usb_host_destroy(&pdata->usb_ctx); + destroy_irq(pdata->irq); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static void ohci_hcd_tilegx_drv_shutdown(struct platform_device *pdev) +{ + usb_hcd_platform_shutdown(pdev); + ohci_hcd_tilegx_drv_remove(pdev); +} + +static struct platform_driver ohci_hcd_tilegx_driver = { + .probe = ohci_hcd_tilegx_drv_probe, + .remove = ohci_hcd_tilegx_drv_remove, + .shutdown = ohci_hcd_tilegx_drv_shutdown, + .driver = { + .name = "tilegx-ohci", + .owner = THIS_MODULE, + } +}; + +MODULE_ALIAS("platform:tilegx-ohci"); diff --git a/include/linux/usb/tilegx.h b/include/linux/usb/tilegx.h new file mode 100644 index 00000000000..2d65e343568 --- /dev/null +++ b/include/linux/usb/tilegx.h @@ -0,0 +1,34 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, version 2. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for + * more details. + * + * Structure to contain platform-specific data related to Tile-Gx USB + * controllers. + */ + +#ifndef _LINUX_USB_TILEGX_H +#define _LINUX_USB_TILEGX_H + +#include + +struct tilegx_usb_platform_data { + /* GXIO device index. */ + int dev_index; + + /* GXIO device context. */ + gxio_usb_host_context_t usb_ctx; + + /* Device IRQ. */ + unsigned int irq; +}; + +#endif /* _LINUX_USB_TILEGX_H */ -- cgit v1.2.3-70-g09d2 From baffab28b13120694fa3ebab08d3e99667a851d2 Mon Sep 17 00:00:00 2001 From: Simon Baatz Date: Thu, 19 Jul 2012 00:04:09 +0200 Subject: ARM: Orion: fix driver probe error handling with respect to clk The clk patches added code to get and enable clocks in the respective driver probe functions. If the probe function failed for some reason after enabling the clock, the clock was not disabled again in many cases. Signed-off-by: Simon Baatz Signed-off-by: Andrew Lumm --- drivers/crypto/mv_cesa.c | 4 ++++ drivers/mmc/host/mvsdio.c | 4 ++++ drivers/mtd/nand/orion_nand.c | 4 ++++ drivers/net/ethernet/marvell/mv643xx_eth.c | 6 ++++++ drivers/usb/host/ehci-orion.c | 4 ++++ sound/soc/kirkwood/kirkwood-i2s.c | 8 +++++++- 6 files changed, 29 insertions(+), 1 deletion(-) (limited to 'drivers/usb/host') diff --git a/drivers/crypto/mv_cesa.c b/drivers/crypto/mv_cesa.c index 1cc6b3f3e26..a4faa893199 100644 --- a/drivers/crypto/mv_cesa.c +++ b/drivers/crypto/mv_cesa.c @@ -1098,6 +1098,10 @@ err_unreg_ecb: crypto_unregister_alg(&mv_aes_alg_ecb); err_irq: free_irq(irq, cp); + if (!IS_ERR(cp->clk)) { + clk_disable_unprepare(cp->clk); + clk_put(cp->clk); + } err_thread: kthread_stop(cp->queue_th); err_unmap_sram: diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index 3b9136c1a47..a61cb5fca22 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -839,6 +839,10 @@ out: if (r) release_resource(r); if (mmc) + if (!IS_ERR_OR_NULL(host->clk)) { + clk_disable_unprepare(host->clk); + clk_put(host->clk); + } mmc_free_host(mmc); return ret; diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index 513dc88a05c..9b8fd3d7716 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -183,6 +183,10 @@ static int __init orion_nand_probe(struct platform_device *pdev) return 0; no_dev: + if (!IS_ERR(clk)) { + clk_disable_unprepare(clk); + clk_put(clk); + } platform_set_drvdata(pdev, NULL); iounmap(io_base); no_res: diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index f0f06b2bc28..4fbba57b8ff 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -2983,6 +2983,12 @@ static int mv643xx_eth_probe(struct platform_device *pdev) return 0; out: +#if defined(CONFIG_HAVE_CLK) + if (!IS_ERR(mp->clk)) { + clk_disable_unprepare(mp->clk); + clk_put(mp->clk); + } +#endif free_netdev(dev); return err; diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 82de1073aa5..c6903e3af29 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -298,6 +298,10 @@ static int __devinit ehci_orion_drv_probe(struct platform_device *pdev) err4: usb_put_hcd(hcd); err3: + if (!IS_ERR(clk)) { + clk_disable_unprepare(clk); + clk_put(clk); + } iounmap(regs); err2: release_mem_region(res->start, resource_size(res)); diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c index fa455675045..7646dd7f30c 100644 --- a/sound/soc/kirkwood/kirkwood-i2s.c +++ b/sound/soc/kirkwood/kirkwood-i2s.c @@ -458,7 +458,13 @@ static __devinit int kirkwood_i2s_dev_probe(struct platform_device *pdev) } clk_prepare_enable(priv->clk); - return snd_soc_register_dai(&pdev->dev, &kirkwood_i2s_dai); + err = snd_soc_register_dai(&pdev->dev, &kirkwood_i2s_dai); + if (!err) + return 0; + dev_err(&pdev->dev, "snd_soc_register_dai failed\n"); + + clk_disable_unprepare(priv->clk); + clk_put(priv->clk); err_ioremap: iounmap(priv->io); -- cgit v1.2.3-70-g09d2 From c311b78b2a2fc644da2e748b7999b9ff081ec00e Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 30 Jul 2012 14:39:49 -0700 Subject: usb/host/r8a66597: remove conditional compilation of clk code With addition of dummy clk_*() calls for non CONFIG_HAVE_CLK cases in clk.h, there is no need to have clk code enclosed in #ifdef CONFIG_HAVE_CLK, #endif macros. Signed-off-by: Viresh Kumar Cc: Greg Kroah-Hartman Cc: Russell King Cc: Mike Turquette Cc: Sergei Shtylyov Cc: viresh kumar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/usb/host/r8a66597-hcd.c | 12 ------------ drivers/usb/host/r8a66597.h | 5 ----- 2 files changed, 17 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index c868be65e76..4c634eb5635 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -95,9 +95,7 @@ static int r8a66597_clock_enable(struct r8a66597 *r8a66597) int i = 0; if (r8a66597->pdata->on_chip) { -#ifdef CONFIG_HAVE_CLK clk_enable(r8a66597->clk); -#endif do { r8a66597_write(r8a66597, SCKE, SYSCFG0); tmp = r8a66597_read(r8a66597, SYSCFG0); @@ -141,9 +139,7 @@ static void r8a66597_clock_disable(struct r8a66597 *r8a66597) udelay(1); if (r8a66597->pdata->on_chip) { -#ifdef CONFIG_HAVE_CLK clk_disable(r8a66597->clk); -#endif } else { r8a66597_bclr(r8a66597, PLLC, SYSCFG0); r8a66597_bclr(r8a66597, XCKE, SYSCFG0); @@ -2406,19 +2402,15 @@ static int __devexit r8a66597_remove(struct platform_device *pdev) del_timer_sync(&r8a66597->rh_timer); usb_remove_hcd(hcd); iounmap(r8a66597->reg); -#ifdef CONFIG_HAVE_CLK if (r8a66597->pdata->on_chip) clk_put(r8a66597->clk); -#endif usb_put_hcd(hcd); return 0; } static int __devinit r8a66597_probe(struct platform_device *pdev) { -#ifdef CONFIG_HAVE_CLK char clk_name[8]; -#endif struct resource *res = NULL, *ires; int irq = -1; void __iomem *reg = NULL; @@ -2482,7 +2474,6 @@ static int __devinit r8a66597_probe(struct platform_device *pdev) r8a66597->irq_sense_low = irq_trigger == IRQF_TRIGGER_LOW; if (r8a66597->pdata->on_chip) { -#ifdef CONFIG_HAVE_CLK snprintf(clk_name, sizeof(clk_name), "usb%d", pdev->id); r8a66597->clk = clk_get(&pdev->dev, clk_name); if (IS_ERR(r8a66597->clk)) { @@ -2491,7 +2482,6 @@ static int __devinit r8a66597_probe(struct platform_device *pdev) ret = PTR_ERR(r8a66597->clk); goto clean_up2; } -#endif r8a66597->max_root_hub = 1; } else r8a66597->max_root_hub = 2; @@ -2531,11 +2521,9 @@ static int __devinit r8a66597_probe(struct platform_device *pdev) return 0; clean_up3: -#ifdef CONFIG_HAVE_CLK if (r8a66597->pdata->on_chip) clk_put(r8a66597->clk); clean_up2: -#endif usb_put_hcd(hcd); clean_up: diff --git a/drivers/usb/host/r8a66597.h b/drivers/usb/host/r8a66597.h index f28782d20ee..672cea307ab 100644 --- a/drivers/usb/host/r8a66597.h +++ b/drivers/usb/host/r8a66597.h @@ -26,10 +26,7 @@ #ifndef __R8A66597_H__ #define __R8A66597_H__ -#ifdef CONFIG_HAVE_CLK #include -#endif - #include #define R8A66597_MAX_NUM_PIPE 10 @@ -113,9 +110,7 @@ struct r8a66597_root_hub { struct r8a66597 { spinlock_t lock; void __iomem *reg; -#ifdef CONFIG_HAVE_CLK struct clk *clk; -#endif struct r8a66597_platdata *pdata; struct r8a66597_device device0; struct r8a66597_root_hub root_hub[R8A66597_MAX_ROOT_HUB]; -- cgit v1.2.3-70-g09d2