diff options
99 files changed, 3605 insertions, 2073 deletions
diff --git a/Documentation/devicetree/bindings/phy/phy-bindings.txt b/Documentation/devicetree/bindings/phy/phy-bindings.txt new file mode 100644 index 000000000000..8ae844fc0c60 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/phy-bindings.txt @@ -0,0 +1,66 @@ +This document explains only the device tree data binding. For general +information about PHY subsystem refer to Documentation/phy.txt + +PHY device node +=============== + +Required Properties: +#phy-cells: Number of cells in a PHY specifier; The meaning of all those + cells is defined by the binding for the phy node. The PHY + provider can use the values in cells to find the appropriate + PHY. + +For example: + +phys: phy { + compatible = "xxx"; + reg = <...>; + . + . + #phy-cells = <1>; + . + . +}; + +That node describes an IP block (PHY provider) that implements 2 different PHYs. +In order to differentiate between these 2 PHYs, an additonal specifier should be +given while trying to get a reference to it. + +PHY user node +============= + +Required Properties: +phys : the phandle for the PHY device (used by the PHY subsystem) +phy-names : the names of the PHY corresponding to the PHYs present in the + *phys* phandle + +Example 1: +usb1: usb_otg_ss@xxx { + compatible = "xxx"; + reg = <xxx>; + . + . + phys = <&usb2_phy>, <&usb3_phy>; + phy-names = "usb2phy", "usb3phy"; + . + . +}; + +This node represents a controller that uses two PHYs, one for usb2 and one for +usb3. + +Example 2: +usb2: usb_otg_ss@xxx { + compatible = "xxx"; + reg = <xxx>; + . + . + phys = <&phys 1>; + phy-names = "usbphy"; + . + . +}; + +This node represents a controller that uses one of the PHYs of the PHY provider +device defined previously. Note that the phy handle has an additional specifier +"1" to differentiate between the two PHYs. diff --git a/Documentation/devicetree/bindings/usb/omap-usb.txt b/Documentation/devicetree/bindings/usb/omap-usb.txt index 9088ab09e200..090e5e22bd2b 100644 --- a/Documentation/devicetree/bindings/usb/omap-usb.txt +++ b/Documentation/devicetree/bindings/usb/omap-usb.txt @@ -3,9 +3,6 @@ OMAP GLUE AND OTHER OMAP SPECIFIC COMPONENTS OMAP MUSB GLUE - compatible : Should be "ti,omap4-musb" or "ti,omap3-musb" - ti,hwmods : must be "usb_otg_hs" - - ti,has-mailbox : to specify that omap uses an external mailbox - (in control module) to communicate with the musb core during device connect - and disconnect. - multipoint : Should be "1" indicating the musb controller supports multipoint. This is a MUSB configuration-specific setting. - num-eps : Specifies the number of endpoints. This is also a @@ -19,6 +16,9 @@ OMAP MUSB GLUE - power : Should be "50". This signifies the controller can supply up to 100mA when operating in host mode. - usb-phy : the phandle for the PHY device + - phys : the phandle for the PHY device (used by generic PHY framework) + - phy-names : the names of the PHY corresponding to the PHYs present in the + *phy* phandle. Optional properties: - ctrl-module : phandle of the control module this glue uses to write to @@ -28,11 +28,12 @@ SOC specific device node entry usb_otg_hs: usb_otg_hs@4a0ab000 { compatible = "ti,omap4-musb"; ti,hwmods = "usb_otg_hs"; - ti,has-mailbox; multipoint = <1>; num-eps = <16>; ram-bits = <12>; ctrl-module = <&omap_control_usb>; + phys = <&usb2_phy>; + phy-names = "usb2-phy"; }; Board specific device node entry @@ -78,22 +79,22 @@ omap_dwc3 { OMAP CONTROL USB Required properties: - - compatible: Should be "ti,omap-control-usb" + - compatible: Should be one of + "ti,control-phy-otghs" - if it has otghs_control mailbox register as on OMAP4. + "ti,control-phy-usb2" - if it has Power down bit in control_dev_conf register + e.g. USB2_PHY on OMAP5. + "ti,control-phy-pipe3" - if it has DPLL and individual Rx & Tx power control + e.g. USB3 PHY and SATA PHY on OMAP5. + "ti,control-phy-dra7usb2" - if it has power down register like USB2 PHY on + DRA7 platform. - reg : Address and length of the register set for the device. It contains - the address of "control_dev_conf" and "otghs_control" or "phy_power_usb" - depending upon omap4 or omap5. - - reg-names: The names of the register addresses corresponding to the registers - filled in "reg". - - ti,type: This is used to differentiate whether the control module has - usb mailbox or usb3 phy power. omap4 has usb mailbox in control module to - notify events to the musb core and omap5 has usb3 phy power register to - power on usb3 phy. Should be "1" if it has mailbox and "2" if it has usb3 - phy power. + the address of "otghs_control" for control-phy-otghs or "power" register + for other types. + - reg-names: should be "otghs_control" control-phy-otghs and "power" for + other types. omap_control_usb: omap-control-usb@4a002300 { - compatible = "ti,omap-control-usb"; - reg = <0x4a002300 0x4>, - <0x4a00233c 0x4>; - reg-names = "control_dev_conf", "otghs_control"; - ti,type = <1>; + compatible = "ti,control-phy-otghs"; + reg = <0x4a00233c 0x4>; + reg-names = "otghs_control"; }; diff --git a/Documentation/devicetree/bindings/usb/usb-phy.txt b/Documentation/devicetree/bindings/usb/usb-phy.txt index 61496f5cb095..c0245c888982 100644 --- a/Documentation/devicetree/bindings/usb/usb-phy.txt +++ b/Documentation/devicetree/bindings/usb/usb-phy.txt @@ -5,6 +5,8 @@ OMAP USB2 PHY Required properties: - compatible: Should be "ti,omap-usb2" - reg : Address and length of the register set for the device. + - #phy-cells: determine the number of cells that should be given in the + phandle while referencing this phy. Optional properties: - ctrl-module : phandle of the control module used by PHY driver to power on @@ -16,6 +18,7 @@ usb2phy@4a0ad080 { compatible = "ti,omap-usb2"; reg = <0x4a0ad080 0x58>; ctrl-module = <&omap_control_usb>; + #phy-cells = <0>; }; OMAP USB3 PHY @@ -25,6 +28,8 @@ Required properties: - reg : Address and length of the register set for the device. - reg-names: The names of the register addresses corresponding to the registers filled in "reg". + - #phy-cells: determine the number of cells that should be given in the + phandle while referencing this phy. Optional properties: - ctrl-module : phandle of the control module used by PHY driver to power on @@ -39,4 +44,5 @@ usb3phy@4a084400 { <0x4a084c00 0x40>; reg-names = "phy_rx", "phy_tx", "pll_ctrl"; ctrl-module = <&omap_control_usb>; + #phy-cells = <0>; }; diff --git a/Documentation/phy.txt b/Documentation/phy.txt new file mode 100644 index 000000000000..0103e4b15b0e --- /dev/null +++ b/Documentation/phy.txt @@ -0,0 +1,166 @@ + PHY SUBSYSTEM + Kishon Vijay Abraham I <kishon@ti.com> + +This document explains the Generic PHY Framework along with the APIs provided, +and how-to-use. + +1. Introduction + +*PHY* is the abbreviation for physical layer. It is used to connect a device +to the physical medium e.g., the USB controller has a PHY to provide functions +such as serialization, de-serialization, encoding, decoding and is responsible +for obtaining the required data transmission rate. Note that some USB +controllers have PHY functionality embedded into it and others use an external +PHY. Other peripherals that use PHY include Wireless LAN, Ethernet, +SATA etc. + +The intention of creating this framework is to bring the PHY drivers spread +all over the Linux kernel to drivers/phy to increase code re-use and for +better code maintainability. + +This framework will be of use only to devices that use external PHY (PHY +functionality is not embedded within the controller). + +2. Registering/Unregistering the PHY provider + +PHY provider refers to an entity that implements one or more PHY instances. +For the simple case where the PHY provider implements only a single instance of +the PHY, the framework provides its own implementation of of_xlate in +of_phy_simple_xlate. If the PHY provider implements multiple instances, it +should provide its own implementation of of_xlate. of_xlate is used only for +dt boot case. + +#define of_phy_provider_register(dev, xlate) \ + __of_phy_provider_register((dev), THIS_MODULE, (xlate)) + +#define devm_of_phy_provider_register(dev, xlate) \ + __devm_of_phy_provider_register((dev), THIS_MODULE, (xlate)) + +of_phy_provider_register and devm_of_phy_provider_register macros can be used to +register the phy_provider and it takes device and of_xlate as +arguments. For the dt boot case, all PHY providers should use one of the above +2 macros to register the PHY provider. + +void devm_of_phy_provider_unregister(struct device *dev, + struct phy_provider *phy_provider); +void of_phy_provider_unregister(struct phy_provider *phy_provider); + +devm_of_phy_provider_unregister and of_phy_provider_unregister can be used to +unregister the PHY. + +3. Creating the PHY + +The PHY driver should create the PHY in order for other peripheral controllers +to make use of it. The PHY framework provides 2 APIs to create the PHY. + +struct phy *phy_create(struct device *dev, const struct phy_ops *ops, + struct phy_init_data *init_data); +struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops, + struct phy_init_data *init_data); + +The PHY drivers can use one of the above 2 APIs to create the PHY by passing +the device pointer, phy ops and init_data. +phy_ops is a set of function pointers for performing PHY operations such as +init, exit, power_on and power_off. *init_data* is mandatory to get a reference +to the PHY in the case of non-dt boot. See section *Board File Initialization* +on how init_data should be used. + +Inorder to dereference the private data (in phy_ops), the phy provider driver +can use phy_set_drvdata() after creating the PHY and use phy_get_drvdata() in +phy_ops to get back the private data. + +4. Getting a reference to the PHY + +Before the controller can make use of the PHY, it has to get a reference to +it. This framework provides the following APIs to get a reference to the PHY. + +struct phy *phy_get(struct device *dev, const char *string); +struct phy *devm_phy_get(struct device *dev, const char *string); + +phy_get and devm_phy_get can be used to get the PHY. In the case of dt boot, +the string arguments should contain the phy name as given in the dt data and +in the case of non-dt boot, it should contain the label of the PHY. +The only difference between the two APIs is that devm_phy_get associates the +device with the PHY using devres on successful PHY get. On driver detach, +release function is invoked on the the devres data and devres data is freed. + +5. Releasing a reference to the PHY + +When the controller no longer needs the PHY, it has to release the reference +to the PHY it has obtained using the APIs mentioned in the above section. The +PHY framework provides 2 APIs to release a reference to the PHY. + +void phy_put(struct phy *phy); +void devm_phy_put(struct device *dev, struct phy *phy); + +Both these APIs are used to release a reference to the PHY and devm_phy_put +destroys the devres associated with this PHY. + +6. Destroying the PHY + +When the driver that created the PHY is unloaded, it should destroy the PHY it +created using one of the following 2 APIs. + +void phy_destroy(struct phy *phy); +void devm_phy_destroy(struct device *dev, struct phy *phy); + +Both these APIs destroy the PHY and devm_phy_destroy destroys the devres +associated with this PHY. + +7. PM Runtime + +This subsystem is pm runtime enabled. So while creating the PHY, +pm_runtime_enable of the phy device created by this subsystem is called and +while destroying the PHY, pm_runtime_disable is called. Note that the phy +device created by this subsystem will be a child of the device that calls +phy_create (PHY provider device). + +So pm_runtime_get_sync of the phy_device created by this subsystem will invoke +pm_runtime_get_sync of PHY provider device because of parent-child relationship. +It should also be noted that phy_power_on and phy_power_off performs +phy_pm_runtime_get_sync and phy_pm_runtime_put respectively. +There are exported APIs like phy_pm_runtime_get, phy_pm_runtime_get_sync, +phy_pm_runtime_put, phy_pm_runtime_put_sync, phy_pm_runtime_allow and +phy_pm_runtime_forbid for performing PM operations. + +8. Board File Initialization + +Certain board file initialization is necessary in order to get a reference +to the PHY in the case of non-dt boot. +Say we have a single device that implements 3 PHYs that of USB, SATA and PCIe, +then in the board file the following initialization should be done. + +struct phy_consumer consumers[] = { + PHY_CONSUMER("dwc3.0", "usb"), + PHY_CONSUMER("pcie.0", "pcie"), + PHY_CONSUMER("sata.0", "sata"), +}; +PHY_CONSUMER takes 2 parameters, first is the device name of the controller +(PHY consumer) and second is the port name. + +struct phy_init_data init_data = { + .consumers = consumers, + .num_consumers = ARRAY_SIZE(consumers), +}; + +static const struct platform_device pipe3_phy_dev = { + .name = "pipe3-phy", + .id = -1, + .dev = { + .platform_data = { + .init_data = &init_data, + }, + }, +}; + +then, while doing phy_create, the PHY driver should pass this init_data + phy_create(dev, ops, pdata->init_data); + +and the controller driver (phy consumer) should pass the port name along with +the device to get a reference to the PHY + phy_get(dev, "pcie"); + +9. DeviceTree Binding + +The documentation for PHY dt binding can be found @ +Documentation/devicetree/bindings/phy/phy-bindings.txt diff --git a/Documentation/pps/pps.txt b/Documentation/pps/pps.txt index d35dcdd82ff6..c03b1be5eb15 100644 --- a/Documentation/pps/pps.txt +++ b/Documentation/pps/pps.txt @@ -66,6 +66,21 @@ In LinuxPPS the PPS sources are simply char devices usually mapped into files /dev/pps0, /dev/pps1, etc.. +PPS with USB to serial devices +------------------------------ + +It is possible to grab the PPS from an USB to serial device. However, +you should take into account the latencies and jitter introduced by +the USB stack. Users has reported clock instability around +-1ms when +synchronized with PPS through USB. This isn't suited for time server +synchronization. + +If your device doesn't report PPS, you can check that the feature is +supported by its driver. Most of the time, you only need to add a call +to usb_serial_handle_dcd_change after checking the DCD status (see +ch341 and pl2303 examples). + + Coding example -------------- diff --git a/MAINTAINERS b/MAINTAINERS index 8a0cbf3cf2c8..a70e33a088f8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3673,6 +3673,14 @@ S: Maintained F: include/asm-generic/ F: include/uapi/asm-generic/ +GENERIC PHY FRAMEWORK +M: Kishon Vijay Abraham I <kishon@ti.com> +L: linux-kernel@vger.kernel.org +T: git git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy.git +S: Supported +F: drivers/phy/ +F: include/linux/phy/ + GENERIC UIO DRIVER FOR PCI DEVICES M: "Michael S. Tsirkin" <mst@redhat.com> L: kvm@vger.kernel.org diff --git a/arch/arm/boot/dts/omap3-beagle-xm.dts b/arch/arm/boot/dts/omap3-beagle-xm.dts index 0c514dc8460c..84838469f0d6 100644 --- a/arch/arm/boot/dts/omap3-beagle-xm.dts +++ b/arch/arm/boot/dts/omap3-beagle-xm.dts @@ -144,6 +144,8 @@ &usb_otg_hs { interface-type = <0>; usb-phy = <&usb2_phy>; + phys = <&usb2_phy>; + phy-names = "usb2-phy"; mode = <3>; power = <50>; }; diff --git a/arch/arm/boot/dts/omap3-evm.dts b/arch/arm/boot/dts/omap3-evm.dts index 7d4329d179c4..4134dd05c3a4 100644 --- a/arch/arm/boot/dts/omap3-evm.dts +++ b/arch/arm/boot/dts/omap3-evm.dts @@ -70,6 +70,8 @@ &usb_otg_hs { interface-type = <0>; usb-phy = <&usb2_phy>; + phys = <&usb2_phy>; + phy-names = "usb2-phy"; mode = <3>; power = <50>; }; diff --git a/arch/arm/boot/dts/omap3-overo.dtsi b/arch/arm/boot/dts/omap3-overo.dtsi index 8f1abec78275..a461d2fd1fb0 100644 --- a/arch/arm/boot/dts/omap3-overo.dtsi +++ b/arch/arm/boot/dts/omap3-overo.dtsi @@ -76,6 +76,8 @@ &usb_otg_hs { interface-type = <0>; usb-phy = <&usb2_phy>; + phys = <&usb2_phy>; + phy-names = "usb2-phy"; mode = <3>; power = <50>; }; diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi index 22d9f2b593d4..ea4054bfdfd4 100644 --- a/arch/arm/boot/dts/omap4.dtsi +++ b/arch/arm/boot/dts/omap4.dtsi @@ -519,7 +519,8 @@ usb2_phy: usb2phy@4a0ad080 { compatible = "ti,omap-usb2"; reg = <0x4a0ad080 0x58>; - ctrl-module = <&omap_control_usb>; + ctrl-module = <&omap_control_usb2phy>; + #phy-cells = <0>; }; }; @@ -643,12 +644,16 @@ }; }; - omap_control_usb: omap-control-usb@4a002300 { - compatible = "ti,omap-control-usb"; - reg = <0x4a002300 0x4>, - <0x4a00233c 0x4>; - reg-names = "control_dev_conf", "otghs_control"; - ti,type = <1>; + omap_control_usb2phy: control-phy@4a002300 { + compatible = "ti,control-phy-usb2"; + reg = <0x4a002300 0x4>; + reg-names = "power"; + }; + + omap_control_usbotg: control-phy@4a00233c { + compatible = "ti,control-phy-otghs"; + reg = <0x4a00233c 0x4>; + reg-names = "otghs_control"; }; usb_otg_hs: usb_otg_hs@4a0ab000 { @@ -658,10 +663,12 @@ interrupt-names = "mc", "dma"; ti,hwmods = "usb_otg_hs"; usb-phy = <&usb2_phy>; + phys = <&usb2_phy>; + phy-names = "usb2-phy"; multipoint = <1>; num-eps = <16>; ram-bits = <12>; - ti,has-mailbox; + ctrl-module = <&omap_control_usbotg>; }; }; }; diff --git a/arch/arm/boot/dts/omap5.dtsi b/arch/arm/boot/dts/omap5.dtsi index 7cdea1bfea09..c0ec6dce30fe 100644 --- a/arch/arm/boot/dts/omap5.dtsi +++ b/arch/arm/boot/dts/omap5.dtsi @@ -626,12 +626,16 @@ hw-caps-temp-alert; }; - omap_control_usb: omap-control-usb@4a002300 { - compatible = "ti,omap-control-usb"; - reg = <0x4a002300 0x4>, - <0x4a002370 0x4>; - reg-names = "control_dev_conf", "phy_power_usb"; - ti,type = <2>; + omap_control_usb2phy: control-phy@4a002300 { + compatible = "ti,control-phy-usb2"; + reg = <0x4a002300 0x4>; + reg-names = "power"; + }; + + omap_control_usb3phy: control-phy@4a002370 { + compatible = "ti,control-phy-pipe3"; + reg = <0x4a002370 0x4>; + reg-names = "power"; }; omap_dwc3@4a020000 { @@ -662,7 +666,7 @@ usb2_phy: usb2phy@4a084000 { compatible = "ti,omap-usb2"; reg = <0x4a084000 0x7c>; - ctrl-module = <&omap_control_usb>; + ctrl-module = <&omap_control_usb2phy>; }; usb3_phy: usb3phy@4a084400 { @@ -671,7 +675,7 @@ <0x4a084800 0x64>, <0x4a084c00 0x40>; reg-names = "phy_rx", "phy_tx", "pll_ctrl"; - ctrl-module = <&omap_control_usb>; + ctrl-module = <&omap_control_usb3phy>; }; }; diff --git a/arch/arm/boot/dts/twl4030.dtsi b/arch/arm/boot/dts/twl4030.dtsi index ae6a17aed9ee..5aba238d1f1e 100644 --- a/arch/arm/boot/dts/twl4030.dtsi +++ b/arch/arm/boot/dts/twl4030.dtsi @@ -86,6 +86,7 @@ usb1v8-supply = <&vusb1v8>; usb3v1-supply = <&vusb3v1>; usb_mode = <1>; + #phy-cells = <0>; }; twl_pwm: pwm { diff --git a/arch/arm/mach-omap2/twl-common.c b/arch/arm/mach-omap2/twl-common.c index c05898fbd634..b0d54dae1bcb 100644 --- a/arch/arm/mach-omap2/twl-common.c +++ b/arch/arm/mach-omap2/twl-common.c @@ -24,6 +24,7 @@ #include <linux/i2c/twl.h> #include <linux/gpio.h> #include <linux/string.h> +#include <linux/phy/phy.h> #include <linux/regulator/machine.h> #include <linux/regulator/fixed.h> @@ -90,8 +91,18 @@ void __init omap_pmic_late_init(void) } #if defined(CONFIG_ARCH_OMAP3) +struct phy_consumer consumers[] = { + PHY_CONSUMER("musb-hdrc.0", "usb"), +}; + +struct phy_init_data init_data = { + .consumers = consumers, + .num_consumers = ARRAY_SIZE(consumers), +}; + static struct twl4030_usb_data omap3_usb_pdata = { .usb_mode = T2_USB_MODE_ULPI, + .init_data = &init_data, }; static int omap3_batt_table[] = { 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/phy/Kconfig b/drivers/phy/Kconfig new file mode 100644 index 000000000000..ac239aca77ec --- /dev/null +++ b/drivers/phy/Kconfig @@ -0,0 +1,41 @@ +# +# 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 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. + +endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile new file mode 100644 index 000000000000..0dd8a9834548 --- /dev/null +++ b/drivers/phy/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the phy drivers. +# + +obj-$(CONFIG_GENERIC_PHY) += phy-core.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/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..2bb7d18ef2d5 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); @@ -717,6 +713,11 @@ __acquires(ci->lock) { int retval; + if (ci->gadget.speed != USB_SPEED_UNKNOWN) { + if (ci->driver) + ci->driver->disconnect(&ci->gadget); + } + spin_unlock(&ci->lock); retval = _gadget_stop_activity(&ci->gadget); if (retval) @@ -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, @@ -1786,34 +1789,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 +1801,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..bd429eaf6ea3 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)) { 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..460bb59cb655 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 @@ -93,7 +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))]; + unsigned long busmap[USB_MAXBUS / (8*sizeof (unsigned long))]; }; static struct usb_busmap busmap; @@ -171,7 +171,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 +194,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 +219,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 +228,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 +256,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 +275,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 +303,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 +428,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 +464,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 +526,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 +546,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 +609,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 +625,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 +683,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 +877,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); @@ -1033,6 +1033,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 +1121,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 @@ -1703,7 +1704,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 +1815,7 @@ rescan: case USB_ENDPOINT_XFER_INT: s = "-intr"; break; default: - s = "-iso"; break; + s = "-iso"; break; }; s; })); @@ -2073,8 +2076,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 +2091,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 +2251,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 @@ -2600,7 +2606,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 +2749,7 @@ err_allocate_root_hub: err_register_bus: hcd_buffer_destroy(hcd); return retval; -} +} EXPORT_SYMBOL_GPL(usb_add_hcd); /** @@ -2818,7 +2824,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 +2846,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..2159f820b159 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -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); @@ -2018,8 +2023,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. diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 5b44cd47da5b..d120a0887202 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. @@ -155,6 +156,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) { @@ -181,6 +197,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) { @@ -206,6 +234,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..59cb5f99467a 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); @@ -764,6 +772,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 +793,7 @@ read_descriptors(struct file *filp, struct kobject *kobj, off -= srclen; } } + usb_unlock_device(udev); return count - nleft; } @@ -870,9 +880,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 +896,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 +917,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; + int s; - intf = to_usb_interface(dev); - udev = interface_to_usbdev(intf); - - 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/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/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/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..74bb8df82876 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -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/net2280.c b/drivers/usb/gadget/net2280.c index 0781bff70015..9fbc8fe5ecc0 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -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/host/Kconfig b/drivers/usb/host/Kconfig index b3f20d7f15de..70cb1a94beff 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 @@ -224,7 +224,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 +367,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 +502,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..0b9fdeecf683 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_USB_EHCI_S5P) += ehci-s5p.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-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..3e3ca839e6ce 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1158,7 +1158,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 @@ -1238,11 +1238,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-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..e321804c3475 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -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); diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 85dd24ed97a6..dcbaad94d607 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1370,10 +1370,12 @@ 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); period = urb->interval; span = sched->span; @@ -1384,69 +1386,31 @@ iso_stream_schedule ( now = ehci_read_frame_index(ehci) & (mod - 1); - /* 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))) { - - /* 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); - - /* USB_ISO_ASAP: Round up to the first available slot */ - if (urb->transfer_flags & URB_ISO_ASAP) - start += (next - start + period - 1) & -period; - - /* - * Not ASAP: Use the next slot in the stream, - * no matter what. - */ - else if (start + span - period < now2) { - ehci_dbg(ehci, "iso underrun %p (%u+%u < %u)\n", - urb, start + base, - span - period, now2 + base); - } - } + /* 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 */ - start += base; - } + /* + * 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); - /* 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 + /* + * 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. */ - else { + if (unlikely(empty && !hcd_periodic_completion_in_progress( + ehci_to_hcd(ehci), urb->ep))) { int done = 0; - base = now & ~0x07; - start = base + SCHEDULING_DELAY; + start = (now & ~0x07) + SCHEDULING_DELAY; /* find a uframe slot with enough bandwidth. * Early uframes are more precious because full-speed @@ -1477,27 +1441,96 @@ iso_stream_schedule ( status = -ENOSPC; goto fail; } + + start = (start - base) & (mod - 1); + goto use_start; } + /* + * 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. + */ + start = (stream->next_uframe - base) & (mod - 1); + 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; + + /* 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 +1643,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)); @@ -1804,10 +1838,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: @@ -2008,7 +2046,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++) { @@ -2178,10 +2216,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-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..2d401927e143 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -434,6 +434,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]; }; diff --git a/drivers/usb/host/hwa-hc.c b/drivers/usb/host/hwa-hc.c index 5b86ffb88f1c..e58b92491eb1 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, diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index caa3764a3407..f2d840370a87 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; @@ -691,8 +636,14 @@ ohci_hcd_at91_drv_suspend(struct platform_device *pdev, pm_message_t mesg) { 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 (device_may_wakeup(&pdev->dev)) + ret = ohci_suspend(hcd, do_wakeup); + if (ret) + return ret; + + if (do_wakeup) enable_irq_wake(hcd->irq); /* @@ -703,13 +654,17 @@ 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(); } - return 0; + return ret; } static int ohci_hcd_at91_drv_resume(struct platform_device *pdev) @@ -730,8 +685,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 +697,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-da8xx.c b/drivers/usb/host/ohci-da8xx.c index 9be59f11e051..f8238a41c98c 100644 --- a/drivers/usb/host/ohci-da8xx.c +++ b/drivers/usb/host/ohci-da8xx.c @@ -406,19 +406,26 @@ static int ohci_hcd_da8xx_drv_remove(struct platform_device *dev) } #ifdef CONFIG_PM -static int ohci_da8xx_suspend(struct platform_device *dev, pm_message_t message) +static int ohci_da8xx_suspend(struct platform_device *pdev, + pm_message_t message) { - struct usb_hcd *hcd = 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; + ohci_da8xx_clock(0); hcd->state = HC_STATE_SUSPENDED; - dev->dev.power.power_state = PMSG_SUSPEND; - return 0; + + return ret; } static int ohci_da8xx_resume(struct platform_device *dev) diff --git a/drivers/usb/host/ohci-ep93xx.c b/drivers/usb/host/ohci-ep93xx.c index 84a20d5223b9..08409bfa1cde 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; - - if ((ret = ohci_init(ohci)) < 0) - return ret; +#define DRIVER_DESC "OHCI EP93xx driver" - 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 const char hcd_name[] = "ohci-ep93xx"; - return 0; -} +static struct hc_driver __read_mostly ohci_ep93xx_hc_driver; -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; @@ -141,13 +112,20 @@ static int ohci_hcd_ep93xx_drv_suspend(struct platform_device *pdev, pm_message_ { 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; - clk_disable(usb_host_clock); - return 0; + ret = ohci_suspend(hcd, do_wakeup); + if (ret) + return ret; + + ep93xx_stop_hc(&pdev->dev); + + return ret; } static int ohci_hcd_ep93xx_drv_resume(struct platform_device *pdev) @@ -166,7 +144,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 +158,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..f5f372e02a99 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -12,24 +12,39 @@ */ #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); @@ -37,9 +52,10 @@ static void exynos_ohci_phy_enable(struct exynos_ohci_hcd *exynos_ohci) 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); @@ -47,63 +63,11 @@ static void exynos_ohci_phy_disable(struct exynos_ohci_hcd *exynos_ohci) 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 +83,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")) @@ -143,17 +111,6 @@ static int exynos_ohci_probe(struct platform_device *pdev) } 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 +147,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 +171,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 +190,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,36 +199,26 @@ 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); + bool do_wakeup = device_may_wakeup(dev); unsigned long flags; int rc = 0; - /* - * Root hub was already suspended. Disable irq emission and - * mark HW unaccessible, bail out if RH has been resumed. Use - * the spinlock to properly synchronize with possible pending - * RH suspend or resume activity. - */ - spin_lock_irqsave(&ohci->lock, flags); - if (ohci->rh_state != OHCI_RH_SUSPENDED && - ohci->rh_state != OHCI_RH_HALTED) { - rc = -EINVAL; - goto fail; - } - - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + rc = ohci_suspend(hcd, do_wakeup); + if (rc) + return rc; + spin_lock_irqsave(&ohci->lock, 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); -fail: spin_unlock_irqrestore(&ohci->lock, flags); return rc; @@ -286,16 +226,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 +246,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 +275,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..310bcfe4ebc4 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1036,6 +1036,7 @@ int ohci_suspend(struct usb_hcd *hcd, bool do_wakeup) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); unsigned long flags; + int rc = 0; /* Disable irq emission and mark HW unaccessible. Use * the spinlock to properly synchronize with possible pending @@ -1048,7 +1049,13 @@ int ohci_suspend(struct usb_hcd *hcd, bool do_wakeup) clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); spin_unlock_irqrestore (&ohci->lock, flags); - return 0; + synchronize_irq(hcd->irq); + + if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) { + ohci_resume(hcd, false); + rc = -EBUSY; + } + return rc; } EXPORT_SYMBOL_GPL(ohci_suspend); @@ -1161,10 +1168,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 +1188,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 +1198,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 +1260,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 +1284,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 +1313,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 +1334,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 +1349,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..b5bf9b7a54fc 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) { @@ -475,28 +426,15 @@ static int ohci_hcd_s3c2410_drv_remove(struct platform_device *pdev) static int ohci_hcd_s3c2410_drv_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - struct ohci_hcd *ohci = hcd_to_ohci(hcd); struct platform_device *pdev = to_platform_device(dev); - unsigned long flags; + bool do_wakeup = device_may_wakeup(dev); int rc = 0; - /* - * Root hub was already suspended. Disable irq emission and - * mark HW unaccessible, bail out if RH has been resumed. Use - * the spinlock to properly synchronize with possible pending - * RH suspend or resume activity. - */ - spin_lock_irqsave(&ohci->lock, flags); - if (ohci->rh_state != OHCI_RH_SUSPENDED) { - rc = -EINVAL; - goto bail; - } - - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + rc = ohci_suspend(hcd, do_wakeup); + if (rc) + return rc; s3c2410_stop_hc(pdev); -bail: - spin_unlock_irqrestore(&ohci->lock, flags); return rc; } @@ -533,4 +471,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..41148f8895a4 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) +#define to_spear_ohci(hcd) (struct spear_ohci *)(hcd_to_ohci(hcd)->priv) -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, - - /* 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,43 +119,50 @@ 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; } #if defined(CONFIG_PM) -static int spear_ohci_hcd_drv_suspend(struct platform_device *dev, +static int spear_ohci_hcd_drv_suspend(struct platform_device *pdev, pm_message_t message) { - struct usb_hcd *hcd = platform_get_drvdata(dev); + struct usb_hcd *hcd = platform_get_drvdata(pdev); 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); + bool do_wakeup = device_may_wakeup(&pdev->dev); + int ret; if (time_before(jiffies, ohci->next_statechange)) msleep(5); ohci->next_statechange = jiffies; - spear_stop_ohci(ohci_p); - return 0; + ret = ohci_suspend(hcd, do_wakeup); + if (ret) + return ret; + + clk_disable_unprepare(sohci_p->clk); + + return ret; } 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 +188,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 2c76ef1320ea..8c6c6d9036d1 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/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-pci.c b/drivers/usb/host/uhci-pci.c index 0f228c46eeda..d89c78dc70c0 100644 --- a/drivers/usb/host/uhci-pci.c +++ b/drivers/usb/host/uhci-pci.c @@ -299,3 +299,5 @@ static struct pci_driver uhci_pci_driver = { }, #endif }; + +MODULE_SOFTDEP("pre: ehci_pci"); 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.h b/drivers/usb/musb/musb_core.h index 65f3917b4fc5..d408a9962f66 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/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/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..3a5dac879094 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -570,6 +570,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)) 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..b44aca3f25dd 100644 --- a/drivers/usb/wusbcore/wa-hc.h +++ b/drivers/usb/wusbcore/wa-hc.h @@ -122,6 +122,11 @@ struct wa_rpipe { }; +enum wa_dti_state { + WA_DTI_TRANSFER_RESULT_PENDING, + WA_DTI_ISOC_PACKET_STATUS_PENDING +}; + /** * Instance of a HWA Host Controller * @@ -181,11 +186,20 @@ struct wahc { spinlock_t rpipe_bm_lock; /* protect rpipe_bm */ 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; s32 status; /* For reading status */ @@ -247,6 +261,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); diff --git a/drivers/usb/wusbcore/wa-rpipe.c b/drivers/usb/wusbcore/wa-rpipe.c index fd4f1ce6256a..554b16bd22f1 100644 --- a/drivers/usb/wusbcore/wa-rpipe.c +++ b/drivers/usb/wusbcore/wa-rpipe.c @@ -361,8 +361,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); diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index 6ad02f57c366..e097da30a26b 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -114,24 +114,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 +169,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 +178,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); } @@ -232,6 +240,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 +286,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 +327,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 +370,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 +385,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 +395,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 +404,7 @@ error_submit: xfer, result); kfree(b); error_kmalloc: - return; + return result; } @@ -422,39 +432,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 %d 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 +495,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 +508,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 +536,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 +556,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 +605,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 +684,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 +704,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 +721,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 +746,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 +829,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 +912,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 +920,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 +938,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 +954,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 +1041,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; @@ -888,17 +1102,28 @@ static int __wa_seg_submit(struct wa_rpipe *rpipe, struct wa_xfer *xfer, struct wa_seg *seg) { int result; - result = usb_submit_urb(&seg->urb, GFP_ATOMIC); + /* 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 +1132,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; @@ -937,8 +1164,9 @@ static void wa_xfer_delayed_run(struct wa_rpipe *rpipe) 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); + 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); @@ -982,11 +1210,10 @@ static int __wa_xfer_submit(struct wa_xfer *xfer) 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); seg->status = WA_SEG_DELAYED; list_add_tail(&seg->list_node, &rpipe->seg_list); } else { @@ -1025,7 +1252,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 +1263,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 +1286,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 +1319,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 +1327,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 +1362,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 +1469,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 +1516,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 +1530,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 +1546,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 +1559,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 +1673,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 +1730,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 +1741,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 +1758,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 +1847,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 +1866,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 +1899,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 %zu 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 +2078,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 +2173,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 +2209,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 +2232,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/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index 81cbbdb96aae..673a3ce67f31 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -26,6 +26,7 @@ #define __TWL_H_ #include <linux/types.h> +#include <linux/phy/phy.h> #include <linux/input/matrix_keypad.h> /* @@ -615,6 +616,7 @@ enum twl4030_usb_mode { struct twl4030_usb_data { enum twl4030_usb_mode usb_mode; unsigned long features; + struct phy_init_data *init_data; int (*phy_init)(struct device *dev); int (*phy_exit)(struct device *dev); diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h new file mode 100644 index 000000000000..6d722695e027 --- /dev/null +++ b/include/linux/phy/phy.h @@ -0,0 +1,270 @@ +/* + * phy.h -- generic phy header file + * + * 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. + */ + +#ifndef __DRIVERS_PHY_H +#define __DRIVERS_PHY_H + +#include <linux/err.h> +#include <linux/of.h> +#include <linux/device.h> +#include <linux/pm_runtime.h> + +struct phy; + +/** + * struct phy_ops - set of function pointers for performing phy operations + * @init: operation to be performed for initializing phy + * @exit: operation to be performed while exiting + * @power_on: powering on the phy + * @power_off: powering off the phy + * @owner: the module owner containing the ops + */ +struct phy_ops { + int (*init)(struct phy *phy); + int (*exit)(struct phy *phy); + int (*power_on)(struct phy *phy); + int (*power_off)(struct phy *phy); + struct module *owner; +}; + +/** + * struct phy - represents the phy device + * @dev: phy device + * @id: id of the phy device + * @ops: function pointers for performing phy operations + * @init_data: list of PHY consumers (non-dt only) + * @mutex: mutex to protect phy_ops + * @init_count: used to protect when the PHY is used by multiple consumers + * @power_count: used to protect when the PHY is used by multiple consumers + */ +struct phy { + struct device dev; + int id; + const struct phy_ops *ops; + struct phy_init_data *init_data; + struct mutex mutex; + int init_count; + int power_count; +}; + +/** + * struct phy_provider - represents the phy provider + * @dev: phy provider device + * @owner: the module owner having of_xlate + * @of_xlate: function pointer to obtain phy instance from phy pointer + * @list: to maintain a linked list of PHY providers + */ +struct phy_provider { + struct device *dev; + struct module *owner; + struct list_head list; + struct phy * (*of_xlate)(struct device *dev, + struct of_phandle_args *args); +}; + +/** + * struct phy_consumer - represents the phy consumer + * @dev_name: the device name of the controller that will use this PHY device + * @port: name given to the consumer port + */ +struct phy_consumer { + const char *dev_name; + const char *port; +}; + +/** + * struct phy_init_data - contains the list of PHY consumers + * @num_consumers: number of consumers for this PHY device + * @consumers: list of PHY consumers + */ +struct phy_init_data { + unsigned int num_consumers; + struct phy_consumer *consumers; +}; + +#define PHY_CONSUMER(_dev_name, _port) \ +{ \ + .dev_name = _dev_name, \ + .port = _port, \ +} + +#define to_phy(dev) (container_of((dev), struct phy, dev)) + +#define of_phy_provider_register(dev, xlate) \ + __of_phy_provider_register((dev), THIS_MODULE, (xlate)) + +#define devm_of_phy_provider_register(dev, xlate) \ + __devm_of_phy_provider_register((dev), THIS_MODULE, (xlate)) + +static inline void phy_set_drvdata(struct phy *phy, void *data) +{ + dev_set_drvdata(&phy->dev, data); +} + +static inline void *phy_get_drvdata(struct phy *phy) +{ + return dev_get_drvdata(&phy->dev); +} + +#if IS_ENABLED(CONFIG_GENERIC_PHY) +int phy_pm_runtime_get(struct phy *phy); +int phy_pm_runtime_get_sync(struct phy *phy); +int phy_pm_runtime_put(struct phy *phy); +int phy_pm_runtime_put_sync(struct phy *phy); +void phy_pm_runtime_allow(struct phy *phy); +void phy_pm_runtime_forbid(struct phy *phy); +int phy_init(struct phy *phy); +int phy_exit(struct phy *phy); +int phy_power_on(struct phy *phy); +int phy_power_off(struct phy *phy); +struct phy *phy_get(struct device *dev, const char *string); +struct phy *devm_phy_get(struct device *dev, const char *string); +void phy_put(struct phy *phy); +void devm_phy_put(struct device *dev, struct phy *phy); +struct phy *of_phy_simple_xlate(struct device *dev, + struct of_phandle_args *args); +struct phy *phy_create(struct device *dev, const struct phy_ops *ops, + struct phy_init_data *init_data); +struct phy *devm_phy_create(struct device *dev, + const struct phy_ops *ops, struct phy_init_data *init_data); +void phy_destroy(struct phy *phy); +void devm_phy_destroy(struct device *dev, struct phy *phy); +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 *__devm_of_phy_provider_register(struct device *dev, + struct module *owner, struct phy * (*of_xlate)(struct device *dev, + struct of_phandle_args *args)); +void of_phy_provider_unregister(struct phy_provider *phy_provider); +void devm_of_phy_provider_unregister(struct device *dev, + struct phy_provider *phy_provider); +#else +static inline int phy_pm_runtime_get(struct phy *phy) +{ + return -ENOSYS; +} + +static inline int phy_pm_runtime_get_sync(struct phy *phy) +{ + return -ENOSYS; +} + +static inline int phy_pm_runtime_put(struct phy *phy) +{ + return -ENOSYS; +} + +static inline int phy_pm_runtime_put_sync(struct phy *phy) +{ + return -ENOSYS; +} + +static inline void phy_pm_runtime_allow(struct phy *phy) +{ + return; +} + +static inline void phy_pm_runtime_forbid(struct phy *phy) +{ + return; +} + +static inline int phy_init(struct phy *phy) +{ + return -ENOSYS; +} + +static inline int phy_exit(struct phy *phy) +{ + return -ENOSYS; +} + +static inline int phy_power_on(struct phy *phy) +{ + return -ENOSYS; +} + +static inline int phy_power_off(struct phy *phy) +{ + return -ENOSYS; +} + +static inline struct phy *phy_get(struct device *dev, const char *string) +{ + return ERR_PTR(-ENOSYS); +} + +static inline struct phy *devm_phy_get(struct device *dev, const char *string) +{ + return ERR_PTR(-ENOSYS); +} + +static inline void phy_put(struct phy *phy) +{ +} + +static inline void devm_phy_put(struct device *dev, struct phy *phy) +{ +} + +static inline struct phy *of_phy_simple_xlate(struct device *dev, + struct of_phandle_args *args) +{ + return ERR_PTR(-ENOSYS); +} + +static inline struct phy *phy_create(struct device *dev, + const struct phy_ops *ops, struct phy_init_data *init_data) +{ + return ERR_PTR(-ENOSYS); +} + +static inline struct phy *devm_phy_create(struct device *dev, + const struct phy_ops *ops, struct phy_init_data *init_data) +{ + return ERR_PTR(-ENOSYS); +} + +static inline void phy_destroy(struct phy *phy) +{ +} + +static inline void devm_phy_destroy(struct device *dev, struct phy *phy) +{ +} + +static inline 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)) +{ + return ERR_PTR(-ENOSYS); +} + +static inline 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)) +{ + return ERR_PTR(-ENOSYS); +} + +static inline void of_phy_provider_unregister(struct phy_provider *phy_provider) +{ +} + +static inline void devm_of_phy_provider_unregister(struct device *dev, + struct phy_provider *phy_provider) +{ +} +#endif + +#endif /* __DRIVERS_PHY_H */ diff --git a/include/linux/usb.h b/include/linux/usb.h index 001629cd1a97..f726c39097e0 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -702,7 +702,7 @@ extern int usb_alloc_streams(struct usb_interface *interface, unsigned int num_streams, gfp_t mem_flags); /* Reverts a group of bulk endpoints back to not using stream IDs. */ -extern void usb_free_streams(struct usb_interface *interface, +extern int usb_free_streams(struct usb_interface *interface, struct usb_host_endpoint **eps, unsigned int num_eps, gfp_t mem_flags); diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 75efc45eaa2f..fc64b6825f5e 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -73,6 +73,7 @@ struct giveback_urb_bh { spinlock_t lock; struct list_head head; struct tasklet_struct bh; + struct usb_host_endpoint *completing_ep; }; struct usb_hcd { @@ -140,6 +141,7 @@ struct usb_hcd { unsigned wireless:1; /* Wireless USB HCD */ unsigned authorized_default:1; unsigned has_tt:1; /* Integrated TT in root hub */ + unsigned amd_resume_bug:1; /* AMD remote wakeup quirk */ unsigned int irq; /* irq allocated */ void __iomem *regs; /* device memory/io */ @@ -378,6 +380,12 @@ static inline int hcd_giveback_urb_in_bh(struct usb_hcd *hcd) return hcd->driver->flags & HCD_BH; } +static inline bool hcd_periodic_completion_in_progress(struct usb_hcd *hcd, + struct usb_host_endpoint *ep) +{ + return hcd->high_prio_bh.completing_ep == ep; +} + extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb); extern int usb_hcd_check_unlink_urb(struct usb_hcd *hcd, struct urb *urb, int status); @@ -428,6 +436,8 @@ extern int usb_hcd_pci_probe(struct pci_dev *dev, extern void usb_hcd_pci_remove(struct pci_dev *dev); extern void usb_hcd_pci_shutdown(struct pci_dev *dev); +extern int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *dev); + #ifdef CONFIG_PM extern const struct dev_pm_ops usb_hcd_pci_pm_ops; #endif diff --git a/include/linux/usb/intel_mid_otg.h b/include/linux/usb/intel_mid_otg.h deleted file mode 100644 index 756cf5543ffd..000000000000 --- a/include/linux/usb/intel_mid_otg.h +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Intel MID (Langwell/Penwell) USB OTG Transceiver driver - * Copyright (C) 2008 - 2010, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#ifndef __INTEL_MID_OTG_H -#define __INTEL_MID_OTG_H - -#include <linux/pm.h> -#include <linux/usb/otg.h> -#include <linux/notifier.h> - -struct intel_mid_otg_xceiv; - -/* This is a common data structure for Intel MID platform to - * save values of the OTG state machine */ -struct otg_hsm { - /* Input */ - int a_bus_resume; - int a_bus_suspend; - int a_conn; - int a_sess_vld; - int a_srp_det; - int a_vbus_vld; - int b_bus_resume; - int b_bus_suspend; - int b_conn; - int b_se0_srp; - int b_ssend_srp; - int b_sess_end; - int b_sess_vld; - int id; -/* id values */ -#define ID_B 0x05 -#define ID_A 0x04 -#define ID_ACA_C 0x03 -#define ID_ACA_B 0x02 -#define ID_ACA_A 0x01 - int power_up; - int adp_change; - int test_device; - - /* Internal variables */ - int a_set_b_hnp_en; - int b_srp_done; - int b_hnp_enable; - int hnp_poll_enable; - - /* Timeout indicator for timers */ - int a_wait_vrise_tmout; - int a_wait_bcon_tmout; - int a_aidl_bdis_tmout; - int a_bidl_adis_tmout; - int a_bidl_adis_tmr; - int a_wait_vfall_tmout; - int b_ase0_brst_tmout; - int b_bus_suspend_tmout; - int b_srp_init_tmout; - int b_srp_fail_tmout; - int b_srp_fail_tmr; - int b_adp_sense_tmout; - - /* Informative variables */ - int a_bus_drop; - int a_bus_req; - int a_clr_err; - int b_bus_req; - int a_suspend_req; - int b_bus_suspend_vld; - - /* Output */ - int drv_vbus; - int loc_conn; - int loc_sof; - - /* Others */ - int vbus_srp_up; -}; - -/* must provide ULPI access function to read/write registers implemented in - * ULPI address space */ -struct iotg_ulpi_access_ops { - int (*read)(struct intel_mid_otg_xceiv *iotg, u8 reg, u8 *val); - int (*write)(struct intel_mid_otg_xceiv *iotg, u8 reg, u8 val); -}; - -#define OTG_A_DEVICE 0x0 -#define OTG_B_DEVICE 0x1 - -/* - * the Intel MID (Langwell/Penwell) otg transceiver driver needs to interact - * with device and host drivers to implement the USB OTG related feature. More - * function members are added based on usb_phy data structure for this - * purpose. - */ -struct intel_mid_otg_xceiv { - struct usb_phy otg; - struct otg_hsm hsm; - - /* base address */ - void __iomem *base; - - /* ops to access ulpi */ - struct iotg_ulpi_access_ops ulpi_ops; - - /* atomic notifier for interrupt context */ - struct atomic_notifier_head iotg_notifier; - - /* start/stop USB Host function */ - int (*start_host)(struct intel_mid_otg_xceiv *iotg); - int (*stop_host)(struct intel_mid_otg_xceiv *iotg); - - /* start/stop USB Peripheral function */ - int (*start_peripheral)(struct intel_mid_otg_xceiv *iotg); - int (*stop_peripheral)(struct intel_mid_otg_xceiv *iotg); - - /* start/stop ADP sense/probe function */ - int (*set_adp_probe)(struct intel_mid_otg_xceiv *iotg, - bool enabled, int dev); - int (*set_adp_sense)(struct intel_mid_otg_xceiv *iotg, - bool enabled); - -#ifdef CONFIG_PM - /* suspend/resume USB host function */ - int (*suspend_host)(struct intel_mid_otg_xceiv *iotg, - pm_message_t message); - int (*resume_host)(struct intel_mid_otg_xceiv *iotg); - - int (*suspend_peripheral)(struct intel_mid_otg_xceiv *iotg, - pm_message_t message); - int (*resume_peripheral)(struct intel_mid_otg_xceiv *iotg); -#endif - -}; -static inline -struct intel_mid_otg_xceiv *otg_to_mid_xceiv(struct usb_phy *otg) -{ - return container_of(otg, struct intel_mid_otg_xceiv, otg); -} - -#define MID_OTG_NOTIFY_CONNECT 0x0001 -#define MID_OTG_NOTIFY_DISCONN 0x0002 -#define MID_OTG_NOTIFY_HSUSPEND 0x0003 -#define MID_OTG_NOTIFY_HRESUME 0x0004 -#define MID_OTG_NOTIFY_CSUSPEND 0x0005 -#define MID_OTG_NOTIFY_CRESUME 0x0006 -#define MID_OTG_NOTIFY_HOSTADD 0x0007 -#define MID_OTG_NOTIFY_HOSTREMOVE 0x0008 -#define MID_OTG_NOTIFY_CLIENTADD 0x0009 -#define MID_OTG_NOTIFY_CLIENTREMOVE 0x000a - -static inline int -intel_mid_otg_register_notifier(struct intel_mid_otg_xceiv *iotg, - struct notifier_block *nb) -{ - return atomic_notifier_chain_register(&iotg->iotg_notifier, nb); -} - -static inline void -intel_mid_otg_unregister_notifier(struct intel_mid_otg_xceiv *iotg, - struct notifier_block *nb) -{ - atomic_notifier_chain_unregister(&iotg->iotg_notifier, nb); -} - -#endif /* __INTEL_MID_OTG_H */ diff --git a/include/linux/usb/musb.h b/include/linux/usb/musb.h index 053c26841cc3..eb505250940a 100644 --- a/include/linux/usb/musb.h +++ b/include/linux/usb/musb.h @@ -99,8 +99,6 @@ struct musb_hdrc_platform_data { /* MUSB_HOST, MUSB_PERIPHERAL, or MUSB_OTG */ u8 mode; - u8 has_mailbox:1; - /* for clk_get() */ const char *clock; diff --git a/include/linux/usb/omap_control_usb.h b/include/linux/usb/omap_control_usb.h index 27b5b8c931b0..596b01918813 100644 --- a/include/linux/usb/omap_control_usb.h +++ b/include/linux/usb/omap_control_usb.h @@ -19,20 +19,23 @@ #ifndef __OMAP_CONTROL_USB_H__ #define __OMAP_CONTROL_USB_H__ +enum omap_control_usb_type { + OMAP_CTRL_TYPE_OTGHS = 1, /* Mailbox OTGHS_CONTROL */ + OMAP_CTRL_TYPE_USB2, /* USB2_PHY, power down in CONTROL_DEV_CONF */ + OMAP_CTRL_TYPE_PIPE3, /* PIPE3 PHY, DPLL & seperate Rx/Tx power */ + OMAP_CTRL_TYPE_DRA7USB2, /* USB2 PHY, power and power_aux e.g. DRA7 */ +}; + struct omap_control_usb { struct device *dev; - u32 __iomem *dev_conf; u32 __iomem *otghs_control; - u32 __iomem *phy_power; + u32 __iomem *power; + u32 __iomem *power_aux; struct clk *sys_clk; - u32 type; -}; - -struct omap_control_usb_platform_data { - u8 type; + enum omap_control_usb_type type; }; enum omap_control_usb_mode { @@ -42,10 +45,6 @@ enum omap_control_usb_mode { USB_MODE_DISCONNECT, }; -/* To differentiate ctrl module IP having either mailbox or USB3 PHY power */ -#define OMAP_CTRL_DEV_TYPE1 0x1 -#define OMAP_CTRL_DEV_TYPE2 0x2 - #define OMAP_CTRL_DEV_PHY_PD BIT(0) #define OMAP_CTRL_DEV_AVALID BIT(0) @@ -63,26 +62,18 @@ enum omap_control_usb_mode { #define OMAP_CTRL_USB3_PHY_TX_RX_POWERON 0x3 #define OMAP_CTRL_USB3_PHY_TX_RX_POWEROFF 0x0 +#define OMAP_CTRL_USB2_PHY_PD BIT(28) + #if IS_ENABLED(CONFIG_OMAP_CONTROL_USB) -extern struct device *omap_get_control_dev(void); extern void omap_control_usb_phy_power(struct device *dev, int on); -extern void omap_control_usb3_phy_power(struct device *dev, bool on); extern void omap_control_usb_set_mode(struct device *dev, enum omap_control_usb_mode mode); #else -static inline struct device *omap_get_control_dev(void) -{ - return ERR_PTR(-ENODEV); -} static inline void omap_control_usb_phy_power(struct device *dev, int on) { } -static inline void omap_control_usb3_phy_power(struct device *dev, int on) -{ -} - static inline void omap_control_usb_set_mode(struct device *dev, enum omap_control_usb_mode mode) { diff --git a/include/linux/usb/wusb-wa.h b/include/linux/usb/wusb-wa.h index 4ff744e2b678..9ae7e299bf77 100644 --- a/include/linux/usb/wusb-wa.h +++ b/include/linux/usb/wusb-wa.h @@ -142,7 +142,7 @@ enum wa_notif_type { struct wa_notif_hdr { u8 bLength; u8 bNotifyType; /* enum wa_notif_type */ -} __attribute__((packed)); +} __packed; /** * HWA DN Received notification [(WUSB] section 8.5.4.2) @@ -158,7 +158,7 @@ struct hwa_notif_dn { u8 bSourceDeviceAddr; /* from errata 2005/07 */ u8 bmAttributes; struct wusb_dn_hdr dndata[]; -} __attribute__((packed)); +} __packed; /* [WUSB] section 8.3.3 */ enum wa_xfer_type { @@ -167,6 +167,8 @@ enum wa_xfer_type { WA_XFER_TYPE_ISO = 0x82, WA_XFER_RESULT = 0x83, WA_XFER_ABORT = 0x84, + WA_XFER_ISO_PACKET_INFO = 0xA0, + WA_XFER_ISO_PACKET_STATUS = 0xA1, }; /* [WUSB] section 8.3.3 */ @@ -177,28 +179,47 @@ struct wa_xfer_hdr { __le32 dwTransferID; /* Host-assigned ID */ __le32 dwTransferLength; /* Length of data to xfer */ u8 bTransferSegment; -} __attribute__((packed)); +} __packed; struct wa_xfer_ctl { struct wa_xfer_hdr hdr; u8 bmAttribute; __le16 wReserved; struct usb_ctrlrequest baSetupData; -} __attribute__((packed)); +} __packed; struct wa_xfer_bi { struct wa_xfer_hdr hdr; u8 bReserved; __le16 wReserved; -} __attribute__((packed)); +} __packed; +/* [WUSB] section 8.5.5 */ struct wa_xfer_hwaiso { struct wa_xfer_hdr hdr; u8 bReserved; __le16 wPresentationTime; __le32 dwNumOfPackets; - /* FIXME: u8 pktdata[]? */ -} __attribute__((packed)); +} __packed; + +struct wa_xfer_packet_info_hwaiso { + __le16 wLength; + u8 bPacketType; + u8 bReserved; + __le16 PacketLength[0]; +} __packed; + +struct wa_xfer_packet_status_len_hwaiso { + __le16 PacketLength; + __le16 PacketStatus; +} __packed; + +struct wa_xfer_packet_status_hwaiso { + __le16 wLength; + u8 bPacketType; + u8 bReserved; + struct wa_xfer_packet_status_len_hwaiso PacketStatus[0]; +} __packed; /* [WUSB] section 8.3.3.5 */ struct wa_xfer_abort { @@ -206,7 +227,7 @@ struct wa_xfer_abort { u8 bRequestType; __le16 wRPipe; /* RPipe index */ __le32 dwTransferID; /* Host-assigned ID */ -} __attribute__((packed)); +} __packed; /** * WA Transfer Complete notification ([WUSB] section 8.3.3.3) @@ -216,7 +237,7 @@ struct wa_notif_xfer { struct wa_notif_hdr hdr; u8 bEndpoint; u8 Reserved; -} __attribute__((packed)); +} __packed; /** Transfer result basic codes [WUSB] table 8-15 */ enum { @@ -243,7 +264,7 @@ struct wa_xfer_result { u8 bTransferSegment; u8 bTransferStatus; __le32 dwNumOfPackets; -} __attribute__((packed)); +} __packed; /** * Wire Adapter Class Descriptor ([WUSB] section 8.5.2.7). @@ -267,7 +288,7 @@ struct usb_wa_descriptor { u8 bPwrOn2PwrGood; u8 bNumMMCIEs; u8 DeviceRemovable; /* FIXME: in DWA this is up to 16 bytes */ -} __attribute__((packed)); +} __packed; /** * HWA Device Information Buffer (WUSB1.0[T8.54]) @@ -277,6 +298,6 @@ struct hwa_dev_info { u8 bDeviceAddress; __le16 wPHYRates; u8 bmDeviceAttribute; -} __attribute__((packed)); +} __packed; #endif /* #ifndef __LINUX_USB_WUSB_WA_H */ |