diff options
Diffstat (limited to 'drivers/usb/host')
59 files changed, 2066 insertions, 2214 deletions
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index b3f20d7..a9707da 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -54,7 +54,7 @@ config USB_EHCI_HCD config USB_EHCI_ROOT_HUB_TT bool "Root Hub Transaction Translators" - depends on USB_EHCI_HCD || USB_CHIPIDEA_HOST + depends on USB_EHCI_HCD ---help--- Some EHCI chips have vendor-specific extensions to integrate transaction translators, so that no OHCI or UHCI companion @@ -66,7 +66,7 @@ config USB_EHCI_ROOT_HUB_TT config USB_EHCI_TT_NEWSCHED bool "Improved Transaction Translator scheduling" - depends on USB_EHCI_HCD || USB_CHIPIDEA_HOST + depends on USB_EHCI_HCD default y ---help--- This changes the periodic scheduling code to fill more of the low @@ -203,12 +203,11 @@ config USB_EHCI_SH Enables support for the on-chip EHCI controller on the SuperH. If you use the PCI EHCI controller, this option is not necessary. -config USB_EHCI_S5P +config USB_EHCI_EXYNOS tristate "EHCI support for Samsung S5P/EXYNOS SoC Series" depends on PLAT_S5P || ARCH_EXYNOS help - Enable support for the Samsung S5Pxxxx and Exynos3/4/5 SOC's - on-chip EHCI controller. + Enable support for the Samsung Exynos SOC's on-chip EHCI controller. config USB_EHCI_MV bool "EHCI support for Marvell PXA/MMP USB controller" @@ -224,7 +223,7 @@ config USB_EHCI_MV on-chip EHCI USB controller" for those. config USB_W90X900_EHCI - bool "W90X900(W90P910) EHCI support" + tristate "W90X900(W90P910) EHCI support" depends on ARCH_W90X900 ---help--- Enables support for the W90X900 USB controller @@ -367,14 +366,54 @@ config USB_OHCI_HCD if USB_OHCI_HCD config USB_OHCI_HCD_OMAP1 - bool "OHCI support for OMAP1/2 chips" + tristate "OHCI support for OMAP1/2 chips" depends on ARCH_OMAP1 default y ---help--- Enables support for the OHCI controller on OMAP1/2 chips. +config USB_OHCI_HCD_SPEAR + tristate "Support for ST SPEAr on-chip OHCI USB controller" + depends on USB_OHCI_HCD && PLAT_SPEAR + default y + ---help--- + Enables support for the on-chip OHCI controller on + ST SPEAr chips. + +config USB_OHCI_HCD_S3C2410 + tristate "OHCI support for Samsung S3C24xx/S3C64xx SoC series" + depends on USB_OHCI_HCD && (ARCH_S3C24XX || ARCH_S3C64XX) + default y + ---help--- + Enables support for the on-chip OHCI controller on + S3C24xx/S3C64xx chips. + +config USB_OHCI_HCD_LPC32XX + tristate "Support for LPC on-chip OHCI USB controller" + depends on USB_OHCI_HCD && ARCH_LPC32XX + default y + ---help--- + Enables support for the on-chip OHCI controller on + NXP chips. + +config USB_OHCI_HCD_PXA27X + tristate "Support for PXA27X/PXA3XX on-chip OHCI USB controller" + depends on USB_OHCI_HCD && (PXA27x || PXA3xx) + default y + ---help--- + Enables support for the on-chip OHCI controller on + PXA27x/PXA3xx chips. + +config USB_OHCI_HCD_AT91 + tristate "Support for Atmel on-chip OHCI USB controller" + depends on USB_OHCI_HCD && ARCH_AT91 + default y + ---help--- + Enables support for the on-chip OHCI controller on + Atmel chips. + config USB_OHCI_HCD_OMAP3 - bool "OHCI support for OMAP3 and later chips" + tristate "OHCI support for OMAP3 and later chips" depends on (ARCH_OMAP3 || ARCH_OMAP4) default y ---help--- @@ -454,8 +493,8 @@ config USB_OHCI_SH If you use the PCI OHCI controller, this option is not necessary. config USB_OHCI_EXYNOS - boolean "OHCI support for Samsung EXYNOS SoC Series" - depends on ARCH_EXYNOS + tristate "OHCI support for Samsung S5P/EXYNOS SoC Series" + depends on PLAT_S5P || ARCH_EXYNOS help Enable support for the Samsung Exynos SOC's on-chip OHCI controller. diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 50b0041..01e879e 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -34,10 +34,11 @@ obj-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o obj-$(CONFIG_USB_EHCI_HCD_OMAP) += ehci-omap.o obj-$(CONFIG_USB_EHCI_HCD_ORION) += ehci-orion.o obj-$(CONFIG_USB_EHCI_HCD_SPEAR) += ehci-spear.o -obj-$(CONFIG_USB_EHCI_S5P) += ehci-s5p.o +obj-$(CONFIG_USB_EHCI_EXYNOS) += ehci-exynos.o obj-$(CONFIG_USB_EHCI_HCD_AT91) += ehci-atmel.o obj-$(CONFIG_USB_EHCI_MSM) += ehci-msm.o obj-$(CONFIG_USB_EHCI_TEGRA) += ehci-tegra.o +obj-$(CONFIG_USB_W90X900_EHCI) += ehci-w90x900.o obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o @@ -46,6 +47,14 @@ obj-$(CONFIG_USB_ISP1362_HCD) += isp1362-hcd.o obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o obj-$(CONFIG_USB_OHCI_HCD_PCI) += ohci-pci.o obj-$(CONFIG_USB_OHCI_HCD_PLATFORM) += ohci-platform.o +obj-$(CONFIG_USB_OHCI_EXYNOS) += ohci-exynos.o +obj-$(CONFIG_USB_OHCI_HCD_OMAP1) += ohci-omap.o +obj-$(CONFIG_USB_OHCI_HCD_OMAP3) += ohci-omap3.o +obj-$(CONFIG_USB_OHCI_HCD_SPEAR) += ohci-spear.o +obj-$(CONFIG_USB_OHCI_HCD_AT91) += ohci-at91.o +obj-$(CONFIG_USB_OHCI_HCD_S3C2410) += ohci-s3c2410.o +obj-$(CONFIG_USB_OHCI_HCD_LPC32XX) += ohci-nxp.o +obj-$(CONFIG_USB_OHCI_HCD_PXA27X) += ohci-pxa27x.o obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o obj-$(CONFIG_USB_FHCI_HCD) += fhci.o diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c index 3b645ff..f417526 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -30,13 +30,17 @@ static const char hcd_name[] = "ehci-atmel"; static struct hc_driver __read_mostly ehci_atmel_hc_driver; /* interface and function clocks */ -static struct clk *iclk, *fclk; +static struct clk *iclk, *fclk, *uclk; static int clocked; /*-------------------------------------------------------------------------*/ static void atmel_start_clock(void) { + if (IS_ENABLED(CONFIG_COMMON_CLK)) { + clk_set_rate(uclk, 48000000); + clk_prepare_enable(uclk); + } clk_prepare_enable(iclk); clk_prepare_enable(fclk); clocked = 1; @@ -46,6 +50,8 @@ static void atmel_stop_clock(void) { clk_disable_unprepare(fclk); clk_disable_unprepare(iclk); + if (IS_ENABLED(CONFIG_COMMON_CLK)) + clk_disable_unprepare(uclk); clocked = 0; } @@ -130,6 +136,14 @@ static int ehci_atmel_drv_probe(struct platform_device *pdev) retval = -ENOENT; goto fail_request_resource; } + if (IS_ENABLED(CONFIG_COMMON_CLK)) { + uclk = devm_clk_get(&pdev->dev, "usb_clk"); + if (IS_ERR(uclk)) { + dev_err(&pdev->dev, "failed to get uclk\n"); + retval = PTR_ERR(uclk); + goto fail_request_resource; + } + } ehci = hcd_to_ehci(hcd); /* registers start at offset 0x0 */ diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index aa5b603..4a9c2ed 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -334,6 +334,7 @@ static inline void remove_debug_files (struct ehci_hcd *bus) { } /* troubleshooting help: expose state in debugfs */ static int debug_async_open(struct inode *, struct file *); +static int debug_bandwidth_open(struct inode *, struct file *); static int debug_periodic_open(struct inode *, struct file *); static int debug_registers_open(struct inode *, struct file *); @@ -347,6 +348,13 @@ static const struct file_operations debug_async_fops = { .release = debug_close, .llseek = default_llseek, }; +static const struct file_operations debug_bandwidth_fops = { + .owner = THIS_MODULE, + .open = debug_bandwidth_open, + .read = debug_output, + .release = debug_close, + .llseek = default_llseek, +}; static const struct file_operations debug_periodic_fops = { .owner = THIS_MODULE, .open = debug_periodic_open, @@ -379,7 +387,7 @@ struct debug_buffer { case QH_LOW_SPEED: tmp = 'l'; break; \ case QH_HIGH_SPEED: tmp = 'h'; break; \ default: tmp = '?'; break; \ - }; tmp; }) + } tmp; }) static inline char token_mark(struct ehci_hcd *ehci, __hc32 token) { @@ -525,6 +533,89 @@ static ssize_t fill_async_buffer(struct debug_buffer *buf) return strlen(buf->output_buf); } +static ssize_t fill_bandwidth_buffer(struct debug_buffer *buf) +{ + struct ehci_hcd *ehci; + struct ehci_tt *tt; + struct ehci_per_sched *ps; + unsigned temp, size; + char *next; + unsigned i; + u8 *bw; + u16 *bf; + u8 budget[EHCI_BANDWIDTH_SIZE]; + + ehci = hcd_to_ehci(bus_to_hcd(buf->bus)); + next = buf->output_buf; + size = buf->alloc_size; + + *next = 0; + + spin_lock_irq(&ehci->lock); + + /* Dump the HS bandwidth table */ + temp = scnprintf(next, size, + "HS bandwidth allocation (us per microframe)\n"); + size -= temp; + next += temp; + for (i = 0; i < EHCI_BANDWIDTH_SIZE; i += 8) { + bw = &ehci->bandwidth[i]; + temp = scnprintf(next, size, + "%2u: %4u%4u%4u%4u%4u%4u%4u%4u\n", + i, bw[0], bw[1], bw[2], bw[3], + bw[4], bw[5], bw[6], bw[7]); + size -= temp; + next += temp; + } + + /* Dump all the FS/LS tables */ + list_for_each_entry(tt, &ehci->tt_list, tt_list) { + temp = scnprintf(next, size, + "\nTT %s port %d FS/LS bandwidth allocation (us per frame)\n", + dev_name(&tt->usb_tt->hub->dev), + tt->tt_port + !!tt->usb_tt->multi); + size -= temp; + next += temp; + + bf = tt->bandwidth; + temp = scnprintf(next, size, + " %5u%5u%5u%5u%5u%5u%5u%5u\n", + bf[0], bf[1], bf[2], bf[3], + bf[4], bf[5], bf[6], bf[7]); + size -= temp; + next += temp; + + temp = scnprintf(next, size, + "FS/LS budget (us per microframe)\n"); + size -= temp; + next += temp; + compute_tt_budget(budget, tt); + for (i = 0; i < EHCI_BANDWIDTH_SIZE; i += 8) { + bw = &budget[i]; + temp = scnprintf(next, size, + "%2u: %4u%4u%4u%4u%4u%4u%4u%4u\n", + i, bw[0], bw[1], bw[2], bw[3], + bw[4], bw[5], bw[6], bw[7]); + size -= temp; + next += temp; + } + list_for_each_entry(ps, &tt->ps_list, ps_list) { + temp = scnprintf(next, size, + "%s ep %02x: %4u @ %2u.%u+%u mask %04x\n", + dev_name(&ps->udev->dev), + ps->ep->desc.bEndpointAddress, + ps->tt_usecs, + ps->bw_phase, ps->phase_uf, + ps->bw_period, ps->cs_mask); + size -= temp; + next += temp; + } + } + spin_unlock_irq(&ehci->lock); + + return next - buf->output_buf; +} + #define DBG_SCHED_LIMIT 64 static ssize_t fill_periodic_buffer(struct debug_buffer *buf) { @@ -571,7 +662,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf) case Q_TYPE_QH: hw = p.qh->hw; temp = scnprintf (next, size, " qh%d-%04x/%p", - p.qh->period, + p.qh->ps.period, hc32_to_cpup(ehci, &hw->hw_info2) /* uframe masks */ @@ -618,7 +709,8 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf) speed_char (scratch), scratch & 0x007f, (scratch >> 8) & 0x000f, type, - p.qh->usecs, p.qh->c_usecs, + p.qh->ps.usecs, + p.qh->ps.c_usecs, temp, 0x7ff & (scratch >> 16)); @@ -645,7 +737,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf) case Q_TYPE_SITD: temp = scnprintf (next, size, " sitd%d-%04x/%p", - p.sitd->stream->interval, + p.sitd->stream->ps.period, hc32_to_cpup(ehci, &p.sitd->hw_uframe) & 0x0000ffff, p.sitd); @@ -918,6 +1010,7 @@ static int debug_close(struct inode *inode, struct file *file) return 0; } + static int debug_async_open(struct inode *inode, struct file *file) { file->private_data = alloc_buffer(inode->i_private, fill_async_buffer); @@ -925,6 +1018,14 @@ static int debug_async_open(struct inode *inode, struct file *file) return file->private_data ? 0 : -ENOMEM; } +static int debug_bandwidth_open(struct inode *inode, struct file *file) +{ + file->private_data = alloc_buffer(inode->i_private, + fill_bandwidth_buffer); + + return file->private_data ? 0 : -ENOMEM; +} + static int debug_periodic_open(struct inode *inode, struct file *file) { struct debug_buffer *buf; @@ -957,6 +1058,10 @@ static inline void create_debug_files (struct ehci_hcd *ehci) &debug_async_fops)) goto file_error; + if (!debugfs_create_file("bandwidth", S_IRUGO, ehci->debug_dir, bus, + &debug_bandwidth_fops)) + goto file_error; + if (!debugfs_create_file("periodic", S_IRUGO, ehci->debug_dir, bus, &debug_periodic_fops)) goto file_error; diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-exynos.c index 7c3de95..016352e 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-exynos.c @@ -1,5 +1,5 @@ /* - * SAMSUNG S5P USB HOST EHCI Controller + * SAMSUNG EXYNOS USB HOST EHCI Controller * * Copyright (C) 2011 Samsung Electronics Co.Ltd * Author: Jingoo Han <jg1.han@samsung.com> @@ -20,7 +20,6 @@ #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/platform_device.h> -#include <linux/platform_data/usb-ehci-s5p.h> #include <linux/usb/phy.h> #include <linux/usb/samsung_usb_phy.h> #include <linux/usb.h> @@ -29,7 +28,7 @@ #include "ehci.h" -#define DRIVER_DESC "EHCI s5p driver" +#define DRIVER_DESC "EHCI EXYNOS driver" #define EHCI_INSNREG00(base) (base + 0x90) #define EHCI_INSNREG00_ENA_INCR16 (0x1 << 25) @@ -40,21 +39,18 @@ (EHCI_INSNREG00_ENA_INCR16 | EHCI_INSNREG00_ENA_INCR8 | \ EHCI_INSNREG00_ENA_INCR4 | EHCI_INSNREG00_ENA_INCRX_ALIGN) -static const char hcd_name[] = "ehci-s5p"; -static struct hc_driver __read_mostly s5p_ehci_hc_driver; +static const char hcd_name[] = "ehci-exynos"; +static struct hc_driver __read_mostly exynos_ehci_hc_driver; -struct s5p_ehci_hcd { +struct exynos_ehci_hcd { struct clk *clk; struct usb_phy *phy; struct usb_otg *otg; - struct s5p_ehci_platdata *pdata; }; -static struct s5p_ehci_platdata empty_platdata; +#define to_exynos_ehci(hcd) (struct exynos_ehci_hcd *)(hcd_to_ehci(hcd)->priv) -#define to_s5p_ehci(hcd) (struct s5p_ehci_hcd *)(hcd_to_ehci(hcd)->priv) - -static void s5p_setup_vbus_gpio(struct platform_device *pdev) +static void exynos_setup_vbus_gpio(struct platform_device *pdev) { struct device *dev = &pdev->dev; int err; @@ -73,10 +69,9 @@ static void s5p_setup_vbus_gpio(struct platform_device *pdev) dev_err(dev, "can't request ehci vbus gpio %d", gpio); } -static int s5p_ehci_probe(struct platform_device *pdev) +static int exynos_ehci_probe(struct platform_device *pdev) { - struct s5p_ehci_platdata *pdata = dev_get_platdata(&pdev->dev); - struct s5p_ehci_hcd *s5p_ehci; + struct exynos_ehci_hcd *exynos_ehci; struct usb_hcd *hcd; struct ehci_hcd *ehci; struct resource *res; @@ -94,48 +89,41 @@ static int 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); + exynos_setup_vbus_gpio(pdev); - hcd = usb_create_hcd(&s5p_ehci_hc_driver, + hcd = usb_create_hcd(&exynos_ehci_hc_driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { dev_err(&pdev->dev, "Unable to create HCD\n"); return -ENOMEM; } - s5p_ehci = to_s5p_ehci(hcd); + exynos_ehci = to_exynos_ehci(hcd); if (of_device_is_compatible(pdev->dev.of_node, - "samsung,exynos5440-ehci")) { - s5p_ehci->pdata = &empty_platdata; + "samsung,exynos5440-ehci")) goto skip_phy; - } phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); if (IS_ERR(phy)) { - /* Fallback to pdata */ - if (!pdata) { - usb_put_hcd(hcd); - dev_warn(&pdev->dev, "no platform data or transceiver defined\n"); - return -EPROBE_DEFER; - } else { - s5p_ehci->pdata = pdata; - } + usb_put_hcd(hcd); + dev_warn(&pdev->dev, "no platform data or transceiver defined\n"); + return -EPROBE_DEFER; } else { - s5p_ehci->phy = phy; - s5p_ehci->otg = phy->otg; + exynos_ehci->phy = phy; + exynos_ehci->otg = phy->otg; } skip_phy: - s5p_ehci->clk = devm_clk_get(&pdev->dev, "usbhost"); + exynos_ehci->clk = devm_clk_get(&pdev->dev, "usbhost"); - if (IS_ERR(s5p_ehci->clk)) { + if (IS_ERR(exynos_ehci->clk)) { dev_err(&pdev->dev, "Failed to get usbhost clock\n"); - err = PTR_ERR(s5p_ehci->clk); + err = PTR_ERR(exynos_ehci->clk); goto fail_clk; } - err = clk_prepare_enable(s5p_ehci->clk); + err = clk_prepare_enable(exynos_ehci->clk); if (err) goto fail_clk; @@ -162,13 +150,11 @@ skip_phy: goto fail_io; } - if (s5p_ehci->otg) - s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self); + if (exynos_ehci->otg) + exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self); - if (s5p_ehci->phy) - usb_phy_init(s5p_ehci->phy); - else if (s5p_ehci->pdata->phy_init) - s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST); + if (exynos_ehci->phy) + usb_phy_init(exynos_ehci->phy); ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs; @@ -187,33 +173,29 @@ skip_phy: return 0; fail_add_hcd: - if (s5p_ehci->phy) - usb_phy_shutdown(s5p_ehci->phy); - else if (s5p_ehci->pdata->phy_exit) - s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST); + if (exynos_ehci->phy) + usb_phy_shutdown(exynos_ehci->phy); fail_io: - clk_disable_unprepare(s5p_ehci->clk); + clk_disable_unprepare(exynos_ehci->clk); fail_clk: usb_put_hcd(hcd); return err; } -static int s5p_ehci_remove(struct platform_device *pdev) +static int exynos_ehci_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); - struct s5p_ehci_hcd *s5p_ehci = to_s5p_ehci(hcd); + struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd); usb_remove_hcd(hcd); - if (s5p_ehci->otg) - s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self); + if (exynos_ehci->otg) + exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self); - if (s5p_ehci->phy) - usb_phy_shutdown(s5p_ehci->phy); - else if (s5p_ehci->pdata->phy_exit) - s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST); + if (exynos_ehci->phy) + usb_phy_shutdown(exynos_ehci->phy); - clk_disable_unprepare(s5p_ehci->clk); + clk_disable_unprepare(exynos_ehci->clk); usb_put_hcd(hcd); @@ -221,45 +203,39 @@ static int s5p_ehci_remove(struct platform_device *pdev) } #ifdef CONFIG_PM -static int s5p_ehci_suspend(struct device *dev) +static int exynos_ehci_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - struct s5p_ehci_hcd *s5p_ehci = to_s5p_ehci(hcd); - struct platform_device *pdev = to_platform_device(dev); + struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd); bool do_wakeup = device_may_wakeup(dev); int rc; rc = ehci_suspend(hcd, do_wakeup); - if (s5p_ehci->otg) - s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self); + if (exynos_ehci->otg) + exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self); - if (s5p_ehci->phy) - usb_phy_shutdown(s5p_ehci->phy); - else if (s5p_ehci->pdata->phy_exit) - s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST); + if (exynos_ehci->phy) + usb_phy_shutdown(exynos_ehci->phy); - clk_disable_unprepare(s5p_ehci->clk); + clk_disable_unprepare(exynos_ehci->clk); return rc; } -static int s5p_ehci_resume(struct device *dev) +static int exynos_ehci_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - struct s5p_ehci_hcd *s5p_ehci = to_s5p_ehci(hcd); - struct platform_device *pdev = to_platform_device(dev); + struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd); - clk_prepare_enable(s5p_ehci->clk); + clk_prepare_enable(exynos_ehci->clk); - if (s5p_ehci->otg) - s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self); + if (exynos_ehci->otg) + exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self); - if (s5p_ehci->phy) - usb_phy_init(s5p_ehci->phy); - else if (s5p_ehci->pdata->phy_init) - s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST); + if (exynos_ehci->phy) + usb_phy_init(exynos_ehci->phy); /* DMA burst Enable */ writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs)); @@ -268,13 +244,13 @@ static int s5p_ehci_resume(struct device *dev) return 0; } #else -#define s5p_ehci_suspend NULL -#define s5p_ehci_resume NULL +#define exynos_ehci_suspend NULL +#define exynos_ehci_resume NULL #endif -static const struct dev_pm_ops s5p_ehci_pm_ops = { - .suspend = s5p_ehci_suspend, - .resume = s5p_ehci_resume, +static const struct dev_pm_ops exynos_ehci_pm_ops = { + .suspend = exynos_ehci_suspend, + .resume = exynos_ehci_resume, }; #ifdef CONFIG_OF @@ -286,40 +262,40 @@ static const struct of_device_id exynos_ehci_match[] = { MODULE_DEVICE_TABLE(of, exynos_ehci_match); #endif -static struct platform_driver s5p_ehci_driver = { - .probe = s5p_ehci_probe, - .remove = s5p_ehci_remove, +static struct platform_driver exynos_ehci_driver = { + .probe = exynos_ehci_probe, + .remove = exynos_ehci_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { - .name = "s5p-ehci", + .name = "exynos-ehci", .owner = THIS_MODULE, - .pm = &s5p_ehci_pm_ops, + .pm = &exynos_ehci_pm_ops, .of_match_table = of_match_ptr(exynos_ehci_match), } }; -static const struct ehci_driver_overrides s5p_overrides __initdata = { - .extra_priv_size = sizeof(struct s5p_ehci_hcd), +static const struct ehci_driver_overrides exynos_overrides __initdata = { + .extra_priv_size = sizeof(struct exynos_ehci_hcd), }; -static int __init ehci_s5p_init(void) +static int __init ehci_exynos_init(void) { if (usb_disabled()) return -ENODEV; pr_info("%s: " DRIVER_DESC "\n", hcd_name); - ehci_init_driver(&s5p_ehci_hc_driver, &s5p_overrides); - return platform_driver_register(&s5p_ehci_driver); + ehci_init_driver(&exynos_ehci_hc_driver, &exynos_overrides); + return platform_driver_register(&exynos_ehci_driver); } -module_init(ehci_s5p_init); +module_init(ehci_exynos_init); -static void __exit ehci_s5p_cleanup(void) +static void __exit ehci_exynos_cleanup(void) { - platform_driver_unregister(&s5p_ehci_driver); + platform_driver_unregister(&exynos_ehci_driver); } -module_exit(ehci_s5p_cleanup); +module_exit(ehci_exynos_cleanup); MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_ALIAS("platform:s5p-ehci"); +MODULE_ALIAS("platform:exynos-ehci"); MODULE_AUTHOR("Jingoo Han"); MODULE_AUTHOR("Joonyoung Shim"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index f2407b2..a06d501 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -57,7 +57,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, pr_debug("initializing FSL-SOC USB Controller\n"); /* Need platform data for setup */ - pdata = (struct fsl_usb2_platform_data *)dev_get_platdata(&pdev->dev); + pdata = dev_get_platdata(&pdev->dev); if (!pdata) { dev_err(&pdev->dev, "No platform data for %s.\n", dev_name(&pdev->dev)); @@ -664,7 +664,7 @@ static const struct hc_driver ehci_fsl_hc_driver = { * generic hardware linkage */ .irq = ehci_irq, - .flags = HCD_USB2 | HCD_MEMORY, + .flags = HCD_USB2 | HCD_MEMORY | HCD_BH, /* * basic lifecycle operations diff --git a/drivers/usb/host/ehci-grlib.c b/drivers/usb/host/ehci-grlib.c index 83ab51a..b52a66c 100644 --- a/drivers/usb/host/ehci-grlib.c +++ b/drivers/usb/host/ehci-grlib.c @@ -43,7 +43,7 @@ static const struct hc_driver ehci_grlib_hc_driver = { * generic hardware linkage */ .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2, + .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, /* * basic lifecycle operations diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 86ab9fd..e8ba4c4 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -110,6 +110,9 @@ MODULE_PARM_DESC (ignore_oc, "ignore bogus hardware overcurrent indications"); #include "ehci.h" #include "pci-quirks.h" +static void compute_tt_budget(u8 budget_table[EHCI_BANDWIDTH_SIZE], + struct ehci_tt *tt); + /* * The MosChip MCS9990 controller updates its microframe counter * a little before the frame counter, and occasionally we will read @@ -484,6 +487,7 @@ static int ehci_init(struct usb_hcd *hcd) INIT_LIST_HEAD(&ehci->intr_qh_list); INIT_LIST_HEAD(&ehci->cached_itd_list); INIT_LIST_HEAD(&ehci->cached_sitd_list); + INIT_LIST_HEAD(&ehci->tt_list); if (HCC_PGM_FRAMELISTLEN(hcc_params)) { /* periodic schedule size can be smaller than default */ @@ -956,6 +960,7 @@ rescan: goto idle_timeout; /* BUG_ON(!list_empty(&stream->free_list)); */ + reserve_release_iso_bandwidth(ehci, stream, -1); kfree(stream); goto done; } @@ -982,6 +987,8 @@ idle_timeout: if (qh->clearing_tt) goto idle_timeout; if (list_empty (&qh->qtd_list)) { + if (qh->ps.bw_uperiod) + reserve_release_intr_bandwidth(ehci, qh, -1); qh_destroy(ehci, qh); break; } @@ -1022,7 +1029,6 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) * the toggle bit in the QH. */ if (qh) { - usb_settoggle(qh->dev, epnum, is_out, 0); if (!list_empty(&qh->qtd_list)) { WARN_ONCE(1, "clear_halt for a busy endpoint\n"); } else { @@ -1030,6 +1036,7 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) * while the QH is active. Unlink it now; * re-linking will call qh_refresh(). */ + usb_settoggle(qh->ps.udev, epnum, is_out, 0); qh->exception = 1; if (eptype == USB_ENDPOINT_XFER_BULK) start_unlink_async(ehci, qh); @@ -1048,6 +1055,19 @@ static int ehci_get_frame (struct usb_hcd *hcd) /*-------------------------------------------------------------------------*/ +/* Device addition and removal */ + +static void ehci_remove_device(struct usb_hcd *hcd, struct usb_device *udev) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + + spin_lock_irq(&ehci->lock); + drop_tt(udev); + spin_unlock_irq(&ehci->lock); +} + +/*-------------------------------------------------------------------------*/ + #ifdef CONFIG_PM /* suspend/resume, section 4.3 */ @@ -1075,6 +1095,14 @@ int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup) clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); spin_unlock_irq(&ehci->lock); + synchronize_irq(hcd->irq); + + /* Check for race with a wakeup request */ + if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) { + ehci_resume(hcd, false); + return -EBUSY; + } + return 0; } EXPORT_SYMBOL_GPL(ehci_suspend); @@ -1158,7 +1186,7 @@ static const struct hc_driver ehci_hc_driver = { * generic hardware linkage */ .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2, + .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, /* * basic lifecycle operations @@ -1191,6 +1219,11 @@ static const struct hc_driver ehci_hc_driver = { .bus_resume = ehci_bus_resume, .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + + /* + * device support + */ + .free_dev = ehci_remove_device, }; void ehci_init_driver(struct hc_driver *drv, @@ -1238,11 +1271,6 @@ MODULE_LICENSE ("GPL"); #define XILINX_OF_PLATFORM_DRIVER ehci_hcd_xilinx_of_driver #endif -#ifdef CONFIG_USB_W90X900_EHCI -#include "ehci-w90x900.c" -#define PLATFORM_DRIVER ehci_hcd_w90x900_driver -#endif - #ifdef CONFIG_USB_OCTEON_EHCI #include "ehci-octeon.c" #define PLATFORM_DRIVER ehci_octeon_driver diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c index 52a7773..c0fb6a8 100644 --- a/drivers/usb/host/ehci-mem.c +++ b/drivers/usb/host/ehci-mem.c @@ -224,11 +224,11 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags) hw->hw_next = EHCI_LIST_END(ehci); hw->hw_qtd_next = EHCI_LIST_END(ehci); hw->hw_alt_next = EHCI_LIST_END(ehci); - hw->hw_token &= ~QTD_STS_ACTIVE; ehci->dummy->hw = hw; for (i = 0; i < ehci->periodic_size; i++) - ehci->periodic[i] = ehci->dummy->qh_dma; + ehci->periodic[i] = cpu_to_hc32(ehci, + ehci->dummy->qh_dma); } else { for (i = 0; i < ehci->periodic_size; i++) ehci->periodic[i] = EHCI_LIST_END(ehci); diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index 0f717dc..f341651 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -42,7 +42,6 @@ static const char hcd_name[] = "ehci-msm"; static struct hc_driver __read_mostly msm_hc_driver; -static struct usb_phy *phy; static int ehci_msm_reset(struct usb_hcd *hcd) { @@ -70,6 +69,7 @@ static int ehci_msm_probe(struct platform_device *pdev) { struct usb_hcd *hcd; struct resource *res; + struct usb_phy *phy; int ret; dev_dbg(&pdev->dev, "ehci_msm proble\n"); @@ -108,10 +108,14 @@ static int ehci_msm_probe(struct platform_device *pdev) * powering up VBUS, mapping of registers address space and power * management. */ - phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); + if (pdev->dev.of_node) + phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0); + else + phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); + if (IS_ERR(phy)) { dev_err(&pdev->dev, "unable to find transceiver\n"); - ret = -ENODEV; + ret = -EPROBE_DEFER; goto put_hcd; } @@ -121,6 +125,7 @@ static int ehci_msm_probe(struct platform_device *pdev) goto put_hcd; } + hcd->phy = phy; device_init_wakeup(&pdev->dev, 1); /* * OTG device parent of HCD takes care of putting @@ -147,7 +152,7 @@ static int ehci_msm_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); - otg_set_host(phy->otg, NULL); + otg_set_host(hcd->phy->otg, NULL); /* FIXME: need to call usb_remove_hcd() here? */ @@ -186,12 +191,19 @@ static const struct dev_pm_ops ehci_msm_dev_pm_ops = { .resume = ehci_msm_pm_resume, }; +static struct of_device_id msm_ehci_dt_match[] = { + { .compatible = "qcom,ehci-host", }, + {} +}; +MODULE_DEVICE_TABLE(of, msm_ehci_dt_match); + static struct platform_driver ehci_msm_driver = { .probe = ehci_msm_probe, .remove = ehci_msm_remove, .driver = { .name = "msm_hsusb_host", .pm = &ehci_msm_dev_pm_ops, + .of_match_table = msm_ehci_dt_match, }, }; diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index 35cdbd8..417c10d 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -96,7 +96,7 @@ static const struct hc_driver mv_ehci_hc_driver = { * generic hardware linkage */ .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2, + .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, /* * basic lifecycle operations diff --git a/drivers/usb/host/ehci-octeon.c b/drivers/usb/host/ehci-octeon.c index 45cc001..ab0397e 100644 --- a/drivers/usb/host/ehci-octeon.c +++ b/drivers/usb/host/ehci-octeon.c @@ -51,7 +51,7 @@ static const struct hc_driver ehci_octeon_hc_driver = { * generic hardware linkage */ .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2, + .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, /* * basic lifecycle operations diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 854c2ec..3e86bf4 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -58,8 +58,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct pci_dev *pdev = to_pci_dev(hcd->self.controller); - struct pci_dev *p_smbus; - u8 rev; u32 temp; int retval; @@ -175,22 +173,12 @@ static int ehci_pci_setup(struct usb_hcd *hcd) /* SB600 and old version of SB700 have a bug in EHCI controller, * which causes usb devices lose response in some cases. */ - if ((pdev->device == 0x4386) || (pdev->device == 0x4396)) { - p_smbus = pci_get_device(PCI_VENDOR_ID_ATI, - PCI_DEVICE_ID_ATI_SBX00_SMBUS, - NULL); - if (!p_smbus) - break; - rev = p_smbus->revision; - if ((pdev->device == 0x4386) || (rev == 0x3a) - || (rev == 0x3b)) { - u8 tmp; - ehci_info(ehci, "applying AMD SB600/SB700 USB " - "freeze workaround\n"); - pci_read_config_byte(pdev, 0x53, &tmp); - pci_write_config_byte(pdev, 0x53, tmp | (1<<3)); - } - pci_dev_put(p_smbus); + if ((pdev->device == 0x4386 || pdev->device == 0x4396) && + usb_amd_hang_symptom_quirk()) { + u8 tmp; + ehci_info(ehci, "applying AMD SB600/SB700 USB freeze workaround\n"); + pci_read_config_byte(pdev, 0x53, &tmp); + pci_write_config_byte(pdev, 0x53, tmp | (1<<3)); } break; case PCI_VENDOR_ID_NETMOS: diff --git a/drivers/usb/host/ehci-pmcmsp.c b/drivers/usb/host/ehci-pmcmsp.c index 601e208..893b707 100644 --- a/drivers/usb/host/ehci-pmcmsp.c +++ b/drivers/usb/host/ehci-pmcmsp.c @@ -286,7 +286,7 @@ static const struct hc_driver ehci_msp_hc_driver = { #else .irq = ehci_irq, #endif - .flags = HCD_MEMORY | HCD_USB2, + .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, /* * basic lifecycle operations diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c index 932293f..6cc5567 100644 --- a/drivers/usb/host/ehci-ppc-of.c +++ b/drivers/usb/host/ehci-ppc-of.c @@ -28,7 +28,7 @@ static const struct hc_driver ehci_ppc_of_hc_driver = { * generic hardware linkage */ .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2, + .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, /* * basic lifecycle operations diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index fd98377..8188542 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c @@ -71,7 +71,7 @@ static const struct hc_driver ps3_ehci_hc_driver = { .product_desc = "PS3 EHCI Host Controller", .hcd_priv_size = sizeof(struct ehci_hcd), .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2, + .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, .reset = ps3_ehci_hc_reset, .start = ehci_run, .stop = ehci_stop, diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index a7f776a..db05bd8 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -105,9 +105,9 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd) is_out = qh->is_out; epnum = (hc32_to_cpup(ehci, &hw->hw_info1) >> 8) & 0x0f; - if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) { + if (unlikely(!usb_gettoggle(qh->ps.udev, epnum, is_out))) { hw->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE); - usb_settoggle (qh->dev, epnum, is_out, 1); + usb_settoggle(qh->ps.udev, epnum, is_out, 1); } } @@ -247,8 +247,6 @@ static int qtd_copy_status ( static void ehci_urb_done(struct ehci_hcd *ehci, struct urb *urb, int status) -__releases(ehci->lock) -__acquires(ehci->lock) { if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) { /* ... update hc-wide periodic stats */ @@ -274,11 +272,8 @@ __acquires(ehci->lock) urb->actual_length, urb->transfer_buffer_length); #endif - /* complete() can reenter this HCD */ usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); - spin_unlock (&ehci->lock); usb_hcd_giveback_urb(ehci_to_hcd(ehci), urb, status); - spin_lock (&ehci->lock); } static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh); @@ -802,26 +797,35 @@ qh_make ( * For control/bulk requests, the HC or TT handles these. */ if (type == PIPE_INTERRUPT) { - qh->usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH, + unsigned tmp; + + qh->ps.usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH, is_input, 0, hb_mult(maxp) * max_packet(maxp))); - qh->start = NO_FRAME; + qh->ps.phase = NO_FRAME; if (urb->dev->speed == USB_SPEED_HIGH) { - qh->c_usecs = 0; + qh->ps.c_usecs = 0; qh->gap_uf = 0; - qh->period = urb->interval >> 3; - if (qh->period == 0 && urb->interval != 1) { + if (urb->interval > 1 && urb->interval < 8) { /* NOTE interval 2 or 4 uframes could work. * But interval 1 scheduling is simpler, and * includes high bandwidth. */ urb->interval = 1; - } else if (qh->period > ehci->periodic_size) { - qh->period = ehci->periodic_size; - urb->interval = qh->period << 3; + } else if (urb->interval > ehci->periodic_size << 3) { + urb->interval = ehci->periodic_size << 3; } + qh->ps.period = urb->interval >> 3; + + /* period for bandwidth allocation */ + tmp = min_t(unsigned, EHCI_BANDWIDTH_SIZE, + 1 << (urb->ep->desc.bInterval - 1)); + + /* Allow urb->interval to override */ + qh->ps.bw_uperiod = min_t(unsigned, tmp, urb->interval); + qh->ps.bw_period = qh->ps.bw_uperiod >> 3; } else { int think_time; @@ -831,27 +835,35 @@ qh_make ( /* FIXME this just approximates SPLIT/CSPLIT times */ if (is_input) { // SPLIT, gap, CSPLIT+DATA - qh->c_usecs = qh->usecs + HS_USECS (0); - qh->usecs = HS_USECS (1); + qh->ps.c_usecs = qh->ps.usecs + HS_USECS(0); + qh->ps.usecs = HS_USECS(1); } else { // SPLIT+DATA, gap, CSPLIT - qh->usecs += HS_USECS (1); - qh->c_usecs = HS_USECS (0); + qh->ps.usecs += HS_USECS(1); + qh->ps.c_usecs = HS_USECS(0); } think_time = tt ? tt->think_time : 0; - qh->tt_usecs = NS_TO_US (think_time + + qh->ps.tt_usecs = NS_TO_US(think_time + usb_calc_bus_time (urb->dev->speed, is_input, 0, max_packet (maxp))); - qh->period = urb->interval; - if (qh->period > ehci->periodic_size) { - qh->period = ehci->periodic_size; - urb->interval = qh->period; - } + if (urb->interval > ehci->periodic_size) + urb->interval = ehci->periodic_size; + qh->ps.period = urb->interval; + + /* period for bandwidth allocation */ + tmp = min_t(unsigned, EHCI_BANDWIDTH_FRAMES, + urb->ep->desc.bInterval); + tmp = rounddown_pow_of_two(tmp); + + /* Allow urb->interval to override */ + qh->ps.bw_period = min_t(unsigned, tmp, urb->interval); + qh->ps.bw_uperiod = qh->ps.bw_period << 3; } } /* support for tt scheduling, and access to toggles */ - qh->dev = urb->dev; + qh->ps.udev = urb->dev; + qh->ps.ep = urb->ep; /* using TT? */ switch (urb->dev->speed) { diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 85dd24e..e113fd7 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -103,83 +103,210 @@ static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr) *hw_p = *shadow_next_periodic(ehci, &here, Q_NEXT_TYPE(ehci, *hw_p)); else - *hw_p = ehci->dummy->qh_dma; + *hw_p = cpu_to_hc32(ehci, ehci->dummy->qh_dma); } -/* how many of the uframe's 125 usecs are allocated? */ -static unsigned short -periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe) +/*-------------------------------------------------------------------------*/ + +/* Bandwidth and TT management */ + +/* Find the TT data structure for this device; create it if necessary */ +static struct ehci_tt *find_tt(struct usb_device *udev) { - __hc32 *hw_p = &ehci->periodic [frame]; - union ehci_shadow *q = &ehci->pshadow [frame]; - unsigned usecs = 0; - struct ehci_qh_hw *hw; - - while (q->ptr) { - switch (hc32_to_cpu(ehci, Q_NEXT_TYPE(ehci, *hw_p))) { - case Q_TYPE_QH: - hw = q->qh->hw; - /* is it in the S-mask? */ - if (hw->hw_info2 & cpu_to_hc32(ehci, 1 << uframe)) - usecs += q->qh->usecs; - /* ... or C-mask? */ - if (hw->hw_info2 & cpu_to_hc32(ehci, - 1 << (8 + uframe))) - usecs += q->qh->c_usecs; - hw_p = &hw->hw_next; - q = &q->qh->qh_next; - break; - // case Q_TYPE_FSTN: - default: - /* for "save place" FSTNs, count the relevant INTR - * bandwidth from the previous frame - */ - if (q->fstn->hw_prev != EHCI_LIST_END(ehci)) { - ehci_dbg (ehci, "ignoring FSTN cost ...\n"); - } - hw_p = &q->fstn->hw_next; - q = &q->fstn->fstn_next; - break; - case Q_TYPE_ITD: - if (q->itd->hw_transaction[uframe]) - usecs += q->itd->stream->usecs; - hw_p = &q->itd->hw_next; - q = &q->itd->itd_next; - break; - case Q_TYPE_SITD: - /* is it in the S-mask? (count SPLIT, DATA) */ - if (q->sitd->hw_uframe & cpu_to_hc32(ehci, - 1 << uframe)) { - if (q->sitd->hw_fullspeed_ep & - cpu_to_hc32(ehci, 1<<31)) - usecs += q->sitd->stream->usecs; - else /* worst case for OUT start-split */ - usecs += HS_USECS_ISO (188); - } + struct usb_tt *utt = udev->tt; + struct ehci_tt *tt, **tt_index, **ptt; + unsigned port; + bool allocated_index = false; + + if (!utt) + return NULL; /* Not below a TT */ + + /* + * Find/create our data structure. + * For hubs with a single TT, we get it directly. + * For hubs with multiple TTs, there's an extra level of pointers. + */ + tt_index = NULL; + if (utt->multi) { + tt_index = utt->hcpriv; + if (!tt_index) { /* Create the index array */ + tt_index = kzalloc(utt->hub->maxchild * + sizeof(*tt_index), GFP_ATOMIC); + if (!tt_index) + return ERR_PTR(-ENOMEM); + utt->hcpriv = tt_index; + allocated_index = true; + } + port = udev->ttport - 1; + ptt = &tt_index[port]; + } else { + port = 0; + ptt = (struct ehci_tt **) &utt->hcpriv; + } + + tt = *ptt; + if (!tt) { /* Create the ehci_tt */ + struct ehci_hcd *ehci = + hcd_to_ehci(bus_to_hcd(udev->bus)); - /* ... C-mask? (count CSPLIT, DATA) */ - if (q->sitd->hw_uframe & - cpu_to_hc32(ehci, 1 << (8 + uframe))) { - /* worst case for IN complete-split */ - usecs += q->sitd->stream->c_usecs; + tt = kzalloc(sizeof(*tt), GFP_ATOMIC); + if (!tt) { + if (allocated_index) { + utt->hcpriv = NULL; + kfree(tt_index); } + return ERR_PTR(-ENOMEM); + } + list_add_tail(&tt->tt_list, &ehci->tt_list); + INIT_LIST_HEAD(&tt->ps_list); + tt->usb_tt = utt; + tt->tt_port = port; + *ptt = tt; + } - hw_p = &q->sitd->hw_next; - q = &q->sitd->sitd_next; - break; + return tt; +} + +/* Release the TT above udev, if it's not in use */ +static void drop_tt(struct usb_device *udev) +{ + struct usb_tt *utt = udev->tt; + struct ehci_tt *tt, **tt_index, **ptt; + int cnt, i; + + if (!utt || !utt->hcpriv) + return; /* Not below a TT, or never allocated */ + + cnt = 0; + if (utt->multi) { + tt_index = utt->hcpriv; + ptt = &tt_index[udev->ttport - 1]; + + /* How many entries are left in tt_index? */ + for (i = 0; i < utt->hub->maxchild; ++i) + cnt += !!tt_index[i]; + } else { + tt_index = NULL; + ptt = (struct ehci_tt **) &utt->hcpriv; + } + + tt = *ptt; + if (!tt || !list_empty(&tt->ps_list)) + return; /* never allocated, or still in use */ + + list_del(&tt->tt_list); + *ptt = NULL; + kfree(tt); + if (cnt == 1) { + utt->hcpriv = NULL; + kfree(tt_index); + } +} + +static void bandwidth_dbg(struct ehci_hcd *ehci, int sign, char *type, + struct ehci_per_sched *ps) +{ + dev_dbg(&ps->udev->dev, + "ep %02x: %s %s @ %u+%u (%u.%u+%u) [%u/%u us] mask %04x\n", + ps->ep->desc.bEndpointAddress, + (sign >= 0 ? "reserve" : "release"), type, + (ps->bw_phase << 3) + ps->phase_uf, ps->bw_uperiod, + ps->phase, ps->phase_uf, ps->period, + ps->usecs, ps->c_usecs, ps->cs_mask); +} + +static void reserve_release_intr_bandwidth(struct ehci_hcd *ehci, + struct ehci_qh *qh, int sign) +{ + unsigned start_uf; + unsigned i, j, m; + int usecs = qh->ps.usecs; + int c_usecs = qh->ps.c_usecs; + int tt_usecs = qh->ps.tt_usecs; + struct ehci_tt *tt; + + if (qh->ps.phase == NO_FRAME) /* Bandwidth wasn't reserved */ + return; + start_uf = qh->ps.bw_phase << 3; + + bandwidth_dbg(ehci, sign, "intr", &qh->ps); + + if (sign < 0) { /* Release bandwidth */ + usecs = -usecs; + c_usecs = -c_usecs; + tt_usecs = -tt_usecs; + } + + /* Entire transaction (high speed) or start-split (full/low speed) */ + for (i = start_uf + qh->ps.phase_uf; i < EHCI_BANDWIDTH_SIZE; + i += qh->ps.bw_uperiod) + ehci->bandwidth[i] += usecs; + + /* Complete-split (full/low speed) */ + if (qh->ps.c_usecs) { + /* NOTE: adjustments needed for FSTN */ + for (i = start_uf; i < EHCI_BANDWIDTH_SIZE; + i += qh->ps.bw_uperiod) { + for ((j = 2, m = 1 << (j+8)); j < 8; (++j, m <<= 1)) { + if (qh->ps.cs_mask & m) + ehci->bandwidth[i+j] += c_usecs; + } } } -#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) - if (usecs > ehci->uframe_periodic_max) - ehci_err (ehci, "uframe %d sched overrun: %d usecs\n", - frame * 8 + uframe, usecs); -#endif - return usecs; + + /* FS/LS bus bandwidth */ + if (tt_usecs) { + tt = find_tt(qh->ps.udev); + if (sign > 0) + list_add_tail(&qh->ps.ps_list, &tt->ps_list); + else + list_del(&qh->ps.ps_list); + + for (i = start_uf >> 3; i < EHCI_BANDWIDTH_FRAMES; + i += qh->ps.bw_period) + tt->bandwidth[i] += tt_usecs; + } } /*-------------------------------------------------------------------------*/ -static int same_tt (struct usb_device *dev1, struct usb_device *dev2) +static void compute_tt_budget(u8 budget_table[EHCI_BANDWIDTH_SIZE], + struct ehci_tt *tt) +{ + struct ehci_per_sched *ps; + unsigned uframe, uf, x; + u8 *budget_line; + + if (!tt) + return; + memset(budget_table, 0, EHCI_BANDWIDTH_SIZE); + + /* Add up the contributions from all the endpoints using this TT */ + list_for_each_entry(ps, &tt->ps_list, ps_list) { + for (uframe = ps->bw_phase << 3; uframe < EHCI_BANDWIDTH_SIZE; + uframe += ps->bw_uperiod) { + budget_line = &budget_table[uframe]; + x = ps->tt_usecs; + + /* propagate the time forward */ + for (uf = ps->phase_uf; uf < 8; ++uf) { + x += budget_line[uf]; + + /* Each microframe lasts 125 us */ + if (x <= 125) { + budget_line[uf] = x; + break; + } else { + budget_line[uf] = 125; + x -= 125; + } + } + } + } +} + +static int __maybe_unused same_tt(struct usb_device *dev1, + struct usb_device *dev2) { if (!dev1->tt || !dev2->tt) return 0; @@ -227,68 +354,6 @@ static inline void carryover_tt_bandwidth(unsigned short tt_usecs[8]) } } -/* How many of the tt's periodic downstream 1000 usecs are allocated? - * - * While this measures the bandwidth in terms of usecs/uframe, - * the low/fullspeed bus has no notion of uframes, so any particular - * low/fullspeed transfer can "carry over" from one uframe to the next, - * since the TT just performs downstream transfers in sequence. - * - * For example two separate 100 usec transfers can start in the same uframe, - * and the second one would "carry over" 75 usecs into the next uframe. - */ -static void -periodic_tt_usecs ( - struct ehci_hcd *ehci, - struct usb_device *dev, - unsigned frame, - unsigned short tt_usecs[8] -) -{ - __hc32 *hw_p = &ehci->periodic [frame]; - union ehci_shadow *q = &ehci->pshadow [frame]; - unsigned char uf; - - memset(tt_usecs, 0, 16); - - while (q->ptr) { - switch (hc32_to_cpu(ehci, Q_NEXT_TYPE(ehci, *hw_p))) { - case Q_TYPE_ITD: - hw_p = &q->itd->hw_next; - q = &q->itd->itd_next; - continue; - case Q_TYPE_QH: - if (same_tt(dev, q->qh->dev)) { - uf = tt_start_uframe(ehci, q->qh->hw->hw_info2); - tt_usecs[uf] += q->qh->tt_usecs; - } - hw_p = &q->qh->hw->hw_next; - q = &q->qh->qh_next; - continue; - case Q_TYPE_SITD: - if (same_tt(dev, q->sitd->urb->dev)) { - uf = tt_start_uframe(ehci, q->sitd->hw_uframe); - tt_usecs[uf] += q->sitd->stream->tt_usecs; - } - hw_p = &q->sitd->hw_next; - q = &q->sitd->sitd_next; - continue; - // case Q_TYPE_FSTN: - default: - ehci_dbg(ehci, "ignoring periodic frame %d FSTN\n", - frame); - hw_p = &q->fstn->hw_next; - q = &q->fstn->fstn_next; - } - } - - carryover_tt_bandwidth(tt_usecs); - - if (max_tt_usecs[7] < tt_usecs[7]) - ehci_err(ehci, "frame %d tt sched overrun: %d usecs\n", - frame, tt_usecs[7] - max_tt_usecs[7]); -} - /* * Return true if the device's tt's downstream bus is available for a * periodic transfer of the specified length (usecs), starting at the @@ -312,20 +377,29 @@ periodic_tt_usecs ( */ static int tt_available ( struct ehci_hcd *ehci, - unsigned period, - struct usb_device *dev, + struct ehci_per_sched *ps, + struct ehci_tt *tt, unsigned frame, - unsigned uframe, - u16 usecs + unsigned uframe ) { + unsigned period = ps->bw_period; + unsigned usecs = ps->tt_usecs; + if ((period == 0) || (uframe >= 7)) /* error */ return 0; - for (; frame < ehci->periodic_size; frame += period) { - unsigned short tt_usecs[8]; + for (frame &= period - 1; frame < EHCI_BANDWIDTH_FRAMES; + frame += period) { + unsigned i, uf; + unsigned short tt_usecs[8]; - periodic_tt_usecs (ehci, dev, frame, tt_usecs); + if (tt->bandwidth[frame] + usecs > 900) + return 0; + + uf = frame << 3; + for (i = 0; i < 8; (++i, ++uf)) + tt_usecs[i] = ehci->tt_budget[uf]; if (max_tt_usecs[uframe] <= tt_usecs[uframe]) return 0; @@ -337,7 +411,7 @@ static int tt_available ( */ if (125 < usecs) { int ufs = (usecs / 125); - int i; + for (i = uframe; i < (uframe + ufs) && i < 8; i++) if (0 < tt_usecs[i]) return 0; @@ -391,7 +465,7 @@ static int tt_no_collision ( continue; case Q_TYPE_QH: hw = here.qh->hw; - if (same_tt (dev, here.qh->dev)) { + if (same_tt(dev, here.qh->ps.udev)) { u32 mask; mask = hc32_to_cpu(ehci, @@ -471,19 +545,19 @@ static void disable_periodic(struct ehci_hcd *ehci) static void qh_link_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) { unsigned i; - unsigned period = qh->period; + unsigned period = qh->ps.period; - dev_dbg (&qh->dev->dev, + dev_dbg(&qh->ps.udev->dev, "link qh%d-%04x/%p start %d [%d/%d us]\n", period, hc32_to_cpup(ehci, &qh->hw->hw_info2) & (QH_CMASK | QH_SMASK), - qh, qh->start, qh->usecs, qh->c_usecs); + qh, qh->ps.phase, qh->ps.usecs, qh->ps.c_usecs); /* high bandwidth, or otherwise every microframe */ if (period == 0) period = 1; - for (i = qh->start; i < ehci->periodic_size; i += period) { + for (i = qh->ps.phase; i < ehci->periodic_size; i += period) { union ehci_shadow *prev = &ehci->pshadow[i]; __hc32 *hw_p = &ehci->periodic[i]; union ehci_shadow here = *prev; @@ -503,7 +577,7 @@ static void qh_link_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) * enables sharing interior tree nodes */ while (here.ptr && qh != here.qh) { - if (qh->period > here.qh->period) + if (qh->ps.period > here.qh->ps.period) break; prev = &here.qh->qh_next; hw_p = &here.qh->hw->hw_next; @@ -523,10 +597,10 @@ static void qh_link_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) qh->xacterrs = 0; qh->exception = 0; - /* update per-qh bandwidth for usbfs */ - ehci_to_hcd(ehci)->self.bandwidth_allocated += qh->period - ? ((qh->usecs + qh->c_usecs) / qh->period) - : (qh->usecs * 8); + /* update per-qh bandwidth for debugfs */ + ehci_to_hcd(ehci)->self.bandwidth_allocated += qh->ps.bw_period + ? ((qh->ps.usecs + qh->ps.c_usecs) / qh->ps.bw_period) + : (qh->ps.usecs * 8); list_add(&qh->intr_node, &ehci->intr_qh_list); @@ -556,22 +630,21 @@ static void qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) */ /* high bandwidth, or otherwise part of every microframe */ - if ((period = qh->period) == 0) - period = 1; + period = qh->ps.period ? : 1; - for (i = qh->start; i < ehci->periodic_size; i += period) + for (i = qh->ps.phase; i < ehci->periodic_size; i += period) periodic_unlink (ehci, i, qh); - /* update per-qh bandwidth for usbfs */ - ehci_to_hcd(ehci)->self.bandwidth_allocated -= qh->period - ? ((qh->usecs + qh->c_usecs) / qh->period) - : (qh->usecs * 8); + /* update per-qh bandwidth for debugfs */ + ehci_to_hcd(ehci)->self.bandwidth_allocated -= qh->ps.bw_period + ? ((qh->ps.usecs + qh->ps.c_usecs) / qh->ps.bw_period) + : (qh->ps.usecs * 8); - dev_dbg (&qh->dev->dev, + dev_dbg(&qh->ps.udev->dev, "unlink qh%d-%04x/%p start %d [%d/%d us]\n", - qh->period, + qh->ps.period, hc32_to_cpup(ehci, &qh->hw->hw_info2) & (QH_CMASK | QH_SMASK), - qh, qh->start, qh->usecs, qh->c_usecs); + qh, qh->ps.phase, qh->ps.usecs, qh->ps.c_usecs); /* qh->qh_next still "live" to HC */ qh->qh_state = QH_STATE_UNLINK; @@ -694,11 +767,9 @@ static int check_period ( struct ehci_hcd *ehci, unsigned frame, unsigned uframe, - unsigned period, + unsigned uperiod, unsigned usecs ) { - int claimed; - /* complete split running into next frame? * given FSTN support, we could sometimes check... */ @@ -708,25 +779,10 @@ static int check_period ( /* convert "usecs we need" to "max already claimed" */ usecs = ehci->uframe_periodic_max - usecs; - /* we "know" 2 and 4 uframe intervals were rejected; so - * for period 0, check _every_ microframe in the schedule. - */ - if (unlikely (period == 0)) { - do { - for (uframe = 0; uframe < 7; uframe++) { - claimed = periodic_usecs (ehci, frame, uframe); - if (claimed > usecs) - return 0; - } - } while ((frame += 1) < ehci->periodic_size); - - /* just check the specified uframe, at that period */ - } else { - do { - claimed = periodic_usecs (ehci, frame, uframe); - if (claimed > usecs) - return 0; - } while ((frame += period) < ehci->periodic_size); + for (uframe += frame << 3; uframe < EHCI_BANDWIDTH_SIZE; + uframe += uperiod) { + if (ehci->bandwidth[uframe] > usecs) + return 0; } // success! @@ -737,40 +793,40 @@ static int check_intr_schedule ( struct ehci_hcd *ehci, unsigned frame, unsigned uframe, - const struct ehci_qh *qh, - __hc32 *c_maskp + struct ehci_qh *qh, + unsigned *c_maskp, + struct ehci_tt *tt ) { int retval = -ENOSPC; u8 mask = 0; - if (qh->c_usecs && uframe >= 6) /* FSTN territory? */ + if (qh->ps.c_usecs && uframe >= 6) /* FSTN territory? */ goto done; - if (!check_period (ehci, frame, uframe, qh->period, qh->usecs)) + if (!check_period(ehci, frame, uframe, qh->ps.bw_uperiod, qh->ps.usecs)) goto done; - if (!qh->c_usecs) { + if (!qh->ps.c_usecs) { retval = 0; *c_maskp = 0; goto done; } #ifdef CONFIG_USB_EHCI_TT_NEWSCHED - if (tt_available (ehci, qh->period, qh->dev, frame, uframe, - qh->tt_usecs)) { + if (tt_available(ehci, &qh->ps, tt, frame, uframe)) { unsigned i; /* TODO : this may need FSTN for SSPLIT in uframe 5. */ - for (i=uframe+1; i<8 && i<uframe+4; i++) - if (!check_period (ehci, frame, i, - qh->period, qh->c_usecs)) + for (i = uframe+2; i < 8 && i <= uframe+4; i++) + if (!check_period(ehci, frame, i, + qh->ps.bw_uperiod, qh->ps.c_usecs)) goto done; else mask |= 1 << i; retval = 0; - *c_maskp = cpu_to_hc32(ehci, mask << 8); + *c_maskp = mask; } #else /* Make sure this tt's buffer is also available for CSPLITs. @@ -781,15 +837,15 @@ static int check_intr_schedule ( * one smart pass... */ mask = 0x03 << (uframe + qh->gap_uf); - *c_maskp = cpu_to_hc32(ehci, mask << 8); + *c_maskp = mask; mask |= 1 << uframe; - if (tt_no_collision (ehci, qh->period, qh->dev, frame, mask)) { - if (!check_period (ehci, frame, uframe + qh->gap_uf + 1, - qh->period, qh->c_usecs)) + if (tt_no_collision(ehci, qh->ps.bw_period, qh->ps.udev, frame, mask)) { + if (!check_period(ehci, frame, uframe + qh->gap_uf + 1, + qh->ps.bw_uperiod, qh->ps.c_usecs)) goto done; - if (!check_period (ehci, frame, uframe + qh->gap_uf, - qh->period, qh->c_usecs)) + if (!check_period(ehci, frame, uframe + qh->gap_uf, + qh->ps.bw_uperiod, qh->ps.c_usecs)) goto done; retval = 0; } @@ -803,62 +859,67 @@ done: */ static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh) { - int status; + int status = 0; unsigned uframe; - __hc32 c_mask; - unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ + unsigned c_mask; struct ehci_qh_hw *hw = qh->hw; + struct ehci_tt *tt; hw->hw_next = EHCI_LIST_END(ehci); - frame = qh->start; /* reuse the previous schedule slots, if we can */ - if (frame < qh->period) { - uframe = ffs(hc32_to_cpup(ehci, &hw->hw_info2) & QH_SMASK); - status = check_intr_schedule (ehci, frame, --uframe, - qh, &c_mask); - } else { - uframe = 0; - c_mask = 0; - status = -ENOSPC; + if (qh->ps.phase != NO_FRAME) { + ehci_dbg(ehci, "reused qh %p schedule\n", qh); + return 0; + } + + uframe = 0; + c_mask = 0; + tt = find_tt(qh->ps.udev); + if (IS_ERR(tt)) { + status = PTR_ERR(tt); + goto done; } + compute_tt_budget(ehci->tt_budget, tt); /* else scan the schedule to find a group of slots such that all * uframes have enough periodic bandwidth available. */ - if (status) { - /* "normal" case, uframing flexible except with splits */ - if (qh->period) { - int i; - - for (i = qh->period; status && i > 0; --i) { - frame = ++ehci->random_frame % qh->period; - for (uframe = 0; uframe < 8; uframe++) { - status = check_intr_schedule (ehci, - frame, uframe, qh, - &c_mask); - if (status == 0) - break; - } + /* "normal" case, uframing flexible except with splits */ + if (qh->ps.bw_period) { + int i; + unsigned frame; + + for (i = qh->ps.bw_period; i > 0; --i) { + frame = ++ehci->random_frame & (qh->ps.bw_period - 1); + for (uframe = 0; uframe < 8; uframe++) { + status = check_intr_schedule(ehci, + frame, uframe, qh, &c_mask, tt); + if (status == 0) + goto got_it; } - - /* qh->period == 0 means every uframe */ - } else { - frame = 0; - status = check_intr_schedule (ehci, 0, 0, qh, &c_mask); } - if (status) - goto done; - qh->start = frame; - /* reset S-frame and (maybe) C-frame masks */ - hw->hw_info2 &= cpu_to_hc32(ehci, ~(QH_CMASK | QH_SMASK)); - hw->hw_info2 |= qh->period - ? cpu_to_hc32(ehci, 1 << uframe) - : cpu_to_hc32(ehci, QH_SMASK); - hw->hw_info2 |= c_mask; - } else - ehci_dbg (ehci, "reused qh %p schedule\n", qh); + /* qh->ps.bw_period == 0 means every uframe */ + } else { + status = check_intr_schedule(ehci, 0, 0, qh, &c_mask, tt); + } + if (status) + goto done; + + got_it: + qh->ps.phase = (qh->ps.period ? ehci->random_frame & + (qh->ps.period - 1) : 0); + qh->ps.bw_phase = qh->ps.phase & (qh->ps.bw_period - 1); + qh->ps.phase_uf = uframe; + qh->ps.cs_mask = qh->ps.period ? + (c_mask << 8) | (1 << uframe) : + QH_SMASK; + + /* reset S-frame and (maybe) C-frame masks */ + hw->hw_info2 &= cpu_to_hc32(ehci, ~(QH_CMASK | QH_SMASK)); + hw->hw_info2 |= cpu_to_hc32(ehci, qh->ps.cs_mask); + reserve_release_intr_bandwidth(ehci, qh, 1); done: return status; @@ -969,7 +1030,8 @@ iso_stream_alloc (gfp_t mem_flags) if (likely (stream != NULL)) { INIT_LIST_HEAD(&stream->td_list); INIT_LIST_HEAD(&stream->free_list); - stream->next_uframe = -1; + stream->next_uframe = NO_FRAME; + stream->ps.phase = NO_FRAME; } return stream; } @@ -978,25 +1040,24 @@ static void iso_stream_init ( struct ehci_hcd *ehci, struct ehci_iso_stream *stream, - struct usb_device *dev, - int pipe, - unsigned interval + struct urb *urb ) { static const u8 smask_out [] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f }; + struct usb_device *dev = urb->dev; u32 buf1; unsigned epnum, maxp; int is_input; - long bandwidth; + unsigned tmp; /* * this might be a "high bandwidth" highspeed endpoint, * as encoded in the ep descriptor's wMaxPacket field */ - epnum = usb_pipeendpoint (pipe); - is_input = usb_pipein (pipe) ? USB_DIR_IN : 0; - maxp = usb_maxpacket(dev, pipe, !is_input); + epnum = usb_pipeendpoint(urb->pipe); + is_input = usb_pipein(urb->pipe) ? USB_DIR_IN : 0; + maxp = usb_endpoint_maxp(&urb->ep->desc); if (is_input) { buf1 = (1 << 11); } else { @@ -1020,9 +1081,19 @@ iso_stream_init ( /* usbfs wants to report the average usecs per frame tied up * when transfers on this endpoint are scheduled ... */ - stream->usecs = HS_USECS_ISO (maxp); - bandwidth = stream->usecs * 8; - bandwidth /= interval; + stream->ps.usecs = HS_USECS_ISO(maxp); + + /* period for bandwidth allocation */ + tmp = min_t(unsigned, EHCI_BANDWIDTH_SIZE, + 1 << (urb->ep->desc.bInterval - 1)); + + /* Allow urb->interval to override */ + stream->ps.bw_uperiod = min_t(unsigned, tmp, urb->interval); + + stream->uperiod = urb->interval; + stream->ps.period = urb->interval >> 3; + stream->bandwidth = stream->ps.usecs * 8 / + stream->ps.bw_uperiod; } else { u32 addr; @@ -1036,36 +1107,46 @@ iso_stream_init ( addr |= dev->tt->hub->devnum << 16; addr |= epnum << 8; addr |= dev->devnum; - stream->usecs = HS_USECS_ISO (maxp); + stream->ps.usecs = HS_USECS_ISO(maxp); think_time = dev->tt ? dev->tt->think_time : 0; - stream->tt_usecs = NS_TO_US (think_time + usb_calc_bus_time ( + stream->ps.tt_usecs = NS_TO_US(think_time + usb_calc_bus_time( dev->speed, is_input, 1, maxp)); hs_transfers = max (1u, (maxp + 187) / 188); if (is_input) { u32 tmp; addr |= 1 << 31; - stream->c_usecs = stream->usecs; - stream->usecs = HS_USECS_ISO (1); - stream->raw_mask = 1; + stream->ps.c_usecs = stream->ps.usecs; + stream->ps.usecs = HS_USECS_ISO(1); + stream->ps.cs_mask = 1; /* c-mask as specified in USB 2.0 11.18.4 3.c */ tmp = (1 << (hs_transfers + 2)) - 1; - stream->raw_mask |= tmp << (8 + 2); + stream->ps.cs_mask |= tmp << (8 + 2); } else - stream->raw_mask = smask_out [hs_transfers - 1]; - bandwidth = stream->usecs + stream->c_usecs; - bandwidth /= interval << 3; + stream->ps.cs_mask = smask_out[hs_transfers - 1]; + + /* period for bandwidth allocation */ + tmp = min_t(unsigned, EHCI_BANDWIDTH_FRAMES, + 1 << (urb->ep->desc.bInterval - 1)); + + /* Allow urb->interval to override */ + stream->ps.bw_period = min_t(unsigned, tmp, urb->interval); + stream->ps.bw_uperiod = stream->ps.bw_period << 3; - /* stream->splits gets created from raw_mask later */ + stream->ps.period = urb->interval; + stream->uperiod = urb->interval << 3; + stream->bandwidth = (stream->ps.usecs + stream->ps.c_usecs) / + stream->ps.bw_period; + + /* stream->splits gets created from cs_mask later */ stream->address = cpu_to_hc32(ehci, addr); } - stream->bandwidth = bandwidth; - stream->udev = dev; + stream->ps.udev = dev; + stream->ps.ep = urb->ep; stream->bEndpointAddress = is_input | epnum; - stream->interval = interval; stream->maxp = maxp; } @@ -1090,9 +1171,7 @@ iso_stream_find (struct ehci_hcd *ehci, struct urb *urb) stream = iso_stream_alloc(GFP_ATOMIC); if (likely (stream != NULL)) { ep->hcpriv = stream; - stream->ep = ep; - iso_stream_init(ehci, stream, urb->dev, urb->pipe, - urb->interval); + iso_stream_init(ehci, stream, urb); } /* if dev->ep [epnum] is a QH, hw is set */ @@ -1137,7 +1216,7 @@ itd_sched_init( dma_addr_t dma = urb->transfer_dma; /* how many uframes are needed for these transfers */ - iso_sched->span = urb->number_of_packets * stream->interval; + iso_sched->span = urb->number_of_packets * stream->uperiod; /* figure out per-uframe itd fields that we'll need later * when we fit new itds into the schedule. @@ -1236,7 +1315,7 @@ itd_urb_transaction ( memset (itd, 0, sizeof *itd); itd->itd_dma = itd_dma; - itd->frame = 9999; /* an invalid value */ + itd->frame = NO_FRAME; list_add (&itd->itd_list, &sched->td_list); } spin_unlock_irqrestore (&ehci->lock, flags); @@ -1249,49 +1328,106 @@ itd_urb_transaction ( /*-------------------------------------------------------------------------*/ +static void reserve_release_iso_bandwidth(struct ehci_hcd *ehci, + struct ehci_iso_stream *stream, int sign) +{ + unsigned uframe; + unsigned i, j; + unsigned s_mask, c_mask, m; + int usecs = stream->ps.usecs; + int c_usecs = stream->ps.c_usecs; + int tt_usecs = stream->ps.tt_usecs; + struct ehci_tt *tt; + + if (stream->ps.phase == NO_FRAME) /* Bandwidth wasn't reserved */ + return; + uframe = stream->ps.bw_phase << 3; + + bandwidth_dbg(ehci, sign, "iso", &stream->ps); + + if (sign < 0) { /* Release bandwidth */ + usecs = -usecs; + c_usecs = -c_usecs; + tt_usecs = -tt_usecs; + } + + if (!stream->splits) { /* High speed */ + for (i = uframe + stream->ps.phase_uf; i < EHCI_BANDWIDTH_SIZE; + i += stream->ps.bw_uperiod) + ehci->bandwidth[i] += usecs; + + } else { /* Full speed */ + s_mask = stream->ps.cs_mask; + c_mask = s_mask >> 8; + + /* NOTE: adjustment needed for frame overflow */ + for (i = uframe; i < EHCI_BANDWIDTH_SIZE; + i += stream->ps.bw_uperiod) { + for ((j = stream->ps.phase_uf, m = 1 << j); j < 8; + (++j, m <<= 1)) { + if (s_mask & m) + ehci->bandwidth[i+j] += usecs; + else if (c_mask & m) + ehci->bandwidth[i+j] += c_usecs; + } + } + + tt = find_tt(stream->ps.udev); + if (sign > 0) + list_add_tail(&stream->ps.ps_list, &tt->ps_list); + else + list_del(&stream->ps.ps_list); + + for (i = uframe >> 3; i < EHCI_BANDWIDTH_FRAMES; + i += stream->ps.bw_period) + tt->bandwidth[i] += tt_usecs; + } +} + static inline int itd_slot_ok ( struct ehci_hcd *ehci, - u32 mod, - u32 uframe, - u8 usecs, - u32 period + struct ehci_iso_stream *stream, + unsigned uframe ) { - uframe %= period; - do { - /* can't commit more than uframe_periodic_max usec */ - if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7) - > (ehci->uframe_periodic_max - usecs)) - return 0; + unsigned usecs; + + /* convert "usecs we need" to "max already claimed" */ + usecs = ehci->uframe_periodic_max - stream->ps.usecs; - /* we know urb->interval is 2^N uframes */ - uframe += period; - } while (uframe < mod); + for (uframe &= stream->ps.bw_uperiod - 1; uframe < EHCI_BANDWIDTH_SIZE; + uframe += stream->ps.bw_uperiod) { + if (ehci->bandwidth[uframe] > usecs) + return 0; + } return 1; } static inline int sitd_slot_ok ( struct ehci_hcd *ehci, - u32 mod, struct ehci_iso_stream *stream, - u32 uframe, + unsigned uframe, struct ehci_iso_sched *sched, - u32 period_uframes + struct ehci_tt *tt ) { - u32 mask, tmp; - u32 frame, uf; + unsigned mask, tmp; + unsigned frame, uf; + + mask = stream->ps.cs_mask << (uframe & 7); - mask = stream->raw_mask << (uframe & 7); + /* for OUT, don't wrap SSPLIT into H-microframe 7 */ + if (((stream->ps.cs_mask & 0xff) << (uframe & 7)) >= (1 << 7)) + return 0; /* for IN, don't wrap CSPLIT into the next frame */ if (mask & ~0xffff) return 0; /* check bandwidth */ - uframe %= period_uframes; + uframe &= stream->ps.bw_uperiod - 1; frame = uframe >> 3; #ifdef CONFIG_USB_EHCI_TT_NEWSCHED @@ -1299,54 +1435,48 @@ sitd_slot_ok ( * tt_available scheduling guarantees 10+% for control/bulk. */ uf = uframe & 7; - if (!tt_available(ehci, period_uframes >> 3, - stream->udev, frame, uf, stream->tt_usecs)) + if (!tt_available(ehci, &stream->ps, tt, frame, uf)) return 0; #else /* tt must be idle for start(s), any gap, and csplit. * assume scheduling slop leaves 10+% for control/bulk. */ - if (!tt_no_collision(ehci, period_uframes >> 3, - stream->udev, frame, mask)) + if (!tt_no_collision(ehci, stream->ps.bw_period, + stream->ps.udev, frame, mask)) return 0; #endif - /* this multi-pass logic is simple, but performance may - * suffer when the schedule data isn't cached. - */ do { - u32 max_used; - - frame = uframe >> 3; - uf = uframe & 7; + unsigned max_used; + unsigned i; /* check starts (OUT uses more than one) */ - max_used = ehci->uframe_periodic_max - stream->usecs; - for (tmp = stream->raw_mask & 0xff; tmp; tmp >>= 1, uf++) { - if (periodic_usecs (ehci, frame, uf) > max_used) + uf = uframe; + max_used = ehci->uframe_periodic_max - stream->ps.usecs; + for (tmp = stream->ps.cs_mask & 0xff; tmp; tmp >>= 1, uf++) { + if (ehci->bandwidth[uf] > max_used) return 0; } /* for IN, check CSPLIT */ - if (stream->c_usecs) { - uf = uframe & 7; - max_used = ehci->uframe_periodic_max - stream->c_usecs; - do { - tmp = 1 << uf; - tmp <<= 8; - if ((stream->raw_mask & tmp) == 0) + if (stream->ps.c_usecs) { + max_used = ehci->uframe_periodic_max - + stream->ps.c_usecs; + uf = uframe & ~7; + tmp = 1 << (2+8); + for (i = (uframe & 7) + 2; i < 8; (++i, tmp <<= 1)) { + if ((stream->ps.cs_mask & tmp) == 0) continue; - if (periodic_usecs (ehci, frame, uf) - > max_used) + if (ehci->bandwidth[uf+i] > max_used) return 0; - } while (++uf < 8); + } } - /* we know urb->interval is 2^N uframes */ - uframe += period_uframes; - } while (uframe < mod); + uframe += stream->ps.bw_uperiod; + } while (uframe < EHCI_BANDWIDTH_SIZE); - stream->splits = cpu_to_hc32(ehci, stream->raw_mask << (uframe & 7)); + stream->ps.cs_mask <<= uframe & 7; + stream->splits = cpu_to_hc32(ehci, stream->ps.cs_mask); return 1; } @@ -1361,8 +1491,6 @@ sitd_slot_ok ( * given EHCI_TUNE_FLS and the slop). Or, write a smarter scheduler! */ -#define SCHEDULING_DELAY 40 /* microframes */ - static int iso_stream_schedule ( struct ehci_hcd *ehci, @@ -1370,134 +1498,184 @@ iso_stream_schedule ( struct ehci_iso_stream *stream ) { - u32 now, base, next, start, period, span; - int status; + u32 now, base, next, start, period, span, now2; + u32 wrap = 0, skip = 0; + int status = 0; unsigned mod = ehci->periodic_size << 3; struct ehci_iso_sched *sched = urb->hcpriv; + bool empty = list_empty(&stream->td_list); + bool new_stream = false; - period = urb->interval; + period = stream->uperiod; span = sched->span; - if (!stream->highspeed) { - period <<= 3; + if (!stream->highspeed) span <<= 3; - } - now = ehci_read_frame_index(ehci) & (mod - 1); + /* Start a new isochronous stream? */ + if (unlikely(empty && !hcd_periodic_completion_in_progress( + ehci_to_hcd(ehci), urb->ep))) { - /* Typical case: reuse current schedule, stream is still active. - * Hopefully there are no gaps from the host falling behind - * (irq delays etc). If there are, the behavior depends on - * whether URB_ISO_ASAP is set. - */ - if (likely (!list_empty (&stream->td_list))) { + /* Schedule the endpoint */ + if (stream->ps.phase == NO_FRAME) { + int done = 0; + struct ehci_tt *tt = find_tt(stream->ps.udev); - /* Take the isochronous scheduling threshold into account */ - if (ehci->i_thresh) - next = now + ehci->i_thresh; /* uframe cache */ - else - next = (now + 2 + 7) & ~0x07; /* full frame cache */ - - /* - * Use ehci->last_iso_frame as the base. There can't be any - * TDs scheduled for earlier than that. - */ - base = ehci->last_iso_frame << 3; - next = (next - base) & (mod - 1); - start = (stream->next_uframe - base) & (mod - 1); - - /* Is the schedule already full? */ - if (unlikely(start < period)) { - ehci_dbg(ehci, "iso sched full %p (%u-%u < %u mod %u)\n", - urb, stream->next_uframe, base, - period, mod); - status = -ENOSPC; - goto fail; - } - - /* Behind the scheduling threshold? */ - if (unlikely(start < next)) { - unsigned now2 = (now - base) & (mod - 1); + if (IS_ERR(tt)) { + status = PTR_ERR(tt); + goto fail; + } + compute_tt_budget(ehci->tt_budget, tt); - /* USB_ISO_ASAP: Round up to the first available slot */ - if (urb->transfer_flags & URB_ISO_ASAP) - start += (next - start + period - 1) & -period; + start = ((-(++ehci->random_frame)) << 3) & (period - 1); - /* - * Not ASAP: Use the next slot in the stream, - * no matter what. + /* find a uframe slot with enough bandwidth. + * Early uframes are more precious because full-speed + * iso IN transfers can't use late uframes, + * and therefore they should be allocated last. */ - else if (start + span - period < now2) { - ehci_dbg(ehci, "iso underrun %p (%u+%u < %u)\n", - urb, start + base, - span - period, now2 + base); + next = start; + start += period; + do { + start--; + /* check schedule: enough space? */ + if (stream->highspeed) { + if (itd_slot_ok(ehci, stream, start)) + done = 1; + } else { + if ((start % 8) >= 6) + continue; + if (sitd_slot_ok(ehci, stream, start, + sched, tt)) + done = 1; + } + } while (start > next && !done); + + /* no room in the schedule */ + if (!done) { + ehci_dbg(ehci, "iso sched full %p", urb); + status = -ENOSPC; + goto fail; } + stream->ps.phase = (start >> 3) & + (stream->ps.period - 1); + stream->ps.bw_phase = stream->ps.phase & + (stream->ps.bw_period - 1); + stream->ps.phase_uf = start & 7; + reserve_release_iso_bandwidth(ehci, stream, 1); + } + + /* New stream is already scheduled; use the upcoming slot */ + else { + start = (stream->ps.phase << 3) + stream->ps.phase_uf; } - start += base; + stream->next_uframe = start; + new_stream = true; } - /* need to schedule; when's the next (u)frame we could start? - * this is bigger than ehci->i_thresh allows; scheduling itself - * isn't free, the delay should handle reasonably slow cpus. it - * can also help high bandwidth if the dma and irq loads don't - * jump until after the queue is primed. + now = ehci_read_frame_index(ehci) & (mod - 1); + + /* Take the isochronous scheduling threshold into account */ + if (ehci->i_thresh) + next = now + ehci->i_thresh; /* uframe cache */ + else + next = (now + 2 + 7) & ~0x07; /* full frame cache */ + + /* + * Use ehci->last_iso_frame as the base. There can't be any + * TDs scheduled for earlier than that. */ - else { - int done = 0; + base = ehci->last_iso_frame << 3; + next = (next - base) & (mod - 1); + start = (stream->next_uframe - base) & (mod - 1); - base = now & ~0x07; - start = base + SCHEDULING_DELAY; + if (unlikely(new_stream)) + goto do_ASAP; - /* find a uframe slot with enough bandwidth. - * Early uframes are more precious because full-speed - * iso IN transfers can't use late uframes, - * and therefore they should be allocated last. - */ - next = start; - start += period; - do { - start--; - /* check schedule: enough space? */ - if (stream->highspeed) { - if (itd_slot_ok(ehci, mod, start, - stream->usecs, period)) - done = 1; - } else { - if ((start % 8) >= 6) - continue; - if (sitd_slot_ok(ehci, mod, stream, - start, sched, period)) - done = 1; - } - } while (start > next && !done); + /* + * Typical case: reuse current schedule, stream may still be active. + * Hopefully there are no gaps from the host falling behind + * (irq delays etc). If there are, the behavior depends on + * whether URB_ISO_ASAP is set. + */ + now2 = (now - base) & (mod - 1); + + /* Is the schedule already full? */ + if (unlikely(!empty && start < period)) { + ehci_dbg(ehci, "iso sched full %p (%u-%u < %u mod %u)\n", + urb, stream->next_uframe, base, period, mod); + status = -ENOSPC; + goto fail; + } + + /* Is the next packet scheduled after the base time? */ + if (likely(!empty || start <= now2 + period)) { + + /* URB_ISO_ASAP: make sure that start >= next */ + if (unlikely(start < next && + (urb->transfer_flags & URB_ISO_ASAP))) + goto do_ASAP; + + /* Otherwise use start, if it's not in the past */ + if (likely(start >= now2)) + goto use_start; - /* no room in the schedule */ - if (!done) { - ehci_dbg(ehci, "iso sched full %p", urb); - status = -ENOSPC; - goto fail; + /* Otherwise we got an underrun while the queue was empty */ + } else { + if (urb->transfer_flags & URB_ISO_ASAP) + goto do_ASAP; + wrap = mod; + now2 += mod; + } + + /* How many uframes and packets do we need to skip? */ + skip = (now2 - start + period - 1) & -period; + if (skip >= span) { /* Entirely in the past? */ + ehci_dbg(ehci, "iso underrun %p (%u+%u < %u) [%u]\n", + urb, start + base, span - period, now2 + base, + base); + + /* Try to keep the last TD intact for scanning later */ + skip = span - period; + + /* Will it come before the current scan position? */ + if (empty) { + skip = span; /* Skip the entire URB */ + status = 1; /* and give it back immediately */ + iso_sched_free(stream, sched); + sched = NULL; } } + urb->error_count = skip / period; + if (sched) + sched->first_packet = urb->error_count; + goto use_start; + do_ASAP: + /* Use the first slot after "next" */ + start = next + ((start - next) & (period - 1)); + + use_start: /* Tried to schedule too far into the future? */ - if (unlikely(start - base + span - period >= mod)) { + if (unlikely(start + span - period >= mod + wrap)) { ehci_dbg(ehci, "request %p would overflow (%u+%u >= %u)\n", - urb, start - base, span - period, mod); + urb, start, span - period, mod + wrap); status = -EFBIG; goto fail; } - stream->next_uframe = start & (mod - 1); + start += base; + stream->next_uframe = (start + skip) & (mod - 1); /* report high speed start in uframes; full speed, in frames */ - urb->start_frame = stream->next_uframe; + urb->start_frame = start & (mod - 1); if (!stream->highspeed) urb->start_frame >>= 3; /* Make sure scan_isoc() sees these */ if (ehci->isoc_count == 0) ehci->last_iso_frame = now >> 3; - return 0; + return status; fail: iso_sched_free(stream, sched); @@ -1610,7 +1788,8 @@ static void itd_link_urb( ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++; /* fill iTDs uframe by uframe */ - for (packet = 0, itd = NULL; packet < urb->number_of_packets; ) { + for (packet = iso_sched->first_packet, itd = NULL; + packet < urb->number_of_packets;) { if (itd == NULL) { /* ASSERT: we have all necessary itds */ // BUG_ON (list_empty (&iso_sched->td_list)); @@ -1630,7 +1809,7 @@ static void itd_link_urb( itd_patch(ehci, itd, iso_sched, packet, uframe); - next_uframe += stream->interval; + next_uframe += stream->uperiod; next_uframe &= mod - 1; packet++; @@ -1770,9 +1949,9 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, ehci_dbg (ehci, "can't get iso stream\n"); return -ENOMEM; } - if (unlikely (urb->interval != stream->interval)) { + if (unlikely(urb->interval != stream->uperiod)) { ehci_dbg (ehci, "can't change iso interval %d --> %d\n", - stream->interval, urb->interval); + stream->uperiod, urb->interval); goto done; } @@ -1804,10 +1983,14 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, if (unlikely(status)) goto done_not_linked; status = iso_stream_schedule(ehci, urb, stream); - if (likely (status == 0)) + if (likely(status == 0)) { itd_link_urb (ehci, urb, ehci->periodic_size << 3, stream); - else + } else if (status > 0) { + status = 0; + ehci_urb_done(ehci, urb, 0); + } else { usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); + } done_not_linked: spin_unlock_irqrestore (&ehci->lock, flags); done: @@ -1833,7 +2016,7 @@ sitd_sched_init( dma_addr_t dma = urb->transfer_dma; /* how many frames are needed for these transfers */ - iso_sched->span = urb->number_of_packets * stream->interval; + iso_sched->span = urb->number_of_packets * stream->ps.period; /* figure out per-frame sitd fields that we'll need later * when we fit new sitds into the schedule. @@ -1925,7 +2108,7 @@ sitd_urb_transaction ( memset (sitd, 0, sizeof *sitd); sitd->sitd_dma = sitd_dma; - sitd->frame = 9999; /* an invalid value */ + sitd->frame = NO_FRAME; list_add (&sitd->sitd_list, &iso_sched->td_list); } @@ -2008,7 +2191,7 @@ static void sitd_link_urb( ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++; /* fill sITDs frame by frame */ - for (packet = 0, sitd = NULL; + for (packet = sched->first_packet, sitd = NULL; packet < urb->number_of_packets; packet++) { @@ -2027,7 +2210,7 @@ static void sitd_link_urb( sitd_link(ehci, (next_uframe >> 3) & (ehci->periodic_size - 1), sitd); - next_uframe += stream->interval << 3; + next_uframe += stream->uperiod; } stream->next_uframe = next_uframe & (mod - 1); @@ -2146,9 +2329,9 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, ehci_dbg (ehci, "can't get iso stream\n"); return -ENOMEM; } - if (urb->interval != stream->interval) { + if (urb->interval != stream->ps.period) { ehci_dbg (ehci, "can't change iso interval %d --> %d\n", - stream->interval, urb->interval); + stream->ps.period, urb->interval); goto done; } @@ -2178,10 +2361,14 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, if (unlikely(status)) goto done_not_linked; status = iso_stream_schedule(ehci, urb, stream); - if (status == 0) + if (likely(status == 0)) { sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream); - else + } else if (status > 0) { + status = 0; + ehci_urb_done(ehci, urb, 0); + } else { usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); + } done_not_linked: spin_unlock_irqrestore (&ehci->lock, flags); done: @@ -2259,7 +2446,8 @@ restart: q.itd->hw_next != EHCI_LIST_END(ehci)) *hw_p = q.itd->hw_next; else - *hw_p = ehci->dummy->qh_dma; + *hw_p = cpu_to_hc32(ehci, + ehci->dummy->qh_dma); type = Q_NEXT_TYPE(ehci, q.itd->hw_next); wmb(); modified = itd_complete (ehci, q.itd); @@ -2294,7 +2482,8 @@ restart: q.sitd->hw_next != EHCI_LIST_END(ehci)) *hw_p = q.sitd->hw_next; else - *hw_p = ehci->dummy->qh_dma; + *hw_p = cpu_to_hc32(ehci, + ehci->dummy->qh_dma); type = Q_NEXT_TYPE(ehci, q.sitd->hw_next); wmb(); modified = sitd_complete (ehci, q.sitd); diff --git a/drivers/usb/host/ehci-sead3.c b/drivers/usb/host/ehci-sead3.c index b2de52d..8a73449 100644 --- a/drivers/usb/host/ehci-sead3.c +++ b/drivers/usb/host/ehci-sead3.c @@ -55,7 +55,7 @@ const struct hc_driver ehci_sead3_hc_driver = { * generic hardware linkage */ .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2, + .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, /* * basic lifecycle operations diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c index 93e59a1..dc899eb 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -36,7 +36,7 @@ static const struct hc_driver ehci_sh_hc_driver = { * generic hardware linkage */ .irq = ehci_irq, - .flags = HCD_USB2 | HCD_MEMORY, + .flags = HCD_USB2 | HCD_MEMORY | HCD_BH, /* * basic lifecycle operations diff --git a/drivers/usb/host/ehci-sysfs.c b/drivers/usb/host/ehci-sysfs.c index 14ced00..f6459df 100644 --- a/drivers/usb/host/ehci-sysfs.c +++ b/drivers/usb/host/ehci-sysfs.c @@ -97,8 +97,7 @@ static ssize_t store_uframe_periodic_max(struct device *dev, { struct ehci_hcd *ehci; unsigned uframe_periodic_max; - unsigned frame, uframe; - unsigned short allocated_max; + unsigned uframe; unsigned long flags; ssize_t ret; @@ -122,16 +121,14 @@ static ssize_t store_uframe_periodic_max(struct device *dev, /* * for request to decrease max periodic bandwidth, we have to check - * every microframe in the schedule to see whether the decrease is - * possible. + * to see whether the decrease is possible. */ if (uframe_periodic_max < ehci->uframe_periodic_max) { - allocated_max = 0; + u8 allocated_max = 0; - for (frame = 0; frame < ehci->periodic_size; ++frame) - for (uframe = 0; uframe < 7; ++uframe) - allocated_max = max(allocated_max, - periodic_usecs (ehci, frame, uframe)); + for (uframe = 0; uframe < EHCI_BANDWIDTH_SIZE; ++uframe) + allocated_max = max(allocated_max, + ehci->bandwidth[uframe]); if (allocated_max > uframe_periodic_max) { ehci_info(ehci, diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 78fa76d..e6d8e26 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -388,7 +388,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) err = clk_prepare_enable(tegra->clk); if (err) - goto cleanup_clk_get; + goto cleanup_hcd_create; tegra_periph_reset_assert(tegra->clk); udelay(1); @@ -465,8 +465,6 @@ cleanup_phy: usb_phy_shutdown(hcd->phy); cleanup_clk_en: clk_disable_unprepare(tegra->clk); -cleanup_clk_get: - clk_put(tegra->clk); cleanup_hcd_create: usb_put_hcd(hcd); return err; diff --git a/drivers/usb/host/ehci-tilegx.c b/drivers/usb/host/ehci-tilegx.c index cca4be9..67026ff 100644 --- a/drivers/usb/host/ehci-tilegx.c +++ b/drivers/usb/host/ehci-tilegx.c @@ -61,7 +61,7 @@ static const struct hc_driver ehci_tilegx_hc_driver = { * Generic hardware linkage. */ .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2, + .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, /* * Basic lifecycle operations. diff --git a/drivers/usb/host/ehci-w90x900.c b/drivers/usb/host/ehci-w90x900.c index 59e0e24..cdad843 100644 --- a/drivers/usb/host/ehci-w90x900.c +++ b/drivers/usb/host/ehci-w90x900.c @@ -11,13 +11,28 @@ * */ +#include <linux/dma-mapping.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> #include <linux/platform_device.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> + +#include "ehci.h" /* enable phy0 and phy1 for w90p910 */ #define ENPHY (0x01<<8) #define PHY0_CTR (0xA4) #define PHY1_CTR (0xA8) +#define DRIVER_DESC "EHCI w90x900 driver" + +static const char hcd_name[] = "ehci-w90x900 "; + +static struct hc_driver __read_mostly ehci_w90x900_hc_driver; + static int usb_w90x900_probe(const struct hc_driver *driver, struct platform_device *pdev) { @@ -90,8 +105,8 @@ err1: return retval; } -static -void usb_w90x900_remove(struct usb_hcd *hcd, struct platform_device *pdev) +static void usb_w90x900_remove(struct usb_hcd *hcd, + struct platform_device *pdev) { usb_remove_hcd(hcd); iounmap(hcd->regs); @@ -99,54 +114,6 @@ void usb_w90x900_remove(struct usb_hcd *hcd, struct platform_device *pdev) usb_put_hcd(hcd); } -static const struct hc_driver ehci_w90x900_hc_driver = { - .description = hcd_name, - .product_desc = "Nuvoton w90x900 EHCI Host Controller", - .hcd_priv_size = sizeof(struct ehci_hcd), - - /* - * generic hardware linkage - */ - .irq = ehci_irq, - .flags = HCD_USB2|HCD_MEMORY, - - /* - * basic lifecycle operations - */ - .reset = 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, -#ifdef CONFIG_PM - .bus_suspend = ehci_bus_suspend, - .bus_resume = ehci_bus_resume, -#endif - .relinquish_port = ehci_relinquish_port, - .port_handed_over = ehci_port_handed_over, - - .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, -}; - static int ehci_w90x900_probe(struct platform_device *pdev) { if (usb_disabled()) @@ -173,7 +140,25 @@ static struct platform_driver ehci_hcd_w90x900_driver = { }, }; +static int __init ehci_w90X900_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + + ehci_init_driver(&ehci_w90x900_hc_driver, NULL); + return platform_driver_register(&ehci_hcd_w90x900_driver); +} +module_init(ehci_w90X900_init); + +static void __exit ehci_w90X900_cleanup(void) +{ + platform_driver_unregister(&ehci_hcd_w90x900_driver); +} +module_exit(ehci_w90X900_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); -MODULE_DESCRIPTION("w90p910 usb ehci driver!"); -MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:w90p910-ehci"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c index eba962e..95979f9 100644 --- a/drivers/usb/host/ehci-xilinx-of.c +++ b/drivers/usb/host/ehci-xilinx-of.c @@ -79,7 +79,7 @@ static const struct hc_driver ehci_xilinx_of_hc_driver = { * generic hardware linkage */ .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2, + .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, /* * basic lifecycle operations diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 291db7d..e8f41c5 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -54,6 +54,28 @@ struct ehci_stats { unsigned long unlink; }; +/* + * Scheduling and budgeting information for periodic transfers, for both + * high-speed devices and full/low-speed devices lying behind a TT. + */ +struct ehci_per_sched { + struct usb_device *udev; /* access to the TT */ + struct usb_host_endpoint *ep; + struct list_head ps_list; /* node on ehci_tt's ps_list */ + u16 tt_usecs; /* time on the FS/LS bus */ + u16 cs_mask; /* C-mask and S-mask bytes */ + u16 period; /* actual period in frames */ + u16 phase; /* actual phase, frame part */ + u8 bw_phase; /* same, for bandwidth + reservation */ + u8 phase_uf; /* uframe part of the phase */ + u8 usecs, c_usecs; /* times on the HS bus */ + u8 bw_uperiod; /* period in microframes, for + bandwidth reservation */ + u8 bw_period; /* same, in frames */ +}; +#define NO_FRAME 29999 /* frame not assigned yet */ + /* ehci_hcd->lock guards shared data against other CPUs: * ehci_hcd: async, unlink, periodic (and shadow), ... * usb_host_endpoint: hcpriv @@ -230,6 +252,15 @@ struct ehci_hcd { /* one per controller */ struct dentry *debug_dir; #endif + /* bandwidth usage */ +#define EHCI_BANDWIDTH_SIZE 64 +#define EHCI_BANDWIDTH_FRAMES (EHCI_BANDWIDTH_SIZE >> 3) + u8 bandwidth[EHCI_BANDWIDTH_SIZE]; + /* us allocated per uframe */ + u8 tt_budget[EHCI_BANDWIDTH_SIZE]; + /* us budgeted per uframe */ + struct list_head tt_list; + /* platform-specific data -- must come last */ unsigned long priv[0] __aligned(sizeof(s64)); }; @@ -385,6 +416,7 @@ struct ehci_qh { struct list_head intr_node; /* list of intr QHs */ struct ehci_qtd *dummy; struct list_head unlink_node; + struct ehci_per_sched ps; /* scheduling info */ unsigned unlink_cycle; @@ -398,16 +430,8 @@ struct ehci_qh { u8 xacterrs; /* XactErr retry counter */ #define QH_XACTERR_MAX 32 /* XactErr retry limit */ - /* periodic schedule info */ - u8 usecs; /* intr bandwidth */ u8 gap_uf; /* uframes split/csplit gap */ - u8 c_usecs; /* ... split completion bw */ - u16 tt_usecs; /* tt downstream bandwidth */ - unsigned short period; /* polling interval */ - unsigned short start; /* where polling starts */ -#define NO_FRAME ((unsigned short)~0) /* pick new start */ - struct usb_device *dev; /* access to TT */ unsigned is_out:1; /* bulk or intr OUT */ unsigned clearing_tt:1; /* Clear-TT-Buf in progress */ unsigned dequeue_during_giveback:1; @@ -434,6 +458,7 @@ struct ehci_iso_packet { struct ehci_iso_sched { struct list_head td_list; unsigned span; + unsigned first_packet; struct ehci_iso_packet packet [0]; }; @@ -449,22 +474,17 @@ struct ehci_iso_stream { u8 highspeed; struct list_head td_list; /* queued itds/sitds */ struct list_head free_list; /* list of unused itds/sitds */ - struct usb_device *udev; - struct usb_host_endpoint *ep; /* output of (re)scheduling */ - int next_uframe; + struct ehci_per_sched ps; /* scheduling info */ + unsigned next_uframe; __hc32 splits; /* the rest is derived from the endpoint descriptor, - * trusting urb->interval == f(epdesc->bInterval) and * including the extra info for hw_bufp[0..2] */ - u8 usecs, c_usecs; - u16 interval; - u16 tt_usecs; + u16 uperiod; /* period in uframes */ u16 maxp; - u16 raw_mask; unsigned bandwidth; /* This is used to initialize iTD's hw_bufp fields */ @@ -579,6 +599,35 @@ struct ehci_fstn { /*-------------------------------------------------------------------------*/ +/* + * USB-2.0 Specification Sections 11.14 and 11.18 + * Scheduling and budgeting split transactions using TTs + * + * A hub can have a single TT for all its ports, or multiple TTs (one for each + * port). The bandwidth and budgeting information for the full/low-speed bus + * below each TT is self-contained and independent of the other TTs or the + * high-speed bus. + * + * "Bandwidth" refers to the number of microseconds on the FS/LS bus allocated + * to an interrupt or isochronous endpoint for each frame. "Budget" refers to + * the best-case estimate of the number of full-speed bytes allocated to an + * endpoint for each microframe within an allocated frame. + * + * Removal of an endpoint invalidates a TT's budget. Instead of trying to + * keep an up-to-date record, we recompute the budget when it is needed. + */ + +struct ehci_tt { + u16 bandwidth[EHCI_BANDWIDTH_FRAMES]; + + struct list_head tt_list; /* List of all ehci_tt's */ + struct list_head ps_list; /* Items using this TT */ + struct usb_tt *usb_tt; + int tt_port; /* TT port number */ +}; + +/*-------------------------------------------------------------------------*/ + /* Prepare the PORTSC wakeup flags during controller suspend/resume */ #define ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup) \ diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c index fce13bc..55486bd 100644 --- a/drivers/usb/host/fotg210-hcd.c +++ b/drivers/usb/host/fotg210-hcd.c @@ -412,7 +412,7 @@ struct debug_buffer { tmp = 'h'; break; \ default: \ tmp = '?'; break; \ - }; tmp; }) + } tmp; }) static inline char token_mark(struct fotg210_hcd *fotg210, __hc32 token) { diff --git a/drivers/usb/host/fusbh200-hcd.c b/drivers/usb/host/fusbh200-hcd.c index 299253c..e1c6d85 100644 --- a/drivers/usb/host/fusbh200-hcd.c +++ b/drivers/usb/host/fusbh200-hcd.c @@ -402,7 +402,7 @@ struct debug_buffer { case QH_LOW_SPEED: tmp = 'l'; break; \ case QH_HIGH_SPEED: tmp = 'h'; break; \ default: tmp = '?'; break; \ - }; tmp; }) + } tmp; }) static inline char token_mark(struct fusbh200_hcd *fusbh200, __hc32 token) { diff --git a/drivers/usb/host/hwa-hc.c b/drivers/usb/host/hwa-hc.c index 5b86ffb..ada0a52 100644 --- a/drivers/usb/host/hwa-hc.c +++ b/drivers/usb/host/hwa-hc.c @@ -199,10 +199,14 @@ static int hwahc_op_get_frame_number(struct usb_hcd *usb_hcd) { struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); + struct wahc *wa = &hwahc->wa; - dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__, - usb_hcd, hwahc); - return -ENOSYS; + /* + * We cannot query the HWA for the WUSB time since that requires sending + * a synchronous URB and this function can be called in_interrupt. + * Instead, query the USB frame number for our parent and use that. + */ + return usb_get_current_frame_number(wa->usb_dev); } static int hwahc_op_urb_enqueue(struct usb_hcd *usb_hcd, struct urb *urb, @@ -566,14 +570,10 @@ found: goto error; } wa->wa_descr = wa_descr = (struct usb_wa_descriptor *) hdr; - /* Make LE fields CPU order */ - wa_descr->bcdWAVersion = le16_to_cpu(wa_descr->bcdWAVersion); - wa_descr->wNumRPipes = le16_to_cpu(wa_descr->wNumRPipes); - wa_descr->wRPipeMaxBlock = le16_to_cpu(wa_descr->wRPipeMaxBlock); - if (wa_descr->bcdWAVersion > 0x0100) + if (le16_to_cpu(wa_descr->bcdWAVersion) > 0x0100) dev_warn(dev, "Wire Adapter v%d.%d newer than groked v1.0\n", - wa_descr->bcdWAVersion & 0xff00 >> 8, - wa_descr->bcdWAVersion & 0x00ff); + le16_to_cpu(wa_descr->bcdWAVersion) & 0xff00 >> 8, + le16_to_cpu(wa_descr->bcdWAVersion) & 0x00ff); result = 0; error: return result; @@ -679,7 +679,8 @@ static void hwahc_security_release(struct hwahc *hwahc) /* nothing to do here so far... */ } -static int hwahc_create(struct hwahc *hwahc, struct usb_interface *iface) +static int hwahc_create(struct hwahc *hwahc, struct usb_interface *iface, + kernel_ulong_t quirks) { int result; struct device *dev = &iface->dev; @@ -724,7 +725,7 @@ static int hwahc_create(struct hwahc *hwahc, struct usb_interface *iface) dev_err(dev, "Can't create WUSB HC structures: %d\n", result); goto error_wusbhc_create; } - result = wa_create(&hwahc->wa, iface); + result = wa_create(&hwahc->wa, iface, quirks); if (result < 0) goto error_wa_create; return 0; @@ -780,7 +781,7 @@ static int hwahc_probe(struct usb_interface *usb_iface, wusbhc = usb_hcd_to_wusbhc(usb_hcd); hwahc = container_of(wusbhc, struct hwahc, wusbhc); hwahc_init(hwahc); - result = hwahc_create(hwahc, usb_iface); + result = hwahc_create(hwahc, usb_iface, id->driver_info); if (result < 0) { dev_err(dev, "Cannot initialize internals: %d\n", result); goto error_hwahc_create; @@ -824,6 +825,12 @@ static void hwahc_disconnect(struct usb_interface *usb_iface) } static struct usb_device_id hwahc_id_table[] = { + /* Alereon 5310 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5310, 0xe0, 0x02, 0x01), + .driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC }, + /* Alereon 5611 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5611, 0xe0, 0x02, 0x01), + .driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC }, /* FIXME: use class labels for this */ { USB_INTERFACE_INFO(0xe0, 0x02, 0x01), }, {}, diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index 6f29aba..935a2dd 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -2108,7 +2108,7 @@ static int isp1362_show(struct seq_file *s, void *unused) default: s = "?"; break; - }; + } s;}), ep->maxpacket) ; list_for_each_entry(urb, &ep->hep->urb_list, urb_list) { seq_printf(s, " urb%p, %d/%d\n", urb, diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index caa3764..476b5a5 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -13,19 +13,24 @@ */ #include <linux/clk.h> -#include <linux/platform_device.h> +#include <linux/dma-mapping.h> #include <linux/of_platform.h> #include <linux/of_gpio.h> +#include <linux/platform_device.h> #include <linux/platform_data/atmel.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> #include <mach/hardware.h> #include <asm/gpio.h> #include <mach/cpu.h> -#ifndef CONFIG_ARCH_AT91 -#error "CONFIG_ARCH_AT91 must be defined." -#endif + +#include "ohci.h" #define valid_port(index) ((index) >= 0 && (index) < AT91_MAX_USBH_PORTS) #define at91_for_each_port(index) \ @@ -33,7 +38,17 @@ /* interface, function and usb clocks; sometimes also an AHB clock */ static struct clk *iclk, *fclk, *uclk, *hclk; +/* interface and function clocks; sometimes also an AHB clock */ + +#define DRIVER_DESC "OHCI Atmel driver" + +static const char hcd_name[] = "ohci-atmel"; + +static struct hc_driver __read_mostly ohci_at91_hc_driver; static int clocked; +static int (*orig_ohci_hub_control)(struct usb_hcd *hcd, u16 typeReq, + u16 wValue, u16 wIndex, char *buf, u16 wLength); +static int (*orig_ohci_hub_status_data)(struct usb_hcd *hcd, char *buf); extern int usb_disabled(void); @@ -117,6 +132,8 @@ static void usb_hcd_at91_remove (struct usb_hcd *, struct platform_device *); static int usb_hcd_at91_probe(const struct hc_driver *driver, struct platform_device *pdev) { + struct at91_usbh_data *board; + struct ohci_hcd *ohci; int retval; struct usb_hcd *hcd = NULL; @@ -177,8 +194,10 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, } } + board = hcd->self.controller->platform_data; + ohci = hcd_to_ohci(hcd); + ohci->num_ports = board->ports; at91_start_hc(pdev); - ohci_hcd_init(hcd_to_ohci(hcd)); retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED); if (retval == 0) @@ -238,36 +257,6 @@ static void usb_hcd_at91_remove(struct usb_hcd *hcd, } /*-------------------------------------------------------------------------*/ - -static int -ohci_at91_reset (struct usb_hcd *hcd) -{ - struct at91_usbh_data *board = dev_get_platdata(hcd->self.controller); - struct ohci_hcd *ohci = hcd_to_ohci (hcd); - int ret; - - if ((ret = ohci_init(ohci)) < 0) - return ret; - - ohci->num_ports = board->ports; - return 0; -} - -static int -ohci_at91_start (struct usb_hcd *hcd) -{ - struct ohci_hcd *ohci = hcd_to_ohci (hcd); - int ret; - - if ((ret = ohci_run(ohci)) < 0) { - dev_err(hcd->self.controller, "can't start %s\n", - hcd->self.bus_name); - ohci_stop(hcd); - return ret; - } - return 0; -} - static void ohci_at91_usb_set_power(struct at91_usbh_data *pdata, int port, int enable) { if (!valid_port(port)) @@ -297,8 +286,8 @@ static int ohci_at91_usb_get_power(struct at91_usbh_data *pdata, int port) */ static int ohci_at91_hub_status_data(struct usb_hcd *hcd, char *buf) { - struct at91_usbh_data *pdata = dev_get_platdata(hcd->self.controller); - int length = ohci_hub_status_data(hcd, buf); + struct at91_usbh_data *pdata = hcd->self.controller->platform_data; + int length = orig_ohci_hub_status_data(hcd, buf); int port; at91_for_each_port(port) { @@ -376,7 +365,8 @@ static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, break; } - ret = ohci_hub_control(hcd, typeReq, wValue, wIndex + 1, buf, wLength); + ret = orig_ohci_hub_control(hcd, typeReq, wValue, wIndex + 1, + buf, wLength); if (ret) goto out; @@ -430,51 +420,6 @@ static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, /*-------------------------------------------------------------------------*/ -static const struct hc_driver ohci_at91_hc_driver = { - .description = hcd_name, - .product_desc = "AT91 OHCI", - .hcd_priv_size = sizeof(struct ohci_hcd), - - /* - * generic hardware linkage - */ - .irq = ohci_irq, - .flags = HCD_USB11 | HCD_MEMORY, - - /* - * basic lifecycle operations - */ - .reset = ohci_at91_reset, - .start = ohci_at91_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_at91_hub_status_data, - .hub_control = ohci_at91_hub_control, -#ifdef CONFIG_PM - .bus_suspend = ohci_bus_suspend, - .bus_resume = ohci_bus_resume, -#endif - .start_port_reset = ohci_start_port_reset, -}; - -/*-------------------------------------------------------------------------*/ - static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data) { struct platform_device *pdev = data; @@ -703,7 +648,11 @@ ohci_hcd_at91_drv_suspend(struct platform_device *pdev, pm_message_t mesg) * REVISIT: some boards will be able to turn VBUS off... */ if (at91_suspend_entering_slow_clock()) { - ohci_usb_reset (ohci); + ohci->hc_control = ohci_readl(ohci, &ohci->regs->control); + ohci->hc_control &= OHCI_CTRL_RWC; + ohci_writel(ohci, ohci->hc_control, &ohci->regs->control); + ohci->rh_state = OHCI_RH_HALTED; + /* flush the writes */ (void) ohci_readl (ohci, &ohci->regs->control); at91_stop_clock(); @@ -730,8 +679,6 @@ static int ohci_hcd_at91_drv_resume(struct platform_device *pdev) #define ohci_hcd_at91_drv_resume NULL #endif -MODULE_ALIAS("platform:at91_ohci"); - static struct platform_driver ohci_hcd_at91_driver = { .probe = ohci_hcd_at91_drv_probe, .remove = ohci_hcd_at91_drv_remove, @@ -744,3 +691,40 @@ static struct platform_driver ohci_hcd_at91_driver = { .of_match_table = of_match_ptr(at91_ohci_dt_ids), }, }; + +static int __init ohci_at91_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + ohci_init_driver(&ohci_at91_hc_driver, NULL); + + /* + * The Atmel HW has some unusual quirks, which require Atmel-specific + * workarounds. We override certain hc_driver functions here to + * achieve that. We explicitly do not enhance ohci_driver_overrides to + * allow this more easily, since this is an unusual case, and we don't + * want to encourage others to override these functions by making it + * too easy. + */ + + orig_ohci_hub_control = ohci_at91_hc_driver.hub_control; + orig_ohci_hub_status_data = ohci_at91_hc_driver.hub_status_data; + + ohci_at91_hc_driver.hub_status_data = ohci_at91_hub_status_data; + ohci_at91_hc_driver.hub_control = ohci_at91_hub_control; + + return platform_driver_register(&ohci_hcd_at91_driver); +} +module_init(ohci_at91_init); + +static void __exit ohci_at91_cleanup(void) +{ + platform_driver_unregister(&ohci_hcd_at91_driver); +} +module_exit(ohci_at91_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:at91_ohci"); diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index 31b81f9..3fca52e 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -17,7 +17,7 @@ case PIPE_BULK: temp = "bulk"; break; \ case PIPE_INTERRUPT: temp = "intr"; break; \ default: temp = "isoc"; break; \ - }; temp;}) + } temp;}) #define pipestring(pipe) edstring(usb_pipetype(pipe)) /* debug| print the main components of an URB diff --git a/drivers/usb/host/ohci-ep93xx.c b/drivers/usb/host/ohci-ep93xx.c deleted file mode 100644 index 84a20d5..0000000 --- a/drivers/usb/host/ohci-ep93xx.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * OHCI HCD (Host Controller Driver) for USB. - * - * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> - * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net> - * (C) Copyright 2002 Hewlett-Packard Company - * - * Bus Glue for ep93xx. - * - * Written by Christopher Hoover <ch@hpl.hp.com> - * Based on fragments of previous driver by Russell King et al. - * - * Modified for LH7A404 from ohci-sa1111.c - * by Durgesh Pattamatta <pattamattad@sharpsec.com> - * - * Modified for pxa27x from ohci-lh7a404.c - * by Nick Bane <nick@cecomputing.co.uk> 26-8-2004 - * - * Modified for ep93xx from ohci-pxa27x.c - * by Lennert Buytenhek <buytenh@wantstofly.org> 28-2-2006 - * Based on an earlier driver by Ray Lehtiniemi - * - * This file is licenced under the GPL. - */ - -#include <linux/clk.h> -#include <linux/device.h> -#include <linux/signal.h> -#include <linux/platform_device.h> - -static struct clk *usb_host_clock; - -static int ohci_ep93xx_start(struct usb_hcd *hcd) -{ - struct ohci_hcd *ohci = hcd_to_ohci(hcd); - int ret; - - if ((ret = ohci_init(ohci)) < 0) - return ret; - - if ((ret = ohci_run(ohci)) < 0) { - dev_err(hcd->self.controller, "can't start %s\n", - hcd->self.bus_name); - ohci_stop(hcd); - return ret; - } - - return 0; -} - -static struct hc_driver ohci_ep93xx_hc_driver = { - .description = hcd_name, - .product_desc = "EP93xx OHCI", - .hcd_priv_size = sizeof(struct ohci_hcd), - .irq = ohci_irq, - .flags = HCD_USB11 | HCD_MEMORY, - .start = ohci_ep93xx_start, - .stop = ohci_stop, - .shutdown = ohci_shutdown, - .urb_enqueue = ohci_urb_enqueue, - .urb_dequeue = ohci_urb_dequeue, - .endpoint_disable = ohci_endpoint_disable, - .get_frame_number = ohci_get_frame, - .hub_status_data = ohci_hub_status_data, - .hub_control = ohci_hub_control, -#ifdef CONFIG_PM - .bus_suspend = ohci_bus_suspend, - .bus_resume = ohci_bus_resume, -#endif - .start_port_reset = ohci_start_port_reset, -}; - -static int ohci_hcd_ep93xx_drv_probe(struct platform_device *pdev) -{ - struct usb_hcd *hcd; - struct resource *res; - int irq; - int ret; - - if (usb_disabled()) - return -ENODEV; - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENXIO; - - hcd = usb_create_hcd(&ohci_ep93xx_hc_driver, &pdev->dev, "ep93xx"); - if (!hcd) - return -ENOMEM; - - hcd->rsrc_start = res->start; - hcd->rsrc_len = resource_size(res); - - hcd->regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(hcd->regs)) { - ret = PTR_ERR(hcd->regs); - goto err_put_hcd; - } - - usb_host_clock = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(usb_host_clock)) { - ret = PTR_ERR(usb_host_clock); - goto err_put_hcd; - } - - clk_enable(usb_host_clock); - - ohci_hcd_init(hcd_to_ohci(hcd)); - - ret = usb_add_hcd(hcd, irq, 0); - if (ret) - goto err_clk_disable; - - return 0; - -err_clk_disable: - clk_disable(usb_host_clock); -err_put_hcd: - usb_put_hcd(hcd); - - return ret; -} - -static int ohci_hcd_ep93xx_drv_remove(struct platform_device *pdev) -{ - struct usb_hcd *hcd = platform_get_drvdata(pdev); - - usb_remove_hcd(hcd); - clk_disable(usb_host_clock); - usb_put_hcd(hcd); - - return 0; -} - -#ifdef CONFIG_PM -static int ohci_hcd_ep93xx_drv_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct usb_hcd *hcd = platform_get_drvdata(pdev); - struct ohci_hcd *ohci = hcd_to_ohci(hcd); - - if (time_before(jiffies, ohci->next_statechange)) - msleep(5); - ohci->next_statechange = jiffies; - - clk_disable(usb_host_clock); - return 0; -} - -static int ohci_hcd_ep93xx_drv_resume(struct platform_device *pdev) -{ - struct usb_hcd *hcd = platform_get_drvdata(pdev); - struct ohci_hcd *ohci = hcd_to_ohci(hcd); - - if (time_before(jiffies, ohci->next_statechange)) - msleep(5); - ohci->next_statechange = jiffies; - - clk_enable(usb_host_clock); - - ohci_resume(hcd, false); - return 0; -} -#endif - - -static struct platform_driver ohci_hcd_ep93xx_driver = { - .probe = ohci_hcd_ep93xx_drv_probe, - .remove = ohci_hcd_ep93xx_drv_remove, - .shutdown = usb_hcd_platform_shutdown, -#ifdef CONFIG_PM - .suspend = ohci_hcd_ep93xx_drv_suspend, - .resume = ohci_hcd_ep93xx_drv_resume, -#endif - .driver = { - .name = "ep93xx-ohci", - .owner = THIS_MODULE, - }, -}; - -MODULE_ALIAS("platform:ep93xx-ohci"); diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index dc6ee9a..a87baed 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -12,98 +12,55 @@ */ #include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> -#include <linux/platform_data/usb-ohci-exynos.h> #include <linux/usb/phy.h> #include <linux/usb/samsung_usb_phy.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> +#include <linux/usb/otg.h> + +#include "ohci.h" + +#define DRIVER_DESC "OHCI EXYNOS driver" + +static const char hcd_name[] = "ohci-exynos"; +static struct hc_driver __read_mostly exynos_ohci_hc_driver; + +#define to_exynos_ohci(hcd) (struct exynos_ohci_hcd *)(hcd_to_ohci(hcd)->priv) struct exynos_ohci_hcd { - struct device *dev; - struct usb_hcd *hcd; struct clk *clk; struct usb_phy *phy; struct usb_otg *otg; - struct exynos4_ohci_platdata *pdata; }; -static void exynos_ohci_phy_enable(struct exynos_ohci_hcd *exynos_ohci) +static void exynos_ohci_phy_enable(struct platform_device *pdev) { - struct platform_device *pdev = to_platform_device(exynos_ohci->dev); + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd); if (exynos_ohci->phy) usb_phy_init(exynos_ohci->phy); - else if (exynos_ohci->pdata && exynos_ohci->pdata->phy_init) - exynos_ohci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST); } -static void exynos_ohci_phy_disable(struct exynos_ohci_hcd *exynos_ohci) +static void exynos_ohci_phy_disable(struct platform_device *pdev) { - struct platform_device *pdev = to_platform_device(exynos_ohci->dev); + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd); if (exynos_ohci->phy) usb_phy_shutdown(exynos_ohci->phy); - else if (exynos_ohci->pdata && exynos_ohci->pdata->phy_exit) - exynos_ohci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST); -} - -static int ohci_exynos_reset(struct usb_hcd *hcd) -{ - return ohci_init(hcd_to_ohci(hcd)); } -static int ohci_exynos_start(struct usb_hcd *hcd) -{ - struct ohci_hcd *ohci = hcd_to_ohci(hcd); - int ret; - - ohci_dbg(ohci, "ohci_exynos_start, ohci:%p", ohci); - - 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 exynos_ohci_hc_driver = { - .description = hcd_name, - .product_desc = "EXYNOS OHCI Host Controller", - .hcd_priv_size = sizeof(struct ohci_hcd), - - .irq = ohci_irq, - .flags = HCD_MEMORY|HCD_USB11, - - .reset = ohci_exynos_reset, - .start = ohci_exynos_start, - .stop = ohci_stop, - .shutdown = ohci_shutdown, - - .get_frame_number = ohci_get_frame, - - .urb_enqueue = ohci_urb_enqueue, - .urb_dequeue = ohci_urb_dequeue, - .endpoint_disable = ohci_endpoint_disable, - - .hub_status_data = ohci_hub_status_data, - .hub_control = ohci_hub_control, -#ifdef CONFIG_PM - .bus_suspend = ohci_bus_suspend, - .bus_resume = ohci_bus_resume, -#endif - .start_port_reset = ohci_start_port_reset, -}; - static int exynos_ohci_probe(struct platform_device *pdev) { - struct exynos4_ohci_platdata *pdata = dev_get_platdata(&pdev->dev); struct exynos_ohci_hcd *exynos_ohci; struct usb_hcd *hcd; - struct ohci_hcd *ohci; struct resource *res; struct usb_phy *phy; int irq; @@ -119,10 +76,14 @@ static int exynos_ohci_probe(struct platform_device *pdev) 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) + hcd = usb_create_hcd(&exynos_ohci_hc_driver, + &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) { + dev_err(&pdev->dev, "Unable to create HCD\n"); return -ENOMEM; + } + + exynos_ohci = to_exynos_ohci(hcd); if (of_device_is_compatible(pdev->dev.of_node, "samsung,exynos5440-ohci")) @@ -130,30 +91,15 @@ static int exynos_ohci_probe(struct platform_device *pdev) phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); if (IS_ERR(phy)) { - /* Fallback to pdata */ - if (!pdata) { - dev_warn(&pdev->dev, "no platform data or transceiver defined\n"); - return -EPROBE_DEFER; - } else { - exynos_ohci->pdata = pdata; - } + usb_put_hcd(hcd); + dev_warn(&pdev->dev, "no platform data or transceiver defined\n"); + return -EPROBE_DEFER; } else { exynos_ohci->phy = phy; exynos_ohci->otg = phy->otg; } skip_phy: - - exynos_ohci->dev = &pdev->dev; - - hcd = usb_create_hcd(&exynos_ohci_hc_driver, &pdev->dev, - dev_name(&pdev->dev)); - if (!hcd) { - dev_err(&pdev->dev, "Unable to create HCD\n"); - return -ENOMEM; - } - - exynos_ohci->hcd = hcd; exynos_ohci->clk = devm_clk_get(&pdev->dev, "usbhost"); if (IS_ERR(exynos_ohci->clk)) { @@ -190,26 +136,21 @@ skip_phy: } if (exynos_ohci->otg) - exynos_ohci->otg->set_host(exynos_ohci->otg, - &exynos_ohci->hcd->self); + exynos_ohci->otg->set_host(exynos_ohci->otg, &hcd->self); - exynos_ohci_phy_enable(exynos_ohci); + platform_set_drvdata(pdev, hcd); - ohci = hcd_to_ohci(hcd); - ohci_hcd_init(ohci); + exynos_ohci_phy_enable(pdev); err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) { dev_err(&pdev->dev, "Failed to add USB HCD\n"); goto fail_add_hcd; } - - platform_set_drvdata(pdev, exynos_ohci); - return 0; fail_add_hcd: - exynos_ohci_phy_disable(exynos_ohci); + exynos_ohci_phy_disable(pdev); fail_io: clk_disable_unprepare(exynos_ohci->clk); fail_clk: @@ -219,16 +160,15 @@ fail_clk: static int exynos_ohci_remove(struct platform_device *pdev) { - struct exynos_ohci_hcd *exynos_ohci = platform_get_drvdata(pdev); - struct usb_hcd *hcd = exynos_ohci->hcd; + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd); usb_remove_hcd(hcd); if (exynos_ohci->otg) - exynos_ohci->otg->set_host(exynos_ohci->otg, - &exynos_ohci->hcd->self); + exynos_ohci->otg->set_host(exynos_ohci->otg, &hcd->self); - exynos_ohci_phy_disable(exynos_ohci); + exynos_ohci_phy_disable(pdev); clk_disable_unprepare(exynos_ohci->clk); @@ -239,8 +179,7 @@ static int exynos_ohci_remove(struct platform_device *pdev) static void exynos_ohci_shutdown(struct platform_device *pdev) { - struct exynos_ohci_hcd *exynos_ohci = platform_get_drvdata(pdev); - struct usb_hcd *hcd = exynos_ohci->hcd; + struct usb_hcd *hcd = platform_get_drvdata(pdev); if (hcd->driver->shutdown) hcd->driver->shutdown(hcd); @@ -249,9 +188,10 @@ static void exynos_ohci_shutdown(struct platform_device *pdev) #ifdef CONFIG_PM static int exynos_ohci_suspend(struct device *dev) { - struct exynos_ohci_hcd *exynos_ohci = dev_get_drvdata(dev); - struct usb_hcd *hcd = exynos_ohci->hcd; + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd); struct ohci_hcd *ohci = hcd_to_ohci(hcd); + struct platform_device *pdev = to_platform_device(dev); unsigned long flags; int rc = 0; @@ -271,10 +211,9 @@ static int exynos_ohci_suspend(struct device *dev) clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); if (exynos_ohci->otg) - exynos_ohci->otg->set_host(exynos_ohci->otg, - &exynos_ohci->hcd->self); + exynos_ohci->otg->set_host(exynos_ohci->otg, &hcd->self); - exynos_ohci_phy_disable(exynos_ohci); + exynos_ohci_phy_disable(pdev); clk_disable_unprepare(exynos_ohci->clk); @@ -286,16 +225,16 @@ fail: static int exynos_ohci_resume(struct device *dev) { - struct exynos_ohci_hcd *exynos_ohci = dev_get_drvdata(dev); - struct usb_hcd *hcd = exynos_ohci->hcd; + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd); + struct platform_device *pdev = to_platform_device(dev); clk_prepare_enable(exynos_ohci->clk); if (exynos_ohci->otg) - exynos_ohci->otg->set_host(exynos_ohci->otg, - &exynos_ohci->hcd->self); + exynos_ohci->otg->set_host(exynos_ohci->otg, &hcd->self); - exynos_ohci_phy_enable(exynos_ohci); + exynos_ohci_phy_enable(pdev); ohci_resume(hcd, false); @@ -306,6 +245,10 @@ static int exynos_ohci_resume(struct device *dev) #define exynos_ohci_resume NULL #endif +static const struct ohci_driver_overrides exynos_overrides __initconst = { + .extra_priv_size = sizeof(struct exynos_ohci_hcd), +}; + static const struct dev_pm_ops exynos_ohci_pm_ops = { .suspend = exynos_ohci_suspend, .resume = exynos_ohci_resume, @@ -331,6 +274,23 @@ static struct platform_driver exynos_ohci_driver = { .of_match_table = of_match_ptr(exynos_ohci_match), } }; +static int __init ohci_exynos_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + ohci_init_driver(&exynos_ohci_hc_driver, &exynos_overrides); + return platform_driver_register(&exynos_ohci_driver); +} +module_init(ohci_exynos_init); + +static void __exit ohci_exynos_cleanup(void) +{ + platform_driver_unregister(&exynos_ohci_driver); +} +module_exit(ohci_exynos_cleanup); MODULE_ALIAS("platform:exynos-ohci"); MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 604cad1..8ada13f 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1161,10 +1161,12 @@ void ohci_init_driver(struct hc_driver *drv, /* Copy the generic table to drv and then apply the overrides */ *drv = ohci_hc_driver; - drv->product_desc = over->product_desc; - drv->hcd_priv_size += over->extra_priv_size; - if (over->reset) - drv->reset = over->reset; + if (over) { + drv->product_desc = over->product_desc; + drv->hcd_priv_size += over->extra_priv_size; + if (over->reset) + drv->reset = over->reset; + } } EXPORT_SYMBOL_GPL(ohci_init_driver); @@ -1179,46 +1181,6 @@ MODULE_LICENSE ("GPL"); #define SA1111_DRIVER ohci_hcd_sa1111_driver #endif -#if defined(CONFIG_ARCH_S3C24XX) || defined(CONFIG_ARCH_S3C64XX) -#include "ohci-s3c2410.c" -#define S3C2410_PLATFORM_DRIVER ohci_hcd_s3c2410_driver -#endif - -#ifdef CONFIG_USB_OHCI_EXYNOS -#include "ohci-exynos.c" -#define EXYNOS_PLATFORM_DRIVER exynos_ohci_driver -#endif - -#ifdef CONFIG_USB_OHCI_HCD_OMAP1 -#include "ohci-omap.c" -#define OMAP1_PLATFORM_DRIVER ohci_hcd_omap_driver -#endif - -#ifdef CONFIG_USB_OHCI_HCD_OMAP3 -#include "ohci-omap3.c" -#define OMAP3_PLATFORM_DRIVER ohci_hcd_omap3_driver -#endif - -#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx) -#include "ohci-pxa27x.c" -#define PLATFORM_DRIVER ohci_hcd_pxa27x_driver -#endif - -#ifdef CONFIG_ARCH_EP93XX -#include "ohci-ep93xx.c" -#define EP93XX_PLATFORM_DRIVER ohci_hcd_ep93xx_driver -#endif - -#ifdef CONFIG_ARCH_AT91 -#include "ohci-at91.c" -#define AT91_PLATFORM_DRIVER ohci_hcd_at91_driver -#endif - -#ifdef CONFIG_ARCH_LPC32XX -#include "ohci-nxp.c" -#define NXP_PLATFORM_DRIVER usb_hcd_nxp_driver -#endif - #ifdef CONFIG_ARCH_DAVINCI_DA8XX #include "ohci-da8xx.c" #define DAVINCI_PLATFORM_DRIVER ohci_hcd_da8xx_driver @@ -1229,11 +1191,6 @@ MODULE_LICENSE ("GPL"); #define OF_PLATFORM_DRIVER ohci_hcd_ppc_of_driver #endif -#ifdef CONFIG_PLAT_SPEAR -#include "ohci-spear.c" -#define SPEAR_PLATFORM_DRIVER spear_ohci_hcd_driver -#endif - #ifdef CONFIG_PPC_PS3 #include "ohci-ps3.c" #define PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver @@ -1296,18 +1253,6 @@ static int __init ohci_hcd_mod_init(void) goto error_platform; #endif -#ifdef OMAP1_PLATFORM_DRIVER - retval = platform_driver_register(&OMAP1_PLATFORM_DRIVER); - if (retval < 0) - goto error_omap1_platform; -#endif - -#ifdef OMAP3_PLATFORM_DRIVER - retval = platform_driver_register(&OMAP3_PLATFORM_DRIVER); - if (retval < 0) - goto error_omap3_platform; -#endif - #ifdef OF_PLATFORM_DRIVER retval = platform_driver_register(&OF_PLATFORM_DRIVER); if (retval < 0) @@ -1332,79 +1277,19 @@ static int __init ohci_hcd_mod_init(void) goto error_tmio; #endif -#ifdef S3C2410_PLATFORM_DRIVER - retval = platform_driver_register(&S3C2410_PLATFORM_DRIVER); - if (retval < 0) - goto error_s3c2410; -#endif - -#ifdef EXYNOS_PLATFORM_DRIVER - retval = platform_driver_register(&EXYNOS_PLATFORM_DRIVER); - if (retval < 0) - goto error_exynos; -#endif - -#ifdef EP93XX_PLATFORM_DRIVER - retval = platform_driver_register(&EP93XX_PLATFORM_DRIVER); - if (retval < 0) - goto error_ep93xx; -#endif - -#ifdef AT91_PLATFORM_DRIVER - retval = platform_driver_register(&AT91_PLATFORM_DRIVER); - if (retval < 0) - goto error_at91; -#endif - -#ifdef NXP_PLATFORM_DRIVER - retval = platform_driver_register(&NXP_PLATFORM_DRIVER); - if (retval < 0) - goto error_nxp; -#endif - #ifdef DAVINCI_PLATFORM_DRIVER retval = platform_driver_register(&DAVINCI_PLATFORM_DRIVER); if (retval < 0) goto error_davinci; #endif -#ifdef SPEAR_PLATFORM_DRIVER - retval = platform_driver_register(&SPEAR_PLATFORM_DRIVER); - if (retval < 0) - goto error_spear; -#endif - return retval; /* Error path */ -#ifdef SPEAR_PLATFORM_DRIVER - platform_driver_unregister(&SPEAR_PLATFORM_DRIVER); - error_spear: -#endif #ifdef DAVINCI_PLATFORM_DRIVER platform_driver_unregister(&DAVINCI_PLATFORM_DRIVER); error_davinci: #endif -#ifdef NXP_PLATFORM_DRIVER - platform_driver_unregister(&NXP_PLATFORM_DRIVER); - error_nxp: -#endif -#ifdef AT91_PLATFORM_DRIVER - platform_driver_unregister(&AT91_PLATFORM_DRIVER); - error_at91: -#endif -#ifdef EP93XX_PLATFORM_DRIVER - platform_driver_unregister(&EP93XX_PLATFORM_DRIVER); - error_ep93xx: -#endif -#ifdef EXYNOS_PLATFORM_DRIVER - platform_driver_unregister(&EXYNOS_PLATFORM_DRIVER); - error_exynos: -#endif -#ifdef S3C2410_PLATFORM_DRIVER - platform_driver_unregister(&S3C2410_PLATFORM_DRIVER); - error_s3c2410: -#endif #ifdef TMIO_OHCI_DRIVER platform_driver_unregister(&TMIO_OHCI_DRIVER); error_tmio: @@ -1421,14 +1306,6 @@ static int __init ohci_hcd_mod_init(void) platform_driver_unregister(&OF_PLATFORM_DRIVER); error_of_platform: #endif -#ifdef OMAP3_PLATFORM_DRIVER - platform_driver_unregister(&OMAP3_PLATFORM_DRIVER); - error_omap3_platform: -#endif -#ifdef OMAP1_PLATFORM_DRIVER - platform_driver_unregister(&OMAP1_PLATFORM_DRIVER); - error_omap1_platform: -#endif #ifdef PLATFORM_DRIVER platform_driver_unregister(&PLATFORM_DRIVER); error_platform: @@ -1450,27 +1327,9 @@ module_init(ohci_hcd_mod_init); static void __exit ohci_hcd_mod_exit(void) { -#ifdef SPEAR_PLATFORM_DRIVER - platform_driver_unregister(&SPEAR_PLATFORM_DRIVER); -#endif #ifdef DAVINCI_PLATFORM_DRIVER platform_driver_unregister(&DAVINCI_PLATFORM_DRIVER); #endif -#ifdef NXP_PLATFORM_DRIVER - platform_driver_unregister(&NXP_PLATFORM_DRIVER); -#endif -#ifdef AT91_PLATFORM_DRIVER - platform_driver_unregister(&AT91_PLATFORM_DRIVER); -#endif -#ifdef EP93XX_PLATFORM_DRIVER - platform_driver_unregister(&EP93XX_PLATFORM_DRIVER); -#endif -#ifdef EXYNOS_PLATFORM_DRIVER - platform_driver_unregister(&EXYNOS_PLATFORM_DRIVER); -#endif -#ifdef S3C2410_PLATFORM_DRIVER - platform_driver_unregister(&S3C2410_PLATFORM_DRIVER); -#endif #ifdef TMIO_OHCI_DRIVER platform_driver_unregister(&TMIO_OHCI_DRIVER); #endif @@ -1483,12 +1342,6 @@ static void __exit ohci_hcd_mod_exit(void) #ifdef OF_PLATFORM_DRIVER platform_driver_unregister(&OF_PLATFORM_DRIVER); #endif -#ifdef OMAP3_PLATFORM_DRIVER - platform_driver_unregister(&OMAP3_PLATFORM_DRIVER); -#endif -#ifdef OMAP1_PLATFORM_DRIVER - platform_driver_unregister(&OMAP1_PLATFORM_DRIVER); -#endif #ifdef PLATFORM_DRIVER platform_driver_unregister(&PLATFORM_DRIVER); #endif diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 2347ab8..61705a7 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -212,10 +212,11 @@ __acquires(ohci->lock) /* Sometimes PCI D3 suspend trashes frame timings ... */ periodic_reinit (ohci); - /* the following code is executed with ohci->lock held and - * irqs disabled if and only if autostopped is true + /* + * The following code is executed with ohci->lock held and + * irqs disabled if and only if autostopped is true. This + * will cause sparse to warn about a "context imbalance". */ - skip_resume: /* interrupts might have been disabled */ ohci_writel (ohci, OHCI_INTR_INIT, &ohci->regs->intrenable); @@ -531,7 +532,7 @@ ohci_hub_descriptor ( temp |= 0x0010; else if (rh & RH_A_OCPM) /* per-port overcurrent reporting? */ temp |= 0x0008; - desc->wHubCharacteristics = (__force __u16)cpu_to_hc16(ohci, temp); + desc->wHubCharacteristics = cpu_to_le16(temp); /* ports removable, and usb 1.0 legacy PortPwrCtrlMask */ rh = roothub_b (ohci); diff --git a/drivers/usb/host/ohci-nxp.c b/drivers/usb/host/ohci-nxp.c index 7d7d507..9ab7e24 100644 --- a/drivers/usb/host/ohci-nxp.c +++ b/drivers/usb/host/ohci-nxp.c @@ -19,10 +19,19 @@ * or implied. */ #include <linux/clk.h> -#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/io.h> #include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> #include <linux/of.h> +#include <linux/platform_device.h> #include <linux/usb/isp1301.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> + +#include "ohci.h" + #include <mach/hardware.h> #include <asm/mach-types.h> @@ -57,6 +66,11 @@ #define start_int_umask(irq) #endif +#define DRIVER_DESC "OHCI NXP driver" + +static const char hcd_name[] = "ohci-nxp"; +static struct hc_driver __read_mostly ohci_nxp_hc_driver; + static struct i2c_client *isp1301_i2c_client; extern int usb_disabled(void); @@ -132,14 +146,14 @@ static inline void isp1301_vbus_off(void) OTG1_VBUS_DRV); } -static void nxp_start_hc(void) +static void ohci_nxp_start_hc(void) { unsigned long tmp = __raw_readl(USB_OTG_STAT_CONTROL) | HOST_EN; __raw_writel(tmp, USB_OTG_STAT_CONTROL); isp1301_vbus_on(); } -static void nxp_stop_hc(void) +static void ohci_nxp_stop_hc(void) { unsigned long tmp; isp1301_vbus_off(); @@ -147,68 +161,9 @@ static void nxp_stop_hc(void) __raw_writel(tmp, USB_OTG_STAT_CONTROL); } -static int ohci_nxp_start(struct usb_hcd *hcd) -{ - struct ohci_hcd *ohci = hcd_to_ohci(hcd); - int ret; - - if ((ret = ohci_init(ohci)) < 0) - return ret; - - if ((ret = ohci_run(ohci)) < 0) { - dev_err(hcd->self.controller, "can't start\n"); - ohci_stop(hcd); - return ret; - } - return 0; -} - -static const struct hc_driver ohci_nxp_hc_driver = { - .description = hcd_name, - .product_desc = "nxp OHCI", - - /* - * generic hardware linkage - */ - .irq = ohci_irq, - .flags = HCD_USB11 | HCD_MEMORY, - - .hcd_priv_size = sizeof(struct ohci_hcd), - /* - * basic lifecycle operations - */ - .start = ohci_nxp_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, -#ifdef CONFIG_PM - .bus_suspend = ohci_bus_suspend, - .bus_resume = ohci_bus_resume, -#endif - .start_port_reset = ohci_start_port_reset, -}; - -static int usb_hcd_nxp_probe(struct platform_device *pdev) +static int ohci_hcd_nxp_probe(struct platform_device *pdev) { struct usb_hcd *hcd = 0; - struct ohci_hcd *ohci; const struct hc_driver *driver = &ohci_nxp_hc_driver; struct resource *res; int ret = 0, irq; @@ -313,17 +268,15 @@ static int usb_hcd_nxp_probe(struct platform_device *pdev) goto fail_resource; } - nxp_start_hc(); + ohci_nxp_start_hc(); platform_set_drvdata(pdev, hcd); - ohci = hcd_to_ohci(hcd); - ohci_hcd_init(ohci); dev_info(&pdev->dev, "at 0x%p, irq %d\n", hcd->regs, hcd->irq); ret = usb_add_hcd(hcd, irq, 0); if (ret == 0) return ret; - nxp_stop_hc(); + ohci_nxp_stop_hc(); fail_resource: usb_put_hcd(hcd); fail_hcd: @@ -345,12 +298,12 @@ fail_disable: return ret; } -static int usb_hcd_nxp_remove(struct platform_device *pdev) +static int ohci_hcd_nxp_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); usb_remove_hcd(hcd); - nxp_stop_hc(); + ohci_nxp_stop_hc(); usb_put_hcd(hcd); clk_disable(usb_pll_clk); clk_put(usb_pll_clk); @@ -366,20 +319,40 @@ static int usb_hcd_nxp_remove(struct platform_device *pdev) MODULE_ALIAS("platform:usb-ohci"); #ifdef CONFIG_OF -static const struct of_device_id usb_hcd_nxp_match[] = { +static const struct of_device_id ohci_hcd_nxp_match[] = { { .compatible = "nxp,ohci-nxp" }, {}, }; -MODULE_DEVICE_TABLE(of, usb_hcd_nxp_match); +MODULE_DEVICE_TABLE(of, ohci_hcd_nxp_match); #endif -static struct platform_driver usb_hcd_nxp_driver = { +static struct platform_driver ohci_hcd_nxp_driver = { .driver = { .name = "usb-ohci", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(usb_hcd_nxp_match), + .of_match_table = of_match_ptr(ohci_hcd_nxp_match), }, - .probe = usb_hcd_nxp_probe, - .remove = usb_hcd_nxp_remove, + .probe = ohci_hcd_nxp_probe, + .remove = ohci_hcd_nxp_remove, }; +static int __init ohci_nxp_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + + ohci_init_driver(&ohci_nxp_hc_driver, NULL); + return platform_driver_register(&ohci_hcd_nxp_driver); +} +module_init(ohci_nxp_init); + +static void __exit ohci_nxp_cleanup(void) +{ + platform_driver_unregister(&ohci_hcd_nxp_driver); +} +module_exit(ohci_nxp_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 31d3a12..f253214 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -14,12 +14,21 @@ * This file is licenced under the GPL. */ -#include <linux/signal.h> -#include <linux/jiffies.h> -#include <linux/platform_device.h> #include <linux/clk.h> +#include <linux/dma-mapping.h> #include <linux/err.h> #include <linux/gpio.h> +#include <linux/io.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/usb/otg.h> +#include <linux/platform_device.h> +#include <linux/signal.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> + +#include "ohci.h" #include <asm/io.h> #include <asm/mach-types.h> @@ -42,10 +51,7 @@ #define OMAP1510_LB_MMU_RAM_H 0xfffec234 #define OMAP1510_LB_MMU_RAM_L 0xfffec238 - -#ifndef CONFIG_ARCH_OMAP -#error "This file is OMAP bus glue. CONFIG_OMAP must be defined." -#endif +#define DRIVER_DESC "OHCI OMAP driver" #ifdef CONFIG_TPS65010 #include <linux/i2c/tps65010.h> @@ -68,8 +74,9 @@ extern int ocpi_enable(void); static struct clk *usb_host_ck; static struct clk *usb_dc_ck; -static int host_enabled; -static int host_initialized; + +static const char hcd_name[] = "ohci-omap"; +static struct hc_driver __read_mostly ohci_omap_hc_driver; static void omap_ohci_clock_power(int on) { @@ -188,7 +195,7 @@ static void start_hnp(struct ohci_hcd *ohci) /*-------------------------------------------------------------------------*/ -static int ohci_omap_init(struct usb_hcd *hcd) +static int ohci_omap_reset(struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci(hcd); struct omap_usb_config *config = dev_get_platdata(hcd->self.controller); @@ -198,9 +205,9 @@ static int ohci_omap_init(struct usb_hcd *hcd) dev_dbg(hcd->self.controller, "starting USB Controller\n"); if (config->otg) { - ohci_to_hcd(ohci)->self.otg_port = config->otg; + hcd->self.otg_port = config->otg; /* default/minimum OTG power budget: 8 mA */ - ohci_to_hcd(ohci)->power_budget = 8; + hcd->power_budget = 8; } /* boards can use OTG transceivers in non-OTG modes */ @@ -238,9 +245,15 @@ static int ohci_omap_init(struct usb_hcd *hcd) omap_1510_local_bus_init(); } - if ((ret = ohci_init(ohci)) < 0) + ret = ohci_setup(hcd); + if (ret < 0) return ret; + if (config->otg || config->rwc) { + ohci->hc_control = OHCI_CTRL_RWC; + writel(OHCI_CTRL_RWC, &ohci->regs->control); + } + /* board-specific power switching and overcurrent support */ if (machine_is_omap_osk() || machine_is_omap_innovator()) { u32 rh = roothub_a (ohci); @@ -281,14 +294,6 @@ static int ohci_omap_init(struct usb_hcd *hcd) return 0; } -static void ohci_omap_stop(struct usb_hcd *hcd) -{ - dev_dbg(hcd->self.controller, "stopping USB Controller\n"); - ohci_stop(hcd); - omap_ohci_clock_power(0); -} - - /*-------------------------------------------------------------------------*/ /** @@ -304,7 +309,6 @@ static int usb_hcd_omap_probe (const struct hc_driver *driver, { int retval, irq; struct usb_hcd *hcd = 0; - struct ohci_hcd *ohci; if (pdev->num_resources != 2) { printk(KERN_ERR "hcd probe: invalid num_resources: %i\n", @@ -354,12 +358,6 @@ static int usb_hcd_omap_probe (const struct hc_driver *driver, goto err2; } - ohci = hcd_to_ohci(hcd); - ohci_hcd_init(ohci); - - host_initialized = 0; - host_enabled = 1; - irq = platform_get_irq(pdev, 0); if (irq < 0) { retval = -ENXIO; @@ -369,11 +367,6 @@ static int usb_hcd_omap_probe (const struct hc_driver *driver, if (retval) goto err3; - host_initialized = 1; - - if (!host_enabled) - omap_ohci_clock_power(0); - return 0; err3: iounmap(hcd->regs); @@ -402,7 +395,9 @@ err0: static inline void usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev) { + dev_dbg(hcd->self.controller, "stopping USB Controller\n"); usb_remove_hcd(hcd); + omap_ohci_clock_power(0); if (!IS_ERR_OR_NULL(hcd->phy)) { (void) otg_set_host(hcd->phy->otg, 0); usb_put_phy(hcd->phy); @@ -418,76 +413,6 @@ usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev) /*-------------------------------------------------------------------------*/ -static int -ohci_omap_start (struct usb_hcd *hcd) -{ - struct omap_usb_config *config; - struct ohci_hcd *ohci = hcd_to_ohci (hcd); - int ret; - - if (!host_enabled) - return 0; - config = dev_get_platdata(hcd->self.controller); - if (config->otg || config->rwc) { - ohci->hc_control = OHCI_CTRL_RWC; - writel(OHCI_CTRL_RWC, &ohci->regs->control); - } - - if ((ret = ohci_run (ohci)) < 0) { - dev_err(hcd->self.controller, "can't start\n"); - ohci_stop (hcd); - return ret; - } - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static const struct hc_driver ohci_omap_hc_driver = { - .description = hcd_name, - .product_desc = "OMAP OHCI", - .hcd_priv_size = sizeof(struct ohci_hcd), - - /* - * generic hardware linkage - */ - .irq = ohci_irq, - .flags = HCD_USB11 | HCD_MEMORY, - - /* - * basic lifecycle operations - */ - .reset = ohci_omap_init, - .start = ohci_omap_start, - .stop = ohci_omap_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, -#ifdef CONFIG_PM - .bus_suspend = ohci_bus_suspend, - .bus_resume = ohci_bus_resume, -#endif - .start_port_reset = ohci_start_port_reset, -}; - -/*-------------------------------------------------------------------------*/ - static int ohci_hcd_omap_drv_probe(struct platform_device *dev) { return usb_hcd_omap_probe(&ohci_omap_hc_driver, dev); @@ -506,16 +431,23 @@ static int ohci_hcd_omap_drv_remove(struct platform_device *dev) #ifdef CONFIG_PM -static int ohci_omap_suspend(struct platform_device *dev, pm_message_t message) +static int ohci_omap_suspend(struct platform_device *pdev, pm_message_t message) { - struct ohci_hcd *ohci = hcd_to_ohci(platform_get_drvdata(dev)); + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + bool do_wakeup = device_may_wakeup(&pdev->dev); + int ret; if (time_before(jiffies, ohci->next_statechange)) msleep(5); ohci->next_statechange = jiffies; + ret = ohci_suspend(hcd, do_wakeup); + if (ret) + return ret; + omap_ohci_clock_power(0); - return 0; + return ret; } static int ohci_omap_resume(struct platform_device *dev) @@ -553,4 +485,29 @@ static struct platform_driver ohci_hcd_omap_driver = { }, }; +static const struct ohci_driver_overrides omap_overrides __initconst = { + .product_desc = "OMAP OHCI", + .reset = ohci_omap_reset +}; + +static int __init ohci_omap_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + + ohci_init_driver(&ohci_omap_hc_driver, &omap_overrides); + return platform_driver_register(&ohci_hcd_omap_driver); +} +module_init(ohci_omap_init); + +static void __exit ohci_omap_cleanup(void) +{ + platform_driver_unregister(&ohci_hcd_omap_driver); +} +module_exit(ohci_omap_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_ALIAS("platform:ohci"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/ohci-omap3.c b/drivers/usb/host/ohci-omap3.c index a09af26..408d06a 100644 --- a/drivers/usb/host/ohci-omap3.c +++ b/drivers/usb/host/ohci-omap3.c @@ -29,90 +29,22 @@ * - add kernel-doc */ +#include <linux/dma-mapping.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/usb/otg.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> -#include <linux/of.h> -#include <linux/dma-mapping.h> - -/*-------------------------------------------------------------------------*/ - -static int ohci_omap3_init(struct usb_hcd *hcd) -{ - dev_dbg(hcd->self.controller, "starting OHCI controller\n"); - - return ohci_init(hcd_to_ohci(hcd)); -} - -/*-------------------------------------------------------------------------*/ - -static int ohci_omap3_start(struct usb_hcd *hcd) -{ - struct ohci_hcd *ohci = hcd_to_ohci(hcd); - int ret; - - /* - * RemoteWakeupConnected has to be set explicitly before - * calling ohci_run. The reset value of RWC is 0. - */ - ohci->hc_control = OHCI_CTRL_RWC; - writel(OHCI_CTRL_RWC, &ohci->regs->control); - - ret = ohci_run(ohci); - - if (ret < 0) { - dev_err(hcd->self.controller, "can't start\n"); - ohci_stop(hcd); - } +#include <linux/usb.h> +#include <linux/usb/hcd.h> - return ret; -} +#include "ohci.h" -/*-------------------------------------------------------------------------*/ +#define DRIVER_DESC "OHCI OMAP3 driver" -static const struct hc_driver ohci_omap3_hc_driver = { - .description = hcd_name, - .product_desc = "OMAP3 OHCI Host Controller", - .hcd_priv_size = sizeof(struct ohci_hcd), - - /* - * generic hardware linkage - */ - .irq = ohci_irq, - .flags = HCD_USB11 | HCD_MEMORY, - - /* - * basic lifecycle operations - */ - .reset = ohci_omap3_init, - .start = ohci_omap3_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, -#ifdef CONFIG_PM - .bus_suspend = ohci_bus_suspend, - .bus_resume = ohci_bus_resume, -#endif - .start_port_reset = ohci_start_port_reset, -}; - -/*-------------------------------------------------------------------------*/ +static const char hcd_name[] = "ohci-omap3"; +static struct hc_driver __read_mostly ohci_omap3_hc_driver; /* * configure so an HC device and id are always provided @@ -129,6 +61,7 @@ static const struct hc_driver ohci_omap3_hc_driver = { static int ohci_hcd_omap3_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct ohci_hcd *ohci; struct usb_hcd *hcd = NULL; void __iomem *regs = NULL; struct resource *res; @@ -185,7 +118,12 @@ static int ohci_hcd_omap3_probe(struct platform_device *pdev) pm_runtime_enable(dev); pm_runtime_get_sync(dev); - ohci_hcd_init(hcd_to_ohci(hcd)); + ohci = hcd_to_ohci(hcd); + /* + * RemoteWakeupConnected has to be set explicitly before + * calling ohci_run. The reset value of RWC is 0. + */ + ohci->hc_control = OHCI_CTRL_RWC; ret = usb_add_hcd(hcd, irq, 0); if (ret) { @@ -248,5 +186,25 @@ static struct platform_driver ohci_hcd_omap3_driver = { }, }; +static int __init ohci_omap3_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + + ohci_init_driver(&ohci_omap3_hc_driver, NULL); + return platform_driver_register(&ohci_hcd_omap3_driver); +} +module_init(ohci_omap3_init); + +static void __exit ohci_omap3_cleanup(void) +{ + platform_driver_unregister(&ohci_hcd_omap3_driver); +} +module_exit(ohci_omap3_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_ALIAS("platform:ohci-omap3"); MODULE_AUTHOR("Anand Gadiyar <gadiyar@ti.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index ec337c2..90879e9 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -150,28 +150,16 @@ static int ohci_quirk_nec(struct usb_hcd *hcd) static int ohci_quirk_amd700(struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci(hcd); - struct pci_dev *amd_smbus_dev; - u8 rev; if (usb_amd_find_chipset_info()) ohci->flags |= OHCI_QUIRK_AMD_PLL; - amd_smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI, - PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL); - if (!amd_smbus_dev) - return 0; - - rev = amd_smbus_dev->revision; - /* SB800 needs pre-fetch fix */ - if ((rev >= 0x40) && (rev <= 0x4f)) { + if (usb_amd_prefetch_quirk()) { ohci->flags |= OHCI_QUIRK_AMD_PREFETCH; ohci_dbg(ohci, "enabled AMD prefetch quirk\n"); } - pci_dev_put(amd_smbus_dev); - amd_smbus_dev = NULL; - return 0; } @@ -323,3 +311,4 @@ module_exit(ohci_pci_cleanup); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); +MODULE_SOFTDEP("pre: ehci_pci"); diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index a4c6410..f351ff5 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -139,14 +139,21 @@ static int ohci_platform_remove(struct platform_device *dev) static int ohci_platform_suspend(struct device *dev) { - struct usb_ohci_pdata *pdata = dev_get_platdata(dev); + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct usb_ohci_pdata *pdata = dev->platform_data; struct platform_device *pdev = container_of(dev, struct platform_device, dev); + bool do_wakeup = device_may_wakeup(dev); + int ret; + + ret = ohci_suspend(hcd, do_wakeup); + if (ret) + return ret; if (pdata->power_suspend) pdata->power_suspend(pdev); - return 0; + return ret; } static int ohci_platform_resume(struct device *dev) diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index 93371a2..deea5d1 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -19,15 +19,26 @@ * This file is licenced under the GPL. */ -#include <linux/device.h> -#include <linux/signal.h> -#include <linux/platform_device.h> #include <linux/clk.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> #include <linux/of_platform.h> #include <linux/of_gpio.h> -#include <mach/hardware.h> #include <linux/platform_data/usb-ohci-pxa27x.h> #include <linux/platform_data/usb-pxa3xx-ulpi.h> +#include <linux/platform_device.h> +#include <linux/signal.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> +#include <linux/usb/otg.h> + +#include <mach/hardware.h> + +#include "ohci.h" + +#define DRIVER_DESC "OHCI PXA27x/PXA3x driver" /* * UHC: USB Host Controller (OHCI-like) register definitions @@ -101,16 +112,16 @@ #define PXA_UHC_MAX_PORTNUM 3 -struct pxa27x_ohci { - /* must be 1st member here for hcd_to_ohci() to work */ - struct ohci_hcd ohci; +static const char hcd_name[] = "ohci-pxa27x"; + +static struct hc_driver __read_mostly ohci_pxa27x_hc_driver; - struct device *dev; +struct pxa27x_ohci { struct clk *clk; void __iomem *mmio_base; }; -#define to_pxa27x_ohci(hcd) (struct pxa27x_ohci *)hcd_to_ohci(hcd) +#define to_pxa27x_ohci(hcd) (struct pxa27x_ohci *)(hcd_to_ohci(hcd)->priv) /* PMM_NPS_MODE -- PMM Non-power switching mode @@ -122,10 +133,10 @@ struct pxa27x_ohci { PMM_PERPORT_MODE -- PMM per port switching mode Ports are powered individually. */ -static int pxa27x_ohci_select_pmm(struct pxa27x_ohci *ohci, int mode) +static int pxa27x_ohci_select_pmm(struct pxa27x_ohci *pxa_ohci, int mode) { - uint32_t uhcrhda = __raw_readl(ohci->mmio_base + UHCRHDA); - uint32_t uhcrhdb = __raw_readl(ohci->mmio_base + UHCRHDB); + uint32_t uhcrhda = __raw_readl(pxa_ohci->mmio_base + UHCRHDA); + uint32_t uhcrhdb = __raw_readl(pxa_ohci->mmio_base + UHCRHDB); switch (mode) { case PMM_NPS_MODE: @@ -149,20 +160,18 @@ static int pxa27x_ohci_select_pmm(struct pxa27x_ohci *ohci, int mode) uhcrhda |= RH_A_NPS; } - __raw_writel(uhcrhda, ohci->mmio_base + UHCRHDA); - __raw_writel(uhcrhdb, ohci->mmio_base + UHCRHDB); + __raw_writel(uhcrhda, pxa_ohci->mmio_base + UHCRHDA); + __raw_writel(uhcrhdb, pxa_ohci->mmio_base + UHCRHDB); return 0; } -extern int usb_disabled(void); - /*-------------------------------------------------------------------------*/ -static inline void pxa27x_setup_hc(struct pxa27x_ohci *ohci, +static inline void pxa27x_setup_hc(struct pxa27x_ohci *pxa_ohci, struct pxaohci_platform_data *inf) { - uint32_t uhchr = __raw_readl(ohci->mmio_base + UHCHR); - uint32_t uhcrhda = __raw_readl(ohci->mmio_base + UHCRHDA); + uint32_t uhchr = __raw_readl(pxa_ohci->mmio_base + UHCHR); + uint32_t uhcrhda = __raw_readl(pxa_ohci->mmio_base + UHCRHDA); if (inf->flags & ENABLE_PORT1) uhchr &= ~UHCHR_SSEP1; @@ -194,17 +203,17 @@ static inline void pxa27x_setup_hc(struct pxa27x_ohci *ohci, uhcrhda |= UHCRHDA_POTPGT(inf->power_on_delay / 2); } - __raw_writel(uhchr, ohci->mmio_base + UHCHR); - __raw_writel(uhcrhda, ohci->mmio_base + UHCRHDA); + __raw_writel(uhchr, pxa_ohci->mmio_base + UHCHR); + __raw_writel(uhcrhda, pxa_ohci->mmio_base + UHCRHDA); } -static inline void pxa27x_reset_hc(struct pxa27x_ohci *ohci) +static inline void pxa27x_reset_hc(struct pxa27x_ohci *pxa_ohci) { - uint32_t uhchr = __raw_readl(ohci->mmio_base + UHCHR); + uint32_t uhchr = __raw_readl(pxa_ohci->mmio_base + UHCHR); - __raw_writel(uhchr | UHCHR_FHR, ohci->mmio_base + UHCHR); + __raw_writel(uhchr | UHCHR_FHR, pxa_ohci->mmio_base + UHCHR); udelay(11); - __raw_writel(uhchr & ~UHCHR_FHR, ohci->mmio_base + UHCHR); + __raw_writel(uhchr & ~UHCHR_FHR, pxa_ohci->mmio_base + UHCHR); } #ifdef CONFIG_PXA27x @@ -213,25 +222,26 @@ extern void pxa27x_clear_otgph(void); #define pxa27x_clear_otgph() do {} while (0) #endif -static int pxa27x_start_hc(struct pxa27x_ohci *ohci, struct device *dev) +static int pxa27x_start_hc(struct pxa27x_ohci *pxa_ohci, struct device *dev) { int retval = 0; struct pxaohci_platform_data *inf; uint32_t uhchr; + struct usb_hcd *hcd = dev_get_drvdata(dev); inf = dev_get_platdata(dev); - clk_prepare_enable(ohci->clk); + clk_prepare_enable(pxa_ohci->clk); - pxa27x_reset_hc(ohci); + pxa27x_reset_hc(pxa_ohci); - uhchr = __raw_readl(ohci->mmio_base + UHCHR) | UHCHR_FSBIR; - __raw_writel(uhchr, ohci->mmio_base + UHCHR); + uhchr = __raw_readl(pxa_ohci->mmio_base + UHCHR) | UHCHR_FSBIR; + __raw_writel(uhchr, pxa_ohci->mmio_base + UHCHR); - while (__raw_readl(ohci->mmio_base + UHCHR) & UHCHR_FSBIR) + while (__raw_readl(pxa_ohci->mmio_base + UHCHR) & UHCHR_FSBIR) cpu_relax(); - pxa27x_setup_hc(ohci, inf); + pxa27x_setup_hc(pxa_ohci, inf); if (inf->init) retval = inf->init(dev); @@ -240,38 +250,39 @@ static int pxa27x_start_hc(struct pxa27x_ohci *ohci, struct device *dev) return retval; if (cpu_is_pxa3xx()) - pxa3xx_u2d_start_hc(&ohci_to_hcd(&ohci->ohci)->self); + pxa3xx_u2d_start_hc(&hcd->self); - uhchr = __raw_readl(ohci->mmio_base + UHCHR) & ~UHCHR_SSE; - __raw_writel(uhchr, ohci->mmio_base + UHCHR); - __raw_writel(UHCHIE_UPRIE | UHCHIE_RWIE, ohci->mmio_base + UHCHIE); + uhchr = __raw_readl(pxa_ohci->mmio_base + UHCHR) & ~UHCHR_SSE; + __raw_writel(uhchr, pxa_ohci->mmio_base + UHCHR); + __raw_writel(UHCHIE_UPRIE | UHCHIE_RWIE, pxa_ohci->mmio_base + UHCHIE); /* Clear any OTG Pin Hold */ pxa27x_clear_otgph(); return 0; } -static void pxa27x_stop_hc(struct pxa27x_ohci *ohci, struct device *dev) +static void pxa27x_stop_hc(struct pxa27x_ohci *pxa_ohci, struct device *dev) { struct pxaohci_platform_data *inf; + struct usb_hcd *hcd = dev_get_drvdata(dev); uint32_t uhccoms; inf = dev_get_platdata(dev); if (cpu_is_pxa3xx()) - pxa3xx_u2d_stop_hc(&ohci_to_hcd(&ohci->ohci)->self); + pxa3xx_u2d_stop_hc(&hcd->self); if (inf->exit) inf->exit(dev); - pxa27x_reset_hc(ohci); + pxa27x_reset_hc(pxa_ohci); /* Host Controller Reset */ - uhccoms = __raw_readl(ohci->mmio_base + UHCCOMS) | 0x01; - __raw_writel(uhccoms, ohci->mmio_base + UHCCOMS); + uhccoms = __raw_readl(pxa_ohci->mmio_base + UHCCOMS) | 0x01; + __raw_writel(uhccoms, pxa_ohci->mmio_base + UHCCOMS); udelay(10); - clk_disable_unprepare(ohci->clk); + clk_disable_unprepare(pxa_ohci->clk); } #ifdef CONFIG_OF @@ -356,7 +367,8 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device int retval, irq; struct usb_hcd *hcd; struct pxaohci_platform_data *inf; - struct pxa27x_ohci *ohci; + struct pxa27x_ohci *pxa_ohci; + struct ohci_hcd *ohci; struct resource *r; struct clk *usb_clk; @@ -409,29 +421,31 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device } /* initialize "struct pxa27x_ohci" */ - ohci = (struct pxa27x_ohci *)hcd_to_ohci(hcd); - ohci->dev = &pdev->dev; - ohci->clk = usb_clk; - ohci->mmio_base = (void __iomem *)hcd->regs; + pxa_ohci = to_pxa27x_ohci(hcd); + pxa_ohci->clk = usb_clk; + pxa_ohci->mmio_base = (void __iomem *)hcd->regs; - if ((retval = pxa27x_start_hc(ohci, &pdev->dev)) < 0) { + retval = pxa27x_start_hc(pxa_ohci, &pdev->dev); + if (retval < 0) { pr_debug("pxa27x_start_hc failed"); goto err3; } /* Select Power Management Mode */ - pxa27x_ohci_select_pmm(ohci, inf->port_mode); + pxa27x_ohci_select_pmm(pxa_ohci, inf->port_mode); if (inf->power_budget) hcd->power_budget = inf->power_budget; - ohci_hcd_init(hcd_to_ohci(hcd)); + /* The value of NDP in roothub_a is incorrect on this hardware */ + ohci = hcd_to_ohci(hcd); + ohci->num_ports = 3; retval = usb_add_hcd(hcd, irq, 0); if (retval == 0) return retval; - pxa27x_stop_hc(ohci, &pdev->dev); + pxa27x_stop_hc(pxa_ohci, &pdev->dev); err3: iounmap(hcd->regs); err2: @@ -459,88 +473,18 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device */ void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *pdev) { - struct pxa27x_ohci *ohci = to_pxa27x_ohci(hcd); + struct pxa27x_ohci *pxa_ohci = to_pxa27x_ohci(hcd); usb_remove_hcd(hcd); - pxa27x_stop_hc(ohci, &pdev->dev); + pxa27x_stop_hc(pxa_ohci, &pdev->dev); iounmap(hcd->regs); release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + clk_put(pxa_ohci->clk); usb_put_hcd(hcd); - clk_put(ohci->clk); -} - -/*-------------------------------------------------------------------------*/ - -static int -ohci_pxa27x_start (struct usb_hcd *hcd) -{ - struct ohci_hcd *ohci = hcd_to_ohci (hcd); - int ret; - - ohci_dbg (ohci, "ohci_pxa27x_start, ohci:%p", ohci); - - /* The value of NDP in roothub_a is incorrect on this hardware */ - ohci->num_ports = 3; - - if ((ret = ohci_init(ohci)) < 0) - return ret; - - if ((ret = ohci_run (ohci)) < 0) { - dev_err(hcd->self.controller, "can't start %s", - hcd->self.bus_name); - ohci_stop (hcd); - return ret; - } - - return 0; } /*-------------------------------------------------------------------------*/ -static const struct hc_driver ohci_pxa27x_hc_driver = { - .description = hcd_name, - .product_desc = "PXA27x OHCI", - .hcd_priv_size = sizeof(struct pxa27x_ohci), - - /* - * generic hardware linkage - */ - .irq = ohci_irq, - .flags = HCD_USB11 | HCD_MEMORY, - - /* - * basic lifecycle operations - */ - .start = ohci_pxa27x_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, -#ifdef CONFIG_PM - .bus_suspend = ohci_bus_suspend, - .bus_resume = ohci_bus_resume, -#endif - .start_port_reset = ohci_start_port_reset, -}; - -/*-------------------------------------------------------------------------*/ - static int ohci_hcd_pxa27x_drv_probe(struct platform_device *pdev) { pr_debug ("In ohci_hcd_pxa27x_drv_probe"); @@ -563,32 +507,42 @@ static int ohci_hcd_pxa27x_drv_remove(struct platform_device *pdev) static int ohci_hcd_pxa27x_drv_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - struct pxa27x_ohci *ohci = to_pxa27x_ohci(hcd); + struct pxa27x_ohci *pxa_ohci = to_pxa27x_ohci(hcd); + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + bool do_wakeup = device_may_wakeup(dev); + int ret; + - if (time_before(jiffies, ohci->ohci.next_statechange)) + if (time_before(jiffies, ohci->next_statechange)) msleep(5); - ohci->ohci.next_statechange = jiffies; + ohci->next_statechange = jiffies; - pxa27x_stop_hc(ohci, dev); - return 0; + ret = ohci_suspend(hcd, do_wakeup); + if (ret) + return ret; + + pxa27x_stop_hc(pxa_ohci, dev); + return ret; } static int ohci_hcd_pxa27x_drv_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - struct pxa27x_ohci *ohci = to_pxa27x_ohci(hcd); + struct pxa27x_ohci *pxa_ohci = to_pxa27x_ohci(hcd); struct pxaohci_platform_data *inf = dev_get_platdata(dev); + struct ohci_hcd *ohci = hcd_to_ohci(hcd); int status; - if (time_before(jiffies, ohci->ohci.next_statechange)) + if (time_before(jiffies, ohci->next_statechange)) msleep(5); - ohci->ohci.next_statechange = jiffies; + ohci->next_statechange = jiffies; - if ((status = pxa27x_start_hc(ohci, dev)) < 0) + status = pxa27x_start_hc(pxa_ohci, dev); + if (status < 0) return status; /* Select Power Management Mode */ - pxa27x_ohci_select_pmm(ohci, inf->port_mode); + pxa27x_ohci_select_pmm(pxa_ohci, inf->port_mode); ohci_resume(hcd, false); return 0; @@ -600,9 +554,6 @@ static const struct dev_pm_ops ohci_hcd_pxa27x_pm_ops = { }; #endif -/* work with hotplug and coldplug */ -MODULE_ALIAS("platform:pxa27x-ohci"); - static struct platform_driver ohci_hcd_pxa27x_driver = { .probe = ohci_hcd_pxa27x_drv_probe, .remove = ohci_hcd_pxa27x_drv_remove, @@ -617,3 +568,27 @@ static struct platform_driver ohci_hcd_pxa27x_driver = { }, }; +static const struct ohci_driver_overrides pxa27x_overrides __initconst = { + .extra_priv_size = sizeof(struct pxa27x_ohci), +}; + +static int __init ohci_pxa27x_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + ohci_init_driver(&ohci_pxa27x_hc_driver, &pxa27x_overrides); + return platform_driver_register(&ohci_hcd_pxa27x_driver); +} +module_init(ohci_pxa27x_init); + +static void __exit ohci_pxa27x_cleanup(void) +{ + platform_driver_unregister(&ohci_hcd_pxa27x_driver); +} +module_exit(ohci_pxa27x_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pxa27x-ohci"); diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c index 4919afa..be3429e 100644 --- a/drivers/usb/host/ohci-s3c2410.c +++ b/drivers/usb/host/ohci-s3c2410.c @@ -19,19 +19,36 @@ * This file is licenced under the GPL. */ -#include <linux/platform_device.h> #include <linux/clk.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> #include <linux/platform_data/usb-ohci-s3c2410.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> + +#include "ohci.h" + #define valid_port(idx) ((idx) == 1 || (idx) == 2) /* clock device associated with the hcd */ + +#define DRIVER_DESC "OHCI S3C2410 driver" + +static const char hcd_name[] = "ohci-s3c2410"; + static struct clk *clk; static struct clk *usb_clk; /* forward definitions */ +static int (*orig_ohci_hub_control)(struct usb_hcd *hcd, u16 typeReq, + u16 wValue, u16 wIndex, char *buf, u16 wLength); +static int (*orig_ohci_hub_status_data)(struct usb_hcd *hcd, char *buf); + static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc); /* conversion functions */ @@ -93,7 +110,7 @@ ohci_s3c2410_hub_status_data(struct usb_hcd *hcd, char *buf) int orig; int portno; - orig = ohci_hub_status_data(hcd, buf); + orig = orig_ohci_hub_status_data(hcd, buf); if (info == NULL) return orig; @@ -164,7 +181,7 @@ static int ohci_s3c2410_hub_control( * process the request straight away and exit */ if (info == NULL) { - ret = ohci_hub_control(hcd, typeReq, wValue, + ret = orig_ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); goto out; } @@ -214,7 +231,7 @@ static int ohci_s3c2410_hub_control( break; } - ret = ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); + ret = orig_ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); if (ret) goto out; @@ -374,8 +391,6 @@ static int usb_hcd_s3c2410_probe(const struct hc_driver *driver, s3c2410_start_hc(dev, hcd); - ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, dev->resource[1].start, 0); if (retval != 0) goto err_ioremap; @@ -392,71 +407,7 @@ static int usb_hcd_s3c2410_probe(const struct hc_driver *driver, /*-------------------------------------------------------------------------*/ -static int -ohci_s3c2410_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_s3c2410_hc_driver = { - .description = hcd_name, - .product_desc = "S3C24XX OHCI", - .hcd_priv_size = sizeof(struct ohci_hcd), - - /* - * generic hardware linkage - */ - .irq = ohci_irq, - .flags = HCD_USB11 | HCD_MEMORY, - - /* - * basic lifecycle operations - */ - .start = ohci_s3c2410_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_s3c2410_hub_status_data, - .hub_control = ohci_s3c2410_hub_control, -#ifdef CONFIG_PM - .bus_suspend = ohci_bus_suspend, - .bus_resume = ohci_bus_resume, -#endif - .start_port_reset = ohci_start_port_reset, -}; - -/* device driver */ +static struct hc_driver __read_mostly ohci_s3c2410_hc_driver; static int ohci_hcd_s3c2410_drv_probe(struct platform_device *pdev) { @@ -533,4 +484,39 @@ static struct platform_driver ohci_hcd_s3c2410_driver = { }, }; +static int __init ohci_s3c2410_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + ohci_init_driver(&ohci_s3c2410_hc_driver, NULL); + + /* + * The Samsung HW has some unusual quirks, which require + * Sumsung-specific workarounds. We override certain hc_driver + * functions here to achieve that. We explicitly do not enhance + * ohci_driver_overrides to allow this more easily, since this + * is an unusual case, and we don't want to encourage others to + * override these functions by making it too easy. + */ + + orig_ohci_hub_control = ohci_s3c2410_hc_driver.hub_control; + orig_ohci_hub_status_data = ohci_s3c2410_hc_driver.hub_status_data; + + ohci_s3c2410_hc_driver.hub_status_data = ohci_s3c2410_hub_status_data; + ohci_s3c2410_hc_driver.hub_control = ohci_s3c2410_hub_control; + + return platform_driver_register(&ohci_hcd_s3c2410_driver); +} +module_init(ohci_s3c2410_init); + +static void __exit ohci_s3c2410_cleanup(void) +{ + platform_driver_unregister(&ohci_hcd_s3c2410_driver); +} +module_exit(ohci_s3c2410_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:s3c2410-ohci"); diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c index d479d5d..2a5de5f 100644 --- a/drivers/usb/host/ohci-sm501.c +++ b/drivers/usb/host/ohci-sm501.c @@ -216,14 +216,21 @@ static int ohci_hcd_sm501_drv_remove(struct platform_device *pdev) static int ohci_sm501_suspend(struct platform_device *pdev, pm_message_t msg) { struct device *dev = &pdev->dev; - struct ohci_hcd *ohci = hcd_to_ohci(platform_get_drvdata(pdev)); + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + bool do_wakeup = device_may_wakeup(dev); + int ret; if (time_before(jiffies, ohci->next_statechange)) msleep(5); ohci->next_statechange = jiffies; + ret = ohci_suspend(hcd, do_wakeup); + if (ret) + return ret; + sm501_unit_power(dev->parent, SM501_GATE_USB_HOST, 0); - return 0; + return ret; } static int ohci_sm501_resume(struct platform_device *pdev) diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c index cc9dd9e..31ff3fc 100644 --- a/drivers/usb/host/ohci-spear.c +++ b/drivers/usb/host/ohci-spear.c @@ -11,92 +11,37 @@ * warranty of any kind, whether express or implied. */ -#include <linux/signal.h> -#include <linux/platform_device.h> #include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> #include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/signal.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> + +#include "ohci.h" +#define DRIVER_DESC "OHCI SPEAr driver" + +static const char hcd_name[] = "SPEAr-ohci"; struct spear_ohci { - struct ohci_hcd ohci; struct clk *clk; }; -#define to_spear_ohci(hcd) (struct spear_ohci *)hcd_to_ohci(hcd) - -static void spear_start_ohci(struct spear_ohci *ohci) -{ - clk_prepare_enable(ohci->clk); -} - -static void spear_stop_ohci(struct spear_ohci *ohci) -{ - clk_disable_unprepare(ohci->clk); -} - -static int ohci_spear_start(struct usb_hcd *hcd) -{ - struct ohci_hcd *ohci = hcd_to_ohci(hcd); - int ret; - - ret = ohci_init(ohci); - if (ret < 0) - return ret; - ohci->regs = hcd->regs; - - ret = ohci_run(ohci); - if (ret < 0) { - dev_err(hcd->self.controller, "can't start\n"); - ohci_stop(hcd); - return ret; - } - - create_debug_files(ohci); - -#ifdef DEBUG - ohci_dump(ohci, 1); -#endif - return 0; -} - -static const struct hc_driver ohci_spear_hc_driver = { - .description = hcd_name, - .product_desc = "SPEAr OHCI", - .hcd_priv_size = sizeof(struct spear_ohci), - - /* generic hardware linkage */ - .irq = ohci_irq, - .flags = HCD_USB11 | HCD_MEMORY, - - /* basic lifecycle operations */ - .start = ohci_spear_start, - .stop = ohci_stop, - .shutdown = ohci_shutdown, -#ifdef CONFIG_PM - .bus_suspend = ohci_bus_suspend, - .bus_resume = ohci_bus_resume, -#endif - - /* 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, +#define to_spear_ohci(hcd) (struct spear_ohci *)(hcd_to_ohci(hcd)->priv) - /* root hub support */ - .hub_status_data = ohci_hub_status_data, - .hub_control = ohci_hub_control, - - .start_port_reset = ohci_start_port_reset, -}; +static struct hc_driver __read_mostly ohci_spear_hc_driver; static int spear_ohci_hcd_drv_probe(struct platform_device *pdev) { const struct hc_driver *driver = &ohci_spear_hc_driver; + struct ohci_hcd *ohci; struct usb_hcd *hcd = NULL; struct clk *usbh_clk; - struct spear_ohci *ohci_p; + struct spear_ohci *sohci_p; struct resource *res; int retval, irq; @@ -151,16 +96,18 @@ static int spear_ohci_hcd_drv_probe(struct platform_device *pdev) goto err_put_hcd; } - ohci_p = (struct spear_ohci *)hcd_to_ohci(hcd); - ohci_p->clk = usbh_clk; - spear_start_ohci(ohci_p); - ohci_hcd_init(hcd_to_ohci(hcd)); + sohci_p = to_spear_ohci(hcd); + sohci_p->clk = usbh_clk; + + clk_prepare_enable(sohci_p->clk); + + ohci = hcd_to_ohci(hcd); retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), 0); if (retval == 0) return retval; - spear_stop_ohci(ohci_p); + clk_disable_unprepare(sohci_p->clk); err_put_hcd: usb_put_hcd(hcd); fail: @@ -172,11 +119,11 @@ fail: static int spear_ohci_hcd_drv_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); - struct spear_ohci *ohci_p = to_spear_ohci(hcd); + struct spear_ohci *sohci_p = to_spear_ohci(hcd); usb_remove_hcd(hcd); - if (ohci_p->clk) - spear_stop_ohci(ohci_p); + if (sohci_p->clk) + clk_disable_unprepare(sohci_p->clk); usb_put_hcd(hcd); return 0; @@ -188,13 +135,14 @@ static int spear_ohci_hcd_drv_suspend(struct platform_device *dev, { struct usb_hcd *hcd = platform_get_drvdata(dev); struct ohci_hcd *ohci = hcd_to_ohci(hcd); - struct spear_ohci *ohci_p = to_spear_ohci(hcd); + struct spear_ohci *sohci_p = to_spear_ohci(hcd); if (time_before(jiffies, ohci->next_statechange)) msleep(5); ohci->next_statechange = jiffies; - spear_stop_ohci(ohci_p); + clk_disable_unprepare(sohci_p->clk); + return 0; } @@ -202,13 +150,13 @@ static int spear_ohci_hcd_drv_resume(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); struct ohci_hcd *ohci = hcd_to_ohci(hcd); - struct spear_ohci *ohci_p = to_spear_ohci(hcd); + struct spear_ohci *sohci_p = to_spear_ohci(hcd); if (time_before(jiffies, ohci->next_statechange)) msleep(5); ohci->next_statechange = jiffies; - spear_start_ohci(ohci_p); + clk_prepare_enable(sohci_p->clk); ohci_resume(hcd, false); return 0; } @@ -234,4 +182,28 @@ static struct platform_driver spear_ohci_hcd_driver = { }, }; +static const struct ohci_driver_overrides spear_overrides __initconst = { + .extra_priv_size = sizeof(struct spear_ohci), +}; +static int __init ohci_spear_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + + ohci_init_driver(&ohci_spear_hc_driver, &spear_overrides); + return platform_driver_register(&spear_ohci_hcd_driver); +} +module_init(ohci_spear_init); + +static void __exit ohci_spear_cleanup(void) +{ + platform_driver_unregister(&spear_ohci_hcd_driver); +} +module_exit(ohci_spear_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Deepak Sikri"); +MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:spear-ohci"); diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index 2c76ef1..dfbdd3a 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -79,11 +79,30 @@ #define USB_INTEL_USB3_PSSEN 0xD8 #define USB_INTEL_USB3PRM 0xDC +/* + * amd_chipset_gen values represent AMD different chipset generations + */ +enum amd_chipset_gen { + NOT_AMD_CHIPSET = 0, + AMD_CHIPSET_SB600, + AMD_CHIPSET_SB700, + AMD_CHIPSET_SB800, + AMD_CHIPSET_HUDSON2, + AMD_CHIPSET_BOLTON, + AMD_CHIPSET_YANGTZE, + AMD_CHIPSET_UNKNOWN, +}; + +struct amd_chipset_type { + enum amd_chipset_gen gen; + u8 rev; +}; + static struct amd_chipset_info { struct pci_dev *nb_dev; struct pci_dev *smbus_dev; int nb_type; - int sb_type; + struct amd_chipset_type sb_type; int isoc_reqs; int probe_count; int probe_result; @@ -91,6 +110,51 @@ static struct amd_chipset_info { static DEFINE_SPINLOCK(amd_lock); +/* + * amd_chipset_sb_type_init - initialize amd chipset southbridge type + * + * AMD FCH/SB generation and revision is identified by SMBus controller + * vendor, device and revision IDs. + * + * Returns: 1 if it is an AMD chipset, 0 otherwise. + */ +static int amd_chipset_sb_type_init(struct amd_chipset_info *pinfo) +{ + u8 rev = 0; + pinfo->sb_type.gen = AMD_CHIPSET_UNKNOWN; + + pinfo->smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI, + PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL); + if (pinfo->smbus_dev) { + rev = pinfo->smbus_dev->revision; + if (rev >= 0x10 && rev <= 0x1f) + pinfo->sb_type.gen = AMD_CHIPSET_SB600; + else if (rev >= 0x30 && rev <= 0x3f) + pinfo->sb_type.gen = AMD_CHIPSET_SB700; + else if (rev >= 0x40 && rev <= 0x4f) + pinfo->sb_type.gen = AMD_CHIPSET_SB800; + } else { + pinfo->smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD, + PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, NULL); + + if (!pinfo->smbus_dev) { + pinfo->sb_type.gen = NOT_AMD_CHIPSET; + return 0; + } + + rev = pinfo->smbus_dev->revision; + if (rev >= 0x11 && rev <= 0x14) + pinfo->sb_type.gen = AMD_CHIPSET_HUDSON2; + else if (rev >= 0x15 && rev <= 0x18) + pinfo->sb_type.gen = AMD_CHIPSET_BOLTON; + else if (rev >= 0x39 && rev <= 0x3a) + pinfo->sb_type.gen = AMD_CHIPSET_YANGTZE; + } + + pinfo->sb_type.rev = rev; + return 1; +} + void sb800_prefetch(struct device *dev, int on) { u16 misc; @@ -106,7 +170,6 @@ EXPORT_SYMBOL_GPL(sb800_prefetch); int usb_amd_find_chipset_info(void) { - u8 rev = 0; unsigned long flags; struct amd_chipset_info info; int ret; @@ -122,27 +185,17 @@ int usb_amd_find_chipset_info(void) memset(&info, 0, sizeof(info)); spin_unlock_irqrestore(&amd_lock, flags); - info.smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI, 0x4385, NULL); - if (info.smbus_dev) { - rev = info.smbus_dev->revision; - if (rev >= 0x40) - info.sb_type = 1; - else if (rev >= 0x30 && rev <= 0x3b) - info.sb_type = 3; - } else { - info.smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD, - 0x780b, NULL); - if (!info.smbus_dev) { - ret = 0; - goto commit; - } - - rev = info.smbus_dev->revision; - if (rev >= 0x11 && rev <= 0x18) - info.sb_type = 2; + if (!amd_chipset_sb_type_init(&info)) { + ret = 0; + goto commit; } - if (info.sb_type == 0) { + /* Below chipset generations needn't enable AMD PLL quirk */ + if (info.sb_type.gen == AMD_CHIPSET_UNKNOWN || + info.sb_type.gen == AMD_CHIPSET_SB600 || + info.sb_type.gen == AMD_CHIPSET_YANGTZE || + (info.sb_type.gen == AMD_CHIPSET_SB700 && + info.sb_type.rev > 0x3b)) { if (info.smbus_dev) { pci_dev_put(info.smbus_dev); info.smbus_dev = NULL; @@ -197,6 +250,39 @@ commit: } EXPORT_SYMBOL_GPL(usb_amd_find_chipset_info); +int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *pdev) +{ + /* Make sure amd chipset type has already been initialized */ + usb_amd_find_chipset_info(); + if (amd_chipset.sb_type.gen != AMD_CHIPSET_YANGTZE) + return 0; + + dev_dbg(&pdev->dev, "QUIRK: Enable AMD remote wakeup fix\n"); + return 1; +} +EXPORT_SYMBOL_GPL(usb_hcd_amd_remote_wakeup_quirk); + +bool usb_amd_hang_symptom_quirk(void) +{ + u8 rev; + + usb_amd_find_chipset_info(); + rev = amd_chipset.sb_type.rev; + /* SB600 and old version of SB700 have hang symptom bug */ + return amd_chipset.sb_type.gen == AMD_CHIPSET_SB600 || + (amd_chipset.sb_type.gen == AMD_CHIPSET_SB700 && + rev >= 0x3a && rev <= 0x3b); +} +EXPORT_SYMBOL_GPL(usb_amd_hang_symptom_quirk); + +bool usb_amd_prefetch_quirk(void) +{ + usb_amd_find_chipset_info(); + /* SB800 needs pre-fetch fix */ + return amd_chipset.sb_type.gen == AMD_CHIPSET_SB800; +} +EXPORT_SYMBOL_GPL(usb_amd_prefetch_quirk); + /* * The hardware normally enables the A-link power management feature, which * lets the system lower the power consumption in idle states. @@ -229,7 +315,9 @@ static void usb_amd_quirk_pll(int disable) } } - if (amd_chipset.sb_type == 1 || amd_chipset.sb_type == 2) { + if (amd_chipset.sb_type.gen == AMD_CHIPSET_SB800 || + amd_chipset.sb_type.gen == AMD_CHIPSET_HUDSON2 || + amd_chipset.sb_type.gen == AMD_CHIPSET_BOLTON) { outb_p(AB_REG_BAR_LOW, 0xcd6); addr_low = inb_p(0xcd7); outb_p(AB_REG_BAR_HIGH, 0xcd6); @@ -240,7 +328,8 @@ static void usb_amd_quirk_pll(int disable) outl_p(0x40, AB_DATA(addr)); outl_p(0x34, AB_INDX(addr)); val = inl_p(AB_DATA(addr)); - } else if (amd_chipset.sb_type == 3) { + } else if (amd_chipset.sb_type.gen == AMD_CHIPSET_SB700 && + amd_chipset.sb_type.rev <= 0x3b) { pci_read_config_dword(amd_chipset.smbus_dev, AB_REG_BAR_SB700, &addr); outl(AX_INDXC, AB_INDX(addr)); @@ -353,7 +442,7 @@ void usb_amd_dev_put(void) amd_chipset.nb_dev = NULL; amd_chipset.smbus_dev = NULL; amd_chipset.nb_type = 0; - amd_chipset.sb_type = 0; + memset(&amd_chipset.sb_type, 0, sizeof(amd_chipset.sb_type)); amd_chipset.isoc_reqs = 0; amd_chipset.probe_result = 0; @@ -799,7 +888,7 @@ void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev) * switchable ports. */ pci_write_config_dword(xhci_pdev, USB_INTEL_USB3_PSSEN, - cpu_to_le32(ports_available)); + ports_available); pci_read_config_dword(xhci_pdev, USB_INTEL_USB3_PSSEN, &ports_available); @@ -821,7 +910,7 @@ void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev) * host. */ pci_write_config_dword(xhci_pdev, USB_INTEL_XUSB2PR, - cpu_to_le32(ports_available)); + ports_available); pci_read_config_dword(xhci_pdev, USB_INTEL_XUSB2PR, &ports_available); diff --git a/drivers/usb/host/pci-quirks.h b/drivers/usb/host/pci-quirks.h index ed6700d..638e88f 100644 --- a/drivers/usb/host/pci-quirks.h +++ b/drivers/usb/host/pci-quirks.h @@ -5,6 +5,8 @@ void uhci_reset_hc(struct pci_dev *pdev, unsigned long base); int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base); int usb_amd_find_chipset_info(void); +bool usb_amd_hang_symptom_quirk(void); +bool usb_amd_prefetch_quirk(void); void usb_amd_dev_put(void); void usb_amd_quirk_pll_disable(void); void usb_amd_quirk_pll_enable(void); diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 5477bf5..79620c3 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1413,7 +1413,7 @@ static int sl811h_show(struct seq_file *s, void *unused) case SL11H_CTL1MASK_SE0: s = " se0/reset"; break; case SL11H_CTL1MASK_K: s = " k/resume"; break; default: s = "j"; break; - }; s; }), + } s; }), (t & SL11H_CTL1MASK_LSPD) ? " lowspeed" : "", (t & SL11H_CTL1MASK_SUSPEND) ? " suspend" : ""); @@ -1446,7 +1446,7 @@ static int sl811h_show(struct seq_file *s, void *unused) case USB_PID_SETUP: s = "setup"; break; case USB_PID_ACK: s = "status"; break; default: s = "?"; break; - }; s;}), + } s;}), ep->maxpacket, ep->nak_count, ep->error_count); list_for_each_entry (urb, &ep->hep->urb_list, urb_list) { diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c index 4557375..8e239cd 100644 --- a/drivers/usb/host/uhci-debug.c +++ b/drivers/usb/host/uhci-debug.c @@ -310,14 +310,14 @@ static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len) unsigned short portsc1, portsc2; - usbcmd = uhci_readw(uhci, 0); - usbstat = uhci_readw(uhci, 2); - usbint = uhci_readw(uhci, 4); - usbfrnum = uhci_readw(uhci, 6); - flbaseadd = uhci_readl(uhci, 8); - sof = uhci_readb(uhci, 12); - portsc1 = uhci_readw(uhci, 16); - portsc2 = uhci_readw(uhci, 18); + usbcmd = uhci_readw(uhci, USBCMD); + usbstat = uhci_readw(uhci, USBSTS); + usbint = uhci_readw(uhci, USBINTR); + usbfrnum = uhci_readw(uhci, USBFRNUM); + flbaseadd = uhci_readl(uhci, USBFLBASEADD); + sof = uhci_readb(uhci, USBSOF); + portsc1 = uhci_readw(uhci, USBPORTSC1); + portsc2 = uhci_readw(uhci, USBPORTSC2); out += sprintf(out, " usbcmd = %04x %s%s%s%s%s%s%s%s\n", usbcmd, diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index 9189bc9..93e17b1 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c @@ -75,8 +75,6 @@ static inline int get_hub_status_data(struct uhci_hcd *uhci, char *buf) return !!*buf; } -#define OK(x) len = (x); break - #define CLR_RH_PORTSTAT(x) \ status = uhci_readw(uhci, port_addr); \ status &= ~(RWC_BITS|WZ_BITS); \ @@ -244,7 +242,7 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); - int status, lstatus, retval = 0, len = 0; + int status, lstatus, retval = 0; unsigned int port = wIndex - 1; unsigned long port_addr = USBPORTSC1 + 2 * port; u16 wPortChange, wPortStatus; @@ -258,7 +256,8 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case GetHubStatus: *(__le32 *)buf = cpu_to_le32(0); - OK(4); /* hub power */ + retval = 4; /* hub power */ + break; case GetPortStatus: if (port >= uhci->rh_numports) goto err; @@ -311,13 +310,14 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, *(__le16 *)buf = cpu_to_le16(wPortStatus); *(__le16 *)(buf + 2) = cpu_to_le16(wPortChange); - OK(4); + retval = 4; + break; case SetHubFeature: /* We don't implement these */ case ClearHubFeature: switch (wValue) { case C_HUB_OVER_CURRENT: case C_HUB_LOCAL_POWER: - OK(0); + break; default: goto err; } @@ -329,7 +329,7 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, switch (wValue) { case USB_PORT_FEAT_SUSPEND: SET_RH_PORTSTAT(USBPORTSC_SUSP); - OK(0); + break; case USB_PORT_FEAT_RESET: SET_RH_PORTSTAT(USBPORTSC_PR); @@ -338,10 +338,10 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, /* USB v2.0 7.1.7.5 */ uhci->ports_timeout = jiffies + msecs_to_jiffies(50); - OK(0); + break; case USB_PORT_FEAT_POWER: /* UHCI has no power switching */ - OK(0); + break; default: goto err; } @@ -356,10 +356,10 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, /* Disable terminates Resume signalling */ uhci_finish_suspend(uhci, port, port_addr); - OK(0); + break; case USB_PORT_FEAT_C_ENABLE: CLR_RH_PORTSTAT(USBPORTSC_PEC); - OK(0); + break; case USB_PORT_FEAT_SUSPEND: if (!(uhci_readw(uhci, port_addr) & USBPORTSC_SUSP)) { @@ -382,32 +382,32 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, uhci->ports_timeout = jiffies + msecs_to_jiffies(20); } - OK(0); + break; case USB_PORT_FEAT_C_SUSPEND: clear_bit(port, &uhci->port_c_suspend); - OK(0); + break; case USB_PORT_FEAT_POWER: /* UHCI has no power switching */ goto err; case USB_PORT_FEAT_C_CONNECTION: CLR_RH_PORTSTAT(USBPORTSC_CSC); - OK(0); + break; case USB_PORT_FEAT_C_OVER_CURRENT: CLR_RH_PORTSTAT(USBPORTSC_OCC); - OK(0); + break; case USB_PORT_FEAT_C_RESET: /* this driver won't report these */ - OK(0); + break; default: goto err; } break; case GetHubDescriptor: - len = min_t(unsigned int, sizeof(root_hub_hub_des), wLength); - memcpy(buf, root_hub_hub_des, len); - if (len > 2) + retval = min_t(unsigned int, sizeof(root_hub_hub_des), wLength); + memcpy(buf, root_hub_hub_des, retval); + if (retval > 2) buf[2] = uhci->rh_numports; - OK(len); + break; default: err: retval = -EPIPE; diff --git a/drivers/usb/host/uhci-pci.c b/drivers/usb/host/uhci-pci.c index 0f228c4..4cd7988 100644 --- a/drivers/usb/host/uhci-pci.c +++ b/drivers/usb/host/uhci-pci.c @@ -162,6 +162,8 @@ static void uhci_shutdown(struct pci_dev *pdev) #ifdef CONFIG_PM +static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated); + static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); @@ -174,12 +176,6 @@ static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead) goto done_okay; /* Already suspended or dead */ - if (uhci->rh_state > UHCI_RH_SUSPENDED) { - dev_warn(uhci_dev(uhci), "Root hub isn't suspended!\n"); - rc = -EBUSY; - goto done; - }; - /* All PCI host controllers are required to disable IRQ generation * at the source, so we must turn off PIRQ. */ @@ -195,8 +191,15 @@ static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) done_okay: clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); -done: spin_unlock_irq(&uhci->lock); + + synchronize_irq(hcd->irq); + + /* Check for race with a wakeup request */ + if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) { + uhci_pci_resume(hcd, false); + rc = -EBUSY; + } return rc; } @@ -299,3 +302,5 @@ static struct pci_driver uhci_pci_driver = { }, #endif }; + +MODULE_SOFTDEP("pre: ehci_pci"); diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c index d033a0e..ded842b 100644 --- a/drivers/usb/host/uhci-platform.c +++ b/drivers/usb/host/uhci-platform.c @@ -105,8 +105,7 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev) uhci->regs = hcd->regs; - ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_DISABLED | - IRQF_SHARED); + ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED); if (ret) goto err_uhci; diff --git a/drivers/usb/host/whci/hcd.c b/drivers/usb/host/whci/hcd.c index ecc88db..1b0888f 100644 --- a/drivers/usb/host/whci/hcd.c +++ b/drivers/usb/host/whci/hcd.c @@ -134,7 +134,7 @@ static int whc_urb_enqueue(struct usb_hcd *usb_hcd, struct urb *urb, default: ret = asl_urb_enqueue(whc, urb, mem_flags); break; - }; + } return ret; } @@ -160,7 +160,7 @@ static int whc_urb_dequeue(struct usb_hcd *usb_hcd, struct urb *urb, int status) default: ret = asl_urb_dequeue(whc, urb, status); break; - }; + } return ret; } diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 773a6b2..805f234 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -296,7 +296,7 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend) /* Wait for last stop endpoint command to finish */ timeleft = wait_for_completion_interruptible_timeout( cmd->completion, - USB_CTRL_SET_TIMEOUT); + XHCI_CMD_DEFAULT_TIMEOUT); if (timeleft <= 0) { xhci_warn(xhci, "%s while waiting for stop endpoint command\n", timeleft == 0 ? "Timeout" : "Signal"); @@ -524,7 +524,8 @@ static void xhci_hub_report_usb3_link_state(u32 *status, u32 status_reg) * the compliance mode timer is deleted. A port won't enter * compliance mode if it has previously entered U0. */ -void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, u16 wIndex) +static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, + u16 wIndex) { u32 all_ports_seen_u0 = ((1 << xhci->num_usb3_ports)-1); bool port_in_u0 = ((status & PORT_PLS_MASK) == XDEV_U0); @@ -1157,18 +1158,6 @@ int xhci_bus_suspend(struct usb_hcd *hcd) t1 = xhci_port_state_to_neutral(t1); if (t1 != t2) xhci_writel(xhci, t2, port_array[port_index]); - - if (hcd->speed != HCD_USB3) { - /* enable remote wake up for USB 2.0 */ - __le32 __iomem *addr; - u32 tmp; - - /* Get the port power control register address. */ - addr = port_array[port_index] + PORTPMSC; - tmp = xhci_readl(xhci, addr); - tmp |= PORT_RWE; - xhci_writel(xhci, tmp, addr); - } } hcd->state = HC_STATE_SUSPENDED; bus_state->next_statechange = jiffies + msecs_to_jiffies(10); @@ -1247,20 +1236,6 @@ int xhci_bus_resume(struct usb_hcd *hcd) xhci_ring_device(xhci, slot_id); } else xhci_writel(xhci, temp, port_array[port_index]); - - if (hcd->speed != HCD_USB3) { - /* disable remote wake up for USB 2.0 */ - __le32 __iomem *addr; - u32 tmp; - - /* Add one to the port status register address to get - * the port power control register address. - */ - addr = port_array[port_index] + PORTPMSC; - tmp = xhci_readl(xhci, addr); - tmp &= ~PORT_RWE; - xhci_writel(xhci, tmp, addr); - } } (void) xhci_readl(xhci, &xhci->op_regs->command); diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 83bcd13..49b8bd0 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1693,9 +1693,7 @@ void xhci_free_command(struct xhci_hcd *xhci, void xhci_mem_cleanup(struct xhci_hcd *xhci) { struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); - struct dev_info *dev_info, *next; struct xhci_cd *cur_cd, *next_cd; - unsigned long flags; int size; int i, j, num_ports; @@ -1756,13 +1754,6 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) scratchpad_free(xhci); - spin_lock_irqsave(&xhci->lock, flags); - list_for_each_entry_safe(dev_info, next, &xhci->lpm_failed_devs, list) { - list_del(&dev_info->list); - kfree(dev_info); - } - spin_unlock_irqrestore(&xhci->lock, flags); - if (!xhci->rh_bw) goto no_bw; @@ -2231,7 +2222,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) u32 page_size, temp; int i; - INIT_LIST_HEAD(&xhci->lpm_failed_devs); INIT_LIST_HEAD(&xhci->cancel_cmd_list); page_size = xhci_readl(xhci, &xhci->op_regs->page_size); diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 236c3aa..b8dffd5 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -35,6 +35,9 @@ #define PCI_VENDOR_ID_ETRON 0x1b6f #define PCI_DEVICE_ID_ASROCK_P67 0x7023 +#define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31 +#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 + static const char hcd_name[] = "xhci_hcd"; /* called after powerup, by probe or system-pm "wakeup" */ @@ -69,6 +72,14 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) "QUIRK: Fresco Logic xHC needs configure" " endpoint cmd after reset endpoint"); } + if (pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_PDK && + pdev->revision == 0x4) { + xhci->quirks |= XHCI_SLOW_SUSPEND; + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "QUIRK: Fresco Logic xHC revision %u" + "must be suspended extra slowly", + pdev->revision); + } /* Fresco Logic confirms: all revisions of this chip do not * support MSI, even though some of them claim to in their PCI * capabilities. @@ -110,6 +121,15 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) xhci->quirks |= XHCI_SPURIOUS_REBOOT; xhci->quirks |= XHCI_AVOID_BEI; } + if (pdev->vendor == PCI_VENDOR_ID_INTEL && + (pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI || + pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI)) { + /* Workaround for occasional spurious wakeups from S5 (or + * any other sleep) on Haswell machines with LPT and LPT-LP + * with the new Intel BIOS + */ + xhci->quirks |= XHCI_SPURIOUS_WAKEUP; + } if (pdev->vendor == PCI_VENDOR_ID_ETRON && pdev->device == PCI_DEVICE_ID_ASROCK_P67) { xhci->quirks |= XHCI_RESET_ON_RESUME; @@ -217,6 +237,11 @@ static void xhci_pci_remove(struct pci_dev *dev) usb_put_hcd(xhci->shared_hcd); } usb_hcd_pci_remove(dev); + + /* Workaround for spurious wakeups at shutdown with HSW */ + if (xhci->quirks & XHCI_SPURIOUS_WAKEUP) + pci_set_power_state(dev, PCI_D3hot); + kfree(xhci); } diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 6bfbd80..1e2f3f4 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -178,7 +178,7 @@ static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring) if (ring->type == TYPE_EVENT && last_trb_on_last_seg(xhci, ring, ring->deq_seg, ring->dequeue)) { - ring->cycle_state = (ring->cycle_state ? 0 : 1); + ring->cycle_state ^= 1; } ring->deq_seg = ring->deq_seg->next; ring->dequeue = ring->deq_seg->trbs; @@ -726,7 +726,7 @@ static void xhci_stop_watchdog_timer_in_irq(struct xhci_hcd *xhci, /* Must be called with xhci->lock held in interrupt context */ static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci, - struct xhci_td *cur_td, int status, char *adjective) + struct xhci_td *cur_td, int status) { struct usb_hcd *hcd; struct urb *urb; @@ -765,10 +765,9 @@ static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci, * 2. Otherwise, we turn all the TRBs in the TD into No-op TRBs (with the chain * bit cleared) so that the HW will skip over them. */ -static void handle_stopped_endpoint(struct xhci_hcd *xhci, +static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id, union xhci_trb *trb, struct xhci_event_cmd *event) { - unsigned int slot_id; unsigned int ep_index; struct xhci_virt_device *virt_dev; struct xhci_ring *ep_ring; @@ -779,10 +778,7 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci, struct xhci_dequeue_state deq_state; - if (unlikely(TRB_TO_SUSPEND_PORT( - le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3])))) { - slot_id = TRB_TO_SLOT_ID( - le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3])); + if (unlikely(TRB_TO_SUSPEND_PORT(le32_to_cpu(trb->generic.field[3])))) { virt_dev = xhci->devs[slot_id]; if (virt_dev) handle_cmd_in_cmd_wait_list(xhci, virt_dev, @@ -795,7 +791,6 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci, } memset(&deq_state, 0, sizeof(deq_state)); - slot_id = TRB_TO_SLOT_ID(le32_to_cpu(trb->generic.field[3])); ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3])); ep = &xhci->devs[slot_id]->eps[ep_index]; @@ -891,7 +886,7 @@ remove_finished_td: /* Doesn't matter what we pass for status, since the core will * just overwrite it (because the URB has been unlinked). */ - xhci_giveback_urb_in_irq(xhci, cur_td, 0, "cancelled"); + xhci_giveback_urb_in_irq(xhci, cur_td, 0); /* Stop processing the cancelled list if the watchdog timer is * running. @@ -1001,7 +996,7 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg) if (!list_empty(&cur_td->cancelled_td_list)) list_del_init(&cur_td->cancelled_td_list); xhci_giveback_urb_in_irq(xhci, cur_td, - -ESHUTDOWN, "killed"); + -ESHUTDOWN); } while (!list_empty(&temp_ep->cancelled_td_list)) { cur_td = list_first_entry( @@ -1010,7 +1005,7 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg) cancelled_td_list); list_del_init(&cur_td->cancelled_td_list); xhci_giveback_urb_in_irq(xhci, cur_td, - -ESHUTDOWN, "killed"); + -ESHUTDOWN); } } } @@ -1077,11 +1072,9 @@ static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci, * endpoint doorbell to restart the ring, but only if there aren't more * cancellations pending. */ -static void handle_set_deq_completion(struct xhci_hcd *xhci, - struct xhci_event_cmd *event, - union xhci_trb *trb) +static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, + union xhci_trb *trb, u32 cmd_comp_code) { - unsigned int slot_id; unsigned int ep_index; unsigned int stream_id; struct xhci_ring *ep_ring; @@ -1089,7 +1082,6 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci, struct xhci_ep_ctx *ep_ctx; struct xhci_slot_ctx *slot_ctx; - slot_id = TRB_TO_SLOT_ID(le32_to_cpu(trb->generic.field[3])); ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3])); stream_id = TRB_TO_STREAM_ID(le32_to_cpu(trb->generic.field[2])); dev = xhci->devs[slot_id]; @@ -1107,11 +1099,11 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci, ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index); slot_ctx = xhci_get_slot_ctx(xhci, dev->out_ctx); - if (GET_COMP_CODE(le32_to_cpu(event->status)) != COMP_SUCCESS) { + if (cmd_comp_code != COMP_SUCCESS) { unsigned int ep_state; unsigned int slot_state; - switch (GET_COMP_CODE(le32_to_cpu(event->status))) { + switch (cmd_comp_code) { case COMP_TRB_ERR: xhci_warn(xhci, "WARN Set TR Deq Ptr cmd invalid because " "of stream ID configuration\n"); @@ -1134,7 +1126,7 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci, default: xhci_warn(xhci, "WARN Set TR Deq Ptr cmd with unknown " "completion code of %u.\n", - GET_COMP_CODE(le32_to_cpu(event->status))); + cmd_comp_code); break; } /* OK what do we do now? The endpoint state is hosed, and we @@ -1171,21 +1163,17 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci, ring_doorbell_for_active_rings(xhci, slot_id, ep_index); } -static void handle_reset_ep_completion(struct xhci_hcd *xhci, - struct xhci_event_cmd *event, - union xhci_trb *trb) +static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id, + union xhci_trb *trb, u32 cmd_comp_code) { - int slot_id; unsigned int ep_index; - slot_id = TRB_TO_SLOT_ID(le32_to_cpu(trb->generic.field[3])); ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3])); /* This command will only fail if the endpoint wasn't halted, * but we don't care. */ xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep, - "Ignoring reset ep completion code of %u", - GET_COMP_CODE(le32_to_cpu(event->status))); + "Ignoring reset ep completion code of %u", cmd_comp_code); /* HW with the reset endpoint quirk needs to have a configure endpoint * command complete before the endpoint can be used. Queue that here @@ -1386,21 +1374,149 @@ static int handle_stopped_cmd_ring(struct xhci_hcd *xhci, return cur_trb_is_good; } +static void xhci_handle_cmd_enable_slot(struct xhci_hcd *xhci, int slot_id, + u32 cmd_comp_code) +{ + if (cmd_comp_code == COMP_SUCCESS) + xhci->slot_id = slot_id; + else + xhci->slot_id = 0; + complete(&xhci->addr_dev); +} + +static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id) +{ + struct xhci_virt_device *virt_dev; + + virt_dev = xhci->devs[slot_id]; + if (!virt_dev) + return; + if (xhci->quirks & XHCI_EP_LIMIT_QUIRK) + /* Delete default control endpoint resources */ + xhci_free_device_endpoint_resources(xhci, virt_dev, true); + xhci_free_virt_device(xhci, slot_id); +} + +static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id, + struct xhci_event_cmd *event, u32 cmd_comp_code) +{ + struct xhci_virt_device *virt_dev; + struct xhci_input_control_ctx *ctrl_ctx; + unsigned int ep_index; + unsigned int ep_state; + u32 add_flags, drop_flags; + + virt_dev = xhci->devs[slot_id]; + if (handle_cmd_in_cmd_wait_list(xhci, virt_dev, event)) + return; + /* + * Configure endpoint commands can come from the USB core + * configuration or alt setting changes, or because the HW + * needed an extra configure endpoint command after a reset + * endpoint command or streams were being configured. + * If the command was for a halted endpoint, the xHCI driver + * is not waiting on the configure endpoint command. + */ + ctrl_ctx = xhci_get_input_control_ctx(xhci, virt_dev->in_ctx); + if (!ctrl_ctx) { + xhci_warn(xhci, "Could not get input context, bad type.\n"); + return; + } + + add_flags = le32_to_cpu(ctrl_ctx->add_flags); + drop_flags = le32_to_cpu(ctrl_ctx->drop_flags); + /* Input ctx add_flags are the endpoint index plus one */ + ep_index = xhci_last_valid_endpoint(add_flags) - 1; + + /* A usb_set_interface() call directly after clearing a halted + * condition may race on this quirky hardware. Not worth + * worrying about, since this is prototype hardware. Not sure + * if this will work for streams, but streams support was + * untested on this prototype. + */ + if (xhci->quirks & XHCI_RESET_EP_QUIRK && + ep_index != (unsigned int) -1 && + add_flags - SLOT_FLAG == drop_flags) { + ep_state = virt_dev->eps[ep_index].ep_state; + if (!(ep_state & EP_HALTED)) + goto bandwidth_change; + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "Completed config ep cmd - " + "last ep index = %d, state = %d", + ep_index, ep_state); + /* Clear internal halted state and restart ring(s) */ + virt_dev->eps[ep_index].ep_state &= ~EP_HALTED; + ring_doorbell_for_active_rings(xhci, slot_id, ep_index); + return; + } +bandwidth_change: + xhci_dbg_trace(xhci, trace_xhci_dbg_context_change, + "Completed config ep cmd"); + virt_dev->cmd_status = cmd_comp_code; + complete(&virt_dev->cmd_completion); + return; +} + +static void xhci_handle_cmd_eval_ctx(struct xhci_hcd *xhci, int slot_id, + struct xhci_event_cmd *event, u32 cmd_comp_code) +{ + struct xhci_virt_device *virt_dev; + + virt_dev = xhci->devs[slot_id]; + if (handle_cmd_in_cmd_wait_list(xhci, virt_dev, event)) + return; + virt_dev->cmd_status = cmd_comp_code; + complete(&virt_dev->cmd_completion); +} + +static void xhci_handle_cmd_addr_dev(struct xhci_hcd *xhci, int slot_id, + u32 cmd_comp_code) +{ + xhci->devs[slot_id]->cmd_status = cmd_comp_code; + complete(&xhci->addr_dev); +} + +static void xhci_handle_cmd_reset_dev(struct xhci_hcd *xhci, int slot_id, + struct xhci_event_cmd *event) +{ + struct xhci_virt_device *virt_dev; + + xhci_dbg(xhci, "Completed reset device command.\n"); + virt_dev = xhci->devs[slot_id]; + if (virt_dev) + handle_cmd_in_cmd_wait_list(xhci, virt_dev, event); + else + xhci_warn(xhci, "Reset device command completion " + "for disabled slot %u\n", slot_id); +} + +static void xhci_handle_cmd_nec_get_fw(struct xhci_hcd *xhci, + struct xhci_event_cmd *event) +{ + if (!(xhci->quirks & XHCI_NEC_HOST)) { + xhci->error_bitmask |= 1 << 6; + return; + } + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "NEC firmware version %2x.%02x", + NEC_FW_MAJOR(le32_to_cpu(event->status)), + NEC_FW_MINOR(le32_to_cpu(event->status))); +} + static void handle_cmd_completion(struct xhci_hcd *xhci, struct xhci_event_cmd *event) { int slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags)); u64 cmd_dma; dma_addr_t cmd_dequeue_dma; - struct xhci_input_control_ctx *ctrl_ctx; - struct xhci_virt_device *virt_dev; - unsigned int ep_index; - struct xhci_ring *ep_ring; - unsigned int ep_state; + u32 cmd_comp_code; + union xhci_trb *cmd_trb; + u32 cmd_type; cmd_dma = le64_to_cpu(event->cmd_trb); + cmd_trb = xhci->cmd_ring->dequeue; cmd_dequeue_dma = xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg, - xhci->cmd_ring->dequeue); + cmd_trb); /* Is the command ring deq ptr out of sync with the deq seg ptr? */ if (cmd_dequeue_dma == 0) { xhci->error_bitmask |= 1 << 4; @@ -1412,19 +1528,17 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, return; } - trace_xhci_cmd_completion(&xhci->cmd_ring->dequeue->generic, - (struct xhci_generic_trb *) event); + trace_xhci_cmd_completion(cmd_trb, (struct xhci_generic_trb *) event); - if ((GET_COMP_CODE(le32_to_cpu(event->status)) == COMP_CMD_ABORT) || - (GET_COMP_CODE(le32_to_cpu(event->status)) == COMP_CMD_STOP)) { + cmd_comp_code = GET_COMP_CODE(le32_to_cpu(event->status)); + if (cmd_comp_code == COMP_CMD_ABORT || cmd_comp_code == COMP_CMD_STOP) { /* If the return value is 0, we think the trb pointed by * command ring dequeue pointer is a good trb. The good * trb means we don't want to cancel the trb, but it have * been stopped by host. So we should handle it normally. * Otherwise, driver should invoke inc_deq() and return. */ - if (handle_stopped_cmd_ring(xhci, - GET_COMP_CODE(le32_to_cpu(event->status)))) { + if (handle_stopped_cmd_ring(xhci, cmd_comp_code)) { inc_deq(xhci, xhci->cmd_ring); return; } @@ -1436,117 +1550,47 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, return; } - switch (le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3]) - & TRB_TYPE_BITMASK) { - case TRB_TYPE(TRB_ENABLE_SLOT): - if (GET_COMP_CODE(le32_to_cpu(event->status)) == COMP_SUCCESS) - xhci->slot_id = slot_id; - else - xhci->slot_id = 0; - complete(&xhci->addr_dev); + cmd_type = TRB_FIELD_TO_TYPE(le32_to_cpu(cmd_trb->generic.field[3])); + switch (cmd_type) { + case TRB_ENABLE_SLOT: + xhci_handle_cmd_enable_slot(xhci, slot_id, cmd_comp_code); break; - case TRB_TYPE(TRB_DISABLE_SLOT): - if (xhci->devs[slot_id]) { - if (xhci->quirks & XHCI_EP_LIMIT_QUIRK) - /* Delete default control endpoint resources */ - xhci_free_device_endpoint_resources(xhci, - xhci->devs[slot_id], true); - xhci_free_virt_device(xhci, slot_id); - } + case TRB_DISABLE_SLOT: + xhci_handle_cmd_disable_slot(xhci, slot_id); break; - case TRB_TYPE(TRB_CONFIG_EP): - virt_dev = xhci->devs[slot_id]; - if (handle_cmd_in_cmd_wait_list(xhci, virt_dev, event)) - break; - /* - * Configure endpoint commands can come from the USB core - * configuration or alt setting changes, or because the HW - * needed an extra configure endpoint command after a reset - * endpoint command or streams were being configured. - * If the command was for a halted endpoint, the xHCI driver - * is not waiting on the configure endpoint command. - */ - ctrl_ctx = xhci_get_input_control_ctx(xhci, - virt_dev->in_ctx); - if (!ctrl_ctx) { - xhci_warn(xhci, "Could not get input context, bad type.\n"); - break; - } - /* Input ctx add_flags are the endpoint index plus one */ - ep_index = xhci_last_valid_endpoint(le32_to_cpu(ctrl_ctx->add_flags)) - 1; - /* A usb_set_interface() call directly after clearing a halted - * condition may race on this quirky hardware. Not worth - * worrying about, since this is prototype hardware. Not sure - * if this will work for streams, but streams support was - * untested on this prototype. - */ - if (xhci->quirks & XHCI_RESET_EP_QUIRK && - ep_index != (unsigned int) -1 && - le32_to_cpu(ctrl_ctx->add_flags) - SLOT_FLAG == - le32_to_cpu(ctrl_ctx->drop_flags)) { - ep_ring = xhci->devs[slot_id]->eps[ep_index].ring; - ep_state = xhci->devs[slot_id]->eps[ep_index].ep_state; - if (!(ep_state & EP_HALTED)) - goto bandwidth_change; - xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, - "Completed config ep cmd - " - "last ep index = %d, state = %d", - ep_index, ep_state); - /* Clear internal halted state and restart ring(s) */ - xhci->devs[slot_id]->eps[ep_index].ep_state &= - ~EP_HALTED; - ring_doorbell_for_active_rings(xhci, slot_id, ep_index); - break; - } -bandwidth_change: - xhci_dbg_trace(xhci, trace_xhci_dbg_context_change, - "Completed config ep cmd"); - xhci->devs[slot_id]->cmd_status = - GET_COMP_CODE(le32_to_cpu(event->status)); - complete(&xhci->devs[slot_id]->cmd_completion); + case TRB_CONFIG_EP: + xhci_handle_cmd_config_ep(xhci, slot_id, event, cmd_comp_code); break; - case TRB_TYPE(TRB_EVAL_CONTEXT): - virt_dev = xhci->devs[slot_id]; - if (handle_cmd_in_cmd_wait_list(xhci, virt_dev, event)) - break; - xhci->devs[slot_id]->cmd_status = GET_COMP_CODE(le32_to_cpu(event->status)); - complete(&xhci->devs[slot_id]->cmd_completion); + case TRB_EVAL_CONTEXT: + xhci_handle_cmd_eval_ctx(xhci, slot_id, event, cmd_comp_code); break; - case TRB_TYPE(TRB_ADDR_DEV): - xhci->devs[slot_id]->cmd_status = GET_COMP_CODE(le32_to_cpu(event->status)); - complete(&xhci->addr_dev); + case TRB_ADDR_DEV: + xhci_handle_cmd_addr_dev(xhci, slot_id, cmd_comp_code); break; - case TRB_TYPE(TRB_STOP_RING): - handle_stopped_endpoint(xhci, xhci->cmd_ring->dequeue, event); + case TRB_STOP_RING: + WARN_ON(slot_id != TRB_TO_SLOT_ID( + le32_to_cpu(cmd_trb->generic.field[3]))); + xhci_handle_cmd_stop_ep(xhci, slot_id, cmd_trb, event); break; - case TRB_TYPE(TRB_SET_DEQ): - handle_set_deq_completion(xhci, event, xhci->cmd_ring->dequeue); + case TRB_SET_DEQ: + WARN_ON(slot_id != TRB_TO_SLOT_ID( + le32_to_cpu(cmd_trb->generic.field[3]))); + xhci_handle_cmd_set_deq(xhci, slot_id, cmd_trb, cmd_comp_code); break; - case TRB_TYPE(TRB_CMD_NOOP): + case TRB_CMD_NOOP: break; - case TRB_TYPE(TRB_RESET_EP): - handle_reset_ep_completion(xhci, event, xhci->cmd_ring->dequeue); + case TRB_RESET_EP: + WARN_ON(slot_id != TRB_TO_SLOT_ID( + le32_to_cpu(cmd_trb->generic.field[3]))); + xhci_handle_cmd_reset_ep(xhci, slot_id, cmd_trb, cmd_comp_code); break; - case TRB_TYPE(TRB_RESET_DEV): - xhci_dbg(xhci, "Completed reset device command.\n"); - slot_id = TRB_TO_SLOT_ID( - le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3])); - virt_dev = xhci->devs[slot_id]; - if (virt_dev) - handle_cmd_in_cmd_wait_list(xhci, virt_dev, event); - else - xhci_warn(xhci, "Reset device command completion " - "for disabled slot %u\n", slot_id); + case TRB_RESET_DEV: + WARN_ON(slot_id != TRB_TO_SLOT_ID( + le32_to_cpu(cmd_trb->generic.field[3]))); + xhci_handle_cmd_reset_dev(xhci, slot_id, event); break; - case TRB_TYPE(TRB_NEC_GET_FW): - if (!(xhci->quirks & XHCI_NEC_HOST)) { - xhci->error_bitmask |= 1 << 6; - break; - } - xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, - "NEC firmware version %2x.%02x", - NEC_FW_MAJOR(le32_to_cpu(event->status)), - NEC_FW_MINOR(le32_to_cpu(event->status))); + case TRB_NEC_GET_FW: + xhci_handle_cmd_nec_get_fw(xhci, event); break; default: /* Skip over unknown commands on the event ring */ diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 1e36dbb..4265b48 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -730,6 +730,9 @@ void xhci_shutdown(struct usb_hcd *hcd) spin_lock_irq(&xhci->lock); xhci_halt(xhci); + /* Workaround for spurious wakeups at shutdown with HSW */ + if (xhci->quirks & XHCI_SPURIOUS_WAKEUP) + xhci_reset(xhci); spin_unlock_irq(&xhci->lock); xhci_cleanup_msix(xhci); @@ -737,6 +740,10 @@ void xhci_shutdown(struct usb_hcd *hcd) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "xhci_shutdown completed - status = %x", xhci_readl(xhci, &xhci->op_regs->status)); + + /* Yet another workaround for spurious wakeups at shutdown with HSW */ + if (xhci->quirks & XHCI_SPURIOUS_WAKEUP) + pci_set_power_state(to_pci_dev(hcd->self.controller), PCI_D3hot); } #ifdef CONFIG_PM @@ -839,6 +846,7 @@ static void xhci_clear_command_ring(struct xhci_hcd *xhci) int xhci_suspend(struct xhci_hcd *xhci) { int rc = 0; + unsigned int delay = XHCI_MAX_HALT_USEC; struct usb_hcd *hcd = xhci_to_hcd(xhci); u32 command; @@ -861,8 +869,12 @@ int xhci_suspend(struct xhci_hcd *xhci) command = xhci_readl(xhci, &xhci->op_regs->command); command &= ~CMD_RUN; xhci_writel(xhci, command, &xhci->op_regs->command); + + /* Some chips from Fresco Logic need an extraordinary delay */ + delay *= (xhci->quirks & XHCI_SLOW_SUSPEND) ? 10 : 1; + if (xhci_handshake(xhci, &xhci->op_regs->status, - STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC)) { + STS_HALT, STS_HALT, delay)) { xhci_warn(xhci, "WARN: xHC CMD_RUN timeout\n"); spin_unlock_irq(&xhci->lock); return -ETIMEDOUT; @@ -3447,7 +3459,7 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev) /* Wait for the Reset Device command to finish */ timeleft = wait_for_completion_interruptible_timeout( reset_device_cmd->completion, - USB_CTRL_SET_TIMEOUT); + XHCI_CMD_DEFAULT_TIMEOUT); if (timeleft <= 0) { xhci_warn(xhci, "%s while waiting for reset device command\n", timeleft == 0 ? "Timeout" : "Signal"); @@ -3571,11 +3583,6 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) del_timer_sync(&virt_dev->eps[i].stop_cmd_timer); } - if (udev->usb2_hw_lpm_enabled) { - xhci_set_usb2_hardware_lpm(hcd, udev, 0); - udev->usb2_hw_lpm_enabled = 0; - } - spin_lock_irqsave(&xhci->lock, flags); /* Don't disable the slot if the host controller is dead. */ state = xhci_readl(xhci, &xhci->op_regs->status); @@ -3709,9 +3716,6 @@ disable_slot: * the device). * We should be protected by the usb_address0_mutex in khubd's hub_port_init, so * we should only issue and wait on one address command at the same time. - * - * We add one to the device address issued by the hardware because the USB core - * uses address 1 for the root hubs (even though they're not really devices). */ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) { @@ -3856,16 +3860,13 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->out_ctx); trace_xhci_address_ctx(xhci, virt_dev->out_ctx, slot_ctx->dev_info >> 27); - /* Use kernel assigned address for devices; store xHC assigned - * address locally. */ - virt_dev->address = (le32_to_cpu(slot_ctx->dev_state) & DEV_ADDR_MASK) - + 1; /* Zero the input context control for later use */ ctrl_ctx->add_flags = 0; ctrl_ctx->drop_flags = 0; xhci_dbg_trace(xhci, trace_xhci_dbg_address, - "Internal device address = %d", virt_dev->address); + "Internal device address = %d", + le32_to_cpu(slot_ctx->dev_state) & DEV_ADDR_MASK); return 0; } @@ -4013,133 +4014,6 @@ static int xhci_calculate_usb2_hw_lpm_params(struct usb_device *udev) return PORT_BESLD(besld) | PORT_L1_TIMEOUT(l1) | PORT_HIRDM(hirdm); } -static int xhci_usb2_software_lpm_test(struct usb_hcd *hcd, - struct usb_device *udev) -{ - struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct dev_info *dev_info; - __le32 __iomem **port_array; - __le32 __iomem *addr, *pm_addr; - u32 temp, dev_id; - unsigned int port_num; - unsigned long flags; - int hird; - int ret; - - if (hcd->speed == HCD_USB3 || !xhci->sw_lpm_support || - !udev->lpm_capable) - return -EINVAL; - - /* we only support lpm for non-hub device connected to root hub yet */ - if (!udev->parent || udev->parent->parent || - udev->descriptor.bDeviceClass == USB_CLASS_HUB) - return -EINVAL; - - spin_lock_irqsave(&xhci->lock, flags); - - /* Look for devices in lpm_failed_devs list */ - dev_id = le16_to_cpu(udev->descriptor.idVendor) << 16 | - le16_to_cpu(udev->descriptor.idProduct); - list_for_each_entry(dev_info, &xhci->lpm_failed_devs, list) { - if (dev_info->dev_id == dev_id) { - ret = -EINVAL; - goto finish; - } - } - - port_array = xhci->usb2_ports; - port_num = udev->portnum - 1; - - if (port_num > HCS_MAX_PORTS(xhci->hcs_params1)) { - xhci_dbg(xhci, "invalid port number %d\n", udev->portnum); - ret = -EINVAL; - goto finish; - } - - /* - * Test USB 2.0 software LPM. - * FIXME: some xHCI 1.0 hosts may implement a new register to set up - * hardware-controlled USB 2.0 LPM. See section 5.4.11 and 4.23.5.1.1.1 - * in the June 2011 errata release. - */ - xhci_dbg(xhci, "test port %d software LPM\n", port_num); - /* - * Set L1 Device Slot and HIRD/BESL. - * Check device's USB 2.0 extension descriptor to determine whether - * HIRD or BESL shoule be used. See USB2.0 LPM errata. - */ - pm_addr = port_array[port_num] + PORTPMSC; - hird = xhci_calculate_hird_besl(xhci, udev); - temp = PORT_L1DS(udev->slot_id) | PORT_HIRD(hird); - xhci_writel(xhci, temp, pm_addr); - - /* Set port link state to U2(L1) */ - addr = port_array[port_num]; - xhci_set_link_state(xhci, port_array, port_num, XDEV_U2); - - /* wait for ACK */ - spin_unlock_irqrestore(&xhci->lock, flags); - msleep(10); - spin_lock_irqsave(&xhci->lock, flags); - - /* Check L1 Status */ - ret = xhci_handshake(xhci, pm_addr, - PORT_L1S_MASK, PORT_L1S_SUCCESS, 125); - if (ret != -ETIMEDOUT) { - /* enter L1 successfully */ - temp = xhci_readl(xhci, addr); - xhci_dbg(xhci, "port %d entered L1 state, port status 0x%x\n", - port_num, temp); - ret = 0; - } else { - temp = xhci_readl(xhci, pm_addr); - xhci_dbg(xhci, "port %d software lpm failed, L1 status %d\n", - port_num, temp & PORT_L1S_MASK); - ret = -EINVAL; - } - - /* Resume the port */ - xhci_set_link_state(xhci, port_array, port_num, XDEV_U0); - - spin_unlock_irqrestore(&xhci->lock, flags); - msleep(10); - spin_lock_irqsave(&xhci->lock, flags); - - /* Clear PLC */ - xhci_test_and_clear_bit(xhci, port_array, port_num, PORT_PLC); - - /* Check PORTSC to make sure the device is in the right state */ - if (!ret) { - temp = xhci_readl(xhci, addr); - xhci_dbg(xhci, "resumed port %d status 0x%x\n", port_num, temp); - if (!(temp & PORT_CONNECT) || !(temp & PORT_PE) || - (temp & PORT_PLS_MASK) != XDEV_U0) { - xhci_dbg(xhci, "port L1 resume fail\n"); - ret = -EINVAL; - } - } - - if (ret) { - /* Insert dev to lpm_failed_devs list */ - xhci_warn(xhci, "device LPM test failed, may disconnect and " - "re-enumerate\n"); - dev_info = kzalloc(sizeof(struct dev_info), GFP_ATOMIC); - if (!dev_info) { - ret = -ENOMEM; - goto finish; - } - dev_info->dev_id = dev_id; - INIT_LIST_HEAD(&dev_info->list); - list_add(&dev_info->list, &xhci->lpm_failed_devs); - } else { - xhci_ring_device(xhci, udev->slot_id); - } - -finish: - spin_unlock_irqrestore(&xhci->lock, flags); - return ret; -} - int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, struct usb_device *udev, int enable) { @@ -4216,7 +4090,7 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, } pm_val &= ~PORT_HIRD_MASK; - pm_val |= PORT_HIRD(hird) | PORT_RWE; + pm_val |= PORT_HIRD(hird) | PORT_RWE | PORT_L1DS(udev->slot_id); xhci_writel(xhci, pm_val, pm_addr); pm_val = xhci_readl(xhci, pm_addr); pm_val |= PORT_HLE; @@ -4224,7 +4098,7 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, /* flush write */ xhci_readl(xhci, pm_addr); } else { - pm_val &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK); + pm_val &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK | PORT_L1DS_MASK); xhci_writel(xhci, pm_val, pm_addr); /* flush write */ xhci_readl(xhci, pm_addr); @@ -4267,24 +4141,26 @@ static int xhci_check_usb2_port_capability(struct xhci_hcd *xhci, int port, int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); - int ret; int portnum = udev->portnum - 1; - ret = xhci_usb2_software_lpm_test(hcd, udev); - if (!ret) { - xhci_dbg(xhci, "software LPM test succeed\n"); - if (xhci->hw_lpm_support == 1 && - xhci_check_usb2_port_capability(xhci, portnum, XHCI_HLC)) { - udev->usb2_hw_lpm_capable = 1; - udev->l1_params.timeout = XHCI_L1_TIMEOUT; - udev->l1_params.besl = XHCI_DEFAULT_BESL; - if (xhci_check_usb2_port_capability(xhci, portnum, - XHCI_BLC)) - udev->usb2_hw_lpm_besl_capable = 1; - ret = xhci_set_usb2_hardware_lpm(hcd, udev, 1); - if (!ret) - udev->usb2_hw_lpm_enabled = 1; - } + if (hcd->speed == HCD_USB3 || !xhci->sw_lpm_support || + !udev->lpm_capable) + return 0; + + /* we only support lpm for non-hub device connected to root hub yet */ + if (!udev->parent || udev->parent->parent || + udev->descriptor.bDeviceClass == USB_CLASS_HUB) + return 0; + + if (xhci->hw_lpm_support == 1 && + xhci_check_usb2_port_capability( + xhci, portnum, XHCI_HLC)) { + udev->usb2_hw_lpm_capable = 1; + udev->l1_params.timeout = XHCI_L1_TIMEOUT; + udev->l1_params.besl = XHCI_DEFAULT_BESL; + if (xhci_check_usb2_port_capability(xhci, portnum, + XHCI_BLC)) + udev->usb2_hw_lpm_besl_capable = 1; } return 0; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 289fbfb..03c74b7 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -383,6 +383,7 @@ struct xhci_op_regs { #define PORT_RWE (1 << 3) #define PORT_HIRD(p) (((p) & 0xf) << 4) #define PORT_HIRD_MASK (0xf << 4) +#define PORT_L1DS_MASK (0xff << 8) #define PORT_L1DS(p) (((p) & 0xff) << 8) #define PORT_HLE (1 << 16) @@ -934,8 +935,6 @@ struct xhci_virt_device { /* Rings saved to ensure old alt settings can be re-instated */ struct xhci_ring **ring_cache; int num_rings_cached; - /* Store xHC assigned device address */ - int address; #define XHCI_MAX_RINGS_CACHED 31 struct xhci_virt_ep eps[31]; struct completion cmd_completion; @@ -1548,6 +1547,8 @@ struct xhci_hcd { #define XHCI_COMP_MODE_QUIRK (1 << 14) #define XHCI_AVOID_BEI (1 << 15) #define XHCI_PLAT (1 << 16) +#define XHCI_SLOW_SUSPEND (1 << 17) +#define XHCI_SPURIOUS_WAKEUP (1 << 18) unsigned int num_active_eps; unsigned int limit_active_eps; /* There are two roothubs to keep track of bus suspend info for */ |