summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile2
-rw-r--r--drivers/media/platform/exynos4-is/Kconfig2
-rw-r--r--drivers/media/platform/exynos4-is/mipi-csis.c13
-rw-r--r--drivers/phy/Kconfig54
-rw-r--r--drivers/phy/Makefile9
-rw-r--r--drivers/phy/phy-core.c698
-rw-r--r--drivers/phy/phy-exynos-dp-video.c111
-rw-r--r--drivers/phy/phy-exynos-mipi-video.c176
-rw-r--r--drivers/phy/phy-omap-usb2.c (renamed from drivers/usb/phy/phy-omap-usb2.c)72
-rw-r--r--drivers/phy/phy-twl4030-usb.c (renamed from drivers/usb/phy/phy-twl4030-usb.c)69
-rw-r--r--drivers/usb/chipidea/bits.h1
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c21
-rw-r--r--drivers/usb/chipidea/core.c100
-rw-r--r--drivers/usb/chipidea/udc.c56
-rw-r--r--drivers/usb/core/devio.c38
-rw-r--r--drivers/usb/core/file.c8
-rw-r--r--drivers/usb/core/hcd-pci.c3
-rw-r--r--drivers/usb/core/hcd.c122
-rw-r--r--drivers/usb/core/hub.c137
-rw-r--r--drivers/usb/core/quirks.c37
-rw-r--r--drivers/usb/core/sysfs.c58
-rw-r--r--drivers/usb/core/urb.c44
-rw-r--r--drivers/usb/core/usb.c2
-rw-r--r--drivers/usb/core/usb.h1
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c2
-rw-r--r--drivers/usb/dwc3/ep0.c6
-rw-r--r--drivers/usb/early/ehci-dbgp.c4
-rw-r--r--drivers/usb/gadget/amd5536udc.c2
-rw-r--r--drivers/usb/gadget/composite.c2
-rw-r--r--drivers/usb/gadget/dummy_hcd.c6
-rw-r--r--drivers/usb/gadget/g_ffs.c2
-rw-r--r--drivers/usb/gadget/goku_udc.c3
-rw-r--r--drivers/usb/gadget/mv_u3d_core.c2
-rw-r--r--drivers/usb/gadget/net2280.c5
-rw-r--r--drivers/usb/gadget/pch_udc.c1
-rw-r--r--drivers/usb/gadget/rndis.c2
-rw-r--r--drivers/usb/gadget/tcm_usb_gadget.c4
-rw-r--r--drivers/usb/host/Kconfig67
-rw-r--r--drivers/usb/host/Makefile12
-rw-r--r--drivers/usb/host/ehci-dbg.c113
-rw-r--r--drivers/usb/host/ehci-exynos.c (renamed from drivers/usb/host/ehci-s5p.c)164
-rw-r--r--drivers/usb/host/ehci-fsl.c4
-rw-r--r--drivers/usb/host/ehci-grlib.c2
-rw-r--r--drivers/usb/host/ehci-hcd.c34
-rw-r--r--drivers/usb/host/ehci-msm.c20
-rw-r--r--drivers/usb/host/ehci-mv.c2
-rw-r--r--drivers/usb/host/ehci-octeon.c2
-rw-r--r--drivers/usb/host/ehci-pci.c24
-rw-r--r--drivers/usb/host/ehci-pmcmsp.c2
-rw-r--r--drivers/usb/host/ehci-ppc-of.c2
-rw-r--r--drivers/usb/host/ehci-ps3.c2
-rw-r--r--drivers/usb/host/ehci-q.c64
-rw-r--r--drivers/usb/host/ehci-sched.c1025
-rw-r--r--drivers/usb/host/ehci-sead3.c2
-rw-r--r--drivers/usb/host/ehci-sh.c2
-rw-r--r--drivers/usb/host/ehci-sysfs.c15
-rw-r--r--drivers/usb/host/ehci-tegra.c4
-rw-r--r--drivers/usb/host/ehci-tilegx.c2
-rw-r--r--drivers/usb/host/ehci-w90x900.c89
-rw-r--r--drivers/usb/host/ehci-xilinx-of.c2
-rw-r--r--drivers/usb/host/ehci.h81
-rw-r--r--drivers/usb/host/fotg210-hcd.c2
-rw-r--r--drivers/usb/host/fusbh200-hcd.c2
-rw-r--r--drivers/usb/host/hwa-hc.c20
-rw-r--r--drivers/usb/host/isp1362-hcd.c2
-rw-r--r--drivers/usb/host/ohci-at91.c156
-rw-r--r--drivers/usb/host/ohci-dbg.c2
-rw-r--r--drivers/usb/host/ohci-ep93xx.c72
-rw-r--r--drivers/usb/host/ohci-exynos.c184
-rw-r--r--drivers/usb/host/ohci-hcd.c159
-rw-r--r--drivers/usb/host/ohci-nxp.c123
-rw-r--r--drivers/usb/host/ohci-omap.c169
-rw-r--r--drivers/usb/host/ohci-omap3.c118
-rw-r--r--drivers/usb/host/ohci-pci.c15
-rw-r--r--drivers/usb/host/ohci-platform.c11
-rw-r--r--drivers/usb/host/ohci-pxa27x.c249
-rw-r--r--drivers/usb/host/ohci-s3c2410.c128
-rw-r--r--drivers/usb/host/ohci-sm501.c11
-rw-r--r--drivers/usb/host/ohci-spear.c140
-rw-r--r--drivers/usb/host/pci-quirks.c137
-rw-r--r--drivers/usb/host/pci-quirks.h2
-rw-r--r--drivers/usb/host/sl811-hcd.c4
-rw-r--r--drivers/usb/host/uhci-debug.c16
-rw-r--r--drivers/usb/host/uhci-hub.c40
-rw-r--r--drivers/usb/host/uhci-pci.c4
-rw-r--r--drivers/usb/host/uhci-platform.c3
-rw-r--r--drivers/usb/host/whci/hcd.c4
-rw-r--r--drivers/usb/misc/usbtest.c2
-rw-r--r--drivers/usb/musb/Kconfig1
-rw-r--r--drivers/usb/musb/musb_core.c2
-rw-r--r--drivers/usb/musb/musb_core.h2
-rw-r--r--drivers/usb/musb/musb_dsps.c2
-rw-r--r--drivers/usb/musb/musb_gadget.c2
-rw-r--r--drivers/usb/musb/musb_host.c2
-rw-r--r--drivers/usb/musb/omap2430.c51
-rw-r--r--drivers/usb/phy/Kconfig22
-rw-r--r--drivers/usb/phy/Makefile2
-rw-r--r--drivers/usb/phy/phy-omap-control.c208
-rw-r--r--drivers/usb/phy/phy-omap-usb3.c32
-rw-r--r--drivers/usb/phy/phy-ulpi-viewport.c2
-rw-r--r--drivers/usb/serial/cyberjack.c2
-rw-r--r--drivers/usb/serial/ftdi_sio.c10
-rw-r--r--drivers/usb/serial/generic.c82
-rw-r--r--drivers/usb/serial/mos7840.c4
-rw-r--r--drivers/usb/wusbcore/cbaf.c4
-rw-r--r--drivers/usb/wusbcore/devconnect.c2
-rw-r--r--drivers/usb/wusbcore/wa-hc.c14
-rw-r--r--drivers/usb/wusbcore/wa-hc.h29
-rw-r--r--drivers/usb/wusbcore/wa-rpipe.c17
-rw-r--r--drivers/usb/wusbcore/wa-xfer.c1075
-rw-r--r--drivers/video/exynos/Kconfig3
-rw-r--r--drivers/video/exynos/exynos_dp_core.c132
-rw-r--r--drivers/video/exynos/exynos_dp_core.h110
-rw-r--r--drivers/video/exynos/exynos_dp_reg.c2
-rw-r--r--drivers/video/exynos/exynos_mipi_dsi.c19
116 files changed, 4585 insertions, 2636 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index aa43b911ccef..8f451449abd3 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -166,4 +166,6 @@ source "drivers/reset/Kconfig"
source "drivers/fmc/Kconfig"
+source "drivers/phy/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index ab93de8297f1..687da899cadb 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -8,6 +8,8 @@
obj-y += irqchip/
obj-y += bus/
+obj-$(CONFIG_GENERIC_PHY) += phy/
+
# GPIO must come after pinctrl as gpios may need to mux pins etc
obj-y += pinctrl/
obj-y += gpio/
diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig
index 53ad0f080179..d2d3b4b61435 100644
--- a/drivers/media/platform/exynos4-is/Kconfig
+++ b/drivers/media/platform/exynos4-is/Kconfig
@@ -29,7 +29,7 @@ config VIDEO_S5P_FIMC
config VIDEO_S5P_MIPI_CSIS
tristate "S5P/EXYNOS MIPI-CSI2 receiver (MIPI-CSIS) driver"
depends on REGULATOR
- select S5P_SETUP_MIPIPHY
+ select GENERIC_PHY
help
This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC MIPI-CSI2
receiver (MIPI-CSIS) devices.
diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c
index 0914230b42de..9fc2af6a0446 100644
--- a/drivers/media/platform/exynos4-is/mipi-csis.c
+++ b/drivers/media/platform/exynos4-is/mipi-csis.c
@@ -20,6 +20,7 @@
#include <linux/memory.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/phy/phy.h>
#include <linux/platform_data/mipi-csis.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -180,6 +181,7 @@ struct csis_drvdata {
* @sd: v4l2_subdev associated with CSIS device instance
* @index: the hardware instance index
* @pdev: CSIS platform device
+ * @phy: pointer to the CSIS generic PHY
* @regs: mmaped I/O registers memory
* @supplies: CSIS regulator supplies
* @clock: CSIS clocks
@@ -203,6 +205,7 @@ struct csis_state {
struct v4l2_subdev sd;
u8 index;
struct platform_device *pdev;
+ struct phy *phy;
void __iomem *regs;
struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES];
struct clk *clock[NUM_CSIS_CLOCKS];
@@ -779,8 +782,8 @@ static int s5pcsis_parse_dt(struct platform_device *pdev,
"samsung,csis-wclk");
state->num_lanes = endpoint.bus.mipi_csi2.num_data_lanes;
-
of_node_put(node);
+
return 0;
}
#else
@@ -829,6 +832,10 @@ static int s5pcsis_probe(struct platform_device *pdev)
return -EINVAL;
}
+ state->phy = devm_phy_get(dev, "csis");
+ if (IS_ERR(state->phy))
+ return PTR_ERR(state->phy);
+
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
state->regs = devm_ioremap_resource(dev, mem_res);
if (IS_ERR(state->regs))
@@ -922,7 +929,7 @@ static int s5pcsis_pm_suspend(struct device *dev, bool runtime)
mutex_lock(&state->lock);
if (state->flags & ST_POWERED) {
s5pcsis_stop_stream(state);
- ret = s5p_csis_phy_enable(state->index, false);
+ ret = phy_power_off(state->phy);
if (ret)
goto unlock;
ret = regulator_bulk_disable(CSIS_NUM_SUPPLIES,
@@ -958,7 +965,7 @@ static int s5pcsis_pm_resume(struct device *dev, bool runtime)
state->supplies);
if (ret)
goto unlock;
- ret = s5p_csis_phy_enable(state->index, true);
+ ret = phy_power_on(state->phy);
if (!ret) {
state->flags |= ST_POWERED;
} else {
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
new file mode 100644
index 000000000000..a344f3d52361
--- /dev/null
+++ b/drivers/phy/Kconfig
@@ -0,0 +1,54 @@
+#
+# PHY
+#
+
+menu "PHY Subsystem"
+
+config GENERIC_PHY
+ tristate "PHY Core"
+ help
+ Generic PHY support.
+
+ This framework is designed to provide a generic interface for PHY
+ devices present in the kernel. This layer will have the generic
+ API by which phy drivers can create PHY using the phy framework and
+ phy users can obtain reference to the PHY. All the users of this
+ framework should select this config.
+
+config PHY_EXYNOS_MIPI_VIDEO
+ tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver"
+ help
+ Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P
+ and EXYNOS SoCs.
+
+config OMAP_USB2
+ tristate "OMAP USB2 PHY Driver"
+ depends on ARCH_OMAP2PLUS
+ select GENERIC_PHY
+ select USB_PHY
+ select OMAP_CONTROL_USB
+ help
+ Enable this to support the transceiver that is part of SOC. This
+ driver takes care of all the PHY functionality apart from comparator.
+ The USB OTG controller communicates with the comparator using this
+ driver.
+
+config TWL4030_USB
+ tristate "TWL4030 USB Transceiver Driver"
+ depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS
+ select GENERIC_PHY
+ select USB_PHY
+ help
+ Enable this to support the USB OTG transceiver on TWL4030
+ family chips (including the TWL5030 and TPS659x0 devices).
+ This transceiver supports high and full speed devices plus,
+ in host mode, low speed.
+
+config PHY_EXYNOS_DP_VIDEO
+ tristate "EXYNOS SoC series Display Port PHY driver"
+ depends on OF
+ select GENERIC_PHY
+ help
+ Support for Display Port PHY found on Samsung EXYNOS SoCs.
+
+endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
new file mode 100644
index 000000000000..d0caae9cfb83
--- /dev/null
+++ b/drivers/phy/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the phy drivers.
+#
+
+obj-$(CONFIG_GENERIC_PHY) += phy-core.o
+obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o
+obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
+obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
+obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
new file mode 100644
index 000000000000..03cf8fb81554
--- /dev/null
+++ b/drivers/phy/phy-core.c
@@ -0,0 +1,698 @@
+/*
+ * phy-core.c -- Generic Phy framework.
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/idr.h>
+#include <linux/pm_runtime.h>
+
+static struct class *phy_class;
+static DEFINE_MUTEX(phy_provider_mutex);
+static LIST_HEAD(phy_provider_list);
+static DEFINE_IDA(phy_ida);
+
+static void devm_phy_release(struct device *dev, void *res)
+{
+ struct phy *phy = *(struct phy **)res;
+
+ phy_put(phy);
+}
+
+static void devm_phy_provider_release(struct device *dev, void *res)
+{
+ struct phy_provider *phy_provider = *(struct phy_provider **)res;
+
+ of_phy_provider_unregister(phy_provider);
+}
+
+static void devm_phy_consume(struct device *dev, void *res)
+{
+ struct phy *phy = *(struct phy **)res;
+
+ phy_destroy(phy);
+}
+
+static int devm_phy_match(struct device *dev, void *res, void *match_data)
+{
+ return res == match_data;
+}
+
+static struct phy *phy_lookup(struct device *device, const char *port)
+{
+ unsigned int count;
+ struct phy *phy;
+ struct device *dev;
+ struct phy_consumer *consumers;
+ struct class_dev_iter iter;
+
+ class_dev_iter_init(&iter, phy_class, NULL, NULL);
+ while ((dev = class_dev_iter_next(&iter))) {
+ phy = to_phy(dev);
+ count = phy->init_data->num_consumers;
+ consumers = phy->init_data->consumers;
+ while (count--) {
+ if (!strcmp(consumers->dev_name, dev_name(device)) &&
+ !strcmp(consumers->port, port)) {
+ class_dev_iter_exit(&iter);
+ return phy;
+ }
+ consumers++;
+ }
+ }
+
+ class_dev_iter_exit(&iter);
+ return ERR_PTR(-ENODEV);
+}
+
+static struct phy_provider *of_phy_provider_lookup(struct device_node *node)
+{
+ struct phy_provider *phy_provider;
+
+ list_for_each_entry(phy_provider, &phy_provider_list, list) {
+ if (phy_provider->dev->of_node == node)
+ return phy_provider;
+ }
+
+ return ERR_PTR(-EPROBE_DEFER);
+}
+
+int phy_pm_runtime_get(struct phy *phy)
+{
+ if (!pm_runtime_enabled(&phy->dev))
+ return -ENOTSUPP;
+
+ return pm_runtime_get(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_get);
+
+int phy_pm_runtime_get_sync(struct phy *phy)
+{
+ if (!pm_runtime_enabled(&phy->dev))
+ return -ENOTSUPP;
+
+ return pm_runtime_get_sync(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_get_sync);
+
+int phy_pm_runtime_put(struct phy *phy)
+{
+ if (!pm_runtime_enabled(&phy->dev))
+ return -ENOTSUPP;
+
+ return pm_runtime_put(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_put);
+
+int phy_pm_runtime_put_sync(struct phy *phy)
+{
+ if (!pm_runtime_enabled(&phy->dev))
+ return -ENOTSUPP;
+
+ return pm_runtime_put_sync(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_put_sync);
+
+void phy_pm_runtime_allow(struct phy *phy)
+{
+ if (!pm_runtime_enabled(&phy->dev))
+ return;
+
+ pm_runtime_allow(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_allow);
+
+void phy_pm_runtime_forbid(struct phy *phy)
+{
+ if (!pm_runtime_enabled(&phy->dev))
+ return;
+
+ pm_runtime_forbid(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_forbid);
+
+int phy_init(struct phy *phy)
+{
+ int ret;
+
+ ret = phy_pm_runtime_get_sync(phy);
+ if (ret < 0 && ret != -ENOTSUPP)
+ return ret;
+
+ mutex_lock(&phy->mutex);
+ if (phy->init_count++ == 0 && phy->ops->init) {
+ ret = phy->ops->init(phy);
+ if (ret < 0) {
+ dev_err(&phy->dev, "phy init failed --> %d\n", ret);
+ goto out;
+ }
+ }
+
+out:
+ mutex_unlock(&phy->mutex);
+ phy_pm_runtime_put(phy);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_init);
+
+int phy_exit(struct phy *phy)
+{
+ int ret;
+
+ ret = phy_pm_runtime_get_sync(phy);
+ if (ret < 0 && ret != -ENOTSUPP)
+ return ret;
+
+ mutex_lock(&phy->mutex);
+ if (--phy->init_count == 0 && phy->ops->exit) {
+ ret = phy->ops->exit(phy);
+ if (ret < 0) {
+ dev_err(&phy->dev, "phy exit failed --> %d\n", ret);
+ goto out;
+ }
+ }
+
+out:
+ mutex_unlock(&phy->mutex);
+ phy_pm_runtime_put(phy);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_exit);
+
+int phy_power_on(struct phy *phy)
+{
+ int ret = -ENOTSUPP;
+
+ ret = phy_pm_runtime_get_sync(phy);
+ if (ret < 0 && ret != -ENOTSUPP)
+ return ret;
+
+ mutex_lock(&phy->mutex);
+ if (phy->power_count++ == 0 && phy->ops->power_on) {
+ ret = phy->ops->power_on(phy);
+ if (ret < 0) {
+ dev_err(&phy->dev, "phy poweron failed --> %d\n", ret);
+ goto out;
+ }
+ }
+
+out:
+ mutex_unlock(&phy->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_power_on);
+
+int phy_power_off(struct phy *phy)
+{
+ int ret = -ENOTSUPP;
+
+ mutex_lock(&phy->mutex);
+ if (--phy->power_count == 0 && phy->ops->power_off) {
+ ret = phy->ops->power_off(phy);
+ if (ret < 0) {
+ dev_err(&phy->dev, "phy poweroff failed --> %d\n", ret);
+ goto out;
+ }
+ }
+
+out:
+ mutex_unlock(&phy->mutex);
+ phy_pm_runtime_put(phy);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_power_off);
+
+/**
+ * of_phy_get() - lookup and obtain a reference to a phy by phandle
+ * @dev: device that requests this phy
+ * @index: the index of the phy
+ *
+ * Returns the phy associated with the given phandle value,
+ * after getting a refcount to it or -ENODEV if there is no such phy or
+ * -EPROBE_DEFER if there is a phandle to the phy, but the device is
+ * not yet loaded. This function uses of_xlate call back function provided
+ * while registering the phy_provider to find the phy instance.
+ */
+static struct phy *of_phy_get(struct device *dev, int index)
+{
+ int ret;
+ struct phy_provider *phy_provider;
+ struct phy *phy = NULL;
+ struct of_phandle_args args;
+
+ ret = of_parse_phandle_with_args(dev->of_node, "phys", "#phy-cells",
+ index, &args);
+ if (ret) {
+ dev_dbg(dev, "failed to get phy in %s node\n",
+ dev->of_node->full_name);
+ return ERR_PTR(-ENODEV);
+ }
+
+ mutex_lock(&phy_provider_mutex);
+ phy_provider = of_phy_provider_lookup(args.np);
+ if (IS_ERR(phy_provider) || !try_module_get(phy_provider->owner)) {
+ phy = ERR_PTR(-EPROBE_DEFER);
+ goto err0;
+ }
+
+ phy = phy_provider->of_xlate(phy_provider->dev, &args);
+ module_put(phy_provider->owner);
+
+err0:
+ mutex_unlock(&phy_provider_mutex);
+ of_node_put(args.np);
+
+ return phy;
+}
+
+/**
+ * phy_put() - release the PHY
+ * @phy: the phy returned by phy_get()
+ *
+ * Releases a refcount the caller received from phy_get().
+ */
+void phy_put(struct phy *phy)
+{
+ if (IS_ERR(phy))
+ return;
+
+ module_put(phy->ops->owner);
+ put_device(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_put);
+
+/**
+ * devm_phy_put() - release the PHY
+ * @dev: device that wants to release this phy
+ * @phy: the phy returned by devm_phy_get()
+ *
+ * destroys the devres associated with this phy and invokes phy_put
+ * to release the phy.
+ */
+void devm_phy_put(struct device *dev, struct phy *phy)
+{
+ int r;
+
+ r = devres_destroy(dev, devm_phy_release, devm_phy_match, phy);
+ dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n");
+}
+EXPORT_SYMBOL_GPL(devm_phy_put);
+
+/**
+ * of_phy_simple_xlate() - returns the phy instance from phy provider
+ * @dev: the PHY provider device
+ * @args: of_phandle_args (not used here)
+ *
+ * Intended to be used by phy provider for the common case where #phy-cells is
+ * 0. For other cases where #phy-cells is greater than '0', the phy provider
+ * should provide a custom of_xlate function that reads the *args* and returns
+ * the appropriate phy.
+ */
+struct phy *of_phy_simple_xlate(struct device *dev, struct of_phandle_args
+ *args)
+{
+ struct phy *phy;
+ struct class_dev_iter iter;
+ struct device_node *node = dev->of_node;
+
+ class_dev_iter_init(&iter, phy_class, NULL, NULL);
+ while ((dev = class_dev_iter_next(&iter))) {
+ phy = to_phy(dev);
+ if (node != phy->dev.of_node)
+ continue;
+
+ class_dev_iter_exit(&iter);
+ return phy;
+ }
+
+ class_dev_iter_exit(&iter);
+ return ERR_PTR(-ENODEV);
+}
+EXPORT_SYMBOL_GPL(of_phy_simple_xlate);
+
+/**
+ * phy_get() - lookup and obtain a reference to a phy.
+ * @dev: device that requests this phy
+ * @string: the phy name as given in the dt data or the name of the controller
+ * port for non-dt case
+ *
+ * Returns the phy driver, after getting a refcount to it; or
+ * -ENODEV if there is no such phy. The caller is responsible for
+ * calling phy_put() to release that count.
+ */
+struct phy *phy_get(struct device *dev, const char *string)
+{
+ int index = 0;
+ struct phy *phy = NULL;
+
+ if (string == NULL) {
+ dev_WARN(dev, "missing string\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (dev->of_node) {
+ index = of_property_match_string(dev->of_node, "phy-names",
+ string);
+ phy = of_phy_get(dev, index);
+ if (IS_ERR(phy)) {
+ dev_err(dev, "unable to find phy\n");
+ return phy;
+ }
+ } else {
+ phy = phy_lookup(dev, string);
+ if (IS_ERR(phy)) {
+ dev_err(dev, "unable to find phy\n");
+ return phy;
+ }
+ }
+
+ if (!try_module_get(phy->ops->owner))
+ return ERR_PTR(-EPROBE_DEFER);
+
+ get_device(&phy->dev);
+
+ return phy;
+}
+EXPORT_SYMBOL_GPL(phy_get);
+
+/**
+ * devm_phy_get() - lookup and obtain a reference to a phy.
+ * @dev: device that requests this phy
+ * @string: the phy name as given in the dt data or phy device name
+ * for non-dt case
+ *
+ * Gets the phy using phy_get(), and associates a device with it using
+ * devres. On driver detach, release function is invoked on the devres data,
+ * then, devres data is freed.
+ */
+struct phy *devm_phy_get(struct device *dev, const char *string)
+{
+ struct phy **ptr, *phy;
+
+ ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ phy = phy_get(dev, string);
+ if (!IS_ERR(phy)) {
+ *ptr = phy;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return phy;
+}
+EXPORT_SYMBOL_GPL(devm_phy_get);
+
+/**
+ * phy_create() - create a new phy
+ * @dev: device that is creating the new phy
+ * @ops: function pointers for performing phy operations
+ * @init_data: contains the list of PHY consumers or NULL
+ *
+ * Called to create a phy using phy framework.
+ */
+struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
+ struct phy_init_data *init_data)
+{
+ int ret;
+ int id;
+ struct phy *phy;
+
+ if (!dev) {
+ dev_WARN(dev, "no device provided for PHY\n");
+ ret = -EINVAL;
+ goto err0;
+ }
+
+ phy = kzalloc(sizeof(*phy), GFP_KERNEL);
+ if (!phy) {
+ ret = -ENOMEM;
+ goto err0;
+ }
+
+ id = ida_simple_get(&phy_ida, 0, 0, GFP_KERNEL);
+ if (id < 0) {
+ dev_err(dev, "unable to get id\n");
+ ret = id;
+ goto err0;
+ }
+
+ device_initialize(&phy->dev);
+ mutex_init(&phy->mutex);
+
+ phy->dev.class = phy_class;
+ phy->dev.parent = dev;
+ phy->dev.of_node = dev->of_node;
+ phy->id = id;
+ phy->ops = ops;
+ phy->init_data = init_data;
+
+ ret = dev_set_name(&phy->dev, "phy-%s.%d", dev_name(dev), id);
+ if (ret)
+ goto err1;
+
+ ret = device_add(&phy->dev);
+ if (ret)
+ goto err1;
+
+ if (pm_runtime_enabled(dev)) {
+ pm_runtime_enable(&phy->dev);
+ pm_runtime_no_callbacks(&phy->dev);
+ }
+
+ return phy;
+
+err1:
+ ida_remove(&phy_ida, phy->id);
+ put_device(&phy->dev);
+ kfree(phy);
+
+err0:
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(phy_create);
+
+/**
+ * devm_phy_create() - create a new phy
+ * @dev: device that is creating the new phy
+ * @ops: function pointers for performing phy operations
+ * @init_data: contains the list of PHY consumers or NULL
+ *
+ * Creates a new PHY device adding it to the PHY class.
+ * While at that, it also associates the device with the phy using devres.
+ * On driver detach, release function is invoked on the devres data,
+ * then, devres data is freed.
+ */
+struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops,
+ struct phy_init_data *init_data)
+{
+ struct phy **ptr, *phy;
+
+ ptr = devres_alloc(devm_phy_consume, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ phy = phy_create(dev, ops, init_data);
+ if (!IS_ERR(phy)) {
+ *ptr = phy;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return phy;
+}
+EXPORT_SYMBOL_GPL(devm_phy_create);
+
+/**
+ * phy_destroy() - destroy the phy
+ * @phy: the phy to be destroyed
+ *
+ * Called to destroy the phy.
+ */
+void phy_destroy(struct phy *phy)
+{
+ pm_runtime_disable(&phy->dev);
+ device_unregister(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_destroy);
+
+/**
+ * devm_phy_destroy() - destroy the PHY
+ * @dev: device that wants to release this phy
+ * @phy: the phy returned by devm_phy_get()
+ *
+ * destroys the devres associated with this phy and invokes phy_destroy
+ * to destroy the phy.
+ */
+void devm_phy_destroy(struct device *dev, struct phy *phy)
+{
+ int r;
+
+ r = devres_destroy(dev, devm_phy_consume, devm_phy_match, phy);
+ dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n");
+}
+EXPORT_SYMBOL_GPL(devm_phy_destroy);
+
+/**
+ * __of_phy_provider_register() - create/register phy provider with the framework
+ * @dev: struct device of the phy provider
+ * @owner: the module owner containing of_xlate
+ * @of_xlate: function pointer to obtain phy instance from phy provider
+ *
+ * Creates struct phy_provider from dev and of_xlate function pointer.
+ * This is used in the case of dt boot for finding the phy instance from
+ * phy provider.
+ */
+struct phy_provider *__of_phy_provider_register(struct device *dev,
+ struct module *owner, struct phy * (*of_xlate)(struct device *dev,
+ struct of_phandle_args *args))
+{
+ struct phy_provider *phy_provider;
+
+ phy_provider = kzalloc(sizeof(*phy_provider), GFP_KERNEL);
+ if (!phy_provider)
+ return ERR_PTR(-ENOMEM);
+
+ phy_provider->dev = dev;
+ phy_provider->owner = owner;
+ phy_provider->of_xlate = of_xlate;
+
+ mutex_lock(&phy_provider_mutex);
+ list_add_tail(&phy_provider->list, &phy_provider_list);
+ mutex_unlock(&phy_provider_mutex);
+
+ return phy_provider;
+}
+EXPORT_SYMBOL_GPL(__of_phy_provider_register);
+
+/**
+ * __devm_of_phy_provider_register() - create/register phy provider with the
+ * framework
+ * @dev: struct device of the phy provider
+ * @owner: the module owner containing of_xlate
+ * @of_xlate: function pointer to obtain phy instance from phy provider
+ *
+ * Creates struct phy_provider from dev and of_xlate function pointer.
+ * This is used in the case of dt boot for finding the phy instance from
+ * phy provider. While at that, it also associates the device with the
+ * phy provider using devres. On driver detach, release function is invoked
+ * on the devres data, then, devres data is freed.
+ */
+struct phy_provider *__devm_of_phy_provider_register(struct device *dev,
+ struct module *owner, struct phy * (*of_xlate)(struct device *dev,
+ struct of_phandle_args *args))
+{
+ struct phy_provider **ptr, *phy_provider;
+
+ ptr = devres_alloc(devm_phy_provider_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ phy_provider = __of_phy_provider_register(dev, owner, of_xlate);
+ if (!IS_ERR(phy_provider)) {
+ *ptr = phy_provider;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return phy_provider;
+}
+EXPORT_SYMBOL_GPL(__devm_of_phy_provider_register);
+
+/**
+ * of_phy_provider_unregister() - unregister phy provider from the framework
+ * @phy_provider: phy provider returned by of_phy_provider_register()
+ *
+ * Removes the phy_provider created using of_phy_provider_register().
+ */
+void of_phy_provider_unregister(struct phy_provider *phy_provider)
+{
+ if (IS_ERR(phy_provider))
+ return;
+
+ mutex_lock(&phy_provider_mutex);
+ list_del(&phy_provider->list);
+ kfree(phy_provider);
+ mutex_unlock(&phy_provider_mutex);
+}
+EXPORT_SYMBOL_GPL(of_phy_provider_unregister);
+
+/**
+ * devm_of_phy_provider_unregister() - remove phy provider from the framework
+ * @dev: struct device of the phy provider
+ *
+ * destroys the devres associated with this phy provider and invokes
+ * of_phy_provider_unregister to unregister the phy provider.
+ */
+void devm_of_phy_provider_unregister(struct device *dev,
+ struct phy_provider *phy_provider) {
+ int r;
+
+ r = devres_destroy(dev, devm_phy_provider_release, devm_phy_match,
+ phy_provider);
+ dev_WARN_ONCE(dev, r, "couldn't find PHY provider device resource\n");
+}
+EXPORT_SYMBOL_GPL(devm_of_phy_provider_unregister);
+
+/**
+ * phy_release() - release the phy
+ * @dev: the dev member within phy
+ *
+ * When the last reference to the device is removed, it is called
+ * from the embedded kobject as release method.
+ */
+static void phy_release(struct device *dev)
+{
+ struct phy *phy;
+
+ phy = to_phy(dev);
+ dev_vdbg(dev, "releasing '%s'\n", dev_name(dev));
+ ida_remove(&phy_ida, phy->id);
+ kfree(phy);
+}
+
+static int __init phy_core_init(void)
+{
+ phy_class = class_create(THIS_MODULE, "phy");
+ if (IS_ERR(phy_class)) {
+ pr_err("failed to create phy class --> %ld\n",
+ PTR_ERR(phy_class));
+ return PTR_ERR(phy_class);
+ }
+
+ phy_class->dev_release = phy_release;
+
+ return 0;
+}
+module_init(phy_core_init);
+
+static void __exit phy_core_exit(void)
+{
+ class_destroy(phy_class);
+}
+module_exit(phy_core_exit);
+
+MODULE_DESCRIPTION("Generic PHY Framework");
+MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-exynos-dp-video.c b/drivers/phy/phy-exynos-dp-video.c
new file mode 100644
index 000000000000..1dbe6ce7b2ce
--- /dev/null
+++ b/drivers/phy/phy-exynos-dp-video.c
@@ -0,0 +1,111 @@
+/*
+ * Samsung EXYNOS SoC series Display Port PHY driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Jingoo Han <jg1.han@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+
+/* DPTX_PHY_CONTROL register */
+#define EXYNOS_DPTX_PHY_ENABLE (1 << 0)
+
+struct exynos_dp_video_phy {
+ void __iomem *regs;
+};
+
+static int __set_phy_state(struct exynos_dp_video_phy *state, unsigned int on)
+{
+ u32 reg;
+
+ reg = readl(state->regs);
+ if (on)
+ reg |= EXYNOS_DPTX_PHY_ENABLE;
+ else
+ reg &= ~EXYNOS_DPTX_PHY_ENABLE;
+ writel(reg, state->regs);
+
+ return 0;
+}
+
+static int exynos_dp_video_phy_power_on(struct phy *phy)
+{
+ struct exynos_dp_video_phy *state = phy_get_drvdata(phy);
+
+ return __set_phy_state(state, 1);
+}
+
+static int exynos_dp_video_phy_power_off(struct phy *phy)
+{
+ struct exynos_dp_video_phy *state = phy_get_drvdata(phy);
+
+ return __set_phy_state(state, 0);
+}
+
+static struct phy_ops exynos_dp_video_phy_ops = {
+ .power_on = exynos_dp_video_phy_power_on,
+ .power_off = exynos_dp_video_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int exynos_dp_video_phy_probe(struct platform_device *pdev)
+{
+ struct exynos_dp_video_phy *state;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct phy_provider *phy_provider;
+ struct phy *phy;
+
+ state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ state->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(state->regs))
+ return PTR_ERR(state->regs);
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(phy_provider))
+ return PTR_ERR(phy_provider);
+
+ phy = devm_phy_create(dev, &exynos_dp_video_phy_ops, NULL);
+ if (IS_ERR(phy)) {
+ dev_err(dev, "failed to create Display Port PHY\n");
+ return PTR_ERR(phy);
+ }
+ phy_set_drvdata(phy, state);
+
+ return 0;
+}
+
+static const struct of_device_id exynos_dp_video_phy_of_match[] = {
+ { .compatible = "samsung,exynos5250-dp-video-phy" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, exynos_dp_video_phy_of_match);
+
+static struct platform_driver exynos_dp_video_phy_driver = {
+ .probe = exynos_dp_video_phy_probe,
+ .driver = {
+ .name = "exynos-dp-video-phy",
+ .owner = THIS_MODULE,
+ .of_match_table = exynos_dp_video_phy_of_match,
+ }
+};
+module_platform_driver(exynos_dp_video_phy_driver);
+
+MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
+MODULE_DESCRIPTION("Samsung EXYNOS SoC DP PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-exynos-mipi-video.c b/drivers/phy/phy-exynos-mipi-video.c
new file mode 100644
index 000000000000..b73b86ab4697
--- /dev/null
+++ b/drivers/phy/phy-exynos-mipi-video.c
@@ -0,0 +1,176 @@
+/*
+ * Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+/* MIPI_PHYn_CONTROL register offset: n = 0..1 */
+#define EXYNOS_MIPI_PHY_CONTROL(n) ((n) * 4)
+#define EXYNOS_MIPI_PHY_ENABLE (1 << 0)
+#define EXYNOS_MIPI_PHY_SRESETN (1 << 1)
+#define EXYNOS_MIPI_PHY_MRESETN (1 << 2)
+#define EXYNOS_MIPI_PHY_RESET_MASK (3 << 1)
+
+enum exynos_mipi_phy_id {
+ EXYNOS_MIPI_PHY_ID_CSIS0,
+ EXYNOS_MIPI_PHY_ID_DSIM0,
+ EXYNOS_MIPI_PHY_ID_CSIS1,
+ EXYNOS_MIPI_PHY_ID_DSIM1,
+ EXYNOS_MIPI_PHYS_NUM
+};
+
+#define is_mipi_dsim_phy_id(id) \
+ ((id) == EXYNOS_MIPI_PHY_ID_DSIM0 || (id) == EXYNOS_MIPI_PHY_ID_DSIM1)
+
+struct exynos_mipi_video_phy {
+ spinlock_t slock;
+ struct video_phy_desc {
+ struct phy *phy;
+ unsigned int index;
+ } phys[EXYNOS_MIPI_PHYS_NUM];
+ void __iomem *regs;
+};
+
+static int __set_phy_state(struct exynos_mipi_video_phy *state,
+ enum exynos_mipi_phy_id id, unsigned int on)
+{
+ void __iomem *addr;
+ u32 reg, reset;
+
+ addr = state->regs + EXYNOS_MIPI_PHY_CONTROL(id / 2);
+
+ if (is_mipi_dsim_phy_id(id))
+ reset = EXYNOS_MIPI_PHY_MRESETN;
+ else
+ reset = EXYNOS_MIPI_PHY_SRESETN;
+
+ spin_lock(&state->slock);
+ reg = readl(addr);
+ if (on)
+ reg |= reset;
+ else
+ reg &= ~reset;
+ writel(reg, addr);
+
+ /* Clear ENABLE bit only if MRESETN, SRESETN bits are not set. */
+ if (on)
+ reg |= EXYNOS_MIPI_PHY_ENABLE;
+ else if (!(reg & EXYNOS_MIPI_PHY_RESET_MASK))
+ reg &= ~EXYNOS_MIPI_PHY_ENABLE;
+
+ writel(reg, addr);
+ spin_unlock(&state->slock);
+ return 0;
+}
+
+#define to_mipi_video_phy(desc) \
+ container_of((desc), struct exynos_mipi_video_phy, phys[(desc)->index]);
+
+static int exynos_mipi_video_phy_power_on(struct phy *phy)
+{
+ struct video_phy_desc *phy_desc = phy_get_drvdata(phy);
+ struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc);
+
+ return __set_phy_state(state, phy_desc->index, 1);
+}
+
+static int exynos_mipi_video_phy_power_off(struct phy *phy)
+{
+ struct video_phy_desc *phy_desc = phy_get_drvdata(phy);
+ struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc);
+
+ return __set_phy_state(state, phy_desc->index, 1);
+}
+
+static struct phy *exynos_mipi_video_phy_xlate(struct device *dev,
+ struct of_phandle_args *args)
+{
+ struct exynos_mipi_video_phy *state = dev_get_drvdata(dev);
+
+ if (WARN_ON(args->args[0] > EXYNOS_MIPI_PHYS_NUM))
+ return ERR_PTR(-ENODEV);
+
+ return state->phys[args->args[0]].phy;
+}
+
+static struct phy_ops exynos_mipi_video_phy_ops = {
+ .power_on = exynos_mipi_video_phy_power_on,
+ .power_off = exynos_mipi_video_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
+{
+ struct exynos_mipi_video_phy *state;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct phy_provider *phy_provider;
+ unsigned int i;
+
+ state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ state->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(state->regs))
+ return PTR_ERR(state->regs);
+
+ dev_set_drvdata(dev, state);
+ spin_lock_init(&state->slock);
+
+ phy_provider = devm_of_phy_provider_register(dev,
+ exynos_mipi_video_phy_xlate);
+ if (IS_ERR(phy_provider))
+ return PTR_ERR(phy_provider);
+
+ for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) {
+ struct phy *phy = devm_phy_create(dev,
+ &exynos_mipi_video_phy_ops, NULL);
+ if (IS_ERR(phy)) {
+ dev_err(dev, "failed to create PHY %d\n", i);
+ return PTR_ERR(phy);
+ }
+
+ state->phys[i].phy = phy;
+ state->phys[i].index = i;
+ phy_set_drvdata(phy, &state->phys[i]);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id exynos_mipi_video_phy_of_match[] = {
+ { .compatible = "samsung,s5pv210-mipi-video-phy" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, exynos_mipi_video_phy_of_match);
+
+static struct platform_driver exynos_mipi_video_phy_driver = {
+ .probe = exynos_mipi_video_phy_probe,
+ .driver = {
+ .of_match_table = exynos_mipi_video_phy_of_match,
+ .name = "exynos-mipi-video-phy",
+ .owner = THIS_MODULE,
+ }
+};
+module_platform_driver(exynos_mipi_video_phy_driver);
+
+MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI CSI-2/DSI PHY driver");
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/phy/phy-omap-usb2.c b/drivers/phy/phy-omap-usb2.c
index d266861d24f7..bfc5c337f99a 100644
--- a/drivers/usb/phy/phy-omap-usb2.c
+++ b/drivers/phy/phy-omap-usb2.c
@@ -28,6 +28,8 @@
#include <linux/pm_runtime.h>
#include <linux/delay.h>
#include <linux/usb/omap_control_usb.h>
+#include <linux/phy/phy.h>
+#include <linux/of_platform.h>
/**
* omap_usb2_set_comparator - links the comparator present in the sytem with
@@ -118,10 +120,42 @@ static int omap_usb2_suspend(struct usb_phy *x, int suspend)
return 0;
}
+static int omap_usb_power_off(struct phy *x)
+{
+ struct omap_usb *phy = phy_get_drvdata(x);
+
+ omap_control_usb_phy_power(phy->control_dev, 0);
+
+ return 0;
+}
+
+static int omap_usb_power_on(struct phy *x)
+{
+ struct omap_usb *phy = phy_get_drvdata(x);
+
+ omap_control_usb_phy_power(phy->control_dev, 1);
+
+ return 0;
+}
+
+static struct phy_ops ops = {
+ .power_on = omap_usb_power_on,
+ .power_off = omap_usb_power_off,
+ .owner = THIS_MODULE,
+};
+
static int omap_usb2_probe(struct platform_device *pdev)
{
- struct omap_usb *phy;
- struct usb_otg *otg;
+ struct omap_usb *phy;
+ struct phy *generic_phy;
+ struct phy_provider *phy_provider;
+ struct usb_otg *otg;
+ struct device_node *node = pdev->dev.of_node;
+ struct device_node *control_node;
+ struct platform_device *control_pdev;
+
+ if (!node)
+ return -EINVAL;
phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
if (!phy) {
@@ -143,12 +177,25 @@ static int omap_usb2_probe(struct platform_device *pdev)
phy->phy.otg = otg;
phy->phy.type = USB_PHY_TYPE_USB2;
- phy->control_dev = omap_get_control_dev();
- if (IS_ERR(phy->control_dev)) {
- dev_dbg(&pdev->dev, "Failed to get control device\n");
- return -ENODEV;
+ phy_provider = devm_of_phy_provider_register(phy->dev,
+ of_phy_simple_xlate);
+ if (IS_ERR(phy_provider))
+ return PTR_ERR(phy_provider);
+
+ control_node = of_parse_phandle(node, "ctrl-module", 0);
+ if (!control_node) {
+ dev_err(&pdev->dev, "Failed to get control device phandle\n");
+ return -EINVAL;
+ }
+
+ control_pdev = of_find_device_by_node(control_node);
+ if (!control_pdev) {
+ dev_err(&pdev->dev, "Failed to get control device\n");
+ return -EINVAL;
}
+ phy->control_dev = &control_pdev->dev;
+
phy->is_suspended = 1;
omap_control_usb_phy_power(phy->control_dev, 0);
@@ -158,6 +205,15 @@ static int omap_usb2_probe(struct platform_device *pdev)
otg->start_srp = omap_usb_start_srp;
otg->phy = &phy->phy;
+ platform_set_drvdata(pdev, phy);
+ pm_runtime_enable(phy->dev);
+
+ generic_phy = devm_phy_create(phy->dev, &ops, NULL);
+ if (IS_ERR(generic_phy))
+ return PTR_ERR(generic_phy);
+
+ phy_set_drvdata(generic_phy, phy);
+
phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k");
if (IS_ERR(phy->wkupclk)) {
dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n");
@@ -173,10 +229,6 @@ static int omap_usb2_probe(struct platform_device *pdev)
usb_add_phy_dev(&phy->phy);
- platform_set_drvdata(pdev, phy);
-
- pm_runtime_enable(phy->dev);
-
return 0;
}
diff --git a/drivers/usb/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c
index 90730c8762b8..e0212d80c75c 100644
--- a/drivers/usb/phy/phy-twl4030-usb.c
+++ b/drivers/phy/phy-twl4030-usb.c
@@ -33,6 +33,7 @@
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/usb/otg.h>
+#include <linux/phy/phy.h>
#include <linux/usb/musb-omap.h>
#include <linux/usb/ulpi.h>
#include <linux/i2c/twl.h>
@@ -421,17 +422,20 @@ static void twl4030_phy_power(struct twl4030_usb *twl, int on)
}
}
-static void twl4030_phy_suspend(struct twl4030_usb *twl, int controller_off)
+static int twl4030_phy_power_off(struct phy *phy)
{
+ struct twl4030_usb *twl = phy_get_drvdata(phy);
+
if (twl->asleep)
- return;
+ return 0;
twl4030_phy_power(twl, 0);
twl->asleep = 1;
dev_dbg(twl->dev, "%s\n", __func__);
+ return 0;
}
-static void __twl4030_phy_resume(struct twl4030_usb *twl)
+static void __twl4030_phy_power_on(struct twl4030_usb *twl)
{
twl4030_phy_power(twl, 1);
twl4030_i2c_access(twl, 1);
@@ -440,11 +444,13 @@ static void __twl4030_phy_resume(struct twl4030_usb *twl)
twl4030_i2c_access(twl, 0);
}
-static void twl4030_phy_resume(struct twl4030_usb *twl)
+static int twl4030_phy_power_on(struct phy *phy)
{
+ struct twl4030_usb *twl = phy_get_drvdata(phy);
+
if (!twl->asleep)
- return;
- __twl4030_phy_resume(twl);
+ return 0;
+ __twl4030_phy_power_on(twl);
twl->asleep = 0;
dev_dbg(twl->dev, "%s\n", __func__);
@@ -457,6 +463,7 @@ static void twl4030_phy_resume(struct twl4030_usb *twl)
cancel_delayed_work(&twl->id_workaround_work);
schedule_delayed_work(&twl->id_workaround_work, HZ);
}
+ return 0;
}
static int twl4030_usb_ldo_init(struct twl4030_usb *twl)
@@ -587,9 +594,9 @@ static void twl4030_id_workaround_work(struct work_struct *work)
}
}
-static int twl4030_usb_phy_init(struct usb_phy *phy)
+static int twl4030_phy_init(struct phy *phy)
{
- struct twl4030_usb *twl = phy_to_twl(phy);
+ struct twl4030_usb *twl = phy_get_drvdata(phy);
enum omap_musb_vbus_id_status status;
/*
@@ -602,25 +609,15 @@ static int twl4030_usb_phy_init(struct usb_phy *phy)
status = twl4030_usb_linkstat(twl);
twl->linkstat = status;
- if (status == OMAP_MUSB_ID_GROUND || status == OMAP_MUSB_VBUS_VALID)
+ if (status == OMAP_MUSB_ID_GROUND || status == OMAP_MUSB_VBUS_VALID) {
omap_musb_mailbox(twl->linkstat);
+ twl4030_phy_power_on(phy);
+ }
sysfs_notify(&twl->dev->kobj, NULL, "vbus");
return 0;
}
-static int twl4030_set_suspend(struct usb_phy *x, int suspend)
-{
- struct twl4030_usb *twl = phy_to_twl(x);
-
- if (suspend)
- twl4030_phy_suspend(twl, 1);
- else
- twl4030_phy_resume(twl);
-
- return 0;
-}
-
static int twl4030_set_peripheral(struct usb_otg *otg,
struct usb_gadget *gadget)
{
@@ -646,13 +643,23 @@ static int twl4030_set_host(struct usb_otg *otg, struct usb_bus *host)
return 0;
}
+static const struct phy_ops ops = {
+ .init = twl4030_phy_init,
+ .power_on = twl4030_phy_power_on,
+ .power_off = twl4030_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
static int twl4030_usb_probe(struct platform_device *pdev)
{
struct twl4030_usb_data *pdata = dev_get_platdata(&pdev->dev);
struct twl4030_usb *twl;
+ struct phy *phy;
int status, err;
struct usb_otg *otg;
struct device_node *np = pdev->dev.of_node;
+ struct phy_provider *phy_provider;
+ struct phy_init_data *init_data = NULL;
twl = devm_kzalloc(&pdev->dev, sizeof *twl, GFP_KERNEL);
if (!twl)
@@ -661,9 +668,10 @@ static int twl4030_usb_probe(struct platform_device *pdev)
if (np)
of_property_read_u32(np, "usb_mode",
(enum twl4030_usb_mode *)&twl->usb_mode);
- else if (pdata)
+ else if (pdata) {
twl->usb_mode = pdata->usb_mode;
- else {
+ init_data = pdata->init_data;
+ } else {
dev_err(&pdev->dev, "twl4030 initialized without pdata\n");
return -EINVAL;
}
@@ -682,13 +690,24 @@ static int twl4030_usb_probe(struct platform_device *pdev)
twl->phy.label = "twl4030";
twl->phy.otg = otg;
twl->phy.type = USB_PHY_TYPE_USB2;
- twl->phy.set_suspend = twl4030_set_suspend;
- twl->phy.init = twl4030_usb_phy_init;
otg->phy = &twl->phy;
otg->set_host = twl4030_set_host;
otg->set_peripheral = twl4030_set_peripheral;
+ phy_provider = devm_of_phy_provider_register(twl->dev,
+ of_phy_simple_xlate);
+ if (IS_ERR(phy_provider))
+ return PTR_ERR(phy_provider);
+
+ phy = devm_phy_create(twl->dev, &ops, init_data);
+ if (IS_ERR(phy)) {
+ dev_dbg(&pdev->dev, "Failed to create PHY\n");
+ return PTR_ERR(phy);
+ }
+
+ phy_set_drvdata(phy, twl);
+
/* init spinlock for workqueue */
spin_lock_init(&twl->lock);
diff --git a/drivers/usb/chipidea/bits.h b/drivers/usb/chipidea/bits.h
index 464584c6ccae..a85713165688 100644
--- a/drivers/usb/chipidea/bits.h
+++ b/drivers/usb/chipidea/bits.h
@@ -48,6 +48,7 @@
#define PORTSC_SUSP BIT(7)
#define PORTSC_HSP BIT(9)
#define PORTSC_PTC (0x0FUL << 16)
+#define PORTSC_PHCD(d) ((d) ? BIT(22) : BIT(23))
/* PTS and PTW for non lpm version only */
#define PORTSC_PTS(d) \
(u32)((((d) & 0x3) << 30) | (((d) & 0x4) ? BIT(25) : 0))
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index be822a2c1776..023d3cb6aa0a 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -108,14 +108,8 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
}
data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0);
- if (!IS_ERR(data->phy)) {
- ret = usb_phy_init(data->phy);
- if (ret) {
- dev_err(&pdev->dev, "unable to init phy: %d\n", ret);
- goto err_clk;
- }
- } else if (PTR_ERR(data->phy) == -EPROBE_DEFER) {
- ret = -EPROBE_DEFER;
+ if (IS_ERR(data->phy)) {
+ ret = PTR_ERR(data->phy);
goto err_clk;
}
@@ -131,7 +125,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
if (ret) {
dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n",
ret);
- goto err_phy;
+ goto err_clk;
}
}
@@ -143,7 +137,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
dev_err(&pdev->dev,
"Can't register ci_hdrc platform device, err=%d\n",
ret);
- goto err_phy;
+ goto err_clk;
}
if (data->usbmisc_data) {
@@ -164,9 +158,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
disable_device:
ci_hdrc_remove_device(data->ci_pdev);
-err_phy:
- if (data->phy)
- usb_phy_shutdown(data->phy);
err_clk:
clk_disable_unprepare(data->clk);
return ret;
@@ -178,10 +169,6 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
ci_hdrc_remove_device(data->ci_pdev);
-
- if (data->phy)
- usb_phy_shutdown(data->phy);
-
clk_disable_unprepare(data->clk);
return 0;
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 23763dcec069..06204b77fc4c 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -172,6 +172,27 @@ u8 hw_port_test_get(struct ci_hdrc *ci)
return hw_read(ci, OP_PORTSC, PORTSC_PTC) >> __ffs(PORTSC_PTC);
}
+/* The PHY enters/leaves low power mode */
+static void ci_hdrc_enter_lpm(struct ci_hdrc *ci, bool enable)
+{
+ enum ci_hw_regs reg = ci->hw_bank.lpm ? OP_DEVLC : OP_PORTSC;
+ bool lpm = !!(hw_read(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm)));
+
+ if (enable && !lpm) {
+ hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm),
+ PORTSC_PHCD(ci->hw_bank.lpm));
+ } else if (!enable && lpm) {
+ hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm),
+ 0);
+ /*
+ * The controller needs at least 1ms to reflect
+ * PHY's status, the PHY also needs some time (less
+ * than 1ms) to leave low power mode.
+ */
+ usleep_range(1500, 2000);
+ }
+}
+
static int hw_device_init(struct ci_hdrc *ci, void __iomem *base)
{
u32 reg;
@@ -199,6 +220,8 @@ static int hw_device_init(struct ci_hdrc *ci, void __iomem *base)
if (ci->hw_ep_max > ENDPT_MAX)
return -ENODEV;
+ ci_hdrc_enter_lpm(ci, false);
+
/* Disable all interrupts bits */
hw_write(ci, OP_USBINTR, 0xffffffff, 0);
@@ -381,6 +404,15 @@ static int ci_get_platdata(struct device *dev,
return PTR_ERR(platdata->reg_vbus);
}
+ if (!platdata->phy_mode)
+ platdata->phy_mode = of_usb_get_phy_mode(dev->of_node);
+
+ if (!platdata->dr_mode)
+ platdata->dr_mode = of_usb_get_dr_mode(dev->of_node);
+
+ if (platdata->dr_mode == USB_DR_MODE_UNKNOWN)
+ platdata->dr_mode = USB_DR_MODE_OTG;
+
return 0;
}
@@ -465,6 +497,33 @@ static void ci_get_otg_capable(struct ci_hdrc *ci)
}
}
+static int ci_usb_phy_init(struct ci_hdrc *ci)
+{
+ if (ci->platdata->phy) {
+ ci->transceiver = ci->platdata->phy;
+ return usb_phy_init(ci->transceiver);
+ } else {
+ ci->global_phy = true;
+ ci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
+ if (IS_ERR(ci->transceiver))
+ ci->transceiver = NULL;
+
+ return 0;
+ }
+}
+
+static void ci_usb_phy_destroy(struct ci_hdrc *ci)
+{
+ if (!ci->transceiver)
+ return;
+
+ otg_set_peripheral(ci->transceiver->otg, NULL);
+ if (ci->global_phy)
+ usb_put_phy(ci->transceiver);
+ else
+ usb_phy_shutdown(ci->transceiver);
+}
+
static int ci_hdrc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -473,7 +532,6 @@ static int ci_hdrc_probe(struct platform_device *pdev)
void __iomem *base;
int ret;
enum usb_dr_mode dr_mode;
- struct device_node *of_node = dev->of_node ?: dev->parent->of_node;
if (!dev->platform_data) {
dev_err(dev, "platform data missing\n");
@@ -493,10 +551,6 @@ static int ci_hdrc_probe(struct platform_device *pdev)
ci->dev = dev;
ci->platdata = dev->platform_data;
- if (ci->platdata->phy)
- ci->transceiver = ci->platdata->phy;
- else
- ci->global_phy = true;
ret = hw_device_init(ci, base);
if (ret < 0) {
@@ -504,27 +558,25 @@ static int ci_hdrc_probe(struct platform_device *pdev)
return -ENODEV;
}
+ ret = ci_usb_phy_init(ci);
+ if (ret) {
+ dev_err(dev, "unable to init phy: %d\n", ret);
+ return ret;
+ }
+
ci->hw_bank.phys = res->start;
ci->irq = platform_get_irq(pdev, 0);
if (ci->irq < 0) {
dev_err(dev, "missing IRQ\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto destroy_phy;
}
ci_get_otg_capable(ci);
- if (!ci->platdata->phy_mode)
- ci->platdata->phy_mode = of_usb_get_phy_mode(of_node);
-
hw_phymode_configure(ci);
- if (!ci->platdata->dr_mode)
- ci->platdata->dr_mode = of_usb_get_dr_mode(of_node);
-
- if (ci->platdata->dr_mode == USB_DR_MODE_UNKNOWN)
- ci->platdata->dr_mode = USB_DR_MODE_OTG;
-
dr_mode = ci->platdata->dr_mode;
/* initialize role(s) before the interrupt is requested */
if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
@@ -537,11 +589,23 @@ static int ci_hdrc_probe(struct platform_device *pdev)
ret = ci_hdrc_gadget_init(ci);
if (ret)
dev_info(dev, "doesn't support gadget\n");
+ if (!ret && ci->transceiver) {
+ ret = otg_set_peripheral(ci->transceiver->otg,
+ &ci->gadget);
+ /*
+ * If we implement all USB functions using chipidea drivers,
+ * it doesn't need to call above API, meanwhile, if we only
+ * use gadget function, calling above API is useless.
+ */
+ if (ret && ret != -ENOTSUPP)
+ goto destroy_phy;
+ }
}
if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]) {
dev_err(dev, "no supported roles\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto destroy_phy;
}
if (ci->is_otg) {
@@ -594,6 +658,8 @@ static int ci_hdrc_probe(struct platform_device *pdev)
free_irq(ci->irq, ci);
stop:
ci_role_destroy(ci);
+destroy_phy:
+ ci_usb_phy_destroy(ci);
return ret;
}
@@ -605,6 +671,8 @@ static int ci_hdrc_remove(struct platform_device *pdev)
dbg_remove_files(ci);
free_irq(ci->irq, ci);
ci_role_destroy(ci);
+ ci_hdrc_enter_lpm(ci, true);
+ ci_usb_phy_destroy(ci);
kfree(ci->hw_bank.regmap);
return 0;
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 9333083dd111..b34c81969cba 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -20,7 +20,6 @@
#include <linux/pm_runtime.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
-#include <linux/usb/otg.h>
#include <linux/usb/chipidea.h>
#include "ci.h"
@@ -686,9 +685,6 @@ static int _gadget_stop_activity(struct usb_gadget *gadget)
usb_ep_fifo_flush(&ci->ep0out->ep);
usb_ep_fifo_flush(&ci->ep0in->ep);
- if (ci->driver)
- ci->driver->disconnect(gadget);
-
/* make sure to disable all endpoints */
gadget_for_each_ep(ep, gadget) {
usb_ep_disable(ep);
@@ -718,6 +714,11 @@ __acquires(ci->lock)
int retval;
spin_unlock(&ci->lock);
+ if (ci->gadget.speed != USB_SPEED_UNKNOWN) {
+ if (ci->driver)
+ ci->driver->disconnect(&ci->gadget);
+ }
+
retval = _gadget_stop_activity(&ci->gadget);
if (retval)
goto done;
@@ -1461,6 +1462,8 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
hw_device_state(ci, ci->ep0out->qh.dma);
dev_dbg(ci->dev, "Connected to host\n");
} else {
+ if (ci->driver)
+ ci->driver->disconnect(&ci->gadget);
hw_device_state(ci, 0);
if (ci->platdata->notify_event)
ci->platdata->notify_event(ci,
@@ -1633,23 +1636,22 @@ static int ci_udc_start(struct usb_gadget *gadget,
retval = usb_ep_enable(&ci->ep0in->ep);
if (retval)
return retval;
- spin_lock_irqsave(&ci->lock, flags);
ci->driver = driver;
pm_runtime_get_sync(&ci->gadget.dev);
if (ci->vbus_active) {
+ spin_lock_irqsave(&ci->lock, flags);
hw_device_reset(ci, USBMODE_CM_DC);
} else {
pm_runtime_put_sync(&ci->gadget.dev);
- goto done;
+ return retval;
}
retval = hw_device_state(ci, ci->ep0out->qh.dma);
+ spin_unlock_irqrestore(&ci->lock, flags);
if (retval)
pm_runtime_put_sync(&ci->gadget.dev);
- done:
- spin_unlock_irqrestore(&ci->lock, flags);
return retval;
}
@@ -1786,34 +1788,9 @@ static int udc_start(struct ci_hdrc *ci)
ci->gadget.ep0 = &ci->ep0in->ep;
- if (ci->global_phy) {
- ci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
- if (IS_ERR(ci->transceiver))
- ci->transceiver = NULL;
- }
-
- if (ci->platdata->flags & CI_HDRC_REQUIRE_TRANSCEIVER) {
- if (ci->transceiver == NULL) {
- retval = -ENODEV;
- goto destroy_eps;
- }
- }
-
- if (ci->transceiver) {
- retval = otg_set_peripheral(ci->transceiver->otg,
- &ci->gadget);
- /*
- * If we implement all USB functions using chipidea drivers,
- * it doesn't need to call above API, meanwhile, if we only
- * use gadget function, calling above API is useless.
- */
- if (retval && retval != -ENOTSUPP)
- goto put_transceiver;
- }
-
retval = usb_add_gadget_udc(dev, &ci->gadget);
if (retval)
- goto remove_trans;
+ goto destroy_eps;
pm_runtime_no_callbacks(&ci->gadget.dev);
pm_runtime_enable(&ci->gadget.dev);
@@ -1823,17 +1800,6 @@ static int udc_start(struct ci_hdrc *ci)
return retval;
-remove_trans:
- if (ci->transceiver) {
- otg_set_peripheral(ci->transceiver->otg, NULL);
- if (ci->global_phy)
- usb_put_phy(ci->transceiver);
- }
-
- dev_err(dev, "error = %i\n", retval);
-put_transceiver:
- if (ci->transceiver && ci->global_phy)
- usb_put_phy(ci->transceiver);
destroy_eps:
destroy_eps(ci);
free_pools:
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 71dc5d768fa5..967152a63bd3 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -914,10 +914,8 @@ static int proc_control(struct dev_state *ps, void __user *arg)
snoop(&dev->dev, "control urb: bRequestType=%02x "
"bRequest=%02x wValue=%04x "
"wIndex=%04x wLength=%04x\n",
- ctrl.bRequestType, ctrl.bRequest,
- __le16_to_cpup(&ctrl.wValue),
- __le16_to_cpup(&ctrl.wIndex),
- __le16_to_cpup(&ctrl.wLength));
+ ctrl.bRequestType, ctrl.bRequest, ctrl.wValue,
+ ctrl.wIndex, ctrl.wLength);
if (ctrl.bRequestType & 0x80) {
if (ctrl.wLength && !access_ok(VERIFY_WRITE, ctrl.data,
ctrl.wLength)) {
@@ -1636,32 +1634,32 @@ static int proc_reapurbnonblock(struct dev_state *ps, void __user *arg)
static int proc_control_compat(struct dev_state *ps,
struct usbdevfs_ctrltransfer32 __user *p32)
{
- struct usbdevfs_ctrltransfer __user *p;
- __u32 udata;
- p = compat_alloc_user_space(sizeof(*p));
- if (copy_in_user(p, p32, (sizeof(*p32) - sizeof(compat_caddr_t))) ||
- get_user(udata, &p32->data) ||
+ struct usbdevfs_ctrltransfer __user *p;
+ __u32 udata;
+ p = compat_alloc_user_space(sizeof(*p));
+ if (copy_in_user(p, p32, (sizeof(*p32) - sizeof(compat_caddr_t))) ||
+ get_user(udata, &p32->data) ||
put_user(compat_ptr(udata), &p->data))
return -EFAULT;
- return proc_control(ps, p);
+ return proc_control(ps, p);
}
static int proc_bulk_compat(struct dev_state *ps,
struct usbdevfs_bulktransfer32 __user *p32)
{
- struct usbdevfs_bulktransfer __user *p;
- compat_uint_t n;
- compat_caddr_t addr;
+ struct usbdevfs_bulktransfer __user *p;
+ compat_uint_t n;
+ compat_caddr_t addr;
- p = compat_alloc_user_space(sizeof(*p));
+ p = compat_alloc_user_space(sizeof(*p));
- if (get_user(n, &p32->ep) || put_user(n, &p->ep) ||
- get_user(n, &p32->len) || put_user(n, &p->len) ||
- get_user(n, &p32->timeout) || put_user(n, &p->timeout) ||
- get_user(addr, &p32->data) || put_user(compat_ptr(addr), &p->data))
- return -EFAULT;
+ if (get_user(n, &p32->ep) || put_user(n, &p->ep) ||
+ get_user(n, &p32->len) || put_user(n, &p->len) ||
+ get_user(n, &p32->timeout) || put_user(n, &p->timeout) ||
+ get_user(addr, &p32->data) || put_user(compat_ptr(addr), &p->data))
+ return -EFAULT;
- return proc_bulk(ps, p);
+ return proc_bulk(ps, p);
}
static int proc_disconnectsignal_compat(struct dev_state *ps, void __user *arg)
{
diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c
index 7421888087a3..3bdfbf88a0ae 100644
--- a/drivers/usb/core/file.c
+++ b/drivers/usb/core/file.c
@@ -8,7 +8,7 @@
* (C) Copyright Deti Fliegl 1999 (new USB architecture)
* (C) Copyright Randy Dunlap 2000
* (C) Copyright David Brownell 2000-2001 (kernel hotplug, usb_device_id,
- more docs, etc)
+ * more docs, etc)
* (C) Copyright Yggdrasil Computing, Inc. 2000
* (usb_device_id matching changes by Adam J. Richter)
* (C) Copyright Greg Kroah-Hartman 2002-2003
@@ -27,7 +27,7 @@
static const struct file_operations *usb_minors[MAX_USB_MINORS];
static DECLARE_RWSEM(minor_rwsem);
-static int usb_open(struct inode * inode, struct file * file)
+static int usb_open(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
const struct file_operations *c;
@@ -44,7 +44,7 @@ static int usb_open(struct inode * inode, struct file * file)
file->f_op = new_fops;
/* Curiouser and curiouser... NULL ->open() as "no device" ? */
if (file->f_op->open)
- err = file->f_op->open(inode,file);
+ err = file->f_op->open(inode, file);
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
@@ -166,7 +166,7 @@ int usb_register_dev(struct usb_interface *intf,
char *temp;
#ifdef CONFIG_USB_DYNAMIC_MINORS
- /*
+ /*
* We don't care what the device tries to start at, we want to start
* at zero to pack the devices into the smallest available space with
* no holes in the minor range.
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index b9d3c43e3859..dfe9d0f22978 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -215,6 +215,9 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
goto disable_pci;
}
+ hcd->amd_resume_bug = (usb_hcd_amd_remote_wakeup_quirk(dev) &&
+ driver->flags & (HCD_USB11 | HCD_USB3)) ? 1 : 0;
+
if (driver->flags & HCD_MEMORY) {
/* EHCI, OHCI */
hcd->rsrc_start = pci_resource_start(dev, 0);
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index d6a8d23f047b..6bffb8c87bc9 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -6,7 +6,7 @@
* (C) Copyright Deti Fliegl 1999
* (C) Copyright Randy Dunlap 2000
* (C) Copyright David Brownell 2000-2002
- *
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
@@ -40,6 +40,7 @@
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/pm_runtime.h>
+#include <linux/types.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
@@ -92,10 +93,7 @@ EXPORT_SYMBOL_GPL (usb_bus_list);
/* used when allocating bus numbers */
#define USB_MAXBUS 64
-struct usb_busmap {
- unsigned long busmap [USB_MAXBUS / (8*sizeof (unsigned long))];
-};
-static struct usb_busmap busmap;
+static DECLARE_BITMAP(busmap, USB_MAXBUS);
/* used when updating list of hcds */
DEFINE_MUTEX(usb_bus_list_lock); /* exported only for usbfs */
@@ -171,7 +169,7 @@ static const u8 usb25_rh_dev_descriptor[18] = {
};
/* usb 2.0 root hub device descriptor */
-static const u8 usb2_rh_dev_descriptor [18] = {
+static const u8 usb2_rh_dev_descriptor[18] = {
0x12, /* __u8 bLength; */
0x01, /* __u8 bDescriptorType; Device */
0x00, 0x02, /* __le16 bcdUSB; v2.0 */
@@ -194,7 +192,7 @@ static const u8 usb2_rh_dev_descriptor [18] = {
/* no usb 2.0 root hub "device qualifier" descriptor: one speed only */
/* usb 1.1 root hub device descriptor */
-static const u8 usb11_rh_dev_descriptor [18] = {
+static const u8 usb11_rh_dev_descriptor[18] = {
0x12, /* __u8 bLength; */
0x01, /* __u8 bDescriptorType; Device */
0x10, 0x01, /* __le16 bcdUSB; v1.1 */
@@ -219,7 +217,7 @@ static const u8 usb11_rh_dev_descriptor [18] = {
/* Configuration descriptors for our root hubs */
-static const u8 fs_rh_config_descriptor [] = {
+static const u8 fs_rh_config_descriptor[] = {
/* one configuration */
0x09, /* __u8 bLength; */
@@ -228,13 +226,13 @@ static const u8 fs_rh_config_descriptor [] = {
0x01, /* __u8 bNumInterfaces; (1) */
0x01, /* __u8 bConfigurationValue; */
0x00, /* __u8 iConfiguration; */
- 0xc0, /* __u8 bmAttributes;
+ 0xc0, /* __u8 bmAttributes;
Bit 7: must be set,
6: Self-powered,
5: Remote wakeup,
4..0: resvd */
0x00, /* __u8 MaxPower; */
-
+
/* USB 1.1:
* USB 2.0, single TT organization (mandatory):
* one interface, protocol 0
@@ -256,17 +254,17 @@ static const u8 fs_rh_config_descriptor [] = {
0x00, /* __u8 if_bInterfaceSubClass; */
0x00, /* __u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
0x00, /* __u8 if_iInterface; */
-
+
/* one endpoint (status change endpoint) */
0x07, /* __u8 ep_bLength; */
0x05, /* __u8 ep_bDescriptorType; Endpoint */
0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
- 0x03, /* __u8 ep_bmAttributes; Interrupt */
- 0x02, 0x00, /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
+ 0x03, /* __u8 ep_bmAttributes; Interrupt */
+ 0x02, 0x00, /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
0xff /* __u8 ep_bInterval; (255ms -- usb 2.0 spec) */
};
-static const u8 hs_rh_config_descriptor [] = {
+static const u8 hs_rh_config_descriptor[] = {
/* one configuration */
0x09, /* __u8 bLength; */
@@ -275,13 +273,13 @@ static const u8 hs_rh_config_descriptor [] = {
0x01, /* __u8 bNumInterfaces; (1) */
0x01, /* __u8 bConfigurationValue; */
0x00, /* __u8 iConfiguration; */
- 0xc0, /* __u8 bmAttributes;
+ 0xc0, /* __u8 bmAttributes;
Bit 7: must be set,
6: Self-powered,
5: Remote wakeup,
4..0: resvd */
0x00, /* __u8 MaxPower; */
-
+
/* USB 1.1:
* USB 2.0, single TT organization (mandatory):
* one interface, protocol 0
@@ -303,12 +301,12 @@ static const u8 hs_rh_config_descriptor [] = {
0x00, /* __u8 if_bInterfaceSubClass; */
0x00, /* __u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
0x00, /* __u8 if_iInterface; */
-
+
/* one endpoint (status change endpoint) */
0x07, /* __u8 ep_bLength; */
0x05, /* __u8 ep_bDescriptorType; Endpoint */
0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
- 0x03, /* __u8 ep_bmAttributes; Interrupt */
+ 0x03, /* __u8 ep_bmAttributes; Interrupt */
/* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8)
* see hub.c:hub_configure() for details. */
(USB_MAXCHILDREN + 1 + 7) / 8, 0x00,
@@ -428,7 +426,7 @@ rh_string(int id, struct usb_hcd const *hcd, u8 *data, unsigned len)
char const *s;
static char const langids[4] = {4, USB_DT_STRING, 0x09, 0x04};
- // language ids
+ /* language ids */
switch (id) {
case 0:
/* Array of LANGID codes (0x0409 is MSFT-speak for "en-us") */
@@ -464,7 +462,7 @@ rh_string(int id, struct usb_hcd const *hcd, u8 *data, unsigned len)
static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
{
struct usb_ctrlrequest *cmd;
- u16 typeReq, wValue, wIndex, wLength;
+ u16 typeReq, wValue, wIndex, wLength;
u8 *ubuf = urb->transfer_buffer;
unsigned len = 0;
int status;
@@ -526,10 +524,10 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
*/
case DeviceRequest | USB_REQ_GET_STATUS:
- tbuf [0] = (device_may_wakeup(&hcd->self.root_hub->dev)
+ tbuf[0] = (device_may_wakeup(&hcd->self.root_hub->dev)
<< USB_DEVICE_REMOTE_WAKEUP)
| (1 << USB_DEVICE_SELF_POWERED);
- tbuf [1] = 0;
+ tbuf[1] = 0;
len = 2;
break;
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
@@ -546,7 +544,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
goto error;
break;
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
- tbuf [0] = 1;
+ tbuf[0] = 1;
len = 1;
/* FALLTHROUGH */
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
@@ -609,13 +607,13 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
}
break;
case DeviceRequest | USB_REQ_GET_INTERFACE:
- tbuf [0] = 0;
+ tbuf[0] = 0;
len = 1;
/* FALLTHROUGH */
case DeviceOutRequest | USB_REQ_SET_INTERFACE:
break;
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
- // wValue == urb->dev->devaddr
+ /* wValue == urb->dev->devaddr */
dev_dbg (hcd->self.controller, "root hub device address %d\n",
wValue);
break;
@@ -625,9 +623,9 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
/* ENDPOINT REQUESTS */
case EndpointRequest | USB_REQ_GET_STATUS:
- // ENDPOINT_HALT flag
- tbuf [0] = 0;
- tbuf [1] = 0;
+ /* ENDPOINT_HALT flag */
+ tbuf[0] = 0;
+ tbuf[1] = 0;
len = 2;
/* FALLTHROUGH */
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
@@ -683,7 +681,7 @@ error:
if (urb->transfer_buffer_length < len)
len = urb->transfer_buffer_length;
urb->actual_length = len;
- // always USB_DIR_IN, toward host
+ /* always USB_DIR_IN, toward host */
memcpy (ubuf, bufp, len);
/* report whether RH hardware supports remote wakeup */
@@ -877,11 +875,11 @@ static ssize_t authorized_default_store(struct device *dev,
usb_hcd = bus_to_hcd(usb_bus);
result = sscanf(buf, "%u\n", &val);
if (result == 1) {
- usb_hcd->authorized_default = val? 1 : 0;
+ usb_hcd->authorized_default = val ? 1 : 0;
result = size;
- }
- else
+ } else {
result = -EINVAL;
+ }
return result;
}
static DEVICE_ATTR_RW(authorized_default);
@@ -941,12 +939,12 @@ static int usb_register_bus(struct usb_bus *bus)
int busnum;
mutex_lock(&usb_bus_list_lock);
- busnum = find_next_zero_bit (busmap.busmap, USB_MAXBUS, 1);
+ busnum = find_next_zero_bit(busmap, USB_MAXBUS, 1);
if (busnum >= USB_MAXBUS) {
printk (KERN_ERR "%s: too many buses\n", usbcore_name);
goto error_find_busnum;
}
- set_bit (busnum, busmap.busmap);
+ set_bit(busnum, busmap);
bus->busnum = busnum;
/* Add it to the local list of buses */
@@ -987,7 +985,7 @@ static void usb_deregister_bus (struct usb_bus *bus)
usb_notify_remove_bus(bus);
- clear_bit (bus->busnum, busmap.busmap);
+ clear_bit(bus->busnum, busmap);
}
/**
@@ -1033,6 +1031,7 @@ static int register_root_hub(struct usb_hcd *hcd)
dev_name(&usb_dev->dev), retval);
return retval;
}
+ usb_dev->lpm_capable = usb_device_supports_lpm(usb_dev);
}
retval = usb_new_device (usb_dev);
@@ -1120,21 +1119,21 @@ long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount)
case USB_SPEED_LOW: /* INTR only */
if (is_input) {
tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L;
- return (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
+ return 64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp;
} else {
tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L;
- return (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
+ return 64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp;
}
case USB_SPEED_FULL: /* ISOC or INTR */
if (isoc) {
tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
- return (((is_input) ? 7268L : 6265L) + BW_HOST_DELAY + tmp);
+ return ((is_input) ? 7268L : 6265L) + BW_HOST_DELAY + tmp;
} else {
tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
- return (9107L + BW_HOST_DELAY + tmp);
+ return 9107L + BW_HOST_DELAY + tmp;
}
case USB_SPEED_HIGH: /* ISOC or INTR */
- // FIXME adjust for input vs output
+ /* FIXME adjust for input vs output */
if (isoc)
tmp = HS_NSECS_ISO (bytecount);
else
@@ -1651,6 +1650,7 @@ int usb_hcd_unlink_urb (struct urb *urb, int status)
static void __usb_hcd_giveback_urb(struct urb *urb)
{
struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
+ struct usb_anchor *anchor = urb->anchor;
int status = urb->unlinked;
unsigned long flags;
@@ -1662,6 +1662,7 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
unmap_urb_for_dma(hcd, urb);
usbmon_urb_complete(&hcd->self, urb, status);
+ usb_anchor_suspend_wakeups(anchor);
usb_unanchor_urb(urb);
/* pass ownership to the completion handler */
@@ -1681,6 +1682,7 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
urb->complete(urb);
local_irq_restore(flags);
+ usb_anchor_resume_wakeups(anchor);
atomic_dec(&urb->use_count);
if (unlikely(atomic_read(&urb->reject)))
wake_up(&usb_kill_urb_queue);
@@ -1703,7 +1705,9 @@ static void usb_giveback_urb_bh(unsigned long param)
urb = list_entry(local_list.next, struct urb, urb_list);
list_del_init(&urb->urb_list);
+ bh->completing_ep = urb->ep;
__usb_hcd_giveback_urb(urb);
+ bh->completing_ep = NULL;
}
/* check if there are new URBs to giveback */
@@ -1812,7 +1816,7 @@ rescan:
case USB_ENDPOINT_XFER_INT:
s = "-intr"; break;
default:
- s = "-iso"; break;
+ s = "-iso"; break;
};
s;
}));
@@ -2073,8 +2077,11 @@ EXPORT_SYMBOL_GPL(usb_alloc_streams);
*
* Reverts a group of bulk endpoints back to not using stream IDs.
* Can fail if we are given bad arguments, or HCD is broken.
+ *
+ * Return: On success, the number of allocated streams. On failure, a negative
+ * error code.
*/
-void usb_free_streams(struct usb_interface *interface,
+int usb_free_streams(struct usb_interface *interface,
struct usb_host_endpoint **eps, unsigned int num_eps,
gfp_t mem_flags)
{
@@ -2085,14 +2092,14 @@ void usb_free_streams(struct usb_interface *interface,
dev = interface_to_usbdev(interface);
hcd = bus_to_hcd(dev->bus);
if (dev->speed != USB_SPEED_SUPER)
- return;
+ return -EINVAL;
/* Streams only apply to bulk endpoints. */
for (i = 0; i < num_eps; i++)
if (!eps[i] || !usb_endpoint_xfer_bulk(&eps[i]->desc))
- return;
+ return -EINVAL;
- hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags);
+ return hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags);
}
EXPORT_SYMBOL_GPL(usb_free_streams);
@@ -2245,7 +2252,7 @@ static void hcd_resume_work(struct work_struct *work)
}
/**
- * usb_hcd_resume_root_hub - called by HCD to resume its root hub
+ * usb_hcd_resume_root_hub - called by HCD to resume its root hub
* @hcd: host controller for this root hub
*
* The USB host controller calls this function when its root hub is
@@ -2324,15 +2331,8 @@ EXPORT_SYMBOL_GPL(usb_bus_start_enum);
irqreturn_t usb_hcd_irq (int irq, void *__hcd)
{
struct usb_hcd *hcd = __hcd;
- unsigned long flags;
irqreturn_t rc;
- /* IRQF_DISABLED doesn't work correctly with shared IRQs
- * when the first handler doesn't use it. So let's just
- * assume it's never used.
- */
- local_irq_save(flags);
-
if (unlikely(HCD_DEAD(hcd) || !HCD_HW_ACCESSIBLE(hcd)))
rc = IRQ_NONE;
else if (hcd->driver->irq(hcd) == IRQ_NONE)
@@ -2340,7 +2340,6 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd)
else
rc = IRQ_HANDLED;
- local_irq_restore(flags);
return rc;
}
EXPORT_SYMBOL_GPL(usb_hcd_irq);
@@ -2547,13 +2546,6 @@ static int usb_hcd_request_irqs(struct usb_hcd *hcd,
if (hcd->driver->irq) {
- /* IRQF_DISABLED doesn't work as advertised when used together
- * with IRQF_SHARED. As usb_hcd_irq() will always disable
- * interrupts we can remove it here.
- */
- if (irqflags & IRQF_SHARED)
- irqflags &= ~IRQF_DISABLED;
-
snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",
hcd->driver->description, hcd->self.busnum);
retval = request_irq(irqnum, &usb_hcd_irq, irqflags,
@@ -2600,7 +2592,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
/* Keep old behaviour if authorized_default is not in [0, 1]. */
if (authorized_default < 0 || authorized_default > 1)
- hcd->authorized_default = hcd->wireless? 0 : 1;
+ hcd->authorized_default = hcd->wireless ? 0 : 1;
else
hcd->authorized_default = authorized_default;
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
@@ -2743,7 +2735,7 @@ err_allocate_root_hub:
err_register_bus:
hcd_buffer_destroy(hcd);
return retval;
-}
+}
EXPORT_SYMBOL_GPL(usb_add_hcd);
/**
@@ -2818,7 +2810,7 @@ void usb_remove_hcd(struct usb_hcd *hcd)
EXPORT_SYMBOL_GPL(usb_remove_hcd);
void
-usb_hcd_platform_shutdown(struct platform_device* dev)
+usb_hcd_platform_shutdown(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
@@ -2840,7 +2832,7 @@ struct usb_mon_operations *mon_ops;
* Notice that the code is minimally error-proof. Because usbmon needs
* symbols from usbcore, usbcore gets referenced and cannot be unloaded first.
*/
-
+
int usb_mon_register (struct usb_mon_operations *ops)
{
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index e6b682c6c236..92dde941fdbe 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -120,7 +120,7 @@ static inline char *portspeed(struct usb_hub *hub, int portstatus)
if (hub_is_superspeed(hub->hdev))
return "5.0 Gb/s";
if (portstatus & USB_PORT_STAT_HIGH_SPEED)
- return "480 Mb/s";
+ return "480 Mb/s";
else if (portstatus & USB_PORT_STAT_LOW_SPEED)
return "1.5 Mb/s";
else
@@ -135,7 +135,7 @@ struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev)
return usb_get_intfdata(hdev->actconfig->interface[0]);
}
-static int usb_device_supports_lpm(struct usb_device *udev)
+int usb_device_supports_lpm(struct usb_device *udev)
{
/* USB 2.1 (and greater) devices indicate LPM support through
* their USB 2.0 Extended Capabilities BOS descriptor.
@@ -156,6 +156,11 @@ static int usb_device_supports_lpm(struct usb_device *udev)
"Power management will be impacted.\n");
return 0;
}
+
+ /* udev is root hub */
+ if (!udev->parent)
+ return 1;
+
if (udev->parent->lpm_capable)
return 1;
@@ -310,9 +315,9 @@ static void usb_set_lpm_parameters(struct usb_device *udev)
return;
udev_u1_del = udev->bos->ss_cap->bU1devExitLat;
- udev_u2_del = udev->bos->ss_cap->bU2DevExitLat;
+ udev_u2_del = le16_to_cpu(udev->bos->ss_cap->bU2DevExitLat);
hub_u1_del = udev->parent->bos->ss_cap->bU1devExitLat;
- hub_u2_del = udev->parent->bos->ss_cap->bU2DevExitLat;
+ hub_u2_del = le16_to_cpu(udev->parent->bos->ss_cap->bU2DevExitLat);
usb_set_lpm_mel(udev, &udev->u1_params, udev_u1_del,
hub, &udev->parent->u1_params, hub_u1_del);
@@ -433,7 +438,7 @@ static void set_port_led(
case HUB_LED_OFF: s = "off"; break;
case HUB_LED_AUTO: s = "auto"; break;
default: s = "??"; break;
- }; s; }),
+ } s; }),
status);
}
@@ -857,7 +862,7 @@ static int hub_hub_status(struct usb_hub *hub,
"%s failed (err = %d)\n", __func__, ret);
} else {
*status = le16_to_cpu(hub->status->hub.wHubStatus);
- *change = le16_to_cpu(hub->status->hub.wHubChange);
+ *change = le16_to_cpu(hub->status->hub.wHubChange);
ret = 0;
}
mutex_unlock(&hub->status_mutex);
@@ -956,7 +961,7 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
*/
set_bit(port1, hub->change_bits);
- kick_khubd(hub);
+ kick_khubd(hub);
}
/**
@@ -1130,6 +1135,11 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
usb_clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_ENABLE);
}
+ if (portchange & USB_PORT_STAT_C_RESET) {
+ need_debounce_delay = true;
+ usb_clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_RESET);
+ }
if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
hub_is_superspeed(hub->hdev)) {
need_debounce_delay = true;
@@ -1361,7 +1371,7 @@ static int hub_configure(struct usb_hub *hub,
if ((wHubCharacteristics & HUB_CHAR_COMPOUND) &&
!(hub_is_superspeed(hdev))) {
int i;
- char portstr [USB_MAXCHILDREN + 1];
+ char portstr[USB_MAXCHILDREN + 1];
for (i = 0; i < hdev->maxchild; i++)
portstr[i] = hub->descriptor->u.hs.DeviceRemovable
@@ -1429,32 +1439,32 @@ static int hub_configure(struct usb_hub *hub,
/* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */
switch (wHubCharacteristics & HUB_CHAR_TTTT) {
- case HUB_TTTT_8_BITS:
- if (hdev->descriptor.bDeviceProtocol != 0) {
- hub->tt.think_time = 666;
- dev_dbg(hub_dev, "TT requires at most %d "
- "FS bit times (%d ns)\n",
- 8, hub->tt.think_time);
- }
- break;
- case HUB_TTTT_16_BITS:
- hub->tt.think_time = 666 * 2;
+ case HUB_TTTT_8_BITS:
+ if (hdev->descriptor.bDeviceProtocol != 0) {
+ hub->tt.think_time = 666;
dev_dbg(hub_dev, "TT requires at most %d "
"FS bit times (%d ns)\n",
- 16, hub->tt.think_time);
- break;
- case HUB_TTTT_24_BITS:
- hub->tt.think_time = 666 * 3;
- dev_dbg(hub_dev, "TT requires at most %d "
- "FS bit times (%d ns)\n",
- 24, hub->tt.think_time);
- break;
- case HUB_TTTT_32_BITS:
- hub->tt.think_time = 666 * 4;
- dev_dbg(hub_dev, "TT requires at most %d "
- "FS bit times (%d ns)\n",
- 32, hub->tt.think_time);
- break;
+ 8, hub->tt.think_time);
+ }
+ break;
+ case HUB_TTTT_16_BITS:
+ hub->tt.think_time = 666 * 2;
+ dev_dbg(hub_dev, "TT requires at most %d "
+ "FS bit times (%d ns)\n",
+ 16, hub->tt.think_time);
+ break;
+ case HUB_TTTT_24_BITS:
+ hub->tt.think_time = 666 * 3;
+ dev_dbg(hub_dev, "TT requires at most %d "
+ "FS bit times (%d ns)\n",
+ 24, hub->tt.think_time);
+ break;
+ case HUB_TTTT_32_BITS:
+ hub->tt.think_time = 666 * 4;
+ dev_dbg(hub_dev, "TT requires at most %d "
+ "FS bit times (%d ns)\n",
+ 32, hub->tt.think_time);
+ break;
}
/* probe() zeroes hub->indicator[] */
@@ -1560,7 +1570,7 @@ static int hub_configure(struct usb_hub *hub,
/* maybe cycle the hub leds */
if (hub->has_indicators && blinkenlights)
- hub->indicator [0] = INDICATOR_CYCLE;
+ hub->indicator[0] = INDICATOR_CYCLE;
for (i = 0; i < hdev->maxchild; i++) {
ret = usb_hub_create_port_device(hub, i + 1);
@@ -1978,7 +1988,7 @@ static void choose_devnum(struct usb_device *udev)
if (devnum >= 128)
devnum = find_next_zero_bit(bus->devmap.devicemap,
128, 1);
- bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1);
+ bus->devnum_next = (devnum >= 127 ? 1 : devnum + 1);
}
if (devnum < 128) {
set_bit(devnum, bus->devmap.devicemap);
@@ -2018,8 +2028,8 @@ static void hub_free_dev(struct usb_device *udev)
* Something got disconnected. Get rid of it and all of its children.
*
* If *pdev is a normal device then the parent hub must already be locked.
- * If *pdev is a root hub then this routine will acquire the
- * usb_bus_list_lock on behalf of the caller.
+ * If *pdev is a root hub then the caller must hold the usb_bus_list_lock,
+ * which protects the set of root hubs as well as the list of buses.
*
* Only hub drivers (including virtual root hub drivers for host
* controllers) should ever call this.
@@ -2232,8 +2242,7 @@ static int usb_enumerate_device(struct usb_device *udev)
udev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL);
udev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL);
udev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL);
- }
- else {
+ } else {
/* read the standard strings and cache them if present */
udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
udev->manufacturer = usb_cache_string(udev,
@@ -2489,7 +2498,7 @@ error_device_descriptor:
usb_autosuspend_device(usb_dev);
error_autoresume:
out_authorized:
- usb_unlock_device(usb_dev); // complements locktree
+ usb_unlock_device(usb_dev); /* complements locktree */
return result;
}
@@ -3108,8 +3117,8 @@ static int finish_port_resume(struct usb_device *udev)
retry_reset_resume:
status = usb_reset_and_verify_device(udev);
- /* 10.5.4.5 says be sure devices in the tree are still there.
- * For now let's assume the device didn't go crazy on resume,
+ /* 10.5.4.5 says be sure devices in the tree are still there.
+ * For now let's assume the device didn't go crazy on resume,
* and device drivers will know about any resume quirks.
*/
if (status == 0) {
@@ -3211,7 +3220,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
if (status == 0 && !port_is_suspended(hub, portstatus))
goto SuspendCleared;
- // dev_dbg(hub->intfdev, "resume port %d\n", port1);
+ /* dev_dbg(hub->intfdev, "resume port %d\n", port1); */
set_bit(port1, hub->busy_bits);
@@ -3855,7 +3864,7 @@ EXPORT_SYMBOL_GPL(usb_enable_ltm);
* Between connect detection and reset signaling there must be a delay
* of 100ms at least for debounce and power-settling. The corresponding
* timer shall restart whenever the downstream port detects a disconnect.
- *
+ *
* Apparently there are some bluetooth and irda-dongles and a number of
* low-speed devices for which this debounce period may last over a second.
* Not covered by the spec - but easy to deal with.
@@ -4055,7 +4064,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
udev->tt = &hub->tt;
udev->ttport = port1;
}
-
+
/* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?
* Because device hardware and firmware is sometimes buggy in
* this area, and this is how Linux has done it for ages.
@@ -4130,11 +4139,11 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
#undef GET_DESCRIPTOR_BUFSIZE
}
- /*
- * If device is WUSB, we already assigned an
- * unauthorized address in the Connect Ack sequence;
- * authorization will assign the final address.
- */
+ /*
+ * If device is WUSB, we already assigned an
+ * unauthorized address in the Connect Ack sequence;
+ * authorization will assign the final address.
+ */
if (udev->wusb == 0) {
for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
retval = hub_set_address(udev, devnum);
@@ -4163,7 +4172,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
msleep(10);
if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3))
break;
- }
+ }
retval = usb_get_device_descriptor(udev, 8);
if (retval < 8) {
@@ -4219,7 +4228,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);
usb_ep0_reinit(udev);
}
-
+
retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
if (retval < (signed)sizeof(udev->descriptor)) {
if (retval != -ENODEV)
@@ -4316,7 +4325,7 @@ hub_power_remaining (struct usb_hub *hub)
}
if (remaining < 0) {
dev_warn(hub->intfdev, "%dmA over power budget!\n",
- - remaining);
+ -remaining);
remaining = 0;
}
return remaining;
@@ -4427,7 +4436,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
if (portstatus & USB_PORT_STAT_ENABLE)
- goto done;
+ goto done;
return;
}
if (hub_is_superspeed(hub->hdev))
@@ -4450,7 +4459,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
}
usb_set_device_state(udev, USB_STATE_POWERED);
- udev->bus_mA = hub->mA_per_port;
+ udev->bus_mA = hub->mA_per_port;
udev->level = hdev->level + 1;
udev->wusb = hub_is_wusb(hub);
@@ -4504,7 +4513,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
goto loop_disable;
}
}
-
+
/* check for devices running slower than they could */
if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200
&& udev->speed == USB_SPEED_FULL
@@ -4564,7 +4573,7 @@ loop:
dev_err(hub_dev, "unable to enumerate USB device on port %d\n",
port1);
}
-
+
done:
hub_port_disable(hub, port1, 1);
if (hcd->driver->relinquish_port && !hub->hdev->parent)
@@ -4729,7 +4738,7 @@ static void hub_events(void)
* EM interference sometimes causes badly
* shielded USB devices to be shutdown by
* the hub, this hack enables them again.
- * Works at least with mouse driver.
+ * Works at least with mouse driver.
*/
if (!(portstatus & USB_PORT_STAT_ENABLE)
&& !connect_change
@@ -4841,7 +4850,7 @@ static void hub_events(void)
dev_dbg(hub_dev, "over-current change\n");
clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
msleep(500); /* Cool down */
- hub_power_on(hub, true);
+ hub_power_on(hub, true);
hub_hub_status(hub, &status, &unused);
if (status & HUB_STATUS_OVERCURRENT)
dev_err(hub_dev, "over-current "
@@ -4861,7 +4870,7 @@ static void hub_events(void)
usb_unlock_device(hdev);
kref_put(&hub->kref, hub_release);
- } /* end while (1) */
+ } /* end while (1) */
}
static int hub_thread(void *__unused)
@@ -4886,7 +4895,7 @@ static int hub_thread(void *__unused)
static const struct usb_device_id hub_id_table[] = {
{ .match_flags = USB_DEVICE_ID_MATCH_VENDOR
- | USB_DEVICE_ID_MATCH_INT_CLASS,
+ | USB_DEVICE_ID_MATCH_INT_CLASS,
.idVendor = USB_VENDOR_GENESYS_LOGIC,
.bInterfaceClass = USB_CLASS_HUB,
.driver_info = HUB_QUIRK_CHECK_PORT_AUTOSUSPEND},
@@ -5120,13 +5129,13 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
if (ret < 0)
goto re_enumerate;
-
+
/* Device might have changed firmware (DFU or similar) */
if (descriptors_changed(udev, &descriptor, bos)) {
dev_info(&udev->dev, "device firmware changed\n");
udev->descriptor = descriptor; /* for disconnect() calls */
goto re_enumerate;
- }
+ }
/* Restore the device's previous configuration */
if (!udev->actconfig)
@@ -5151,7 +5160,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
udev->actconfig->desc.bConfigurationValue, ret);
mutex_unlock(hcd->bandwidth_mutex);
goto re_enumerate;
- }
+ }
mutex_unlock(hcd->bandwidth_mutex);
usb_set_device_state(udev, USB_STATE_CONFIGURED);
@@ -5198,7 +5207,7 @@ done:
usb_release_bos_descriptor(udev);
udev->bos = bos;
return 0;
-
+
re_enumerate:
/* LPM state doesn't matter when we're about to destroy the device. */
hub_port_logical_disconnect(parent_hub, port1);
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 01fe36273f3b..12924dbfdc2c 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -13,6 +13,7 @@
#include <linux/usb.h>
#include <linux/usb/quirks.h>
+#include <linux/usb/hcd.h>
#include "usb.h"
/* Lists of quirky USB devices, split in device quirks and interface quirks.
@@ -161,6 +162,21 @@ static const struct usb_device_id usb_interface_quirk_list[] = {
{ } /* terminating entry must be last */
};
+static const struct usb_device_id usb_amd_resume_quirk_list[] = {
+ /* Lenovo Mouse with Pixart controller */
+ { USB_DEVICE(0x17ef, 0x602e), .driver_info = USB_QUIRK_RESET_RESUME },
+
+ /* Pixart Mouse */
+ { USB_DEVICE(0x093a, 0x2500), .driver_info = USB_QUIRK_RESET_RESUME },
+ { USB_DEVICE(0x093a, 0x2510), .driver_info = USB_QUIRK_RESET_RESUME },
+ { USB_DEVICE(0x093a, 0x2521), .driver_info = USB_QUIRK_RESET_RESUME },
+
+ /* Logitech Optical Mouse M90/M100 */
+ { USB_DEVICE(0x046d, 0xc05a), .driver_info = USB_QUIRK_RESET_RESUME },
+
+ { } /* terminating entry must be last */
+};
+
static bool usb_match_any_interface(struct usb_device *udev,
const struct usb_device_id *id)
{
@@ -187,6 +203,18 @@ static bool usb_match_any_interface(struct usb_device *udev,
return false;
}
+static int usb_amd_resume_quirk(struct usb_device *udev)
+{
+ struct usb_hcd *hcd;
+
+ hcd = bus_to_hcd(udev->bus);
+ /* The device should be attached directly to root hub */
+ if (udev->level == 1 && hcd->amd_resume_bug == 1)
+ return 1;
+
+ return 0;
+}
+
static u32 __usb_detect_quirks(struct usb_device *udev,
const struct usb_device_id *id)
{
@@ -212,6 +240,15 @@ static u32 __usb_detect_quirks(struct usb_device *udev,
void usb_detect_quirks(struct usb_device *udev)
{
udev->quirks = __usb_detect_quirks(udev, usb_quirk_list);
+
+ /*
+ * Pixart-based mice would trigger remote wakeup issue on AMD
+ * Yangtze chipset, so set them as RESET_RESUME flag.
+ */
+ if (usb_amd_resume_quirk(udev))
+ udev->quirks |= __usb_detect_quirks(udev,
+ usb_amd_resume_quirk_list);
+
if (udev->quirks)
dev_dbg(&udev->dev, "USB quirks for this device: %x\n",
udev->quirks);
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 6d2c8edb1ffe..5cf431b0424c 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -23,14 +23,16 @@ static ssize_t field##_show(struct device *dev, \
{ \
struct usb_device *udev; \
struct usb_host_config *actconfig; \
+ ssize_t rc = 0; \
\
udev = to_usb_device(dev); \
+ usb_lock_device(udev); \
actconfig = udev->actconfig; \
if (actconfig) \
- return sprintf(buf, format_string, \
+ rc = sprintf(buf, format_string, \
actconfig->desc.field); \
- else \
- return 0; \
+ usb_unlock_device(udev); \
+ return rc; \
} \
#define usb_actconfig_attr(field, format_string) \
@@ -45,12 +47,15 @@ static ssize_t bMaxPower_show(struct device *dev,
{
struct usb_device *udev;
struct usb_host_config *actconfig;
+ ssize_t rc = 0;
udev = to_usb_device(dev);
+ usb_lock_device(udev);
actconfig = udev->actconfig;
- if (!actconfig)
- return 0;
- return sprintf(buf, "%dmA\n", usb_get_max_power(udev, actconfig));
+ if (actconfig)
+ rc = sprintf(buf, "%dmA\n", usb_get_max_power(udev, actconfig));
+ usb_unlock_device(udev);
+ return rc;
}
static DEVICE_ATTR_RO(bMaxPower);
@@ -59,12 +64,15 @@ static ssize_t configuration_show(struct device *dev,
{
struct usb_device *udev;
struct usb_host_config *actconfig;
+ ssize_t rc = 0;
udev = to_usb_device(dev);
+ usb_lock_device(udev);
actconfig = udev->actconfig;
- if ((!actconfig) || (!actconfig->string))
- return 0;
- return sprintf(buf, "%s\n", actconfig->string);
+ if (actconfig && actconfig->string)
+ rc = sprintf(buf, "%s\n", actconfig->string);
+ usb_unlock_device(udev);
+ return rc;
}
static DEVICE_ATTR_RO(configuration);
@@ -390,7 +398,8 @@ static DEVICE_ATTR_RW(autosuspend);
static const char on_string[] = "on";
static const char auto_string[] = "auto";
-static void warn_level(void) {
+static void warn_level(void)
+{
static int level_warned;
if (!level_warned) {
@@ -644,7 +653,7 @@ static ssize_t authorized_store(struct device *dev,
result = usb_deauthorize_device(usb_dev);
else
result = usb_authorize_device(usb_dev);
- return result < 0? result : size;
+ return result < 0 ? result : size;
}
static DEVICE_ATTR_IGNORE_LOCKDEP(authorized, S_IRUGO | S_IWUSR,
authorized_show, authorized_store);
@@ -764,6 +773,7 @@ read_descriptors(struct file *filp, struct kobject *kobj,
* Following that are the raw descriptor entries for all the
* configurations (config plus subsidiary descriptors).
*/
+ usb_lock_device(udev);
for (cfgno = -1; cfgno < udev->descriptor.bNumConfigurations &&
nleft > 0; ++cfgno) {
if (cfgno < 0) {
@@ -784,6 +794,7 @@ read_descriptors(struct file *filp, struct kobject *kobj,
off -= srclen;
}
}
+ usb_unlock_device(udev);
return count - nleft;
}
@@ -870,9 +881,7 @@ static ssize_t interface_show(struct device *dev, struct device_attribute *attr,
char *string;
intf = to_usb_interface(dev);
- string = intf->cur_altsetting->string;
- barrier(); /* The altsetting might change! */
-
+ string = ACCESS_ONCE(intf->cur_altsetting->string);
if (!string)
return 0;
return sprintf(buf, "%s\n", string);
@@ -888,7 +897,7 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
intf = to_usb_interface(dev);
udev = interface_to_usbdev(intf);
- alt = intf->cur_altsetting;
+ alt = ACCESS_ONCE(intf->cur_altsetting);
return sprintf(buf, "usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02X"
"ic%02Xisc%02Xip%02Xin%02X\n",
@@ -909,23 +918,14 @@ static ssize_t supports_autosuspend_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct usb_interface *intf;
- struct usb_device *udev;
- int ret;
-
- intf = to_usb_interface(dev);
- udev = interface_to_usbdev(intf);
+ int s;
- usb_lock_device(udev);
+ device_lock(dev);
/* Devices will be autosuspended even when an interface isn't claimed */
- if (!intf->dev.driver ||
- to_usb_driver(intf->dev.driver)->supports_autosuspend)
- ret = sprintf(buf, "%u\n", 1);
- else
- ret = sprintf(buf, "%u\n", 0);
- usb_unlock_device(udev);
+ s = (!dev->driver || to_usb_driver(dev->driver)->supports_autosuspend);
+ device_unlock(dev);
- return ret;
+ return sprintf(buf, "%u\n", s);
}
static DEVICE_ATTR_RO(supports_autosuspend);
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index c12bc790a6a7..e62208356c89 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -138,13 +138,19 @@ void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor)
}
EXPORT_SYMBOL_GPL(usb_anchor_urb);
+static int usb_anchor_check_wakeup(struct usb_anchor *anchor)
+{
+ return atomic_read(&anchor->suspend_wakeups) == 0 &&
+ list_empty(&anchor->urb_list);
+}
+
/* Callers must hold anchor->lock */
static void __usb_unanchor_urb(struct urb *urb, struct usb_anchor *anchor)
{
urb->anchor = NULL;
list_del(&urb->anchor_list);
usb_put_urb(urb);
- if (list_empty(&anchor->urb_list))
+ if (usb_anchor_check_wakeup(anchor))
wake_up(&anchor->wait);
}
@@ -846,6 +852,39 @@ void usb_unlink_anchored_urbs(struct usb_anchor *anchor)
EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
/**
+ * usb_anchor_suspend_wakeups
+ * @anchor: the anchor you want to suspend wakeups on
+ *
+ * Call this to stop the last urb being unanchored from waking up any
+ * usb_wait_anchor_empty_timeout waiters. This is used in the hcd urb give-
+ * back path to delay waking up until after the completion handler has run.
+ */
+void usb_anchor_suspend_wakeups(struct usb_anchor *anchor)
+{
+ if (anchor)
+ atomic_inc(&anchor->suspend_wakeups);
+}
+EXPORT_SYMBOL_GPL(usb_anchor_suspend_wakeups);
+
+/**
+ * usb_anchor_resume_wakeups
+ * @anchor: the anchor you want to resume wakeups on
+ *
+ * Allow usb_wait_anchor_empty_timeout waiters to be woken up again, and
+ * wake up any current waiters if the anchor is empty.
+ */
+void usb_anchor_resume_wakeups(struct usb_anchor *anchor)
+{
+ if (!anchor)
+ return;
+
+ atomic_dec(&anchor->suspend_wakeups);
+ if (usb_anchor_check_wakeup(anchor))
+ wake_up(&anchor->wait);
+}
+EXPORT_SYMBOL_GPL(usb_anchor_resume_wakeups);
+
+/**
* usb_wait_anchor_empty_timeout - wait for an anchor to be unused
* @anchor: the anchor you want to become unused
* @timeout: how long you are willing to wait in milliseconds
@@ -858,7 +897,8 @@ EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor,
unsigned int timeout)
{
- return wait_event_timeout(anchor->wait, list_empty(&anchor->urb_list),
+ return wait_event_timeout(anchor->wait,
+ usb_anchor_check_wakeup(anchor),
msecs_to_jiffies(timeout));
}
EXPORT_SYMBOL_GPL(usb_wait_anchor_empty_timeout);
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 0a6ee2e70b25..4d1144990d4c 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -497,7 +497,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
dev->authorized = 1;
else {
dev->authorized = usb_hcd->authorized_default;
- dev->wusb = usb_bus_is_wusb(bus)? 1 : 0;
+ dev->wusb = usb_bus_is_wusb(bus) ? 1 : 0;
}
return dev;
}
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 823857767a16..c49383669cd8 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -35,6 +35,7 @@ extern int usb_get_device_descriptor(struct usb_device *dev,
unsigned int size);
extern int usb_get_bos_descriptor(struct usb_device *dev);
extern void usb_release_bos_descriptor(struct usb_device *dev);
+extern int usb_device_supports_lpm(struct usb_device *udev);
extern char *usb_cache_string(struct usb_device *udev, int index);
extern int usb_set_configuration(struct usb_device *dev, int configuration);
extern int usb_choose_configuration(struct usb_device *udev);
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index 2e252aae51ca..31443aeedcdb 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -165,7 +165,6 @@ static int dwc3_pci_probe(struct pci_dev *pci,
return 0;
err3:
- pci_set_drvdata(pci, NULL);
platform_device_put(dwc3);
err1:
pci_disable_device(pci);
@@ -180,7 +179,6 @@ static void dwc3_pci_remove(struct pci_dev *pci)
platform_device_unregister(glue->dwc3);
platform_device_unregister(glue->usb2_phy);
platform_device_unregister(glue->usb3_phy);
- pci_set_drvdata(pci, NULL);
pci_disable_device(pci);
}
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 7fa93f4bc507..95f7649c71a7 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -352,7 +352,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
break;
default:
return -EINVAL;
- };
+ }
response_pkt = (__le16 *) dwc->setup_buf;
*response_pkt = cpu_to_le16(usb_status);
@@ -470,7 +470,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
default:
return -EINVAL;
- };
+ }
return 0;
}
@@ -709,7 +709,7 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
dev_vdbg(dwc->dev, "Forwarding to gadget driver\n");
ret = dwc3_ep0_delegate_req(dwc, ctrl);
break;
- };
+ }
return ret;
}
diff --git a/drivers/usb/early/ehci-dbgp.c b/drivers/usb/early/ehci-dbgp.c
index 5e29ddeb4d33..8cfc3191be50 100644
--- a/drivers/usb/early/ehci-dbgp.c
+++ b/drivers/usb/early/ehci-dbgp.c
@@ -568,10 +568,6 @@ try_again:
dbgp_printk("Could not find attached debug device\n");
goto err;
}
- if (ret < 0) {
- dbgp_printk("Attached device is not a debug device\n");
- goto err;
- }
dbgp_endpoint_out = dbgp_desc.bDebugOutEndpoint;
dbgp_endpoint_in = dbgp_desc.bDebugInEndpoint;
diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c
index a9a4346c83aa..54a1e2954cea 100644
--- a/drivers/usb/gadget/amd5536udc.c
+++ b/drivers/usb/gadget/amd5536udc.c
@@ -3078,8 +3078,6 @@ static void udc_pci_remove(struct pci_dev *pdev)
if (dev->active)
pci_disable_device(pdev);
- pci_set_drvdata(pdev, NULL);
-
udc_remove(dev);
}
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index d4f0f3305759..3e7ae707f691 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -354,7 +354,7 @@ static u8 encode_bMaxPower(enum usb_device_speed speed,
return DIV_ROUND_UP(val, 8);
default:
return DIV_ROUND_UP(val, 2);
- };
+ }
}
static int config_buf(struct usb_configuration *config,
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index b8a2376971a4..8f4dae310923 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/dummy_hcd.c
@@ -544,7 +544,7 @@ static int dummy_enable(struct usb_ep *_ep,
default:
val = "ctrl";
break;
- }; val; }),
+ } val; }),
max, ep->stream_en ? "enabled" : "disabled");
/* at this point real hardware should be NAKing transfers
@@ -2271,7 +2271,7 @@ static inline ssize_t show_urb(char *buf, size_t size, struct urb *urb)
default:
s = "?";
break;
- }; s; }),
+ } s; }),
ep, ep ? (usb_pipein(urb->pipe) ? "in" : "out") : "",
({ char *s; \
switch (usb_pipetype(urb->pipe)) { \
@@ -2287,7 +2287,7 @@ static inline ssize_t show_urb(char *buf, size_t size, struct urb *urb)
default: \
s = "-iso"; \
break; \
- }; s; }),
+ } s; }),
urb->actual_length, urb->transfer_buffer_length);
}
diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c
index 5327c82472ed..2344efe4f4ce 100644
--- a/drivers/usb/gadget/g_ffs.c
+++ b/drivers/usb/gadget/g_ffs.c
@@ -76,7 +76,9 @@ struct gfs_ffs_obj {
USB_GADGET_COMPOSITE_OPTIONS();
+#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS
USB_ETHERNET_MODULE_PARAMETERS();
+#endif
static struct usb_device_descriptor gfs_dev_desc = {
.bLength = sizeof gfs_dev_desc,
diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c
index c64deb9e3d62..f82768015715 100644
--- a/drivers/usb/gadget/goku_udc.c
+++ b/drivers/usb/gadget/goku_udc.c
@@ -1165,7 +1165,7 @@ static int udc_proc_read(struct seq_file *m, void *v)
s = "invalid"; break;
default:
s = "?"; break;
- }; s; }),
+ } s; }),
(tmp & EPxSTATUS_TOGGLE) ? "data1" : "data0",
(tmp & EPxSTATUS_SUSPEND) ? " suspend" : "",
(tmp & EPxSTATUS_FIFO_DISABLE) ? " disable" : "",
@@ -1701,7 +1701,6 @@ static void goku_remove(struct pci_dev *pdev)
if (dev->enabled)
pci_disable_device(pdev);
- pci_set_drvdata(pdev, NULL);
dev->regs = NULL;
INFO(dev, "unbind\n");
diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/mv_u3d_core.c
index 561b30efb8ee..5b06c989951c 100644
--- a/drivers/usb/gadget/mv_u3d_core.c
+++ b/drivers/usb/gadget/mv_u3d_core.c
@@ -1936,7 +1936,7 @@ static int mv_u3d_probe(struct platform_device *dev)
}
u3d->irq = r->start;
if (request_irq(u3d->irq, mv_u3d_irq,
- IRQF_DISABLED | IRQF_SHARED, driver_name, u3d)) {
+ IRQF_SHARED, driver_name, u3d)) {
u3d->irq = 0;
dev_err(&dev->dev, "Request irq %d for u3d failed\n",
u3d->irq);
diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c
index 0781bff70015..fc852177c087 100644
--- a/drivers/usb/gadget/net2280.c
+++ b/drivers/usb/gadget/net2280.c
@@ -129,7 +129,7 @@ static char *type_string (u8 bmAttributes)
case USB_ENDPOINT_XFER_BULK: return "bulk";
case USB_ENDPOINT_XFER_ISOC: return "iso";
case USB_ENDPOINT_XFER_INT: return "intr";
- };
+ }
return "control";
}
#endif
@@ -1630,7 +1630,7 @@ static ssize_t queues_show(struct device *_dev, struct device_attribute *attr,
val = "intr"; break;
default:
val = "iso"; break;
- }; val; }),
+ } val; }),
usb_endpoint_maxp (d) & 0x1fff,
ep->dma ? "dma" : "pio", ep->fifo_size
);
@@ -2680,7 +2680,6 @@ static void net2280_remove (struct pci_dev *pdev)
if (dev->enabled)
pci_disable_device (pdev);
device_remove_file (&pdev->dev, &dev_attr_registers);
- pci_set_drvdata (pdev, NULL);
INFO (dev, "unbind\n");
}
diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c
index 24174e1d1564..32d5e923750b 100644
--- a/drivers/usb/gadget/pch_udc.c
+++ b/drivers/usb/gadget/pch_udc.c
@@ -3080,7 +3080,6 @@ static void pch_udc_remove(struct pci_dev *pdev)
if (dev->active)
pci_disable_device(pdev);
kfree(dev);
- pci_set_drvdata(pdev, NULL);
}
#ifdef CONFIG_PM
diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c
index 9575085ded81..a3ad732bc812 100644
--- a/drivers/usb/gadget/rndis.c
+++ b/drivers/usb/gadget/rndis.c
@@ -1068,7 +1068,7 @@ static int rndis_proc_show(struct seq_file *m, void *v)
s = "RNDIS_INITIALIZED"; break;
case RNDIS_DATA_INITIALIZED:
s = "RNDIS_DATA_INITIALIZED"; break;
- }; s; }),
+ } s; }),
param->medium,
(param->media_state) ? 0 : param->speed*100,
(param->media_state) ? "disconnected" : "connected",
diff --git a/drivers/usb/gadget/tcm_usb_gadget.c b/drivers/usb/gadget/tcm_usb_gadget.c
index 0ff33396eef3..eccea1df702d 100644
--- a/drivers/usb/gadget/tcm_usb_gadget.c
+++ b/drivers/usb/gadget/tcm_usb_gadget.c
@@ -472,7 +472,7 @@ static int usbg_bot_setup(struct usb_function *f,
bot_enqueue_cmd_cbw(fu);
return 0;
break;
- };
+ }
return -ENOTSUPP;
}
@@ -617,7 +617,7 @@ static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req)
default:
BUG();
- };
+ }
return;
cleanup:
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index b3f20d7f15de..80e72fba01f4 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,62 @@ 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_EP93XX
+ tristate "Support for EP93XX on-chip OHCI USB controller"
+ depends on USB_OHCI_HCD && ARCH_EP93XX
+ default y
+ ---help---
+ Enables support for the on-chip OHCI controller on
+ EP93XX 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 +501,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 50b0041c09a9..9dc11c2ee454 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,15 @@ 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_EP93XX) += ohci-ep93xx.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-dbg.c b/drivers/usb/host/ehci-dbg.c
index aa5b603f3933..4a9c2edbcb2b 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 7c3de95c7054..016352e0f5a7 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 f2407b2e8a99..a06d5012201f 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 83ab51af250f..b52a66ce92e8 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 86ab9fd9fe9e..e66706aa9f0c 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 */
@@ -1158,7 +1178,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 +1211,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 +1263,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-msm.c b/drivers/usb/host/ehci-msm.c
index 0f717dc688b7..f341651d6f6c 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 35cdbd88bbbe..417c10da9450 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 45cc00158412..ab0397e4d8f3 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 854c2ec7b699..3e86bf4371b3 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 601e208bd782..893b707f0000 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 932293fa32de..6cc5567bf9c8 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 fd983771b025..8188542ba17e 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 a7f776a13eb1..db05bd8ee9d5 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 85dd24ed97a6..7ce5c2a2fe31 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -106,80 +106,207 @@ static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr)
*hw_p = 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,
+ __hc32 *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:
diff --git a/drivers/usb/host/ehci-sead3.c b/drivers/usb/host/ehci-sead3.c
index b2de52d39614..8a734498079b 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 93e59a13bc1f..dc899eb2b861 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 14ced00ba220..f6459dfb6f54 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 78fa76da3324..e6d8e26e48cc 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 cca4be90a864..67026ffbf9a8 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 59e0e24c753f..cdad8438c02b 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 eba962e6ebfb..95979f9f4381 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 291db7d09f22..e8f41c5e771b 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 fce13bcc4a3e..55486bd23cf1 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 299253c826c7..e1c6d850a7e1 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 5b86ffb88f1c..e5fb3cfd57a9 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;
diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c
index 6f29abad6815..935a2dd367a8 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 caa3764a3407..476b5a5baf25 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 31b81f9eacdc..3fca52ec02ac 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
index 84a20d5223b9..492f681c70f2 100644
--- a/drivers/usb/host/ohci-ep93xx.c
+++ b/drivers/usb/host/ohci-ep93xx.c
@@ -25,50 +25,23 @@
#include <linux/clk.h>
#include <linux/device.h>
-#include <linux/signal.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/signal.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
-static struct clk *usb_host_clock;
+#include "ohci.h"
-static int ohci_ep93xx_start(struct usb_hcd *hcd)
-{
- struct ohci_hcd *ohci = hcd_to_ohci(hcd);
- int ret;
+#define DRIVER_DESC "OHCI EP93xx driver"
- if ((ret = ohci_init(ohci)) < 0)
- return ret;
+static const char hcd_name[] = "ohci-ep93xx";
- 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;
- }
+static struct hc_driver __read_mostly ohci_ep93xx_hc_driver;
- 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 struct clk *usb_host_clock;
static int ohci_hcd_ep93xx_drv_probe(struct platform_device *pdev)
{
@@ -109,8 +82,6 @@ static int ohci_hcd_ep93xx_drv_probe(struct platform_device *pdev)
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;
@@ -166,7 +137,6 @@ static int ohci_hcd_ep93xx_drv_resume(struct platform_device *pdev)
}
#endif
-
static struct platform_driver ohci_hcd_ep93xx_driver = {
.probe = ohci_hcd_ep93xx_drv_probe,
.remove = ohci_hcd_ep93xx_drv_remove,
@@ -181,4 +151,24 @@ static struct platform_driver ohci_hcd_ep93xx_driver = {
},
};
+static int __init ohci_ep93xx_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+ ohci_init_driver(&ohci_ep93xx_hc_driver, NULL);
+ return platform_driver_register(&ohci_hcd_ep93xx_driver);
+}
+module_init(ohci_ep93xx_init);
+
+static void __exit ohci_ep93xx_cleanup(void)
+{
+ platform_driver_unregister(&ohci_hcd_ep93xx_driver);
+}
+module_exit(ohci_ep93xx_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:ep93xx-ohci");
diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c
index dc6ee9adacf5..a87baedc0aa7 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 604cad1bcf9c..8ada13f8dde2 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-nxp.c b/drivers/usb/host/ohci-nxp.c
index 7d7d507d54e8..9ab7e24ba65d 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 31d3a12eb486..f253214741ba 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 a09af26f69ed..408d06a68571 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 ec337c2bd5e0..90879e9ccbec 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 a4c6410f0ed4..f351ff5b171f 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 93371a235e82..deea5d1d6394 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 4919afa4125e..be3429e08d90 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 d479d5ddab88..2a5de5fecd8f 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 cc9dd9e4f05e..31ff3fc4e26f 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 08ef2829a7e2..dfbdd3aefe98 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;
diff --git a/drivers/usb/host/pci-quirks.h b/drivers/usb/host/pci-quirks.h
index ed6700d00fe6..638e88f7a28b 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 5477bf5df218..79620c39217e 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 455737546525..8e239cdd95d5 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 9189bc984c98..93e17b12fb33 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 0f228c46eeda..8de0da0e6dfc 100644
--- a/drivers/usb/host/uhci-pci.c
+++ b/drivers/usb/host/uhci-pci.c
@@ -178,7 +178,7 @@ static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
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.
@@ -299,3 +299,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 d033a0ec7f0d..ded842bc6578 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 ecc88db804e0..1b0888f8da9a 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/misc/usbtest.c b/drivers/usb/misc/usbtest.c
index aa28ac8c7607..3e91d3e98ee8 100644
--- a/drivers/usb/misc/usbtest.c
+++ b/drivers/usb/misc/usbtest.c
@@ -120,7 +120,7 @@ get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf)
struct usb_host_endpoint *e;
e = alt->endpoint + ep;
- switch (e->desc.bmAttributes) {
+ switch (usb_endpoint_type(&e->desc)) {
case USB_ENDPOINT_XFER_BULK:
break;
case USB_ENDPOINT_XFER_ISOC:
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index c258a97ef1b0..0440e2807280 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -75,6 +75,7 @@ config USB_MUSB_TUSB6010
config USB_MUSB_OMAP2PLUS
tristate "OMAP2430 and onwards"
depends on ARCH_OMAP2PLUS
+ select GENERIC_PHY
config USB_MUSB_AM35X
tristate "AM35x"
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index cd70cc886171..208785e36d52 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -617,7 +617,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
/* case 3 << MUSB_DEVCTL_VBUS_SHIFT: */
default:
s = "VALID"; break;
- }; s; }),
+ } s; }),
VBUSERR_RETRY_COUNT - musb->vbuserr_retry,
musb->port1_status);
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index 1c5bf75ee8ff..29f7cd7c7964 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -46,6 +46,7 @@
#include <linux/usb.h>
#include <linux/usb/otg.h>
#include <linux/usb/musb.h>
+#include <linux/phy/phy.h>
struct musb;
struct musb_hw_ep;
@@ -341,6 +342,7 @@ struct musb {
u16 int_tx;
struct usb_phy *xceiv;
+ struct phy *phy;
int nIrq;
unsigned irq_wake:1;
diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index bd4138d80a48..1a5574c2d586 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -443,7 +443,7 @@ static int get_musb_port_mode(struct device *dev)
case USB_DR_MODE_OTG:
default:
return MUSB_PORT_MODE_DUAL_ROLE;
- };
+ }
}
static int dsps_create_musb_pdev(struct dsps_glue *glue,
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 3671898a4535..d2d3a173b315 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -1121,7 +1121,7 @@ static int musb_gadget_enable(struct usb_ep *ep,
case USB_ENDPOINT_XFER_BULK: s = "bulk"; break;
case USB_ENDPOINT_XFER_INT: s = "int"; break;
default: s = "iso"; break;
- }; s; }),
+ } s; }),
musb_ep->is_in ? "IN" : "OUT",
musb_ep->dma ? "dma, " : "",
musb_ep->packet_sz);
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 9a2b8c85f19a..6582a20bec05 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -253,7 +253,7 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
case USB_ENDPOINT_XFER_BULK: s = "-bulk"; break;
case USB_ENDPOINT_XFER_ISOC: s = "-iso"; break;
default: s = "-intr"; break;
- }; s; }),
+ } s; }),
epnum, buf + offset, len);
/* Configure endpoint */
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
index 59d2245db1c8..9eab645fed8b 100644
--- a/drivers/usb/musb/omap2430.c
+++ b/drivers/usb/musb/omap2430.c
@@ -38,6 +38,7 @@
#include <linux/delay.h>
#include <linux/usb/musb-omap.h>
#include <linux/usb/omap_control_usb.h>
+#include <linux/of_platform.h>
#include "musb_core.h"
#include "omap2430.h"
@@ -348,11 +349,21 @@ static int omap2430_musb_init(struct musb *musb)
* up through ULPI. TWL4030-family PMICs include one,
* which needs a driver, drivers aren't always needed.
*/
- if (dev->parent->of_node)
+ if (dev->parent->of_node) {
+ musb->phy = devm_phy_get(dev->parent, "usb2-phy");
+
+ /* We can't totally remove musb->xceiv as of now because
+ * musb core uses xceiv.state and xceiv.otg. Once we have
+ * a separate state machine to handle otg, these can be moved
+ * out of xceiv and then we can start using the generic PHY
+ * framework
+ */
musb->xceiv = devm_usb_get_phy_by_phandle(dev->parent,
"usb-phy", 0);
- else
+ } else {
musb->xceiv = devm_usb_get_phy_dev(dev, 0);
+ musb->phy = devm_phy_get(dev, "usb");
+ }
if (IS_ERR(musb->xceiv)) {
status = PTR_ERR(musb->xceiv);
@@ -364,6 +375,10 @@ static int omap2430_musb_init(struct musb *musb)
return -EPROBE_DEFER;
}
+ if (IS_ERR(musb->phy)) {
+ pr_err("HS USB OTG: no PHY configured\n");
+ return PTR_ERR(musb->phy);
+ }
musb->isr = omap2430_musb_interrupt;
status = pm_runtime_get_sync(dev);
@@ -397,7 +412,7 @@ static int omap2430_musb_init(struct musb *musb)
if (glue->status != OMAP_MUSB_UNKNOWN)
omap_musb_set_mailbox(glue);
- usb_phy_init(musb->xceiv);
+ phy_init(musb->phy);
pm_runtime_put_noidle(musb->controller);
return 0;
@@ -460,6 +475,7 @@ static int omap2430_musb_exit(struct musb *musb)
del_timer_sync(&musb_idle_timer);
omap2430_low_level_exit(musb);
+ phy_exit(musb->phy);
return 0;
}
@@ -509,8 +525,12 @@ static int omap2430_probe(struct platform_device *pdev)
glue->dev = &pdev->dev;
glue->musb = musb;
glue->status = OMAP_MUSB_UNKNOWN;
+ glue->control_otghs = ERR_PTR(-ENODEV);
if (np) {
+ struct device_node *control_node;
+ struct platform_device *control_pdev;
+
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
dev_err(&pdev->dev,
@@ -539,22 +559,20 @@ static int omap2430_probe(struct platform_device *pdev)
of_property_read_u32(np, "ram-bits", (u32 *)&config->ram_bits);
of_property_read_u32(np, "power", (u32 *)&pdata->power);
config->multipoint = of_property_read_bool(np, "multipoint");
- pdata->has_mailbox = of_property_read_bool(np,
- "ti,has-mailbox");
pdata->board_data = data;
pdata->config = config;
- }
- if (pdata->has_mailbox) {
- glue->control_otghs = omap_get_control_dev();
- if (IS_ERR(glue->control_otghs)) {
- dev_vdbg(&pdev->dev, "Failed to get control device\n");
- ret = PTR_ERR(glue->control_otghs);
- goto err2;
+ control_node = of_parse_phandle(np, "ctrl-module", 0);
+ if (control_node) {
+ control_pdev = of_find_device_by_node(control_node);
+ if (!control_pdev) {
+ dev_err(&pdev->dev, "Failed to get control device\n");
+ ret = -EINVAL;
+ goto err2;
+ }
+ glue->control_otghs = &control_pdev->dev;
}
- } else {
- glue->control_otghs = ERR_PTR(-ENODEV);
}
pdata->platform_ops = &omap2430_ops;
@@ -638,7 +656,7 @@ static int omap2430_runtime_suspend(struct device *dev)
OTG_INTERFSEL);
omap2430_low_level_exit(musb);
- usb_phy_set_suspend(musb->xceiv, 1);
+ phy_power_off(musb->phy);
}
return 0;
@@ -653,8 +671,7 @@ static int omap2430_runtime_resume(struct device *dev)
omap2430_low_level_init(musb);
musb_writel(musb->mregs, OTG_INTERFSEL,
musb->context.otg_interfsel);
-
- usb_phy_set_suspend(musb->xceiv, 0);
+ phy_power_on(musb->phy);
}
return 0;
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index d5589f9c60a9..64b8bef1919e 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -66,17 +66,6 @@ config OMAP_CONTROL_USB
power on the USB2 PHY is present in OMAP4 and OMAP5. OMAP5 has an
additional register to power on USB3 PHY.
-config OMAP_USB2
- tristate "OMAP USB2 PHY Driver"
- depends on ARCH_OMAP2PLUS
- select OMAP_CONTROL_USB
- select USB_PHY
- help
- Enable this to support the transceiver that is part of SOC. This
- driver takes care of all the PHY functionality apart from comparator.
- The USB OTG controller communicates with the comparator using this
- driver.
-
config OMAP_USB3
tristate "OMAP USB3 PHY Driver"
depends on ARCH_OMAP2PLUS || COMPILE_TEST
@@ -93,6 +82,7 @@ config AM335X_CONTROL_USB
config AM335X_PHY_USB
tristate "AM335x USB PHY Driver"
+ depends on ARM || COMPILE_TEST
select USB_PHY
select AM335X_CONTROL_USB
select NOP_USB_XCEIV
@@ -123,16 +113,6 @@ config SAMSUNG_USB3PHY
Enable this to support Samsung USB 3.0 (Super Speed) phy controller
for samsung SoCs.
-config TWL4030_USB
- tristate "TWL4030 USB Transceiver Driver"
- depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS
- select USB_PHY
- help
- Enable this to support the USB OTG transceiver on TWL4030
- family chips (including the TWL5030 and TPS659x0 devices).
- This transceiver supports high and full speed devices plus,
- in host mode, low speed.
-
config TWL6030_USB
tristate "TWL6030 USB Transceiver Driver"
depends on TWL4030_CORE && OMAP_USB2 && USB_MUSB_OMAP2PLUS
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index 2135e85f46ed..9c3736109c2c 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -15,12 +15,10 @@ obj-$(CONFIG_NOP_USB_XCEIV) += phy-generic.o
obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o
obj-$(CONFIG_AM335X_CONTROL_USB) += phy-am335x-control.o
obj-$(CONFIG_AM335X_PHY_USB) += phy-am335x.o
-obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o
obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o
obj-$(CONFIG_SAMSUNG_USB2PHY) += phy-samsung-usb2.o
obj-$(CONFIG_SAMSUNG_USB3PHY) += phy-samsung-usb3.o
-obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
obj-$(CONFIG_TWL6030_USB) += phy-twl6030-usb.o
obj-$(CONFIG_USB_EHCI_TEGRA) += phy-tegra-usb.o
obj-$(CONFIG_USB_GPIO_VBUS) += phy-gpio-vbus-usb.o
diff --git a/drivers/usb/phy/phy-omap-control.c b/drivers/usb/phy/phy-omap-control.c
index a4dda8e12562..09c5ace1edd8 100644
--- a/drivers/usb/phy/phy-omap-control.c
+++ b/drivers/usb/phy/phy-omap-control.c
@@ -20,87 +20,77 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/usb/omap_control_usb.h>
-static struct omap_control_usb *control_usb;
-
-/**
- * omap_get_control_dev - returns the device pointer for this control device
- *
- * This API should be called to get the device pointer for this control
- * module device. This device pointer should be used for called other
- * exported API's in this driver.
- *
- * To be used by PHY driver and glue driver.
- */
-struct device *omap_get_control_dev(void)
-{
- if (!control_usb)
- return ERR_PTR(-ENODEV);
-
- return control_usb->dev;
-}
-EXPORT_SYMBOL_GPL(omap_get_control_dev);
-
/**
- * omap_control_usb3_phy_power - power on/off the serializer using control
- * module
+ * omap_control_usb_phy_power - power on/off the phy using control module reg
* @dev: the control module device
- * @on: 0 to off and 1 to on based on powering on or off the PHY
- *
- * usb3 PHY driver should call this API to power on or off the PHY.
+ * @on: 0 or 1, based on powering on or off the PHY
*/
-void omap_control_usb3_phy_power(struct device *dev, bool on)
+void omap_control_usb_phy_power(struct device *dev, int on)
{
u32 val;
unsigned long rate;
- struct omap_control_usb *control_usb = dev_get_drvdata(dev);
+ struct omap_control_usb *control_usb;
- if (control_usb->type != OMAP_CTRL_DEV_TYPE2)
+ if (IS_ERR(dev) || !dev) {
+ pr_err("%s: invalid device\n", __func__);
return;
+ }
- rate = clk_get_rate(control_usb->sys_clk);
- rate = rate/1000000;
-
- val = readl(control_usb->phy_power);
-
- if (on) {
- val &= ~(OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK |
- OMAP_CTRL_USB_PWRCTL_CLK_FREQ_MASK);
- val |= OMAP_CTRL_USB3_PHY_TX_RX_POWERON <<
- OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT;
- val |= rate << OMAP_CTRL_USB_PWRCTL_CLK_FREQ_SHIFT;
- } else {
- val &= ~OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK;
- val |= OMAP_CTRL_USB3_PHY_TX_RX_POWEROFF <<
- OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT;
+ control_usb = dev_get_drvdata(dev);
+ if (!control_usb) {
+ dev_err(dev, "%s: invalid control usb device\n", __func__);
+ return;
}
- writel(val, control_usb->phy_power);
-}
-EXPORT_SYMBOL_GPL(omap_control_usb3_phy_power);
+ if (control_usb->type == OMAP_CTRL_TYPE_OTGHS)
+ return;
-/**
- * omap_control_usb_phy_power - power on/off the phy using control module reg
- * @dev: the control module device
- * @on: 0 or 1, based on powering on or off the PHY
- */
-void omap_control_usb_phy_power(struct device *dev, int on)
-{
- u32 val;
- struct omap_control_usb *control_usb = dev_get_drvdata(dev);
+ val = readl(control_usb->power);
- val = readl(control_usb->dev_conf);
+ switch (control_usb->type) {
+ case OMAP_CTRL_TYPE_USB2:
+ if (on)
+ val &= ~OMAP_CTRL_DEV_PHY_PD;
+ else
+ val |= OMAP_CTRL_DEV_PHY_PD;
+ break;
- if (on)
- val &= ~OMAP_CTRL_DEV_PHY_PD;
- else
- val |= OMAP_CTRL_DEV_PHY_PD;
+ case OMAP_CTRL_TYPE_PIPE3:
+ rate = clk_get_rate(control_usb->sys_clk);
+ rate = rate/1000000;
+
+ if (on) {
+ val &= ~(OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK |
+ OMAP_CTRL_USB_PWRCTL_CLK_FREQ_MASK);
+ val |= OMAP_CTRL_USB3_PHY_TX_RX_POWERON <<
+ OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT;
+ val |= rate << OMAP_CTRL_USB_PWRCTL_CLK_FREQ_SHIFT;
+ } else {
+ val &= ~OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK;
+ val |= OMAP_CTRL_USB3_PHY_TX_RX_POWEROFF <<
+ OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT;
+ }
+ break;
- writel(val, control_usb->dev_conf);
+ case OMAP_CTRL_TYPE_DRA7USB2:
+ if (on)
+ val &= ~OMAP_CTRL_USB2_PHY_PD;
+ else
+ val |= OMAP_CTRL_USB2_PHY_PD;
+ break;
+ default:
+ dev_err(dev, "%s: type %d not recognized\n",
+ __func__, control_usb->type);
+ break;
+ }
+
+ writel(val, control_usb->power);
}
EXPORT_SYMBOL_GPL(omap_control_usb_phy_power);
@@ -172,11 +162,19 @@ void omap_control_usb_set_mode(struct device *dev,
{
struct omap_control_usb *ctrl_usb;
- if (IS_ERR(dev) || control_usb->type != OMAP_CTRL_DEV_TYPE1)
+ if (IS_ERR(dev) || !dev)
return;
ctrl_usb = dev_get_drvdata(dev);
+ if (!ctrl_usb) {
+ dev_err(dev, "Invalid control usb device\n");
+ return;
+ }
+
+ if (ctrl_usb->type != OMAP_CTRL_TYPE_OTGHS)
+ return;
+
switch (mode) {
case USB_MODE_HOST:
omap_control_usb_host_mode(ctrl_usb);
@@ -193,12 +191,46 @@ void omap_control_usb_set_mode(struct device *dev,
}
EXPORT_SYMBOL_GPL(omap_control_usb_set_mode);
+#ifdef CONFIG_OF
+
+static const enum omap_control_usb_type otghs_data = OMAP_CTRL_TYPE_OTGHS;
+static const enum omap_control_usb_type usb2_data = OMAP_CTRL_TYPE_USB2;
+static const enum omap_control_usb_type pipe3_data = OMAP_CTRL_TYPE_PIPE3;
+static const enum omap_control_usb_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2;
+
+static const struct of_device_id omap_control_usb_id_table[] = {
+ {
+ .compatible = "ti,control-phy-otghs",
+ .data = &otghs_data,
+ },
+ {
+ .compatible = "ti,control-phy-usb2",
+ .data = &usb2_data,
+ },
+ {
+ .compatible = "ti,control-phy-pipe3",
+ .data = &pipe3_data,
+ },
+ {
+ .compatible = "ti,control-phy-dra7usb2",
+ .data = &dra7usb2_data,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, omap_control_usb_id_table);
+#endif
+
+
static int omap_control_usb_probe(struct platform_device *pdev)
{
struct resource *res;
- struct device_node *np = pdev->dev.of_node;
- struct omap_control_usb_platform_data *pdata =
- dev_get_platdata(&pdev->dev);
+ const struct of_device_id *of_id;
+ struct omap_control_usb *control_usb;
+
+ of_id = of_match_device(of_match_ptr(omap_control_usb_id_table),
+ &pdev->dev);
+ if (!of_id)
+ return -EINVAL;
control_usb = devm_kzalloc(&pdev->dev, sizeof(*control_usb),
GFP_KERNEL);
@@ -207,40 +239,27 @@ static int omap_control_usb_probe(struct platform_device *pdev)
return -ENOMEM;
}
- if (np) {
- of_property_read_u32(np, "ti,type", &control_usb->type);
- } else if (pdata) {
- control_usb->type = pdata->type;
- } else {
- dev_err(&pdev->dev, "no pdata present\n");
- return -EINVAL;
- }
-
- control_usb->dev = &pdev->dev;
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "control_dev_conf");
- control_usb->dev_conf = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(control_usb->dev_conf))
- return PTR_ERR(control_usb->dev_conf);
+ control_usb->dev = &pdev->dev;
+ control_usb->type = *(enum omap_control_usb_type *)of_id->data;
- if (control_usb->type == OMAP_CTRL_DEV_TYPE1) {
+ if (control_usb->type == OMAP_CTRL_TYPE_OTGHS) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"otghs_control");
control_usb->otghs_control = devm_ioremap_resource(
&pdev->dev, res);
if (IS_ERR(control_usb->otghs_control))
return PTR_ERR(control_usb->otghs_control);
- }
-
- if (control_usb->type == OMAP_CTRL_DEV_TYPE2) {
+ } else {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "phy_power_usb");
- control_usb->phy_power = devm_ioremap_resource(
- &pdev->dev, res);
- if (IS_ERR(control_usb->phy_power))
- return PTR_ERR(control_usb->phy_power);
+ "power");
+ control_usb->power = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(control_usb->power)) {
+ dev_err(&pdev->dev, "Couldn't get power register\n");
+ return PTR_ERR(control_usb->power);
+ }
+ }
+ if (control_usb->type == OMAP_CTRL_TYPE_PIPE3) {
control_usb->sys_clk = devm_clk_get(control_usb->dev,
"sys_clkin");
if (IS_ERR(control_usb->sys_clk)) {
@@ -249,20 +268,11 @@ static int omap_control_usb_probe(struct platform_device *pdev)
}
}
-
dev_set_drvdata(control_usb->dev, control_usb);
return 0;
}
-#ifdef CONFIG_OF
-static const struct of_device_id omap_control_usb_id_table[] = {
- { .compatible = "ti,omap-control-usb" },
- {}
-};
-MODULE_DEVICE_TABLE(of, omap_control_usb_id_table);
-#endif
-
static struct platform_driver omap_control_usb_driver = {
.probe = omap_control_usb_probe,
.driver = {
diff --git a/drivers/usb/phy/phy-omap-usb3.c b/drivers/usb/phy/phy-omap-usb3.c
index 4e8a0405f956..0c6ba29bdddd 100644
--- a/drivers/usb/phy/phy-omap-usb3.c
+++ b/drivers/usb/phy/phy-omap-usb3.c
@@ -26,6 +26,7 @@
#include <linux/pm_runtime.h>
#include <linux/delay.h>
#include <linux/usb/omap_control_usb.h>
+#include <linux/of_platform.h>
#define PLL_STATUS 0x00000004
#define PLL_GO 0x00000008
@@ -100,7 +101,7 @@ static int omap_usb3_suspend(struct usb_phy *x, int suspend)
udelay(1);
} while (--timeout);
- omap_control_usb3_phy_power(phy->control_dev, 0);
+ omap_control_usb_phy_power(phy->control_dev, 0);
phy->is_suspended = 1;
} else if (!suspend && phy->is_suspended) {
@@ -189,15 +190,21 @@ static int omap_usb3_init(struct usb_phy *x)
if (ret)
return ret;
- omap_control_usb3_phy_power(phy->control_dev, 1);
+ omap_control_usb_phy_power(phy->control_dev, 1);
return 0;
}
static int omap_usb3_probe(struct platform_device *pdev)
{
- struct omap_usb *phy;
- struct resource *res;
+ struct omap_usb *phy;
+ struct resource *res;
+ struct device_node *node = pdev->dev.of_node;
+ struct device_node *control_node;
+ struct platform_device *control_pdev;
+
+ if (!node)
+ return -EINVAL;
phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
if (!phy) {
@@ -239,13 +246,20 @@ static int omap_usb3_probe(struct platform_device *pdev)
return -EINVAL;
}
- phy->control_dev = omap_get_control_dev();
- if (IS_ERR(phy->control_dev)) {
- dev_dbg(&pdev->dev, "Failed to get control device\n");
- return -ENODEV;
+ control_node = of_parse_phandle(node, "ctrl-module", 0);
+ if (!control_node) {
+ dev_err(&pdev->dev, "Failed to get control device phandle\n");
+ return -EINVAL;
}
+ control_pdev = of_find_device_by_node(control_node);
+ if (!control_pdev) {
+ dev_err(&pdev->dev, "Failed to get control device\n");
+ return -EINVAL;
+ }
+
+ phy->control_dev = &control_pdev->dev;
- omap_control_usb3_phy_power(phy->control_dev, 0);
+ omap_control_usb_phy_power(phy->control_dev, 0);
usb_add_phy_dev(&phy->phy);
platform_set_drvdata(pdev, phy);
diff --git a/drivers/usb/phy/phy-ulpi-viewport.c b/drivers/usb/phy/phy-ulpi-viewport.c
index 7c22a5390fc3..18bb8264b5a0 100644
--- a/drivers/usb/phy/phy-ulpi-viewport.c
+++ b/drivers/usb/phy/phy-ulpi-viewport.c
@@ -36,7 +36,7 @@ static int ulpi_viewport_wait(void __iomem *view, u32 mask)
return 0;
udelay(1);
- };
+ }
return -ETIMEDOUT;
}
diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c
index 781426230d69..6e1b69d0f5f5 100644
--- a/drivers/usb/serial/cyberjack.c
+++ b/drivers/usb/serial/cyberjack.c
@@ -279,7 +279,7 @@ static void cyberjack_read_int_callback(struct urb *urb)
old_rdtodo = priv->rdtodo;
- if (old_rdtodo + size < old_rdtodo) {
+ if (old_rdtodo > SHRT_MAX - size) {
dev_dbg(dev, "To many bulk_in urbs to do.\n");
spin_unlock(&priv->lock);
goto resubmit;
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index c45f9c0a1b34..f53298d32099 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -1966,8 +1966,16 @@ static int ftdi_process_packet(struct usb_serial_port *port,
port->icount.dsr++;
if (diff_status & FTDI_RS0_RI)
port->icount.rng++;
- if (diff_status & FTDI_RS0_RLSD)
+ if (diff_status & FTDI_RS0_RLSD) {
+ struct tty_struct *tty;
+
port->icount.dcd++;
+ tty = tty_port_tty_get(&port->port);
+ if (tty)
+ usb_serial_handle_dcd_change(port, tty,
+ status & FTDI_RS0_RLSD);
+ tty_kref_put(tty);
+ }
wake_up_interruptible(&port->port.delta_msr_wait);
priv->prev_status = status;
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
index 1f31e6b4c251..2b01ec8651c2 100644
--- a/drivers/usb/serial/generic.c
+++ b/drivers/usb/serial/generic.c
@@ -7,7 +7,6 @@
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
- *
*/
#include <linux/kernel.h>
@@ -37,7 +36,6 @@ MODULE_PARM_DESC(product, "User specified USB idProduct");
static struct usb_device_id generic_device_ids[2]; /* Initially all zeroes. */
-/* All of the device info needed for the Generic Serial Converter */
struct usb_serial_driver usb_serial_generic_device = {
.driver = {
.owner = THIS_MODULE,
@@ -66,7 +64,6 @@ int usb_serial_generic_register(void)
generic_device_ids[0].match_flags =
USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT;
- /* register our generic driver with ourselves */
retval = usb_serial_register_drivers(serial_drivers,
"usbserial_generic", generic_device_ids);
#endif
@@ -76,7 +73,6 @@ int usb_serial_generic_register(void)
void usb_serial_generic_deregister(void)
{
#ifdef CONFIG_USB_SERIAL_GENERIC
- /* remove our generic driver */
usb_serial_deregister_drivers(serial_drivers);
#endif
}
@@ -86,13 +82,11 @@ int usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port
int result = 0;
unsigned long flags;
- /* clear the throttle flags */
spin_lock_irqsave(&port->lock, flags);
port->throttled = 0;
port->throttle_req = 0;
spin_unlock_irqrestore(&port->lock, flags);
- /* if we have a bulk endpoint, start reading from it */
if (port->bulk_in_size)
result = usb_serial_generic_submit_read_urbs(port, GFP_KERNEL);
@@ -127,12 +121,16 @@ int usb_serial_generic_prepare_write_buffer(struct usb_serial_port *port,
}
/**
- * usb_serial_generic_write_start - kick off an URB write
- * @port: Pointer to the &struct usb_serial_port data
+ * usb_serial_generic_write_start - start writing buffered data
+ * @port: usb-serial port
+ * @mem_flags: flags to use for memory allocations
+ *
+ * Serialised using USB_SERIAL_WRITE_BUSY flag.
*
- * Returns zero on success, or a negative errno value
+ * Return: Zero on success or if busy, otherwise a negative errno value.
*/
-static int usb_serial_generic_write_start(struct usb_serial_port *port)
+int usb_serial_generic_write_start(struct usb_serial_port *port,
+ gfp_t mem_flags)
{
struct urb *urb;
int count, result;
@@ -163,7 +161,7 @@ retry:
spin_unlock_irqrestore(&port->lock, flags);
clear_bit(i, &port->write_urbs_free);
- result = usb_submit_urb(urb, GFP_ATOMIC);
+ result = usb_submit_urb(urb, mem_flags);
if (result) {
dev_err_console(port, "%s - error submitting urb: %d\n",
__func__, result);
@@ -175,34 +173,34 @@ retry:
clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags);
return result;
}
-
- /* Try sending off another urb, unless in irq context (in which case
- * there will be no free urb). */
- if (!in_irq())
+ /*
+ * Try sending off another urb, unless called from completion handler
+ * (in which case there will be no free urb or no data).
+ */
+ if (mem_flags != GFP_ATOMIC)
goto retry;
clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags);
return 0;
}
+EXPORT_SYMBOL_GPL(usb_serial_generic_write_start);
/**
- * usb_serial_generic_write - generic write function for serial USB devices
- * @tty: Pointer to &struct tty_struct for the device
- * @port: Pointer to the &usb_serial_port structure for the device
- * @buf: Pointer to the data to write
- * @count: Number of bytes to write
+ * usb_serial_generic_write - generic write function
+ * @tty: tty for the port
+ * @port: usb-serial port
+ * @buf: data to write
+ * @count: number of bytes to write
*
- * Returns the number of characters actually written, which may be anything
- * from zero to @count. If an error occurs, it returns the negative errno
- * value.
+ * Return: The number of characters buffered, which may be anything from
+ * zero to @count, or a negative errno value.
*/
int usb_serial_generic_write(struct tty_struct *tty,
struct usb_serial_port *port, const unsigned char *buf, int count)
{
int result;
- /* only do something if we have a bulk out endpoint */
if (!port->bulk_out_size)
return -ENODEV;
@@ -210,7 +208,7 @@ int usb_serial_generic_write(struct tty_struct *tty,
return 0;
count = kfifo_in_locked(&port->write_fifo, buf, count, &port->lock);
- result = usb_serial_generic_write_start(port);
+ result = usb_serial_generic_write_start(port, GFP_KERNEL);
if (result)
return result;
@@ -337,10 +335,11 @@ void usb_serial_generic_process_read_urb(struct urb *urb)
if (!urb->actual_length)
return;
-
- /* The per character mucking around with sysrq path it too slow for
- stuff like 3G modems, so shortcircuit it in the 99.9999999% of cases
- where the USB serial is not a console anyway */
+ /*
+ * The per character mucking around with sysrq path it too slow for
+ * stuff like 3G modems, so shortcircuit it in the 99.9999999% of
+ * cases where the USB serial is not a console anyway.
+ */
if (!port->port.console || !port->sysrq)
tty_insert_flip_string(&port->port, ch, urb->actual_length);
else {
@@ -413,7 +412,7 @@ void usb_serial_generic_write_bulk_callback(struct urb *urb)
kfifo_reset_out(&port->write_fifo);
spin_unlock_irqrestore(&port->lock, flags);
} else {
- usb_serial_generic_write_start(port);
+ usb_serial_generic_write_start(port, GFP_ATOMIC);
}
usb_serial_port_softint(port);
@@ -425,8 +424,6 @@ void usb_serial_generic_throttle(struct tty_struct *tty)
struct usb_serial_port *port = tty->driver_data;
unsigned long flags;
- /* Set the throttle request flag. It will be picked up
- * by usb_serial_generic_read_bulk_callback(). */
spin_lock_irqsave(&port->lock, flags);
port->throttle_req = 1;
spin_unlock_irqrestore(&port->lock, flags);
@@ -438,7 +435,6 @@ void usb_serial_generic_unthrottle(struct tty_struct *tty)
struct usb_serial_port *port = tty->driver_data;
int was_throttled;
- /* Clear the throttle flags */
spin_lock_irq(&port->lock);
was_throttled = port->throttled;
port->throttled = port->throttle_req = 0;
@@ -558,10 +554,10 @@ int usb_serial_handle_break(struct usb_serial_port *port)
EXPORT_SYMBOL_GPL(usb_serial_handle_break);
/**
- * usb_serial_handle_dcd_change - handle a change of carrier detect state
- * @port: usb_serial_port structure for the open port
- * @tty: tty_struct structure for the port
- * @status: new carrier detect status, nonzero if active
+ * usb_serial_handle_dcd_change - handle a change of carrier detect state
+ * @port: usb-serial port
+ * @tty: tty for the port
+ * @status: new carrier detect status, nonzero if active
*/
void usb_serial_handle_dcd_change(struct usb_serial_port *usb_port,
struct tty_struct *tty, unsigned int status)
@@ -570,6 +566,16 @@ void usb_serial_handle_dcd_change(struct usb_serial_port *usb_port,
dev_dbg(&usb_port->dev, "%s - status %d\n", __func__, status);
+ if (tty) {
+ struct tty_ldisc *ld = tty_ldisc_ref(tty);
+
+ if (ld) {
+ if (ld->ops->dcd_change)
+ ld->ops->dcd_change(tty, status);
+ tty_ldisc_deref(ld);
+ }
+ }
+
if (status)
wake_up_interruptible(&port->open_wait);
else if (tty && !C_CLOCAL(tty))
@@ -595,7 +601,7 @@ int usb_serial_generic_resume(struct usb_serial *serial)
}
if (port->bulk_out_size) {
- r = usb_serial_generic_write_start(port);
+ r = usb_serial_generic_write_start(port, GFP_NOIO);
if (r < 0)
c++;
}
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index fdf953539c62..e5bdd987b9e8 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -1532,7 +1532,11 @@ static int mos7840_tiocmget(struct tty_struct *tty)
return -ENODEV;
status = mos7840_get_uart_reg(port, MODEM_STATUS_REGISTER, &msr);
+ if (status != 1)
+ return -EIO;
status = mos7840_get_uart_reg(port, MODEM_CONTROL_REGISTER, &mcr);
+ if (status != 1)
+ return -EIO;
result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0)
| ((mcr & MCR_RTS) ? TIOCM_RTS : 0)
| ((mcr & MCR_LOOPBACK) ? TIOCM_LOOP : 0)
diff --git a/drivers/usb/wusbcore/cbaf.c b/drivers/usb/wusbcore/cbaf.c
index 7f78f300f8fb..010f99087506 100644
--- a/drivers/usb/wusbcore/cbaf.c
+++ b/drivers/usb/wusbcore/cbaf.c
@@ -208,9 +208,9 @@ static int cbaf_check(struct cbaf *cbaf)
ar_name = "ASSOCIATE";
ar_assoc = 1;
break;
- };
+ }
break;
- };
+ }
dev_dbg(dev, "Association request #%02u: 0x%04x/%04x "
"(%zu bytes): %s\n",
diff --git a/drivers/usb/wusbcore/devconnect.c b/drivers/usb/wusbcore/devconnect.c
index 33a12788f9ca..e538b72c4e3a 100644
--- a/drivers/usb/wusbcore/devconnect.c
+++ b/drivers/usb/wusbcore/devconnect.c
@@ -973,7 +973,7 @@ int wusb_usb_ncb(struct notifier_block *nb, unsigned long val,
default:
WARN_ON(1);
result = NOTIFY_BAD;
- };
+ }
return result;
}
diff --git a/drivers/usb/wusbcore/wa-hc.c b/drivers/usb/wusbcore/wa-hc.c
index a09b65ebd9bb..6c09b0e4672b 100644
--- a/drivers/usb/wusbcore/wa-hc.c
+++ b/drivers/usb/wusbcore/wa-hc.c
@@ -44,11 +44,11 @@ int wa_create(struct wahc *wa, struct usb_interface *iface)
/* Fill up Data Transfer EP pointers */
wa->dti_epd = &iface->cur_altsetting->endpoint[1].desc;
wa->dto_epd = &iface->cur_altsetting->endpoint[2].desc;
- wa->xfer_result_size = usb_endpoint_maxp(wa->dti_epd);
- wa->xfer_result = kmalloc(wa->xfer_result_size, GFP_KERNEL);
- if (wa->xfer_result == NULL) {
+ wa->dti_buf_size = usb_endpoint_maxp(wa->dti_epd);
+ wa->dti_buf = kmalloc(wa->dti_buf_size, GFP_KERNEL);
+ if (wa->dti_buf == NULL) {
result = -ENOMEM;
- goto error_xfer_result_alloc;
+ goto error_dti_buf_alloc;
}
result = wa_nep_create(wa, iface);
if (result < 0) {
@@ -59,8 +59,8 @@ int wa_create(struct wahc *wa, struct usb_interface *iface)
return 0;
error_nep_create:
- kfree(wa->xfer_result);
-error_xfer_result_alloc:
+ kfree(wa->dti_buf);
+error_dti_buf_alloc:
wa_rpipes_destroy(wa);
error_rpipes_create:
return result;
@@ -76,7 +76,7 @@ void __wa_destroy(struct wahc *wa)
usb_kill_urb(wa->buf_in_urb);
usb_put_urb(wa->buf_in_urb);
}
- kfree(wa->xfer_result);
+ kfree(wa->dti_buf);
wa_nep_destroy(wa);
wa_rpipes_destroy(wa);
}
diff --git a/drivers/usb/wusbcore/wa-hc.h b/drivers/usb/wusbcore/wa-hc.h
index cf250c21e946..41afaa6d01d2 100644
--- a/drivers/usb/wusbcore/wa-hc.h
+++ b/drivers/usb/wusbcore/wa-hc.h
@@ -117,11 +117,17 @@ struct wa_rpipe {
struct wahc *wa;
spinlock_t seg_lock;
struct list_head seg_list;
+ struct list_head list_node;
atomic_t segs_available;
u8 buffer[1]; /* For reads/writes on USB */
};
+enum wa_dti_state {
+ WA_DTI_TRANSFER_RESULT_PENDING,
+ WA_DTI_ISOC_PACKET_STATUS_PENDING
+};
+
/**
* Instance of a HWA Host Controller
*
@@ -178,14 +184,26 @@ struct wahc {
u16 rpipes;
unsigned long *rpipe_bm; /* rpipe usage bitmap */
- spinlock_t rpipe_bm_lock; /* protect rpipe_bm */
+ struct list_head rpipe_delayed_list; /* delayed RPIPES. */
+ spinlock_t rpipe_lock; /* protect rpipe_bm and delayed list */
struct mutex rpipe_mutex; /* assigning resources to endpoints */
+ /*
+ * dti_state is used to track the state of the dti_urb. When dti_state
+ * is WA_DTI_ISOC_PACKET_STATUS_PENDING, dti_isoc_xfer_in_progress and
+ * dti_isoc_xfer_seg identify which xfer the incoming isoc packet status
+ * refers to.
+ */
+ enum wa_dti_state dti_state;
+ u32 dti_isoc_xfer_in_progress;
+ u8 dti_isoc_xfer_seg;
struct urb *dti_urb; /* URB for reading xfer results */
struct urb *buf_in_urb; /* URB for reading data in */
struct edc dti_edc; /* DTI error density counter */
- struct wa_xfer_result *xfer_result; /* real size = dti_ep maxpktsize */
- size_t xfer_result_size;
+ void *dti_buf;
+ size_t dti_buf_size;
+
+ unsigned long dto_in_use; /* protect dto endoint serialization. */
s32 status; /* For reading status */
@@ -239,7 +257,8 @@ static inline void wa_nep_disarm(struct wahc *wa)
/* RPipes */
static inline void wa_rpipe_init(struct wahc *wa)
{
- spin_lock_init(&wa->rpipe_bm_lock);
+ INIT_LIST_HEAD(&wa->rpipe_delayed_list);
+ spin_lock_init(&wa->rpipe_lock);
mutex_init(&wa->rpipe_mutex);
}
@@ -247,6 +266,7 @@ static inline void wa_init(struct wahc *wa)
{
edc_init(&wa->nep_edc);
atomic_set(&wa->notifs_queued, 0);
+ wa->dti_state = WA_DTI_TRANSFER_RESULT_PENDING;
wa_rpipe_init(wa);
edc_init(&wa->dti_edc);
INIT_LIST_HEAD(&wa->xfer_list);
@@ -255,6 +275,7 @@ static inline void wa_init(struct wahc *wa)
spin_lock_init(&wa->xfer_list_lock);
INIT_WORK(&wa->xfer_enqueue_work, wa_urb_enqueue_run);
INIT_WORK(&wa->xfer_error_work, wa_process_errored_transfers_run);
+ wa->dto_in_use = 0;
atomic_set(&wa->xfer_id_count, 1);
}
diff --git a/drivers/usb/wusbcore/wa-rpipe.c b/drivers/usb/wusbcore/wa-rpipe.c
index fd4f1ce6256a..1ed068accb76 100644
--- a/drivers/usb/wusbcore/wa-rpipe.c
+++ b/drivers/usb/wusbcore/wa-rpipe.c
@@ -143,17 +143,18 @@ static void rpipe_init(struct wa_rpipe *rpipe)
kref_init(&rpipe->refcnt);
spin_lock_init(&rpipe->seg_lock);
INIT_LIST_HEAD(&rpipe->seg_list);
+ INIT_LIST_HEAD(&rpipe->list_node);
}
static unsigned rpipe_get_idx(struct wahc *wa, unsigned rpipe_idx)
{
unsigned long flags;
- spin_lock_irqsave(&wa->rpipe_bm_lock, flags);
+ spin_lock_irqsave(&wa->rpipe_lock, flags);
rpipe_idx = find_next_zero_bit(wa->rpipe_bm, wa->rpipes, rpipe_idx);
if (rpipe_idx < wa->rpipes)
set_bit(rpipe_idx, wa->rpipe_bm);
- spin_unlock_irqrestore(&wa->rpipe_bm_lock, flags);
+ spin_unlock_irqrestore(&wa->rpipe_lock, flags);
return rpipe_idx;
}
@@ -162,9 +163,9 @@ static void rpipe_put_idx(struct wahc *wa, unsigned rpipe_idx)
{
unsigned long flags;
- spin_lock_irqsave(&wa->rpipe_bm_lock, flags);
+ spin_lock_irqsave(&wa->rpipe_lock, flags);
clear_bit(rpipe_idx, wa->rpipe_bm);
- spin_unlock_irqrestore(&wa->rpipe_bm_lock, flags);
+ spin_unlock_irqrestore(&wa->rpipe_lock, flags);
}
void rpipe_destroy(struct kref *_rpipe)
@@ -361,8 +362,10 @@ static int rpipe_aim(struct wa_rpipe *rpipe, struct wahc *wa,
epcd->bMaxSequence, 32U), 2U);
rpipe->descr.bMaxDataSequence = epcd_max_sequence - 1;
rpipe->descr.bInterval = ep->desc.bInterval;
- /* FIXME: bOverTheAirInterval */
- rpipe->descr.bOverTheAirInterval = 0; /* 0 if not isoc */
+ if (usb_endpoint_xfer_isoc(&ep->desc))
+ rpipe->descr.bOverTheAirInterval = epcd->bOverTheAirInterval;
+ else
+ rpipe->descr.bOverTheAirInterval = 0; /* 0 if not isoc */
/* FIXME: xmit power & preamble blah blah */
rpipe->descr.bmAttribute = (ep->desc.bmAttributes &
USB_ENDPOINT_XFERTYPE_MASK);
@@ -477,7 +480,7 @@ error:
*/
int wa_rpipes_create(struct wahc *wa)
{
- wa->rpipes = wa->wa_descr->wNumRPipes;
+ wa->rpipes = le16_to_cpu(wa->wa_descr->wNumRPipes);
wa->rpipe_bm = kzalloc(BITS_TO_LONGS(wa->rpipes)*sizeof(unsigned long),
GFP_KERNEL);
if (wa->rpipe_bm == NULL)
diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c
index 6ad02f57c366..f1e9a386beca 100644
--- a/drivers/usb/wusbcore/wa-xfer.c
+++ b/drivers/usb/wusbcore/wa-xfer.c
@@ -107,6 +107,7 @@ enum wa_seg_status {
};
static void wa_xfer_delayed_run(struct wa_rpipe *);
+static int __wa_xfer_delayed_run(struct wa_rpipe *rpipe, int *dto_waiting);
/*
* Life cycle governed by 'struct urb' (the refcount of the struct is
@@ -114,24 +115,24 @@ static void wa_xfer_delayed_run(struct wa_rpipe *);
* struct).
*/
struct wa_seg {
- struct urb urb;
- struct urb *dto_urb; /* for data output? */
+ struct urb tr_urb; /* transfer request urb. */
+ struct urb *isoc_pack_desc_urb; /* for isoc packet descriptor. */
+ struct urb *dto_urb; /* for data output. */
struct list_head list_node; /* for rpipe->req_list */
struct wa_xfer *xfer; /* out xfer */
u8 index; /* which segment we are */
enum wa_seg_status status;
ssize_t result; /* bytes xfered or error */
struct wa_xfer_hdr xfer_hdr;
- u8 xfer_extra[]; /* xtra space for xfer_hdr_ctl */
};
static inline void wa_seg_init(struct wa_seg *seg)
{
- usb_init_urb(&seg->urb);
+ usb_init_urb(&seg->tr_urb);
/* set the remaining memory to 0. */
- memset(((void *)seg) + sizeof(seg->urb), 0,
- sizeof(*seg) - sizeof(seg->urb));
+ memset(((void *)seg) + sizeof(seg->tr_urb), 0,
+ sizeof(*seg) - sizeof(seg->tr_urb));
}
/*
@@ -169,7 +170,7 @@ static inline void wa_xfer_init(struct wa_xfer *xfer)
/*
* Destroy a transfer structure
*
- * Note that freeing xfer->seg[cnt]->urb will free the containing
+ * Note that freeing xfer->seg[cnt]->tr_urb will free the containing
* xfer->seg[cnt] memory that was allocated by __wa_xfer_setup_segs.
*/
static void wa_xfer_destroy(struct kref *_xfer)
@@ -178,9 +179,17 @@ static void wa_xfer_destroy(struct kref *_xfer)
if (xfer->seg) {
unsigned cnt;
for (cnt = 0; cnt < xfer->segs; cnt++) {
- usb_free_urb(xfer->seg[cnt]->dto_urb);
- usb_free_urb(&xfer->seg[cnt]->urb);
+ struct wa_seg *seg = xfer->seg[cnt];
+ if (seg) {
+ usb_free_urb(seg->isoc_pack_desc_urb);
+ if (seg->dto_urb) {
+ kfree(seg->dto_urb->sg);
+ usb_free_urb(seg->dto_urb);
+ }
+ usb_free_urb(&seg->tr_urb);
+ }
}
+ kfree(xfer->seg);
}
kfree(xfer);
}
@@ -196,6 +205,59 @@ static void wa_xfer_put(struct wa_xfer *xfer)
}
/*
+ * Try to get exclusive access to the DTO endpoint resource. Return true
+ * if successful.
+ */
+static inline int __wa_dto_try_get(struct wahc *wa)
+{
+ return (test_and_set_bit(0, &wa->dto_in_use) == 0);
+}
+
+/* Release the DTO endpoint resource. */
+static inline void __wa_dto_put(struct wahc *wa)
+{
+ clear_bit_unlock(0, &wa->dto_in_use);
+}
+
+/* Service RPIPEs that are waiting on the DTO resource. */
+static void wa_check_for_delayed_rpipes(struct wahc *wa)
+{
+ unsigned long flags;
+ int dto_waiting = 0;
+ struct wa_rpipe *rpipe;
+
+ spin_lock_irqsave(&wa->rpipe_lock, flags);
+ while (!list_empty(&wa->rpipe_delayed_list) && !dto_waiting) {
+ rpipe = list_first_entry(&wa->rpipe_delayed_list,
+ struct wa_rpipe, list_node);
+ __wa_xfer_delayed_run(rpipe, &dto_waiting);
+ /* remove this RPIPE from the list if it is not waiting. */
+ if (!dto_waiting) {
+ pr_debug("%s: RPIPE %d serviced and removed from delayed list.\n",
+ __func__,
+ le16_to_cpu(rpipe->descr.wRPipeIndex));
+ list_del_init(&rpipe->list_node);
+ }
+ }
+ spin_unlock_irqrestore(&wa->rpipe_lock, flags);
+}
+
+/* add this RPIPE to the end of the delayed RPIPE list. */
+static void wa_add_delayed_rpipe(struct wahc *wa, struct wa_rpipe *rpipe)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&wa->rpipe_lock, flags);
+ /* add rpipe to the list if it is not already on it. */
+ if (list_empty(&rpipe->list_node)) {
+ pr_debug("%s: adding RPIPE %d to the delayed list.\n",
+ __func__, le16_to_cpu(rpipe->descr.wRPipeIndex));
+ list_add_tail(&rpipe->list_node, &wa->rpipe_delayed_list);
+ }
+ spin_unlock_irqrestore(&wa->rpipe_lock, flags);
+}
+
+/*
* xfer is referenced
*
* xfer->lock has to be unlocked
@@ -232,6 +294,31 @@ static void wa_xfer_completion(struct wa_xfer *xfer)
}
/*
+ * Initialize a transfer's ID
+ *
+ * We need to use a sequential number; if we use the pointer or the
+ * hash of the pointer, it can repeat over sequential transfers and
+ * then it will confuse the HWA....wonder why in hell they put a 32
+ * bit handle in there then.
+ */
+static void wa_xfer_id_init(struct wa_xfer *xfer)
+{
+ xfer->id = atomic_add_return(1, &xfer->wa->xfer_id_count);
+}
+
+/* Return the xfer's ID. */
+static inline u32 wa_xfer_id(struct wa_xfer *xfer)
+{
+ return xfer->id;
+}
+
+/* Return the xfer's ID in transport format (little endian). */
+static inline __le32 wa_xfer_id_le32(struct wa_xfer *xfer)
+{
+ return cpu_to_le32(xfer->id);
+}
+
+/*
* If transfer is done, wrap it up and return true
*
* xfer->lock has to be locked
@@ -253,33 +340,37 @@ static unsigned __wa_xfer_is_done(struct wa_xfer *xfer)
switch (seg->status) {
case WA_SEG_DONE:
if (found_short && seg->result > 0) {
- dev_dbg(dev, "xfer %p#%u: bad short segments (%zu)\n",
- xfer, cnt, seg->result);
+ dev_dbg(dev, "xfer %p ID %08X#%u: bad short segments (%zu)\n",
+ xfer, wa_xfer_id(xfer), cnt,
+ seg->result);
urb->status = -EINVAL;
goto out;
}
urb->actual_length += seg->result;
- if (seg->result < xfer->seg_size
+ if (!(usb_pipeisoc(xfer->urb->pipe))
+ && seg->result < xfer->seg_size
&& cnt != xfer->segs-1)
found_short = 1;
- dev_dbg(dev, "xfer %p#%u: DONE short %d "
+ dev_dbg(dev, "xfer %p ID %08X#%u: DONE short %d "
"result %zu urb->actual_length %d\n",
- xfer, seg->index, found_short, seg->result,
- urb->actual_length);
+ xfer, wa_xfer_id(xfer), seg->index, found_short,
+ seg->result, urb->actual_length);
break;
case WA_SEG_ERROR:
xfer->result = seg->result;
- dev_dbg(dev, "xfer %p#%u: ERROR result %zu\n",
- xfer, seg->index, seg->result);
+ dev_dbg(dev, "xfer %p ID %08X#%u: ERROR result %zu(0x%08zX)\n",
+ xfer, wa_xfer_id(xfer), seg->index, seg->result,
+ seg->result);
goto out;
case WA_SEG_ABORTED:
- dev_dbg(dev, "xfer %p#%u ABORTED: result %d\n",
- xfer, seg->index, urb->status);
+ dev_dbg(dev, "xfer %p ID %08X#%u ABORTED: result %d\n",
+ xfer, wa_xfer_id(xfer), seg->index,
+ urb->status);
xfer->result = urb->status;
goto out;
default:
- dev_warn(dev, "xfer %p#%u: is_done bad state %d\n",
- xfer, cnt, seg->status);
+ dev_warn(dev, "xfer %p ID %08X#%u: is_done bad state %d\n",
+ xfer, wa_xfer_id(xfer), cnt, seg->status);
xfer->result = -EINVAL;
goto out;
}
@@ -290,29 +381,6 @@ out:
}
/*
- * Initialize a transfer's ID
- *
- * We need to use a sequential number; if we use the pointer or the
- * hash of the pointer, it can repeat over sequential transfers and
- * then it will confuse the HWA....wonder why in hell they put a 32
- * bit handle in there then.
- */
-static void wa_xfer_id_init(struct wa_xfer *xfer)
-{
- xfer->id = atomic_add_return(1, &xfer->wa->xfer_id_count);
-}
-
-/*
- * Return the xfer's ID associated with xfer
- *
- * Need to generate a
- */
-static u32 wa_xfer_id(struct wa_xfer *xfer)
-{
- return xfer->id;
-}
-
-/*
* Search for a transfer list ID on the HCD's URB list
*
* For 32 bit architectures, we use the pointer itself; for 64 bits, a
@@ -356,15 +424,11 @@ static void __wa_xfer_abort_cb(struct urb *urb)
*
* The callback (see above) does nothing but freeing up the data by
* putting the URB. Because the URB is allocated at the head of the
- * struct, the whole space we allocated is kfreed.
- *
- * We'll get an 'aborted transaction' xfer result on DTI, that'll
- * politely ignore because at this point the transaction has been
- * marked as aborted already.
+ * struct, the whole space we allocated is kfreed. *
*/
-static void __wa_xfer_abort(struct wa_xfer *xfer)
+static int __wa_xfer_abort(struct wa_xfer *xfer)
{
- int result;
+ int result = -ENOMEM;
struct device *dev = &xfer->wa->usb_iface->dev;
struct wa_xfer_abort_buffer *b;
struct wa_rpipe *rpipe = xfer->ep->hcpriv;
@@ -375,7 +439,7 @@ static void __wa_xfer_abort(struct wa_xfer *xfer)
b->cmd.bLength = sizeof(b->cmd);
b->cmd.bRequestType = WA_XFER_ABORT;
b->cmd.wRPipe = rpipe->descr.wRPipeIndex;
- b->cmd.dwTransferID = wa_xfer_id(xfer);
+ b->cmd.dwTransferID = wa_xfer_id_le32(xfer);
usb_init_urb(&b->urb);
usb_fill_bulk_urb(&b->urb, xfer->wa->usb_dev,
@@ -385,7 +449,7 @@ static void __wa_xfer_abort(struct wa_xfer *xfer)
result = usb_submit_urb(&b->urb, GFP_ATOMIC);
if (result < 0)
goto error_submit;
- return; /* callback frees! */
+ return result; /* callback frees! */
error_submit:
@@ -394,7 +458,7 @@ error_submit:
xfer, result);
kfree(b);
error_kmalloc:
- return;
+ return result;
}
@@ -422,39 +486,53 @@ static ssize_t __wa_xfer_setup_sizes(struct wa_xfer *xfer,
result = sizeof(struct wa_xfer_bi);
break;
case USB_ENDPOINT_XFER_ISOC:
- dev_err(dev, "FIXME: ISOC not implemented\n");
- result = -ENOSYS;
- goto error;
+ if (usb_pipeout(urb->pipe)) {
+ *pxfer_type = WA_XFER_TYPE_ISO;
+ result = sizeof(struct wa_xfer_hwaiso);
+ } else {
+ dev_err(dev, "FIXME: ISOC IN not implemented\n");
+ result = -ENOSYS;
+ goto error;
+ }
+ break;
default:
/* never happens */
BUG();
result = -EINVAL; /* shut gcc up */
- };
+ }
xfer->is_inbound = urb->pipe & USB_DIR_IN ? 1 : 0;
xfer->is_dma = urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP ? 1 : 0;
- xfer->seg_size = le16_to_cpu(rpipe->descr.wBlocks)
- * 1 << (xfer->wa->wa_descr->bRPipeBlockSize - 1);
- /* Compute the segment size and make sure it is a multiple of
- * the maxpktsize (WUSB1.0[8.3.3.1])...not really too much of
- * a check (FIXME) */
+
maxpktsize = le16_to_cpu(rpipe->descr.wMaxPacketSize);
- if (xfer->seg_size < maxpktsize) {
- dev_err(dev, "HW BUG? seg_size %zu smaller than maxpktsize "
- "%zu\n", xfer->seg_size, maxpktsize);
- result = -EINVAL;
- goto error;
- }
- xfer->seg_size = (xfer->seg_size / maxpktsize) * maxpktsize;
- xfer->segs = DIV_ROUND_UP(urb->transfer_buffer_length, xfer->seg_size);
- if (xfer->segs >= WA_SEGS_MAX) {
- dev_err(dev, "BUG? ops, number of segments %d bigger than %d\n",
- (int)(urb->transfer_buffer_length / xfer->seg_size),
- WA_SEGS_MAX);
- result = -EINVAL;
- goto error;
+ if ((rpipe->descr.bmAttribute & 0x3) == USB_ENDPOINT_XFER_ISOC) {
+ xfer->seg_size = maxpktsize;
+ xfer->segs = urb->number_of_packets;
+ } else {
+ xfer->seg_size = le16_to_cpu(rpipe->descr.wBlocks)
+ * 1 << (xfer->wa->wa_descr->bRPipeBlockSize - 1);
+ /* Compute the segment size and make sure it is a multiple of
+ * the maxpktsize (WUSB1.0[8.3.3.1])...not really too much of
+ * a check (FIXME) */
+ if (xfer->seg_size < maxpktsize) {
+ dev_err(dev,
+ "HW BUG? seg_size %zu smaller than maxpktsize %zu\n",
+ xfer->seg_size, maxpktsize);
+ result = -EINVAL;
+ goto error;
+ }
+ xfer->seg_size = (xfer->seg_size / maxpktsize) * maxpktsize;
+ xfer->segs = DIV_ROUND_UP(urb->transfer_buffer_length,
+ xfer->seg_size);
+ if (xfer->segs >= WA_SEGS_MAX) {
+ dev_err(dev, "BUG? oops, number of segments %zu bigger than %d\n",
+ (urb->transfer_buffer_length/xfer->seg_size),
+ WA_SEGS_MAX);
+ result = -EINVAL;
+ goto error;
+ }
+ if (xfer->segs == 0 && *pxfer_type == WA_XFER_TYPE_CTL)
+ xfer->segs = 1;
}
- if (xfer->segs == 0 && *pxfer_type == WA_XFER_TYPE_CTL)
- xfer->segs = 1;
error:
return result;
}
@@ -471,7 +549,7 @@ static void __wa_xfer_setup_hdr0(struct wa_xfer *xfer,
xfer_hdr0->bLength = xfer_hdr_size;
xfer_hdr0->bRequestType = xfer_type;
xfer_hdr0->wRPipe = rpipe->descr.wRPipeIndex;
- xfer_hdr0->dwTransferID = wa_xfer_id(xfer);
+ xfer_hdr0->dwTransferID = wa_xfer_id_le32(xfer);
xfer_hdr0->bTransferSegment = 0;
switch (xfer_type) {
case WA_XFER_TYPE_CTL: {
@@ -484,8 +562,26 @@ static void __wa_xfer_setup_hdr0(struct wa_xfer *xfer,
}
case WA_XFER_TYPE_BI:
break;
- case WA_XFER_TYPE_ISO:
- printk(KERN_ERR "FIXME: ISOC not implemented\n");
+ case WA_XFER_TYPE_ISO: {
+ struct wa_xfer_hwaiso *xfer_iso =
+ container_of(xfer_hdr0, struct wa_xfer_hwaiso, hdr);
+ struct wa_xfer_packet_info_hwaiso *packet_desc =
+ ((void *)xfer_iso) + xfer_hdr_size;
+ struct usb_iso_packet_descriptor *iso_frame_desc =
+ &(xfer->urb->iso_frame_desc[0]);
+ /* populate the isoc section of the transfer request. */
+ xfer_iso->dwNumOfPackets = cpu_to_le32(1);
+ /*
+ * populate isoc packet descriptor. This assumes 1
+ * packet per segment.
+ */
+ packet_desc->wLength = cpu_to_le16(sizeof(*packet_desc) +
+ sizeof(packet_desc->PacketLength[0]));
+ packet_desc->bPacketType = WA_XFER_ISO_PACKET_INFO;
+ packet_desc->PacketLength[0] =
+ cpu_to_le16(iso_frame_desc->length);
+ break;
+ }
default:
BUG();
};
@@ -494,12 +590,12 @@ static void __wa_xfer_setup_hdr0(struct wa_xfer *xfer,
/*
* Callback for the OUT data phase of the segment request
*
- * Check wa_seg_cb(); most comments also apply here because this
+ * Check wa_seg_tr_cb(); most comments also apply here because this
* function does almost the same thing and they work closely
* together.
*
* If the seg request has failed but this DTO phase has succeeded,
- * wa_seg_cb() has already failed the segment and moved the
+ * wa_seg_tr_cb() has already failed the segment and moved the
* status to WA_SEG_ERROR, so this will go through 'case 0' and
* effectively do nothing.
*/
@@ -514,6 +610,10 @@ static void wa_seg_dto_cb(struct urb *urb)
unsigned rpipe_ready = 0;
u8 done = 0;
+ /* free the sg if it was used. */
+ kfree(urb->sg);
+ urb->sg = NULL;
+
switch (urb->status) {
case 0:
spin_lock_irqsave(&xfer->lock, flags);
@@ -559,6 +659,72 @@ static void wa_seg_dto_cb(struct urb *urb)
}
/*
+ * Callback for the isoc packet descriptor phase of the segment request
+ *
+ * Check wa_seg_tr_cb(); most comments also apply here because this
+ * function does almost the same thing and they work closely
+ * together.
+ *
+ * If the seg request has failed but this phase has succeeded,
+ * wa_seg_tr_cb() has already failed the segment and moved the
+ * status to WA_SEG_ERROR, so this will go through 'case 0' and
+ * effectively do nothing.
+ */
+static void wa_seg_iso_pack_desc_cb(struct urb *urb)
+{
+ struct wa_seg *seg = urb->context;
+ struct wa_xfer *xfer = seg->xfer;
+ struct wahc *wa;
+ struct device *dev;
+ struct wa_rpipe *rpipe;
+ unsigned long flags;
+ unsigned rpipe_ready = 0;
+ u8 done = 0;
+
+ switch (urb->status) {
+ case 0:
+ spin_lock_irqsave(&xfer->lock, flags);
+ wa = xfer->wa;
+ dev = &wa->usb_iface->dev;
+ dev_dbg(dev, "iso xfer %p#%u: packet descriptor done\n",
+ xfer, seg->index);
+ if (xfer->is_inbound && seg->status < WA_SEG_PENDING)
+ seg->status = WA_SEG_PENDING;
+ spin_unlock_irqrestore(&xfer->lock, flags);
+ break;
+ case -ECONNRESET: /* URB unlinked; no need to do anything */
+ case -ENOENT: /* as it was done by the who unlinked us */
+ break;
+ default: /* Other errors ... */
+ spin_lock_irqsave(&xfer->lock, flags);
+ wa = xfer->wa;
+ dev = &wa->usb_iface->dev;
+ rpipe = xfer->ep->hcpriv;
+ pr_err_ratelimited("iso xfer %p#%u: packet descriptor error %d\n",
+ xfer, seg->index, urb->status);
+ if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS,
+ EDC_ERROR_TIMEFRAME)){
+ dev_err(dev, "DTO: URB max acceptable errors exceeded, resetting device\n");
+ wa_reset_all(wa);
+ }
+ if (seg->status != WA_SEG_ERROR) {
+ usb_unlink_urb(seg->dto_urb);
+ seg->status = WA_SEG_ERROR;
+ seg->result = urb->status;
+ xfer->segs_done++;
+ __wa_xfer_abort(xfer);
+ rpipe_ready = rpipe_avail_inc(rpipe);
+ done = __wa_xfer_is_done(xfer);
+ }
+ spin_unlock_irqrestore(&xfer->lock, flags);
+ if (done)
+ wa_xfer_completion(xfer);
+ if (rpipe_ready)
+ wa_xfer_delayed_run(rpipe);
+ }
+}
+
+/*
* Callback for the segment request
*
* If successful transition state (unless already transitioned or
@@ -572,11 +738,11 @@ static void wa_seg_dto_cb(struct urb *urb)
* We have to check before setting the status to WA_SEG_PENDING
* because sometimes the xfer result callback arrives before this
* callback (geeeeeeze), so it might happen that we are already in
- * another state. As well, we don't set it if the transfer is inbound,
+ * another state. As well, we don't set it if the transfer is not inbound,
* as in that case, wa_seg_dto_cb will do it when the OUT data phase
* finishes.
*/
-static void wa_seg_cb(struct urb *urb)
+static void wa_seg_tr_cb(struct urb *urb)
{
struct wa_seg *seg = urb->context;
struct wa_xfer *xfer = seg->xfer;
@@ -592,8 +758,11 @@ static void wa_seg_cb(struct urb *urb)
spin_lock_irqsave(&xfer->lock, flags);
wa = xfer->wa;
dev = &wa->usb_iface->dev;
- dev_dbg(dev, "xfer %p#%u: request done\n", xfer, seg->index);
- if (xfer->is_inbound && seg->status < WA_SEG_PENDING)
+ dev_dbg(dev, "xfer %p ID 0x%08X#%u: request done\n",
+ xfer, wa_xfer_id(xfer), seg->index);
+ if (xfer->is_inbound &&
+ seg->status < WA_SEG_PENDING &&
+ !(usb_pipeisoc(xfer->urb->pipe)))
seg->status = WA_SEG_PENDING;
spin_unlock_irqrestore(&xfer->lock, flags);
break;
@@ -606,14 +775,16 @@ static void wa_seg_cb(struct urb *urb)
dev = &wa->usb_iface->dev;
rpipe = xfer->ep->hcpriv;
if (printk_ratelimit())
- dev_err(dev, "xfer %p#%u: request error %d\n",
- xfer, seg->index, urb->status);
+ dev_err(dev, "xfer %p ID 0x%08X#%u: request error %d\n",
+ xfer, wa_xfer_id(xfer), seg->index,
+ urb->status);
if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS,
EDC_ERROR_TIMEFRAME)){
dev_err(dev, "DTO: URB max acceptable errors "
"exceeded, resetting device\n");
wa_reset_all(wa);
}
+ usb_unlink_urb(seg->isoc_pack_desc_urb);
usb_unlink_urb(seg->dto_urb);
seg->status = WA_SEG_ERROR;
seg->result = urb->status;
@@ -629,9 +800,11 @@ static void wa_seg_cb(struct urb *urb)
}
}
-/* allocate an SG list to store bytes_to_transfer bytes and copy the
+/*
+ * Allocate an SG list to store bytes_to_transfer bytes and copy the
* subset of the in_sg that matches the buffer subset
- * we are about to transfer. */
+ * we are about to transfer.
+ */
static struct scatterlist *wa_xfer_create_subset_sg(struct scatterlist *in_sg,
const unsigned int bytes_transferred,
const unsigned int bytes_to_transfer, unsigned int *out_num_sgs)
@@ -710,6 +883,74 @@ static struct scatterlist *wa_xfer_create_subset_sg(struct scatterlist *in_sg,
}
/*
+ * Populate DMA buffer info for the isoc dto urb.
+ */
+static void __wa_populate_dto_urb_iso(struct wa_xfer *xfer,
+ struct wa_seg *seg, int curr_iso_frame)
+{
+ /*
+ * dto urb buffer address and size pulled from
+ * iso_frame_desc.
+ */
+ seg->dto_urb->transfer_dma = xfer->urb->transfer_dma +
+ xfer->urb->iso_frame_desc[curr_iso_frame].offset;
+ seg->dto_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ seg->dto_urb->sg = NULL;
+ seg->dto_urb->num_sgs = 0;
+ seg->dto_urb->transfer_buffer_length =
+ xfer->urb->iso_frame_desc[curr_iso_frame].length;
+}
+
+/*
+ * Populate buffer ptr and size, DMA buffer or SG list for the dto urb.
+ */
+static int __wa_populate_dto_urb(struct wa_xfer *xfer,
+ struct wa_seg *seg, size_t buf_itr_offset, size_t buf_itr_size)
+{
+ int result = 0;
+
+ if (xfer->is_dma) {
+ seg->dto_urb->transfer_dma =
+ xfer->urb->transfer_dma + buf_itr_offset;
+ seg->dto_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ seg->dto_urb->sg = NULL;
+ seg->dto_urb->num_sgs = 0;
+ } else {
+ /* do buffer or SG processing. */
+ seg->dto_urb->transfer_flags &=
+ ~URB_NO_TRANSFER_DMA_MAP;
+ /* this should always be 0 before a resubmit. */
+ seg->dto_urb->num_mapped_sgs = 0;
+
+ if (xfer->urb->transfer_buffer) {
+ seg->dto_urb->transfer_buffer =
+ xfer->urb->transfer_buffer +
+ buf_itr_offset;
+ seg->dto_urb->sg = NULL;
+ seg->dto_urb->num_sgs = 0;
+ } else {
+ seg->dto_urb->transfer_buffer = NULL;
+
+ /*
+ * allocate an SG list to store seg_size bytes
+ * and copy the subset of the xfer->urb->sg that
+ * matches the buffer subset we are about to
+ * read.
+ */
+ seg->dto_urb->sg = wa_xfer_create_subset_sg(
+ xfer->urb->sg,
+ buf_itr_offset, buf_itr_size,
+ &(seg->dto_urb->num_sgs));
+ if (!(seg->dto_urb->sg))
+ result = -ENOMEM;
+ }
+ }
+ seg->dto_urb->transfer_buffer_length = buf_itr_size;
+
+ return result;
+}
+
+/*
* Allocate the segs array and initialize each of them
*
* The segments are freed by wa_xfer_destroy() when the xfer use count
@@ -725,7 +966,7 @@ static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size)
struct usb_device *usb_dev = xfer->wa->usb_dev;
const struct usb_endpoint_descriptor *dto_epd = xfer->wa->dto_epd;
struct wa_seg *seg;
- size_t buf_itr, buf_size, buf_itr_size;
+ size_t buf_itr, buf_size, buf_itr_size, iso_pkt_descr_size = 0;
result = -ENOMEM;
xfer->seg = kcalloc(xfer->segs, sizeof(xfer->seg[0]), GFP_ATOMIC);
@@ -733,6 +974,17 @@ static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size)
goto error_segs_kzalloc;
buf_itr = 0;
buf_size = xfer->urb->transfer_buffer_length;
+
+ if (usb_pipeisoc(xfer->urb->pipe)) {
+ /*
+ * This calculation assumes one isoc packet per xfer segment.
+ * It will need to be updated if this changes.
+ */
+ iso_pkt_descr_size = sizeof(struct wa_xfer_packet_info_hwaiso) +
+ sizeof(__le16);
+ alloc_size += iso_pkt_descr_size;
+ }
+
for (cnt = 0; cnt < xfer->segs; cnt++) {
seg = xfer->seg[cnt] = kmalloc(alloc_size, GFP_ATOMIC);
if (seg == NULL)
@@ -740,11 +992,11 @@ static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size)
wa_seg_init(seg);
seg->xfer = xfer;
seg->index = cnt;
- usb_fill_bulk_urb(&seg->urb, usb_dev,
+ usb_fill_bulk_urb(&seg->tr_urb, usb_dev,
usb_sndbulkpipe(usb_dev,
dto_epd->bEndpointAddress),
&seg->xfer_hdr, xfer_hdr_size,
- wa_seg_cb, seg);
+ wa_seg_tr_cb, seg);
buf_itr_size = min(buf_size, xfer->seg_size);
if (xfer->is_inbound == 0 && buf_size > 0) {
/* outbound data. */
@@ -756,69 +1008,56 @@ static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size)
usb_sndbulkpipe(usb_dev,
dto_epd->bEndpointAddress),
NULL, 0, wa_seg_dto_cb, seg);
- if (xfer->is_dma) {
- seg->dto_urb->transfer_dma =
- xfer->urb->transfer_dma + buf_itr;
- seg->dto_urb->transfer_flags |=
- URB_NO_TRANSFER_DMA_MAP;
- seg->dto_urb->transfer_buffer = NULL;
- seg->dto_urb->sg = NULL;
- seg->dto_urb->num_sgs = 0;
+
+ if (usb_pipeisoc(xfer->urb->pipe)) {
+ /* iso packet descriptor. */
+ seg->isoc_pack_desc_urb =
+ usb_alloc_urb(0, GFP_ATOMIC);
+ if (seg->isoc_pack_desc_urb == NULL)
+ goto error_iso_pack_desc_alloc;
+ /*
+ * The buffer for the isoc packet descriptor
+ * after the transfer request header in the
+ * segment object memory buffer.
+ */
+ usb_fill_bulk_urb(
+ seg->isoc_pack_desc_urb, usb_dev,
+ usb_sndbulkpipe(usb_dev,
+ dto_epd->bEndpointAddress),
+ (void *)(&seg->xfer_hdr) +
+ xfer_hdr_size,
+ iso_pkt_descr_size,
+ wa_seg_iso_pack_desc_cb, seg);
+
+ /* fill in the xfer buffer information. */
+ __wa_populate_dto_urb_iso(xfer, seg, cnt);
} else {
- /* do buffer or SG processing. */
- seg->dto_urb->transfer_flags &=
- ~URB_NO_TRANSFER_DMA_MAP;
- /* this should always be 0 before a resubmit. */
- seg->dto_urb->num_mapped_sgs = 0;
-
- if (xfer->urb->transfer_buffer) {
- seg->dto_urb->transfer_buffer =
- xfer->urb->transfer_buffer +
- buf_itr;
- seg->dto_urb->sg = NULL;
- seg->dto_urb->num_sgs = 0;
- } else {
- /* allocate an SG list to store seg_size
- bytes and copy the subset of the
- xfer->urb->sg that matches the
- buffer subset we are about to read.
- */
- seg->dto_urb->sg =
- wa_xfer_create_subset_sg(
- xfer->urb->sg,
- buf_itr, buf_itr_size,
- &(seg->dto_urb->num_sgs));
-
- if (!(seg->dto_urb->sg)) {
- seg->dto_urb->num_sgs = 0;
- goto error_sg_alloc;
- }
-
- seg->dto_urb->transfer_buffer = NULL;
- }
+ /* fill in the xfer buffer information. */
+ result = __wa_populate_dto_urb(xfer, seg,
+ buf_itr, buf_itr_size);
+ if (result < 0)
+ goto error_seg_outbound_populate;
+
+ buf_itr += buf_itr_size;
+ buf_size -= buf_itr_size;
}
- seg->dto_urb->transfer_buffer_length = buf_itr_size;
}
seg->status = WA_SEG_READY;
- buf_itr += buf_itr_size;
- buf_size -= buf_itr_size;
}
return 0;
-error_sg_alloc:
+ /*
+ * Free the memory for the current segment which failed to init.
+ * Use the fact that cnt is left at were it failed. The remaining
+ * segments will be cleaned up by wa_xfer_destroy.
+ */
+error_iso_pack_desc_alloc:
+error_seg_outbound_populate:
usb_free_urb(xfer->seg[cnt]->dto_urb);
error_dto_alloc:
kfree(xfer->seg[cnt]);
- cnt--;
+ xfer->seg[cnt] = NULL;
error_seg_kmalloc:
- /* use the fact that cnt is left at were it failed */
- for (; cnt >= 0; cnt--) {
- if (xfer->seg[cnt] && xfer->is_inbound == 0) {
- usb_free_urb(xfer->seg[cnt]->dto_urb);
- kfree(xfer->seg[cnt]->dto_urb->sg);
- }
- kfree(xfer->seg[cnt]);
- }
error_segs_kzalloc:
return result;
}
@@ -856,21 +1095,50 @@ static int __wa_xfer_setup(struct wa_xfer *xfer, struct urb *urb)
wa_xfer_id_init(xfer);
__wa_xfer_setup_hdr0(xfer, xfer_hdr0, xfer_type, xfer_hdr_size);
- /* Fill remainig headers */
+ /* Fill remaining headers */
xfer_hdr = xfer_hdr0;
- transfer_size = urb->transfer_buffer_length;
- xfer_hdr0->dwTransferLength = transfer_size > xfer->seg_size ?
- xfer->seg_size : transfer_size;
- transfer_size -= xfer->seg_size;
- for (cnt = 1; cnt < xfer->segs; cnt++) {
- xfer_hdr = &xfer->seg[cnt]->xfer_hdr;
- memcpy(xfer_hdr, xfer_hdr0, xfer_hdr_size);
- xfer_hdr->bTransferSegment = cnt;
- xfer_hdr->dwTransferLength = transfer_size > xfer->seg_size ?
- cpu_to_le32(xfer->seg_size)
- : cpu_to_le32(transfer_size);
- xfer->seg[cnt]->status = WA_SEG_READY;
+ if (xfer_type == WA_XFER_TYPE_ISO) {
+ xfer_hdr0->dwTransferLength =
+ cpu_to_le32(xfer->urb->iso_frame_desc[0].length);
+ for (cnt = 1; cnt < xfer->segs; cnt++) {
+ struct usb_iso_packet_descriptor *iso_frame_desc =
+ &(xfer->urb->iso_frame_desc[cnt]);
+ struct wa_xfer_packet_info_hwaiso *packet_desc;
+
+ xfer_hdr = &xfer->seg[cnt]->xfer_hdr;
+ packet_desc = ((void *)xfer_hdr) + xfer_hdr_size;
+ /*
+ * Copy values from the 0th header and isoc packet
+ * descriptor. Segment specific values are set below.
+ */
+ memcpy(xfer_hdr, xfer_hdr0,
+ xfer_hdr_size + sizeof(*packet_desc));
+ xfer_hdr->bTransferSegment = cnt;
+ xfer_hdr->dwTransferLength =
+ cpu_to_le32(iso_frame_desc->length);
+ /* populate isoc packet descriptor length. */
+ packet_desc->PacketLength[0] =
+ cpu_to_le16(iso_frame_desc->length);
+
+ xfer->seg[cnt]->status = WA_SEG_READY;
+ }
+ } else {
+ transfer_size = urb->transfer_buffer_length;
+ xfer_hdr0->dwTransferLength = transfer_size > xfer->seg_size ?
+ cpu_to_le32(xfer->seg_size) :
+ cpu_to_le32(transfer_size);
transfer_size -= xfer->seg_size;
+ for (cnt = 1; cnt < xfer->segs; cnt++) {
+ xfer_hdr = &xfer->seg[cnt]->xfer_hdr;
+ memcpy(xfer_hdr, xfer_hdr0, xfer_hdr_size);
+ xfer_hdr->bTransferSegment = cnt;
+ xfer_hdr->dwTransferLength =
+ transfer_size > xfer->seg_size ?
+ cpu_to_le32(xfer->seg_size)
+ : cpu_to_le32(transfer_size);
+ xfer->seg[cnt]->status = WA_SEG_READY;
+ transfer_size -= xfer->seg_size;
+ }
}
xfer_hdr->bTransferSegment |= 0x80; /* this is the last segment */
result = 0;
@@ -885,20 +1153,35 @@ error_setup_sizes:
* rpipe->seg_lock is held!
*/
static int __wa_seg_submit(struct wa_rpipe *rpipe, struct wa_xfer *xfer,
- struct wa_seg *seg)
+ struct wa_seg *seg, int *dto_done)
{
int result;
- result = usb_submit_urb(&seg->urb, GFP_ATOMIC);
+
+ /* default to done unless we encounter a multi-frame isoc segment. */
+ *dto_done = 1;
+
+ /* submit the transfer request. */
+ result = usb_submit_urb(&seg->tr_urb, GFP_ATOMIC);
if (result < 0) {
- printk(KERN_ERR "xfer %p#%u: REQ submit failed: %d\n",
- xfer, seg->index, result);
+ pr_err("%s: xfer %p#%u: REQ submit failed: %d\n",
+ __func__, xfer, seg->index, result);
goto error_seg_submit;
}
+ /* submit the isoc packet descriptor if present. */
+ if (seg->isoc_pack_desc_urb) {
+ result = usb_submit_urb(seg->isoc_pack_desc_urb, GFP_ATOMIC);
+ if (result < 0) {
+ pr_err("%s: xfer %p#%u: ISO packet descriptor submit failed: %d\n",
+ __func__, xfer, seg->index, result);
+ goto error_iso_pack_desc_submit;
+ }
+ }
+ /* submit the out data if this is an out request. */
if (seg->dto_urb) {
result = usb_submit_urb(seg->dto_urb, GFP_ATOMIC);
if (result < 0) {
- printk(KERN_ERR "xfer %p#%u: DTO submit failed: %d\n",
- xfer, seg->index, result);
+ pr_err("%s: xfer %p#%u: DTO submit failed: %d\n",
+ __func__, xfer, seg->index, result);
goto error_dto_submit;
}
}
@@ -907,7 +1190,9 @@ static int __wa_seg_submit(struct wa_rpipe *rpipe, struct wa_xfer *xfer,
return 0;
error_dto_submit:
- usb_unlink_urb(&seg->urb);
+ usb_unlink_urb(seg->isoc_pack_desc_urb);
+error_iso_pack_desc_submit:
+ usb_unlink_urb(&seg->tr_urb);
error_seg_submit:
seg->status = WA_SEG_ERROR;
seg->result = result;
@@ -915,30 +1200,37 @@ error_seg_submit:
}
/*
- * Execute more queued request segments until the maximum concurrent allowed
+ * Execute more queued request segments until the maximum concurrent allowed.
+ * Return true if the DTO resource was acquired and released.
*
* The ugly unlock/lock sequence on the error path is needed as the
* xfer->lock normally nests the seg_lock and not viceversa.
- *
*/
-static void wa_xfer_delayed_run(struct wa_rpipe *rpipe)
+static int __wa_xfer_delayed_run(struct wa_rpipe *rpipe, int *dto_waiting)
{
- int result;
+ int result, dto_acquired = 0, dto_done = 0;
struct device *dev = &rpipe->wa->usb_iface->dev;
struct wa_seg *seg;
struct wa_xfer *xfer;
unsigned long flags;
+ *dto_waiting = 0;
+
spin_lock_irqsave(&rpipe->seg_lock, flags);
while (atomic_read(&rpipe->segs_available) > 0
- && !list_empty(&rpipe->seg_list)) {
+ && !list_empty(&rpipe->seg_list)
+ && (dto_acquired = __wa_dto_try_get(rpipe->wa))) {
seg = list_first_entry(&(rpipe->seg_list), struct wa_seg,
list_node);
list_del(&seg->list_node);
xfer = seg->xfer;
- result = __wa_seg_submit(rpipe, xfer, seg);
- dev_dbg(dev, "xfer %p#%u submitted from delayed [%d segments available] %d\n",
- xfer, seg->index, atomic_read(&rpipe->segs_available), result);
+ result = __wa_seg_submit(rpipe, xfer, seg, &dto_done);
+ /* release the dto resource if this RPIPE is done with it. */
+ if (dto_done)
+ __wa_dto_put(rpipe->wa);
+ dev_dbg(dev, "xfer %p ID %08X#%u submitted from delayed [%d segments available] %d\n",
+ xfer, wa_xfer_id(xfer), seg->index,
+ atomic_read(&rpipe->segs_available), result);
if (unlikely(result < 0)) {
spin_unlock_irqrestore(&rpipe->seg_lock, flags);
spin_lock_irqsave(&xfer->lock, flags);
@@ -948,7 +1240,37 @@ static void wa_xfer_delayed_run(struct wa_rpipe *rpipe)
spin_lock_irqsave(&rpipe->seg_lock, flags);
}
}
+ /*
+ * Mark this RPIPE as waiting if dto was not acquired, there are
+ * delayed segs and no active transfers to wake us up later.
+ */
+ if (!dto_acquired && !list_empty(&rpipe->seg_list)
+ && (atomic_read(&rpipe->segs_available) ==
+ le16_to_cpu(rpipe->descr.wRequests)))
+ *dto_waiting = 1;
+
spin_unlock_irqrestore(&rpipe->seg_lock, flags);
+
+ return dto_done;
+}
+
+static void wa_xfer_delayed_run(struct wa_rpipe *rpipe)
+{
+ int dto_waiting;
+ int dto_done = __wa_xfer_delayed_run(rpipe, &dto_waiting);
+
+ /*
+ * If this RPIPE is waiting on the DTO resource, add it to the tail of
+ * the waiting list.
+ * Otherwise, if the WA DTO resource was acquired and released by
+ * __wa_xfer_delayed_run, another RPIPE may have attempted to acquire
+ * DTO and failed during that time. Check the delayed list and process
+ * any waiters. Start searching from the next RPIPE index.
+ */
+ if (dto_waiting)
+ wa_add_delayed_rpipe(rpipe->wa, rpipe);
+ else if (dto_done)
+ wa_check_for_delayed_rpipes(rpipe->wa);
}
/*
@@ -960,7 +1282,7 @@ static void wa_xfer_delayed_run(struct wa_rpipe *rpipe)
*/
static int __wa_xfer_submit(struct wa_xfer *xfer)
{
- int result;
+ int result, dto_acquired = 0, dto_done = 0, dto_waiting = 0;
struct wahc *wa = xfer->wa;
struct device *dev = &wa->usb_iface->dev;
unsigned cnt;
@@ -979,27 +1301,56 @@ static int __wa_xfer_submit(struct wa_xfer *xfer)
result = 0;
spin_lock_irqsave(&rpipe->seg_lock, flags);
for (cnt = 0; cnt < xfer->segs; cnt++) {
+ int delay_seg = 1;
+
available = atomic_read(&rpipe->segs_available);
empty = list_empty(&rpipe->seg_list);
seg = xfer->seg[cnt];
- dev_dbg(dev, "xfer %p#%u: available %u empty %u (%s)\n",
- xfer, cnt, available, empty,
+ dev_dbg(dev, "xfer %p ID 0x%08X#%u: available %u empty %u (%s)\n",
+ xfer, wa_xfer_id(xfer), cnt, available, empty,
available == 0 || !empty ? "delayed" : "submitted");
- if (available == 0 || !empty) {
- dev_dbg(dev, "xfer %p#%u: delayed\n", xfer, cnt);
+ if (available && empty) {
+ /*
+ * Only attempt to acquire DTO if we have a segment
+ * to send.
+ */
+ dto_acquired = __wa_dto_try_get(rpipe->wa);
+ if (dto_acquired) {
+ delay_seg = 0;
+ result = __wa_seg_submit(rpipe, xfer, seg,
+ &dto_done);
+ if (dto_done)
+ __wa_dto_put(rpipe->wa);
+
+ if (result < 0) {
+ __wa_xfer_abort(xfer);
+ goto error_seg_submit;
+ }
+ }
+ }
+
+ if (delay_seg) {
seg->status = WA_SEG_DELAYED;
list_add_tail(&seg->list_node, &rpipe->seg_list);
- } else {
- result = __wa_seg_submit(rpipe, xfer, seg);
- if (result < 0) {
- __wa_xfer_abort(xfer);
- goto error_seg_submit;
- }
}
xfer->segs_submitted++;
}
error_seg_submit:
+ /*
+ * Mark this RPIPE as waiting if dto was not acquired, there are
+ * delayed segs and no active transfers to wake us up later.
+ */
+ if (!dto_acquired && !list_empty(&rpipe->seg_list)
+ && (atomic_read(&rpipe->segs_available) ==
+ le16_to_cpu(rpipe->descr.wRequests)))
+ dto_waiting = 1;
spin_unlock_irqrestore(&rpipe->seg_lock, flags);
+
+ if (dto_waiting)
+ wa_add_delayed_rpipe(rpipe->wa, rpipe);
+ else if (dto_done)
+ wa_check_for_delayed_rpipes(rpipe->wa);
+
return result;
}
@@ -1025,7 +1376,7 @@ error_seg_submit:
* result never kicks in, the xfer will timeout from the USB code and
* dequeue() will be called.
*/
-static void wa_urb_enqueue_b(struct wa_xfer *xfer)
+static int wa_urb_enqueue_b(struct wa_xfer *xfer)
{
int result;
unsigned long flags;
@@ -1036,18 +1387,22 @@ static void wa_urb_enqueue_b(struct wa_xfer *xfer)
unsigned done;
result = rpipe_get_by_ep(wa, xfer->ep, urb, xfer->gfp);
- if (result < 0)
+ if (result < 0) {
+ pr_err("%s: error_rpipe_get\n", __func__);
goto error_rpipe_get;
+ }
result = -ENODEV;
/* FIXME: segmentation broken -- kills DWA */
mutex_lock(&wusbhc->mutex); /* get a WUSB dev */
if (urb->dev == NULL) {
mutex_unlock(&wusbhc->mutex);
+ pr_err("%s: error usb dev gone\n", __func__);
goto error_dev_gone;
}
wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, urb->dev);
if (wusb_dev == NULL) {
mutex_unlock(&wusbhc->mutex);
+ pr_err("%s: error wusb dev gone\n", __func__);
goto error_dev_gone;
}
mutex_unlock(&wusbhc->mutex);
@@ -1055,21 +1410,28 @@ static void wa_urb_enqueue_b(struct wa_xfer *xfer)
spin_lock_irqsave(&xfer->lock, flags);
xfer->wusb_dev = wusb_dev;
result = urb->status;
- if (urb->status != -EINPROGRESS)
+ if (urb->status != -EINPROGRESS) {
+ pr_err("%s: error_dequeued\n", __func__);
goto error_dequeued;
+ }
result = __wa_xfer_setup(xfer, urb);
- if (result < 0)
+ if (result < 0) {
+ pr_err("%s: error_xfer_setup\n", __func__);
goto error_xfer_setup;
+ }
result = __wa_xfer_submit(xfer);
- if (result < 0)
+ if (result < 0) {
+ pr_err("%s: error_xfer_submit\n", __func__);
goto error_xfer_submit;
+ }
spin_unlock_irqrestore(&xfer->lock, flags);
- return;
+ return 0;
- /* this is basically wa_xfer_completion() broken up wa_xfer_giveback()
- * does a wa_xfer_put() that will call wa_xfer_destroy() and clean
- * upundo setup().
+ /*
+ * this is basically wa_xfer_completion() broken up wa_xfer_giveback()
+ * does a wa_xfer_put() that will call wa_xfer_destroy() and undo
+ * setup().
*/
error_xfer_setup:
error_dequeued:
@@ -1081,8 +1443,7 @@ error_dev_gone:
rpipe_put(xfer->ep->hcpriv);
error_rpipe_get:
xfer->result = result;
- wa_xfer_giveback(xfer);
- return;
+ return result;
error_xfer_submit:
done = __wa_xfer_is_done(xfer);
@@ -1090,6 +1451,8 @@ error_xfer_submit:
spin_unlock_irqrestore(&xfer->lock, flags);
if (done)
wa_xfer_completion(xfer);
+ /* return success since the completion routine will run. */
+ return 0;
}
/*
@@ -1123,7 +1486,8 @@ void wa_urb_enqueue_run(struct work_struct *ws)
list_del_init(&xfer->list_node);
urb = xfer->urb;
- wa_urb_enqueue_b(xfer);
+ if (wa_urb_enqueue_b(xfer) < 0)
+ wa_xfer_giveback(xfer);
usb_put_urb(urb); /* taken when queuing */
}
}
@@ -1229,7 +1593,19 @@ int wa_urb_enqueue(struct wahc *wa, struct usb_host_endpoint *ep,
spin_unlock_irqrestore(&wa->xfer_list_lock, my_flags);
queue_work(wusbd, &wa->xfer_enqueue_work);
} else {
- wa_urb_enqueue_b(xfer);
+ result = wa_urb_enqueue_b(xfer);
+ if (result < 0) {
+ /*
+ * URB submit/enqueue failed. Clean up, return an
+ * error and do not run the callback. This avoids
+ * an infinite submit/complete loop.
+ */
+ dev_err(dev, "%s: URB enqueue failed: %d\n",
+ __func__, result);
+ wa_put(xfer->wa);
+ wa_xfer_put(xfer);
+ return result;
+ }
}
return 0;
@@ -1264,7 +1640,7 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb)
struct wa_xfer *xfer;
struct wa_seg *seg;
struct wa_rpipe *rpipe;
- unsigned cnt;
+ unsigned cnt, done = 0, xfer_abort_pending;
unsigned rpipe_ready = 0;
xfer = urb->hcpriv;
@@ -1278,6 +1654,7 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb)
goto out;
}
spin_lock_irqsave(&xfer->lock, flags);
+ pr_debug("%s: DEQUEUE xfer id 0x%08X\n", __func__, wa_xfer_id(xfer));
rpipe = xfer->ep->hcpriv;
if (rpipe == NULL) {
pr_debug("%s: xfer id 0x%08X has no RPIPE. %s",
@@ -1293,9 +1670,11 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb)
if (xfer->seg == NULL) /* still hasn't reached */
goto out_unlock; /* setup(), enqueue_b() completes */
/* Ok, the xfer is in flight already, it's been setup and submitted.*/
- __wa_xfer_abort(xfer);
+ xfer_abort_pending = __wa_xfer_abort(xfer) >= 0;
for (cnt = 0; cnt < xfer->segs; cnt++) {
seg = xfer->seg[cnt];
+ pr_debug("%s: xfer id 0x%08X#%d status = %d\n",
+ __func__, wa_xfer_id(xfer), cnt, seg->status);
switch (seg->status) {
case WA_SEG_NOTREADY:
case WA_SEG_READY:
@@ -1304,42 +1683,50 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb)
WARN_ON(1);
break;
case WA_SEG_DELAYED:
+ /*
+ * delete from rpipe delayed list. If no segments on
+ * this xfer have been submitted, __wa_xfer_is_done will
+ * trigger a giveback below. Otherwise, the submitted
+ * segments will be completed in the DTI interrupt.
+ */
seg->status = WA_SEG_ABORTED;
spin_lock_irqsave(&rpipe->seg_lock, flags2);
list_del(&seg->list_node);
xfer->segs_done++;
- rpipe_ready = rpipe_avail_inc(rpipe);
spin_unlock_irqrestore(&rpipe->seg_lock, flags2);
break;
- case WA_SEG_SUBMITTED:
- seg->status = WA_SEG_ABORTED;
- usb_unlink_urb(&seg->urb);
- if (xfer->is_inbound == 0)
- usb_unlink_urb(seg->dto_urb);
- xfer->segs_done++;
- rpipe_ready = rpipe_avail_inc(rpipe);
- break;
- case WA_SEG_PENDING:
- seg->status = WA_SEG_ABORTED;
- xfer->segs_done++;
- rpipe_ready = rpipe_avail_inc(rpipe);
- break;
- case WA_SEG_DTI_PENDING:
- usb_unlink_urb(wa->dti_urb);
- seg->status = WA_SEG_ABORTED;
- xfer->segs_done++;
- rpipe_ready = rpipe_avail_inc(rpipe);
- break;
case WA_SEG_DONE:
case WA_SEG_ERROR:
case WA_SEG_ABORTED:
break;
+ /*
+ * In the states below, the HWA device already knows
+ * about the transfer. If an abort request was sent,
+ * allow the HWA to process it and wait for the
+ * results. Otherwise, the DTI state and seg completed
+ * counts can get out of sync.
+ */
+ case WA_SEG_SUBMITTED:
+ case WA_SEG_PENDING:
+ case WA_SEG_DTI_PENDING:
+ /*
+ * Check if the abort was successfully sent. This could
+ * be false if the HWA has been removed but we haven't
+ * gotten the disconnect notification yet.
+ */
+ if (!xfer_abort_pending) {
+ seg->status = WA_SEG_ABORTED;
+ rpipe_ready = rpipe_avail_inc(rpipe);
+ xfer->segs_done++;
+ }
+ break;
}
}
xfer->result = urb->status; /* -ENOENT or -ECONNRESET */
- __wa_xfer_is_done(xfer);
+ done = __wa_xfer_is_done(xfer);
spin_unlock_irqrestore(&xfer->lock, flags);
- wa_xfer_completion(xfer);
+ if (done)
+ wa_xfer_completion(xfer);
if (rpipe_ready)
wa_xfer_delayed_run(rpipe);
return 0;
@@ -1410,13 +1797,56 @@ static int wa_xfer_status_to_errno(u8 status)
}
/*
+ * If a last segment flag and/or a transfer result error is encountered,
+ * no other segment transfer results will be returned from the device.
+ * Mark the remaining submitted or pending xfers as completed so that
+ * the xfer will complete cleanly.
+ */
+static void wa_complete_remaining_xfer_segs(struct wa_xfer *xfer,
+ struct wa_seg *incoming_seg)
+{
+ int index;
+ struct wa_rpipe *rpipe = xfer->ep->hcpriv;
+
+ for (index = incoming_seg->index + 1; index < xfer->segs_submitted;
+ index++) {
+ struct wa_seg *current_seg = xfer->seg[index];
+
+ BUG_ON(current_seg == NULL);
+
+ switch (current_seg->status) {
+ case WA_SEG_SUBMITTED:
+ case WA_SEG_PENDING:
+ case WA_SEG_DTI_PENDING:
+ rpipe_avail_inc(rpipe);
+ /*
+ * do not increment RPIPE avail for the WA_SEG_DELAYED case
+ * since it has not been submitted to the RPIPE.
+ */
+ case WA_SEG_DELAYED:
+ xfer->segs_done++;
+ current_seg->status = incoming_seg->status;
+ break;
+ case WA_SEG_ABORTED:
+ break;
+ default:
+ WARN(1, "%s: xfer 0x%08X#%d. bad seg status = %d\n",
+ __func__, wa_xfer_id(xfer), index,
+ current_seg->status);
+ break;
+ }
+ }
+}
+
+/*
* Process a xfer result completion message
*
- * inbound transfers: need to schedule a DTI read
+ * inbound transfers: need to schedule a buf_in_urb read
*
* FIXME: this function needs to be broken up in parts
*/
-static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer)
+static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer,
+ struct wa_xfer_result *xfer_result)
{
int result;
struct device *dev = &wa->usb_iface->dev;
@@ -1424,8 +1854,7 @@ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer)
u8 seg_idx;
struct wa_seg *seg;
struct wa_rpipe *rpipe;
- struct wa_xfer_result *xfer_result = wa->xfer_result;
- u8 done = 0;
+ unsigned done = 0;
u8 usb_status;
unsigned rpipe_ready = 0;
@@ -1436,8 +1865,8 @@ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer)
seg = xfer->seg[seg_idx];
rpipe = xfer->ep->hcpriv;
usb_status = xfer_result->bTransferStatus;
- dev_dbg(dev, "xfer %p#%u: bTransferStatus 0x%02x (seg status %u)\n",
- xfer, seg_idx, usb_status, seg->status);
+ dev_dbg(dev, "xfer %p ID 0x%08X#%u: bTransferStatus 0x%02x (seg status %u)\n",
+ xfer, wa_xfer_id(xfer), seg_idx, usb_status, seg->status);
if (seg->status == WA_SEG_ABORTED
|| seg->status == WA_SEG_ERROR) /* already handled */
goto segment_aborted;
@@ -1453,12 +1882,19 @@ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer)
seg->result = wa_xfer_status_to_errno(usb_status);
dev_err(dev, "DTI: xfer %p#:%08X:%u failed (0x%02x)\n",
xfer, xfer->id, seg->index, usb_status);
+ seg->status = ((usb_status & 0x7F) == WA_XFER_STATUS_ABORTED) ?
+ WA_SEG_ABORTED : WA_SEG_ERROR;
goto error_complete;
}
/* FIXME: we ignore warnings, tally them for stats */
if (usb_status & 0x40) /* Warning?... */
usb_status = 0; /* ... pass */
- if (xfer->is_inbound) { /* IN data phase: read to buffer */
+ if (usb_pipeisoc(xfer->urb->pipe)) {
+ /* set up WA state to read the isoc packet status next. */
+ wa->dti_isoc_xfer_in_progress = wa_xfer_id(xfer);
+ wa->dti_isoc_xfer_seg = seg_idx;
+ wa->dti_state = WA_DTI_ISOC_PACKET_STATUS_PENDING;
+ } else if (xfer->is_inbound) { /* IN data phase: read to buffer */
seg->status = WA_SEG_DTI_PENDING;
BUG_ON(wa->buf_in_urb->status == -EINPROGRESS);
/* this should always be 0 before a resubmit. */
@@ -1535,12 +1971,14 @@ error_submit_buf_in:
xfer, seg_idx, result);
seg->result = result;
kfree(wa->buf_in_urb->sg);
+ wa->buf_in_urb->sg = NULL;
error_sg_alloc:
__wa_xfer_abort(xfer);
-error_complete:
seg->status = WA_SEG_ERROR;
+error_complete:
xfer->segs_done++;
rpipe_ready = rpipe_avail_inc(rpipe);
+ wa_complete_remaining_xfer_segs(xfer, seg);
done = __wa_xfer_is_done(xfer);
/*
* queue work item to clear STALL for control endpoints.
@@ -1552,10 +1990,8 @@ error_complete:
dev_info(dev, "Control EP stall. Queue delayed work.\n");
spin_lock_irq(&wa->xfer_list_lock);
- /* remove xfer from xfer_list. */
- list_del(&xfer->list_node);
- /* add xfer to xfer_errored_list. */
- list_add_tail(&xfer->list_node, &wa->xfer_errored_list);
+ /* move xfer from xfer_list to xfer_errored_list. */
+ list_move_tail(&xfer->list_node, &wa->xfer_errored_list);
spin_unlock_irq(&wa->xfer_list_lock);
spin_unlock_irqrestore(&xfer->lock, flags);
queue_work(wusbd, &wa->xfer_error_work);
@@ -1587,6 +2023,85 @@ segment_aborted:
}
/*
+ * Process a isochronous packet status message
+ *
+ * inbound transfers: need to schedule a buf_in_urb read
+ */
+static void wa_process_iso_packet_status(struct wahc *wa, struct urb *urb)
+{
+ struct device *dev = &wa->usb_iface->dev;
+ struct wa_xfer_packet_status_hwaiso *packet_status;
+ struct wa_xfer *xfer;
+ unsigned long flags;
+ struct wa_seg *seg;
+ struct wa_rpipe *rpipe;
+ unsigned done = 0;
+ unsigned rpipe_ready = 0;
+ const int expected_size = sizeof(*packet_status) +
+ sizeof(packet_status->PacketStatus[0]);
+
+ /* We have a xfer result buffer; check it */
+ dev_dbg(dev, "DTI: isoc packet status %d bytes at %p\n",
+ urb->actual_length, urb->transfer_buffer);
+ if (urb->actual_length != expected_size) {
+ dev_err(dev, "DTI Error: isoc packet status--bad urb length (%d bytes vs %d needed)\n",
+ urb->actual_length, expected_size);
+ goto error_parse_buffer;
+ }
+ packet_status = (struct wa_xfer_packet_status_hwaiso *)(wa->dti_buf);
+ if (le16_to_cpu(packet_status->wLength) != expected_size) {
+ dev_err(dev, "DTI Error: isoc packet status--bad length %u\n",
+ le16_to_cpu(packet_status->wLength));
+ goto error_parse_buffer;
+ }
+ if (packet_status->bPacketType != WA_XFER_ISO_PACKET_STATUS) {
+ dev_err(dev, "DTI Error: isoc packet status--bad type 0x%02x\n",
+ packet_status->bPacketType);
+ goto error_parse_buffer;
+ }
+ xfer = wa_xfer_get_by_id(wa, wa->dti_isoc_xfer_in_progress);
+ if (xfer == NULL) {
+ dev_err(dev, "DTI Error: isoc packet status--unknown xfer 0x%08x\n",
+ wa->dti_isoc_xfer_in_progress);
+ goto error_parse_buffer;
+ }
+ spin_lock_irqsave(&xfer->lock, flags);
+ if (unlikely(wa->dti_isoc_xfer_seg >= xfer->segs))
+ goto error_bad_seg;
+ seg = xfer->seg[wa->dti_isoc_xfer_seg];
+ rpipe = xfer->ep->hcpriv;
+
+ /* set urb isoc packet status and length. */
+ xfer->urb->iso_frame_desc[seg->index].status =
+ wa_xfer_status_to_errno(
+ le16_to_cpu(packet_status->PacketStatus[0].PacketStatus));
+ xfer->urb->iso_frame_desc[seg->index].actual_length =
+ le16_to_cpu(packet_status->PacketStatus[0].PacketLength);
+
+ if (!xfer->is_inbound) {
+ /* OUT transfer, complete it -- */
+ seg->status = WA_SEG_DONE;
+ xfer->segs_done++;
+ rpipe_ready = rpipe_avail_inc(rpipe);
+ done = __wa_xfer_is_done(xfer);
+ }
+ spin_unlock_irqrestore(&xfer->lock, flags);
+ wa->dti_state = WA_DTI_TRANSFER_RESULT_PENDING;
+ if (done)
+ wa_xfer_completion(xfer);
+ if (rpipe_ready)
+ wa_xfer_delayed_run(rpipe);
+ wa_xfer_put(xfer);
+ return;
+
+error_bad_seg:
+ spin_unlock_irqrestore(&xfer->lock, flags);
+ wa_xfer_put(xfer);
+error_parse_buffer:
+ return;
+}
+
+/*
* Callback for the IN data phase
*
* If successful transition state; otherwise, take a note of the
@@ -1687,56 +2202,61 @@ static void wa_buf_in_cb(struct urb *urb)
* We go back to OFF when we detect a ENOENT or ESHUTDOWN (or too many
* errors) in the URBs.
*/
-static void wa_xfer_result_cb(struct urb *urb)
+static void wa_dti_cb(struct urb *urb)
{
int result;
struct wahc *wa = urb->context;
struct device *dev = &wa->usb_iface->dev;
- struct wa_xfer_result *xfer_result;
u32 xfer_id;
- struct wa_xfer *xfer;
u8 usb_status;
BUG_ON(wa->dti_urb != urb);
switch (wa->dti_urb->status) {
case 0:
- /* We have a xfer result buffer; check it */
- dev_dbg(dev, "DTI: xfer result %d bytes at %p\n",
- urb->actual_length, urb->transfer_buffer);
- if (wa->dti_urb->actual_length != sizeof(*xfer_result)) {
- dev_err(dev, "DTI Error: xfer result--bad size "
- "xfer result (%d bytes vs %zu needed)\n",
- urb->actual_length, sizeof(*xfer_result));
- break;
- }
- xfer_result = wa->xfer_result;
- if (xfer_result->hdr.bLength != sizeof(*xfer_result)) {
- dev_err(dev, "DTI Error: xfer result--"
- "bad header length %u\n",
- xfer_result->hdr.bLength);
- break;
- }
- if (xfer_result->hdr.bNotifyType != WA_XFER_RESULT) {
- dev_err(dev, "DTI Error: xfer result--"
- "bad header type 0x%02x\n",
- xfer_result->hdr.bNotifyType);
- break;
- }
- usb_status = xfer_result->bTransferStatus & 0x3f;
- if (usb_status == WA_XFER_STATUS_NOT_FOUND)
- /* taken care of already */
- break;
- xfer_id = xfer_result->dwTransferID;
- xfer = wa_xfer_get_by_id(wa, xfer_id);
- if (xfer == NULL) {
- /* FIXME: transaction might have been cancelled */
- dev_err(dev, "DTI Error: xfer result--"
- "unknown xfer 0x%08x (status 0x%02x)\n",
- xfer_id, usb_status);
- break;
+ if (wa->dti_state == WA_DTI_TRANSFER_RESULT_PENDING) {
+ struct wa_xfer_result *xfer_result;
+ struct wa_xfer *xfer;
+
+ /* We have a xfer result buffer; check it */
+ dev_dbg(dev, "DTI: xfer result %d bytes at %p\n",
+ urb->actual_length, urb->transfer_buffer);
+ if (urb->actual_length != sizeof(*xfer_result)) {
+ dev_err(dev, "DTI Error: xfer result--bad size xfer result (%d bytes vs %zu needed)\n",
+ urb->actual_length,
+ sizeof(*xfer_result));
+ break;
+ }
+ xfer_result = (struct wa_xfer_result *)(wa->dti_buf);
+ if (xfer_result->hdr.bLength != sizeof(*xfer_result)) {
+ dev_err(dev, "DTI Error: xfer result--bad header length %u\n",
+ xfer_result->hdr.bLength);
+ break;
+ }
+ if (xfer_result->hdr.bNotifyType != WA_XFER_RESULT) {
+ dev_err(dev, "DTI Error: xfer result--bad header type 0x%02x\n",
+ xfer_result->hdr.bNotifyType);
+ break;
+ }
+ usb_status = xfer_result->bTransferStatus & 0x3f;
+ if (usb_status == WA_XFER_STATUS_NOT_FOUND)
+ /* taken care of already */
+ break;
+ xfer_id = le32_to_cpu(xfer_result->dwTransferID);
+ xfer = wa_xfer_get_by_id(wa, xfer_id);
+ if (xfer == NULL) {
+ /* FIXME: transaction not found. */
+ dev_err(dev, "DTI Error: xfer result--unknown xfer 0x%08x (status 0x%02x)\n",
+ xfer_id, usb_status);
+ break;
+ }
+ wa_xfer_result_chew(wa, xfer, xfer_result);
+ wa_xfer_put(xfer);
+ } else if (wa->dti_state == WA_DTI_ISOC_PACKET_STATUS_PENDING) {
+ wa_process_iso_packet_status(wa, urb);
+ } else {
+ dev_err(dev, "DTI Error: unexpected EP state = %d\n",
+ wa->dti_state);
}
- wa_xfer_result_chew(wa, xfer);
- wa_xfer_put(xfer);
break;
case -ENOENT: /* (we killed the URB)...so, no broadcast */
case -ESHUTDOWN: /* going away! */
@@ -1777,7 +2297,7 @@ out:
* don't really set it up and start it until the first xfer complete
* notification arrives, which is what we do here.
*
- * Follow up in wa_xfer_result_cb(), as that's where the whole state
+ * Follow up in wa_dti_cb(), as that's where the whole state
* machine starts.
*
* So here we just initialize the DTI URB for reading transfer result
@@ -1813,8 +2333,8 @@ void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr)
usb_fill_bulk_urb(
wa->dti_urb, wa->usb_dev,
usb_rcvbulkpipe(wa->usb_dev, 0x80 | notif_xfer->bEndpoint),
- wa->xfer_result, wa->xfer_result_size,
- wa_xfer_result_cb, wa);
+ wa->dti_buf, wa->dti_buf_size,
+ wa_dti_cb, wa);
wa->buf_in_urb = usb_alloc_urb(0, GFP_KERNEL);
if (wa->buf_in_urb == NULL) {
@@ -1836,6 +2356,7 @@ out:
error_dti_urb_submit:
usb_put_urb(wa->buf_in_urb);
+ wa->buf_in_urb = NULL;
error_buf_in_urb_alloc:
usb_put_urb(wa->dti_urb);
wa->dti_urb = NULL;
diff --git a/drivers/video/exynos/Kconfig b/drivers/video/exynos/Kconfig
index 1b035b2eb6b6..1129d0e9e640 100644
--- a/drivers/video/exynos/Kconfig
+++ b/drivers/video/exynos/Kconfig
@@ -16,6 +16,7 @@ if EXYNOS_VIDEO
config EXYNOS_MIPI_DSI
bool "EXYNOS MIPI DSI driver support."
depends on ARCH_S5PV210 || ARCH_EXYNOS
+ select GENERIC_PHY
help
This enables support for MIPI-DSI device.
@@ -29,7 +30,7 @@ config EXYNOS_LCD_S6E8AX0
config EXYNOS_DP
bool "EXYNOS DP driver support"
- depends on ARCH_EXYNOS
+ depends on OF && ARCH_EXYNOS
default n
help
This enables support for DP device.
diff --git a/drivers/video/exynos/exynos_dp_core.c b/drivers/video/exynos/exynos_dp_core.c
index 12bbede3b091..5e1a71580051 100644
--- a/drivers/video/exynos/exynos_dp_core.c
+++ b/drivers/video/exynos/exynos_dp_core.c
@@ -19,8 +19,7 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/of.h>
-
-#include <video/exynos_dp.h>
+#include <linux/phy/phy.h>
#include "exynos_dp_core.h"
@@ -894,26 +893,17 @@ static void exynos_dp_hotplug(struct work_struct *work)
dev_err(dp->dev, "unable to config video\n");
}
-#ifdef CONFIG_OF
-static struct exynos_dp_platdata *exynos_dp_dt_parse_pdata(struct device *dev)
+static struct video_info *exynos_dp_dt_parse_pdata(struct device *dev)
{
struct device_node *dp_node = dev->of_node;
- struct exynos_dp_platdata *pd;
struct video_info *dp_video_config;
- pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
- if (!pd) {
- dev_err(dev, "memory allocation for pdata failed\n");
- return ERR_PTR(-ENOMEM);
- }
dp_video_config = devm_kzalloc(dev,
sizeof(*dp_video_config), GFP_KERNEL);
-
if (!dp_video_config) {
dev_err(dev, "memory allocation for video config failed\n");
return ERR_PTR(-ENOMEM);
}
- pd->video_info = dp_video_config;
dp_video_config->h_sync_polarity =
of_property_read_bool(dp_node, "hsync-active-high");
@@ -960,7 +950,7 @@ static struct exynos_dp_platdata *exynos_dp_dt_parse_pdata(struct device *dev)
return ERR_PTR(-EINVAL);
}
- return pd;
+ return dp_video_config;
}
static int exynos_dp_dt_parse_phydata(struct exynos_dp_device *dp)
@@ -971,8 +961,11 @@ static int exynos_dp_dt_parse_phydata(struct exynos_dp_device *dp)
dp_phy_node = of_find_node_by_name(dp_phy_node, "dptx-phy");
if (!dp_phy_node) {
- dev_err(dp->dev, "could not find dptx-phy node\n");
- return -ENODEV;
+ dp->phy = devm_phy_get(dp->dev, "dp");
+ if (IS_ERR(dp->phy))
+ return PTR_ERR(dp->phy);
+ else
+ return 0;
}
if (of_property_read_u32(dp_phy_node, "reg", &phy_base)) {
@@ -1003,48 +996,34 @@ err:
static void exynos_dp_phy_init(struct exynos_dp_device *dp)
{
- u32 reg;
-
- reg = __raw_readl(dp->phy_addr);
- reg |= dp->enable_mask;
- __raw_writel(reg, dp->phy_addr);
-}
-
-static void exynos_dp_phy_exit(struct exynos_dp_device *dp)
-{
- u32 reg;
-
- reg = __raw_readl(dp->phy_addr);
- reg &= ~(dp->enable_mask);
- __raw_writel(reg, dp->phy_addr);
-}
-#else
-static struct exynos_dp_platdata *exynos_dp_dt_parse_pdata(struct device *dev)
-{
- return NULL;
-}
-
-static int exynos_dp_dt_parse_phydata(struct exynos_dp_device *dp)
-{
- return -EINVAL;
-}
-
-static void exynos_dp_phy_init(struct exynos_dp_device *dp)
-{
- return;
+ if (dp->phy) {
+ phy_power_on(dp->phy);
+ } else if (dp->phy_addr) {
+ u32 reg;
+
+ reg = __raw_readl(dp->phy_addr);
+ reg |= dp->enable_mask;
+ __raw_writel(reg, dp->phy_addr);
+ }
}
static void exynos_dp_phy_exit(struct exynos_dp_device *dp)
{
- return;
+ if (dp->phy) {
+ phy_power_off(dp->phy);
+ } else if (dp->phy_addr) {
+ u32 reg;
+
+ reg = __raw_readl(dp->phy_addr);
+ reg &= ~(dp->enable_mask);
+ __raw_writel(reg, dp->phy_addr);
+ }
}
-#endif /* CONFIG_OF */
static int exynos_dp_probe(struct platform_device *pdev)
{
struct resource *res;
struct exynos_dp_device *dp;
- struct exynos_dp_platdata *pdata;
int ret = 0;
@@ -1057,21 +1036,13 @@ static int exynos_dp_probe(struct platform_device *pdev)
dp->dev = &pdev->dev;
- if (pdev->dev.of_node) {
- pdata = exynos_dp_dt_parse_pdata(&pdev->dev);
- if (IS_ERR(pdata))
- return PTR_ERR(pdata);
+ dp->video_info = exynos_dp_dt_parse_pdata(&pdev->dev);
+ if (IS_ERR(dp->video_info))
+ return PTR_ERR(dp->video_info);
- ret = exynos_dp_dt_parse_phydata(dp);
- if (ret)
- return ret;
- } else {
- pdata = pdev->dev.platform_data;
- if (!pdata) {
- dev_err(&pdev->dev, "no platform data\n");
- return -EINVAL;
- }
- }
+ ret = exynos_dp_dt_parse_phydata(dp);
+ if (ret)
+ return ret;
dp->clock = devm_clk_get(&pdev->dev, "dp");
if (IS_ERR(dp->clock)) {
@@ -1095,15 +1066,7 @@ static int exynos_dp_probe(struct platform_device *pdev)
INIT_WORK(&dp->hotplug_work, exynos_dp_hotplug);
- dp->video_info = pdata->video_info;
-
- if (pdev->dev.of_node) {
- if (dp->phy_addr)
- exynos_dp_phy_init(dp);
- } else {
- if (pdata->phy_init)
- pdata->phy_init();
- }
+ exynos_dp_phy_init(dp);
exynos_dp_init_dp(dp);
@@ -1121,18 +1084,11 @@ static int exynos_dp_probe(struct platform_device *pdev)
static int exynos_dp_remove(struct platform_device *pdev)
{
- struct exynos_dp_platdata *pdata = pdev->dev.platform_data;
struct exynos_dp_device *dp = platform_get_drvdata(pdev);
flush_work(&dp->hotplug_work);
- if (pdev->dev.of_node) {
- if (dp->phy_addr)
- exynos_dp_phy_exit(dp);
- } else {
- if (pdata->phy_exit)
- pdata->phy_exit();
- }
+ exynos_dp_phy_exit(dp);
clk_disable_unprepare(dp->clock);
@@ -1143,20 +1099,13 @@ static int exynos_dp_remove(struct platform_device *pdev)
#ifdef CONFIG_PM_SLEEP
static int exynos_dp_suspend(struct device *dev)
{
- struct exynos_dp_platdata *pdata = dev->platform_data;
struct exynos_dp_device *dp = dev_get_drvdata(dev);
disable_irq(dp->irq);
flush_work(&dp->hotplug_work);
- if (dev->of_node) {
- if (dp->phy_addr)
- exynos_dp_phy_exit(dp);
- } else {
- if (pdata->phy_exit)
- pdata->phy_exit();
- }
+ exynos_dp_phy_exit(dp);
clk_disable_unprepare(dp->clock);
@@ -1165,16 +1114,9 @@ static int exynos_dp_suspend(struct device *dev)
static int exynos_dp_resume(struct device *dev)
{
- struct exynos_dp_platdata *pdata = dev->platform_data;
struct exynos_dp_device *dp = dev_get_drvdata(dev);
- if (dev->of_node) {
- if (dp->phy_addr)
- exynos_dp_phy_init(dp);
- } else {
- if (pdata->phy_init)
- pdata->phy_init();
- }
+ exynos_dp_phy_init(dp);
clk_prepare_enable(dp->clock);
@@ -1203,7 +1145,7 @@ static struct platform_driver exynos_dp_driver = {
.name = "exynos-dp",
.owner = THIS_MODULE,
.pm = &exynos_dp_pm_ops,
- .of_match_table = of_match_ptr(exynos_dp_match),
+ .of_match_table = exynos_dp_match,
},
};
diff --git a/drivers/video/exynos/exynos_dp_core.h b/drivers/video/exynos/exynos_dp_core.h
index 6c567bbf2fb8..607e36d0c147 100644
--- a/drivers/video/exynos/exynos_dp_core.h
+++ b/drivers/video/exynos/exynos_dp_core.h
@@ -13,6 +13,99 @@
#ifndef _EXYNOS_DP_CORE_H
#define _EXYNOS_DP_CORE_H
+#define DP_TIMEOUT_LOOP_COUNT 100
+#define MAX_CR_LOOP 5
+#define MAX_EQ_LOOP 5
+
+enum link_rate_type {
+ LINK_RATE_1_62GBPS = 0x06,
+ LINK_RATE_2_70GBPS = 0x0a
+};
+
+enum link_lane_count_type {
+ LANE_COUNT1 = 1,
+ LANE_COUNT2 = 2,
+ LANE_COUNT4 = 4
+};
+
+enum link_training_state {
+ START,
+ CLOCK_RECOVERY,
+ EQUALIZER_TRAINING,
+ FINISHED,
+ FAILED
+};
+
+enum voltage_swing_level {
+ VOLTAGE_LEVEL_0,
+ VOLTAGE_LEVEL_1,
+ VOLTAGE_LEVEL_2,
+ VOLTAGE_LEVEL_3,
+};
+
+enum pre_emphasis_level {
+ PRE_EMPHASIS_LEVEL_0,
+ PRE_EMPHASIS_LEVEL_1,
+ PRE_EMPHASIS_LEVEL_2,
+ PRE_EMPHASIS_LEVEL_3,
+};
+
+enum pattern_set {
+ PRBS7,
+ D10_2,
+ TRAINING_PTN1,
+ TRAINING_PTN2,
+ DP_NONE
+};
+
+enum color_space {
+ COLOR_RGB,
+ COLOR_YCBCR422,
+ COLOR_YCBCR444
+};
+
+enum color_depth {
+ COLOR_6,
+ COLOR_8,
+ COLOR_10,
+ COLOR_12
+};
+
+enum color_coefficient {
+ COLOR_YCBCR601,
+ COLOR_YCBCR709
+};
+
+enum dynamic_range {
+ VESA,
+ CEA
+};
+
+enum pll_status {
+ PLL_UNLOCKED,
+ PLL_LOCKED
+};
+
+enum clock_recovery_m_value_type {
+ CALCULATED_M,
+ REGISTER_M
+};
+
+enum video_timing_recognition_type {
+ VIDEO_TIMING_FROM_CAPTURE,
+ VIDEO_TIMING_FROM_REGISTER
+};
+
+enum analog_power_block {
+ AUX_BLOCK,
+ CH0_BLOCK,
+ CH1_BLOCK,
+ CH2_BLOCK,
+ CH3_BLOCK,
+ ANALOG_TOTAL,
+ POWER_ALL
+};
+
enum dp_irq_type {
DP_IRQ_TYPE_HP_CABLE_IN,
DP_IRQ_TYPE_HP_CABLE_OUT,
@@ -20,6 +113,22 @@ enum dp_irq_type {
DP_IRQ_TYPE_UNKNOWN,
};
+struct video_info {
+ char *name;
+
+ bool h_sync_polarity;
+ bool v_sync_polarity;
+ bool interlaced;
+
+ enum color_space color_space;
+ enum dynamic_range dynamic_range;
+ enum color_coefficient ycbcr_coeff;
+ enum color_depth color_depth;
+
+ enum link_rate_type link_rate;
+ enum link_lane_count_type lane_count;
+};
+
struct link_train {
int eq_loop;
int cr_loop[4];
@@ -42,6 +151,7 @@ struct exynos_dp_device {
struct video_info *video_info;
struct link_train link_train;
struct work_struct hotplug_work;
+ struct phy *phy;
};
/* exynos_dp_reg.c */
diff --git a/drivers/video/exynos/exynos_dp_reg.c b/drivers/video/exynos/exynos_dp_reg.c
index 29d9d035c73a..b70da5052ff0 100644
--- a/drivers/video/exynos/exynos_dp_reg.c
+++ b/drivers/video/exynos/exynos_dp_reg.c
@@ -14,8 +14,6 @@
#include <linux/io.h>
#include <linux/delay.h>
-#include <video/exynos_dp.h>
-
#include "exynos_dp_core.h"
#include "exynos_dp_reg.h"
diff --git a/drivers/video/exynos/exynos_mipi_dsi.c b/drivers/video/exynos/exynos_mipi_dsi.c
index 32e540600f99..00b3a52c1d68 100644
--- a/drivers/video/exynos/exynos_mipi_dsi.c
+++ b/drivers/video/exynos/exynos_mipi_dsi.c
@@ -30,6 +30,7 @@
#include <linux/interrupt.h>
#include <linux/kthread.h>
#include <linux/notifier.h>
+#include <linux/phy/phy.h>
#include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/err.h>
@@ -156,8 +157,7 @@ static int exynos_mipi_dsi_blank_mode(struct mipi_dsim_device *dsim, int power)
exynos_mipi_regulator_enable(dsim);
/* enable MIPI-DSI PHY. */
- if (dsim->pd->phy_enable)
- dsim->pd->phy_enable(pdev, true);
+ phy_power_on(dsim->phy);
clk_enable(dsim->clock);
@@ -373,6 +373,10 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev)
return ret;
}
+ dsim->phy = devm_phy_get(&pdev->dev, "dsim");
+ if (IS_ERR(dsim->phy))
+ return PTR_ERR(dsim->phy);
+
dsim->clock = devm_clk_get(&pdev->dev, "dsim0");
if (IS_ERR(dsim->clock)) {
dev_err(&pdev->dev, "failed to get dsim clock source\n");
@@ -439,8 +443,7 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev)
exynos_mipi_regulator_enable(dsim);
/* enable MIPI-DSI PHY. */
- if (dsim->pd->phy_enable)
- dsim->pd->phy_enable(pdev, true);
+ phy_power_on(dsim->phy);
exynos_mipi_update_cfg(dsim);
@@ -504,9 +507,8 @@ static int exynos_mipi_dsi_suspend(struct device *dev)
if (client_drv && client_drv->suspend)
client_drv->suspend(client_dev);
- /* enable MIPI-DSI PHY. */
- if (dsim->pd->phy_enable)
- dsim->pd->phy_enable(pdev, false);
+ /* disable MIPI-DSI PHY. */
+ phy_power_off(dsim->phy);
clk_disable(dsim->clock);
@@ -536,8 +538,7 @@ static int exynos_mipi_dsi_resume(struct device *dev)
exynos_mipi_regulator_enable(dsim);
/* enable MIPI-DSI PHY. */
- if (dsim->pd->phy_enable)
- dsim->pd->phy_enable(pdev, true);
+ phy_power_on(dsim->phy);
clk_enable(dsim->clock);
OpenPOWER on IntegriCloud